Preface

In the previous chapter, we completed the deployment of Docker on a non-virtualized Windows environment. If you haven't read it yet, the author strongly recommends that you first complete the prerequisite steps: How to Use Docker with Windows Containers on Windows.

In this chapter, we will explore the challenges and scenarios that Docker faces in real-world production environments.

TL; DR

Docker on the Windows Container edition is exceptionally unique (especially within the broader Docker ecosystem), and we must build images for various infrastructure components ourselves.

The disaster struck immediately

Those who were quick to act have likely already completed the setup steps from the previous article and followed along with a simple Hello World. Up to this point, everything has looked perfectly smooth.

However, when we actually try to start a market image, Docker delivers a heavy blow.

This example attempts to start a Docker Registry image.

file

Surprisingly, Docker Hub, the largest and most commonly used public image repository, does not provide official support for a Windows-compatible Registry.

We quickly found a community-contributed registry-windows in the marketplace.

This example attempts to start the stefanscherer/registry-windows image.

file

Sure enough, the Windows-compatible image was successfully downloaded. But when we tried to start it, Docker dealt us another fatal blow.

file

It simply failed to start, and mercilessly threw back a system incompatibility error at us.

Restrictions from Windows Container

In short, because Windows user mode and kernel mode are closely coupled, and Windows Containers rely heavily on specific user-mode calls into the kernel, even Microsoft cannot guarantee that unforeseen issues won't arise across different versions. Therefore, Windows Containers proactively refuse to start images that pose risks due to version mismatches.

file

Source: Windows container version compatibility

The Real! First Image

It has to be said that when the author saw the restricted content, a momentary sense of suffocation immediately surged up, filling the entire brain. For this Docker, we were forced to manually package the image ourselves. Fortunately, nowadays the vast majority of programs are launched using various runtimes, and these runtimes, along with the programs, are highly likely to run on Windows as well. Aside from needing to manually package these images, there is nothing else particularly noteworthy.

Back to Registry

The distribution program that Docker Registry runs is written in Golang. Fortunately, in the first Image, we were able to easily get Golang running on Windows. Let's write a simple Dockerfile to package an Image.

Compile and Package Image

mkdir registry
cd registry
vim Dockerfile

Registry Dockerfile


FROM golang as build

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

RUN git clone -q https://github.com/distribution/distribution ; \
    cd distribution ; \
    go build -o registry.exe cmd/registry/main.go

FROM mcr.microsoft.com/windows/nanoserver:ltsc2022

COPY --from=build /go/distribution/registry.exe /registry.exe
COPY config.yml /config/config.yml

EXPOSE 5000

ENTRYPOINT ["\\registry.exe"]
CMD ["serve", "/config/config.yml"]

In this Dockerfile, we do the following:

  • Use golang as our build image
  • Set powershell as the command-line program to be executed subsequently
  • Fetch the distribution repository for compilation
  • Prepare a release image package
  • Copy the main program into the release image package
  • Open port 5000
  • Set the default startup program for the image

Next, we need to configure some initial options for distribution in the config.yml file

vim config.yml

For specific options, refer to CNCF Distribution - Configuring a registry

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
validation:
  disabled: true

Start building our image after saving

docker build -t registry-windows .

file

Done!

Testing a Real Hello World

Let's try to start it.

docker run -it registry-windows 

file

We have successfully started Docker Registry in our Docker environment.

Let's press Ctrl + C to terminate this temporary instance and run it in the background using a more stable method.

mkdir /c/registry

docker run -d -p 5000:5000 --restart=always -v C:/registry:C:/registry --name registry registry-windows

This set of commands includes some additional settings compared to before.

Parameter Description
-d Run the container as a background process
-p 5000:5000 Map port 5000 inside the container to port 5000 on the host
--restart=always Restart the container on any shutdown, except when the host is manually stopped
-v C:/registry:C:/registry Mount the host directory C:/registry to C:/registry inside the container
--name registry Assign the name registry to this instance

We can now see it in the list of running containers using the docker ps command.

file

Saving Images Using Registry

Because the Registry we just started is not yet protected by TLS, configuring TLS certificates for the Registry is out of scope for this article. For details, please refer to Verify repository client with certificates

Configure an insecure registry for the local machine

vim /C/ProgramData/docker/config/daemon.json

Add the following content to the file

{
  "insecure-registries": ["127.0.0.1:5000"]
}

Restart docker

net stop docker
net start docker

Now, we can try to commit the registry-windows package we just built to the Registry.

#为registry-windows设置一个 tag
docker tag registry-windows localhost:5000/registry:1.0.0

docker push localhost:5000/registry:1.0.0

file

All done!

Conclusion

Although Docker in Windows Containers has certain limitations in production environments, it is still worth a try. While this approach sacrifices Docker's out-of-the-box usability, finding images that run directly is often a matter of luck, as such compatible images are hard to come by...

Recommended repository: https://github.com/StefanScherer/dockerfiles-windows

Stefan Scherer has written Dockerfiles supporting Windows Containers for a wide range of infrastructure applications. By referring to this repository, you can quickly build images in your own Docker setup that meet your specific needs, saving a significant amount of debugging time.