前言

在上一个章节中,我们完成了在没有虚拟化的 Windows 中部署 Docker 的操作,如果你此时还没有读过它,笔者非常建议你先完成前置步骤 如何在 Windows 上利用 Windows Continer 使用 Docker

本篇中,我们将带你一起看看实际生产中,这个Docker需要面临的问题和情况

TL; DR

Windows Continer 版本的 Docker 实在是太特殊了(对于整个Docker生态而言),我们必须为各种基础设施自己来制作镜像

灾难立刻就来了

手快的朋友,一定已经在第一时间完成了上一篇内容中的搭建操作,也跟着笔者进行了一个简单的 Hello World . 到此为止,一切内容看起来都是那么的祥和.

当我们真正想要启动一个市场镜像时,docker 会给我们带来承重一击

本示例试图启动一个 Docker Registry 的镜像

file

作为最大最常用的公共镜像库 Docker Hub 居然没有为我们提供官方支持 Windows 的 Registry

我们很快在市场中找到了一个来自社区的 registry-windows

本示例试图启动 stefanscherer/registry-windows 镜像

file

果然适配了Windows的镜像被我们顺利的下载下来了,当我们启动它时,Docker 再次向我们带来致命一击

file

它居然无法启动,还和我们无情的甩出了一张 系统不兼容

来自 Windows Continer 的限制

简而言之, 由于 Windows 用户模式 (用户态) 与 内核模式 (内核态) 相互耦合,但Continer的设计上很大程度的依赖一些用户模式下对内核的特殊调用,Windows 自己也不能保证跨版本是否会出现不可预料的问题,因此 Windows Continer 主动的拒绝启动这些因为版本不一致而含有风险的镜像

file

来自 Windows container version compatibility

真正的! 第一个 Image

不得不说当笔者看到限制的内容时,煞那间窒息的感觉用上头,充满了整个大脑,为了这个 Docker ,我们不得不手动为自己打包镜像,所幸的是,如今绝大部分的程序都是使用各种运行时启动的,而这些运行时与程序大概率也能在 Windows 中运行,我们除了需要手动打包这些镜像以外,也没有什么其他值得注意的地方了。

回到 Registry

Docker Registry 所运行的程序 distribution 是由 Golang 编写的,很幸运,在第一个 Image 中,我们很轻松的就能让 Golang 在 Windows 中跑起来,让我们来编写一个简单的 Dockerfile 来打包一个 Image

编译 、打包 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"]

在这个 Dockerfile 中,我们做了这些事情

  • 使用了 golang 来作为我们的构建镜像
  • 将powershell设置为之后执行的命令行程序
  • 获取 distribution 的仓库进行编译
  • 准备一个用于发布的镜像包
  • 将主要的程序复制到发布镜像包中
  • 打开 5000 端口
  • 设置镜像默认启动程序

接下来。我们要为 distribution 配置一些初始选项放在 config.yml 文件中

vim config.yml

具体选项参考 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

保存后开始我们的镜像构建

docker build -t registry-windows .

file

大功告成!

测试真正的 Hello World

让我们试着启动一下它

docker run -it registry-windows 

file

我们已经成功的在我们的 Docker 中启动了 Docker Registry

让我们按下 Ctrl + C 终止这个这个临时的实例,使用一种更稳定的方式在后台运行

mkdir /c/registry

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

这组个命令相对之前增加了一些设置

参数 解释
-d 将容器作为后台进程运行
-p 5000:5000 将容器内部的端口 5000 映射到主机的端口 5000
--restart=always 除主机主动操作停止以外,任何的停机都会重启容器
-v C:/registry:C:/registry 将主机中的目录 C:/registry 挂在到容器内的 C:/registry
--name registry 为这个实例起一个名字 registry

现在我们可以通过 docker ps 命令在运行中的容器列表中看到它了

file

使用 Registry 保存 Image

由于我们刚刚启动的 Registry 尚未受到TLS的保护,关于如何设置Registry的TLS证书不在本篇范围,具体请参考 Verify repository client with certificates

为本机配置不安全的注册表

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

在文件中添加以下内容

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

重启 docker

net stop docker
net start docker

现在,我们可以试着将我们刚刚完成打包的 registry-windows 提交到 Registry

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

docker push localhost:5000/registry:1.0.0

file

大功告成!

结语

虽然 Windows Continer 中的 Docker 在实际生产中有一定的受限,但仍值得我们一试,虽然这种方式舍弃了 Docker 本身开箱即用的能力,除非运气足够好,很难发现能直接运行的镜像...

推荐仓库: https://github.com/StefanScherer/dockerfiles-windows

Stefan Scherer 为大量的基础设施应用编写了支持 Windows Continer 的 Dockerfile. 参考本仓库,可以快速的在自己的 Docker 中打包出符合需求的镜像,节省大量调试时间