Repository navigation
Conversation
The containerd Garden backend only builds on Linux. Before it can build for Windows, the parts that only make sense on Linux need files of their own. Move the default PATH handling, the stdin CloseIO handling, the runc process listing used by the killer and the /etc/passwd based rootfs manager into _linux.go files. Move PrivilegedMode and rootfsDir out of the Linux-only spec.go into privileged_mode.go and rootfs.go. The backend now calls defaultNetwork, defaultSeccompProfiles and defaultTaskOpts instead of naming CNI, the seccomp profiles and WithNoNewKeyring itself. loadResources moves from worker_linux.go to worker.go. Linux behaviour does not change. concourse#4365 Signed-off-by: Kurt McAlpine <kurt@linux.com>
Windows workers only have the houdini runtime, which runs every step directly on the host. Add containerd as an opt-in runtime for Windows workers with `--runtime containerd`. houdini stays the default, and the flag description marks the Windows containerd runtime as experimental. The worker starts containerd.exe on a named pipe and runs the existing Garden backend against it. The shared runtime files now build for linux || windows, with Windows versions of the Linux-only pieces: - spec_windows.go turns a Garden container spec into a Windows OCI spec. It mounts init.exe (cmd/init/windows) into the container as the init process, converts the POSIX paths the web node sends (/tmp/build/...) to C:\ paths, and maps CPU shares and memory limits to Windows resources. - prepareRootfs converts the streamed rootfs volume into a Windows base layer with hcsshim and creates the scratch layer that the runhcs shim needs. Flattened multi-layer images carry registry deltas and machine state from the containers that built them. It merges the last layer's delta hives into the base hives and removes that state before it processes the layer. - HCNNetwork gives each container a network namespace and an endpoint on a NAT HNS network, and creates the network when it is missing. `--containerd-network-name`, `--containerd-network-pool` and `--containerd-network-gateway` configure it. - The killer reads the runhcs process details, and stdin stays open after start because the runhcs shim closes it as soon as CloseIO is called. - rootfsDir accepts raw:// URIs whose Windows path the web node percent-encoded. - CmdRunner kills containerd when the shutdown signal cannot be delivered, which is always the case on Windows. Windows workers also register the resource types in `--resource-types`, as Linux workers do. concourse#4365 Signed-off-by: Kurt McAlpine <kurt@linux.com>
A Windows image built on a case-sensitive filesystem can hold two paths that differ only in case. Extracted onto a case-insensitive filesystem, the second entry overwrites the first, and that fails when the first entry is read-only. When creating a regular file fails with a permission error, make the existing file writable and create it again. Signed-off-by: Kurt McAlpine <kurt@linux.com>
A streamed rootfs volume loses NTFS security descriptors, reparse point metadata and the per-layer registry state. Servercore images need these to boot. A natively pulled servercore image boots, and the same image streamed as a volume fails with driver initialization errors. Tasks on Windows workers can now set `rootfs_uri: oci://<image-ref>`. The Windows spec records the reference in an annotation, and the backend creates the container with Client.NewContainerWithImage. That pulls the image through containerd and lets the Windows snapshotter manage the layers, so pulled images are cached across containers. Container.Delete removes the container's snapshot. concourse#4365 Signed-off-by: Kurt McAlpine <kurt@linux.com>
The Windows containerd runtime had two ways to get a container's rootfs. A streamed rootfs volume was flattened into a single base layer. That drops the NTFS security descriptors and file attributes that Windows layers keep in their tar headers, and all but the last layer's registry hive deltas, so servercore images failed to boot. An oci:// rootfs made containerd pull the image itself, which bypasses resources and their credentials, versions and caching. The registry-image resource now writes the rootfs of a Windows image as an OCI image layout. The Windows runtime copies the layout's blobs into containerd's content store under a lease, unpacks them with the windows snapshotter, and creates the container on a snapshot of the image. A Windows image reaches a task the way any image does: through a get step or a worker's resource types, streamed between workers as a volume. Unpacking first checks that the snapshotter supports the image's platform, so an image for another Windows build fails before any layer is extracted. Layers stay unpacked while a container uses them, so containers that share an image share its layers. Deleting a container removes its snapshot. When creating the container fails, the runtime deletes the network namespace it attached. This removes the oci:// rootfs scheme, the rootfs flattening and the registry hive merging that flattening needed. concourse#4365 Signed-off-by: Kurt McAlpine <kurt@linux.com>
containerd names a process's stdio pipes on Windows after the process
ID alone, as \\.\pipe\ctr-<id>-stdin and so on. Every task step runs
its process with the ID "task", and pipe names are global to the host.
A task that started on a worker while another task was running failed
with:
task exec: failed to create stdin pipe \\.\pipe\ctr-task-stdin:
open \\.\pipe\ctr-task-stdin: Access is denied.
The Windows runtime now names the pipes after the container handle and
the process ID. The ID that containerd knows the process by is
unchanged.
concourse#4365
…ntime A Windows worker ran every container with process isolation, which shares the host's kernel, so it could only run images built for the host's Windows version. `--containerd-isolation=hyperv` boots each container in its own utility VM, which runs the image's kernel. Unpacking skips the snapshotter platform check for these containers, because that check requires the image to match the host's Windows build. A Server 2022 worker ran a Server 2019 nanoserver image this way. With process isolation the same image failed the platform check. `--containerd-hyperv-cpus` and `--containerd-hyperv-memory-mb` size the utility VM. Unset, the runtime's defaults of 2 processors and 1024 MB apply. A container's memory limit sizes its utility VM in place of the memory flag. The host commits a utility VM's whole memory when the VM starts, so the VM size times the number of containers running at once has to fit the host's commit limit. Past the limit a container fails to create. The worker needs the Hyper-V role. Without it the worker starts, and each container fails to create. concourse#4365
The Windows runtime unpacked an image under a lease and never stored it. Once the last container on the image was deleted, nothing referred to the image's content or its unpacked layers, and containerd's garbage collector removed them. The next build copied and unpacked the whole image again, inside the worker's create lock. The runtime now stores the image in containerd's image store, named by its digest. Importing an image layout labels each blob with the blobs it refers to, which garbage collection needs in order to keep the content of a stored image. A later container on the same image skips the copy and the unpack. Nothing removes a stored image. The worker's disk has to hold every image the worker has run, and replacing the worker reclaims it. concourse#4365
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes proposed by this PR
Stacked on #9766. Relates to #4365. Only the last three commits are new, and this compare view shows them without #9766.
This follows up two review comments by @alicul on #9766, the question about Hyper-V isolation and the request to keep unpacked images. Testing them also turned up a bug in #9766.
task, and pipe names are global to a Windows host. On worker: add an experimental containerd runtime for Windows workers #9766 a task that starts while another task is running on the same worker fails withfailed to create stdin pipe \\.\pipe\ctr-task-stdin: Access is denied. The pipes are now named after the container handle and the process ID. This commit stands alone and can move into worker: add an experimental containerd runtime for Windows workers #9766.--containerd-isolation=process|hypervdefaults toprocess. Withhyperv, each container boots in its own utility VM that runs the image's kernel, and unpacking skips the snapshotter platform check, so a Server 2022 worker runs anltsc2019image.--containerd-hyperv-cpusand--containerd-hyperv-memory-mbsize the utility VM. Unset, the runtime's defaults of 2 processors and 1024 MB apply. A container's memory limit sizes its utility VM in place of the memory flag. The worker needs the Hyper-V role.Notes to reviewer
How I tested it, on one web node with a Linux worker and three Windows Server 2022 workers (build 20348, m8i.xlarge with 4 processors and 16 GB, gp3 disks, Defender excluding the work directory) running containerd 2.3.7. Two of the Windows workers have the Hyper-V role. On EC2 that runs through nested virtualization, so every Hyper-V number below includes that cost.
Hyper-V against process isolation
Each mode ran the same task 5 or 6 times on each of the two Hyper-V hosts, 32 builds in all with no failures. A ratio compares a mode with process isolation on the same host, and a range spans the two hosts. Container start comes from 64 builds of a one-line task. Memory was read with four idle containers on one host, or three at the 8192 MB size.
processhyperv, default sizehyperv, 4 processors and 8192 MBhyperv.The paging file is too small for this operation to complete, and the build showed onlyfailed to create shim task: ttrpc: closed.container_limits: {memory: 256MB}booted a 254 MiB utility VM with 78 MiB free for the task.hcs::GetServiceProperties: Catastrophic failure.ltsc2019andltsc2025nanoserver images ran on a Server 2022 worker underhyperv.processrejected both withsnapshotter windows does not support platform. Microsoft's version compatibility page documents Hyper-V isolation for images older than the host, so I would not rely on theltsc2025result.Stored images
processand 14 seconds underhyperv. Without this commit it took 19 to 21 and 27 to 29 seconds. That is 5 builds per host on each side.processand 9 to 11 seconds underhyperv.nanoserver:ltsc2022. Before this commit it kept nothing.processstill rejects anltsc2019image thathypervstored on the same worker, and runs anltsc2022image thathypervstored.Stdio pipes
TestProcessIOCreatorKeepsContainersApartpasses on Server 2022 and fails there without the fix.processand underhyperv. After it, all four passed in both modes.go testof./worker/runtime,./worker/runtime/specand./worker/runtime/libcontainerdpasses on Linux. I ran the new Windows-only test on a Server 2022 host and did not rerun the other Windows tests.