Atlantis
GitHub 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

4. Docker替代工具

Created At 2024-07-18 Updated on 2025-10-25

1. 前言

在早期的 Kubernetes 中,Docker 是运行容器的唯一选择,后来随着矛盾增加和对 Docker 公司的围剿,陆续出现了rkt、Containerd、CRI-O,并规划逐渐废弃对 Docker 的支持。

Kubernetes 1.19(2020年8月)中 Docker 标记为废弃,Kubernetes 1.20(2020年12月)Docker 正式废弃,如果集群要继续使用 Docker,需要部署 cri-dockerd 作为接入层。

Docker 本身有比较重的历史包袱,并不是运行 Pod 的最佳选择,而如今运行 Container 方面,也有了一些竞争对手,就是这里要谈到的 Containerd 与 Podman。

如果常用 Containerd 作为容器运行时,那就选择 nerdctl + Containerd 替代 Docker;如果是红帽工具链的重度使用者,以 CRI-O 作为容器运行时,那么可以选择 Podman(以及 Skopeo 和 Buildah),两者的依赖项并不冲突,有需要的话可以同时安装。

下面是在 64 位 X86 的 Debian 12 系统上使用 nerdctl + Containerd 与 Podman 替代日常 Docker 使用的示例。

2. Containerd

2.1 安装

Containerd 脱胎于 Docker 的 libcontainer,后期独立出来并捐献给了 CNCF,而 nerdctl 则是为 Containerd 开发的兼容 Docker 命令行的工具。这里使用 v1.7.6 版本完整包,它携带了 Containerd v1.7.16 与 BuildKit v0.12.5,由于我经常需要构建多架构镜像,因此还需要单独下载 BuildKit 的完整包,下面是操作步骤:

安装iptables

apt install -y iptables

下载并安装nerdctl

wget -c https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-full-1.7.6-linux-amd64.tar.gz
tar Cxzvvf /usr/local nerdctl-full-1.7.6-linux-amd64.tar.gz

下载并安装buildkit

wget -c wget https://github.com/moby/buildkit/releases/download/v0.12.5/buildkit-v0.12.5.linux-amd64.tar.gz
tar Cxzvvf /usr/local buildkit-v0.12.5.linux-amd64.tar.gz

启动containerd与buildkit守护进程

systemctl enable --now containerd
systemctl enable --now buildkit

最后验证下 containerd 服务是否正常

➜  ~ nerdctl info
Client:
 Namespace:	default
 Debug Mode:	false

Server:
 Server Version: v1.7.16
 Storage Driver: overlayfs
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2
...

nerdctl 兼容 Docker 命令行,我们可以直接创建一个软链接来使用,这样就不需要修改以前常用的脚本。

pushd /usr/local/bin && ln -sfv nerdctl docker && popd

2.2 容器管理

nerdctl 提供了与 Docker 使用习惯一致的命令行来管理容器。

  1. 运行容器:nerdctl run -d --restart unless-stopped --name nginx -p 8080:80 nginx:1.20-alpine
  2. 列出所有容器:nerdctl ps -a
  3. 查看容器日志:nerdctl logs -f --tail=100 nginx
  4. 登录容器:nerdctl exec -it nginx sh

nerdctl 默认使用 default 命名空间执行命令,如果我们正在使用 Containerd 作为集群的容器运行时,就可以指定 k8s.io 命名空间来管理由 Kubelet 创建出的容器,如下:

➜  ~ nerdctl namespace ls
NAME            CONTAINERS    IMAGES    VOLUMES    LABELS
k8s.io          94            96        0
moby            0             0         0
moby_history    0             0         0
➜  ~ nerdctl -n k8s.io container ls
CONTAINER ID    IMAGE                                                 COMMAND                   CREATED         STATUS    PORTS    NAMES
009113c869f2    docker.io/rancher/mirrored-pause:3.6                  "/pause"                  2 months ago    Up                 k8s://harbor/harbor-portal-77b89cd6b4-fxmhn
00ef51f80d3b    docker.io/linuxserver/bookstack:23.10.4               "/init"                   2 months ago    Up                 k8s://app/bookstack-0/bookstack
01bcb120bf75    docker.io/goharbor/harbor-portal:v2.5.3               "nginx -g daemon off;"    2 months ago    Up                 k8s://harbor/harbor-portal-77b89cd6b4-fxmhn/portal
0fdf81cf75ae    docker.io/rancher/mirrored-pause:3.6                  "/pause"                  2 months ago    Up                 k8s://kube-system/traefik-7c865f98b8-fblbg
...

2.3 镜像管理

Containerd 与 Docker 在镜像管理方面最显著的区别是:

  1. 默认使用 OCI 镜像格式
  2. 支持多架构镜像

Containerd 本身不支持构建镜像,执行 nerdctl build 时实际上是访问 buildkitd 提交构建镜像的任务,与执行 docker buildx 命令在 buildkitd 容器中构建镜像类似。

对于单架构镜像而言,拉取镜像、构建镜像、推送镜像等操作,nerdctl 与 Docker 使用习惯一致,下面以一个 alpine 基础镜像为例,Dockerfile 如下:

ARG VERSION
FROM alpine:$VERSION
ENV TZ=Asia/Shanghai
RUN apk add --no-cache bash curl wget git make htop tzdata
CMD [ "bash" ]#

拉取 alpine:3.18,构建自定义镜像 wbuntu/alpine:3.18 并推送,如下:

➜  alpine git:(master) ✗ nerdctl pull alpine:3.18
docker.io/library/alpine:3.18:                                                    resolved       |++++++++++++++++++++++++++++++++++++++|
index-sha256:1875c923b73448b558132e7d4a44b815d078779ed7a73f76209c6372de95ea8d:    done           |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:d9a39933bee4ccb6d934b7b5632cdf8c42658f3cecc5029681338f397142af6e: done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:8fd7cac70a4aaabb31d459b03e3534b14341c31429b678b650039ac45a606cfc:   done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 4.0 s                                                                    total:  2.1 Ki (541.0 B/s)
➜  alpine git:(master) ✗ nerdctl build --build-arg VERSION=3.18 -t wbuntu/alpine:3.18 .
[+] Building 1.6s (7/7) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                   0.0s
 => => transferring dockerfile: 164B                                                                                                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/alpine:3.18                                                                                                                                                                         1.3s
 => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                                                          0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                                                                                        0.0s
 => [1/2] FROM docker.io/library/alpine:3.18@sha256:1875c923b73448b558132e7d4a44b815d078779ed7a73f76209c6372de95ea8d                                                                                                                   0.0s
 => => resolve docker.io/library/alpine:3.18@sha256:1875c923b73448b558132e7d4a44b815d078779ed7a73f76209c6372de95ea8d                                                                                                                   0.0s
 => CACHED [2/2] RUN apk add --no-cache bash curl wget git make htop tzdata                                                                                                                                                            0.0s
 => exporting to docker image format                                                                                                                                                                                                   0.2s
 => => exporting layers                                                                                                                                                                                                                0.0s
 => => exporting manifest sha256:94db150bc131b4e69ecdb90d610295c668b955cc50b0aa2ff9372a59106101de                                                                                                                                      0.0s
 => => exporting config sha256:a0f4cabbaf6c53a3d3a01606b1297ca990df75da42b72d67186173118199395c                                                                                                                                        0.0s
 => => sending tarball                                                                                                                                                                                                                 0.2s
unpacking docker.io/wbuntu/alpine:3.18 (sha256:94db150bc131b4e69ecdb90d610295c668b955cc50b0aa2ff9372a59106101de)...
Loaded image: docker.io/wbuntu/alpine:3.18
➜  alpine git:(master) ✗ nerdctl push wbuntu/alpine:3.18

而多架构镜像有一些区别,在 pull 和 push 命令行中可以添加 --all-platforms 来拉取和推送所有架构的镜像,build 命令中可以为 --platform 指定多个架构,下面以我常用的一个 gost 镜像为例,buildkit 默认提供了一些环境变量用于区分架构,Dockerfile 如下:

FROM wbuntu/alpine:3.18
ARG VERSION
ARG TARGETARCH
COPY download.sh /usr/bin/download.sh
RUN /usr/bin/download.sh $VERSION $TARGETARCH
CMD ["/usr/bin/gost","-C","/etc/gost/config.json"]

使用 wbuntu/alpine:3.18 作为基础镜像,构建自定义镜像 wbuntu/gost:v2.11.5 并推送,如下:

➜  gost git:(master) ✗ nerdctl build --build-arg VERSION=v2.11.5 --platform linux/amd64,linux/arm64 -t wbuntu/gost:v2.11.5 .
[+] Building 5.4s (11/12)
[+] Building 5.5s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                   0.0s
 => => transferring dockerfile: 225B                                                                                                                                                                                                   0.0s
 => [linux/arm64 internal] load metadata for docker.io/wbuntu/alpine:3.18                                                                                                                                                              0.7s
 => [linux/amd64 internal] load metadata for docker.io/wbuntu/alpine:3.18                                                                                                                                                              0.7s
 => [internal] load .dockerignore                                                                                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                                                                                        0.0s
 => [linux/amd64 1/3] FROM docker.io/wbuntu/alpine:3.18@sha256:f59c3e7dac85edc5ba6ca7e8b1e7f83a5547325d7c19e9a06acfa2715053a950                                                                                                        0.0s
 => => resolve docker.io/wbuntu/alpine:3.18@sha256:f59c3e7dac85edc5ba6ca7e8b1e7f83a5547325d7c19e9a06acfa2715053a950                                                                                                                    0.0s
 => [linux/arm64 1/3] FROM docker.io/wbuntu/alpine:3.18@sha256:f59c3e7dac85edc5ba6ca7e8b1e7f83a5547325d7c19e9a06acfa2715053a950                                                                                                        0.0s
 => => resolve docker.io/wbuntu/alpine:3.18@sha256:f59c3e7dac85edc5ba6ca7e8b1e7f83a5547325d7c19e9a06acfa2715053a950                                                                                                                    0.0s
 => [internal] load build context                                                                                                                                                                                                      0.0s
 => => transferring context: 33B                                                                                                                                                                                                       0.0s
 => [linux/arm64 2/3] COPY download.sh /usr/bin/download.sh                                                                                                                                                                            0.0s
 => [linux/amd64 2/3] COPY download.sh /usr/bin/download.sh                                                                                                                                                                            0.0s
 => [linux/arm64 3/3] RUN /usr/bin/download.sh v2.11.5 arm64                                                                                                                                                                           3.5s
 => [linux/amd64 3/3] RUN /usr/bin/download.sh v2.11.5 amd64                                                                                                                                                                           1.9s
 => exporting to oci image format                                                                                                                                                                                                      1.2s
 => => exporting layers                                                                                                                                                                                                                0.6s
 => => exporting manifest sha256:cbc82e262f8c94507130b07afa6c0de9f9ce2b1f6e7865b1be7b7e4ce3dcd2d4                                                                                                                                      0.0s
 => => exporting config sha256:41b2d861771bb29bcf4662a781dc5a6869034495bed5db5ff788ea30eddb3a84                                                                                                                                        0.0s
 => => exporting manifest sha256:b0c5df8f7742fc137fe6727069b9da00729ba40c13acafe0335cdd552275da85                                                                                                                                      0.0s
 => => exporting config sha256:83b757d63b16256ec23290b41a5ca90279b18f22bc00195f336faaf0c464483a                                                                                                                                        0.0s
 => => exporting manifest list sha256:f778f4f19a6e7dc4adba9b8c704ad283b3fb62d9e5721920583d4639b7e77a09                                                                                                                                 0.0s
 => => sending tarball                                                                                                                                                                                                                 0.6s
Loaded image: docker.io/wbuntu/gost:v2.11.5
➜  gost git:(master) ✗ nerdctl push --all-platforms wbuntu/gost:v2.11.5
index-v2.11.5@sha256:f778f4f19a6e7dc4adba9b8c704ad283b3fb62d9e5721920583d4639b7e77a09: done           |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:b0c5df8f7742fc137fe6727069b9da00729ba40c13acafe0335cdd552275da85:      done           |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:cbc82e262f8c94507130b07afa6c0de9f9ce2b1f6e7865b1be7b7e4ce3dcd2d4:      done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:41b2d861771bb29bcf4662a781dc5a6869034495bed5db5ff788ea30eddb3a84:        done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:83b757d63b16256ec23290b41a5ca90279b18f22bc00195f336faaf0c464483a:        done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 22.7s

本地也可以直接查看多架构镜像,目前显示上还有一些局限性,PLATFORM 列不支持展示多架构,,如下:

➜  gost git:(master) ✗ nerdctl image ls
REPOSITORY                  TAG            IMAGE ID        CREATED           PLATFORM       SIZE         BLOB SIZE
wbuntu/alpine               3.18           f59c3e7dac85    7 hours ago       linux/amd64    25.2 MiB     9.9 MiB
wbuntu/alpine               3.18           f59c3e7dac85    7 hours ago       linux/arm64    0.0 B        10.0 MiB
wbuntu/gost                 v2.11.5        f778f4f19a6e    2 hours ago       linux/amd64    38.2 MiB     15.0 MiB
wbuntu/gost                 v2.11.5        f778f4f19a6e    2 hours ago       linux/arm64    0.0 B        14.7 MiB

实际上显示的是 OCI 多架构镜像的 index 文件中最靠前的镜像架构:

alt text

2.4 compose

nerdctl 提供了 compose 子命令来兼容 docker-compose,但还存在一些未实现的功能,例如:services.<SERVICE>.linksservices.<SERVICE>.build.extra_hosts等,详细内容参考:docs/compose.md

2.5 镜像加速

如果是国内用户,大多需要解决拉取镜像失败的问题,我维护了一个加速器,Containerd 的配置可以参考:常用容器引擎-Containerd

3. Podman

在我看来,作为开源软件界大腕的Red Hat 有一个习惯:如果无法主导一个重要开源项目,那就从头开发一整套替代品,再用于 Red Hat 产品体系。

Podman、Skopeo、Buildah、CRI-O 等都是在红帽的大手笔投入下开发的,CRI-O 意在争夺 CRI 接口的标准实现,Podman 则是 Docker 的替代品,在 Docker 公司推出面向桌面端的 Docker Desktop 后,Podman Desktop 应运而生,软件方面只有红帽能做到这种级别的碰瓷。

3.1 安装

Debian 12 官方源中已经提供了 Podman,目前版本为 4.3.1,Debian 12 以前的系统中提供的是 3.X 版本,与 4.X 版本在命令行和参数上有一些差异,推荐使用 4.X 版本,使用 apt 命令即可安装,如下:

apt install -y podman skopeo buildah podman-docker podman-compose

其中 podman-docker 提供了兼容 Docker 命令行的工具,podman-compose 提供了兼容 docker-compose 的工具集。

3.2 常用操作

Podman 最初的口号是配置一个 alias 即兼容所有 Docker 常用操作:alias docker=podman(已安装 podman-docker 时无需配置),如下:

➜  ~ alias docker=podman
➜  ~ docker version
Client:       Podman Engine
Version:      4.3.1
API Version:  4.3.1
Go Version:   go1.19.8
Built:        Thu Jan  1 08:00:00 1970
OS/Arch:      linux/amd64
➜  ~ podman pull alpine:3.18
Trying to pull docker.io/library/alpine:3.18...
Getting image source signatures
Copying blob 73baa7ef167e skipped: already exists
Copying config 8fd7cac70a done
Writing manifest to image destination
Storing signatures
8fd7cac70a4aaabb31d459b03e3534b14341c31429b678b650039ac45a606cfc
➜  ~ podman run --rm alpine:3.18 date
Thu Jul 18 21:43:29 UTC 2024
➜  ~ podman-compose version
['podman', '--version', '']
using podman version: 4.3.1
podman-composer version  1.0.3
podman --version
podman version 4.3.1
exit code: 0

此外 Podman 也提供了用户命名空间、无特权模式(rootless)、无守护进程等特性,配合 skopeo、buildash 等有许多玩法。

不过作为 Docker 的替代工具方面,我还有以下关注点:

  1. 重启策略:缺点,受限于无守护进程模式,使用 --restart-policy=unless-stopped 时,在系统重启后不会自动重启容器(Docker 和 Containerd 没有这个问题),在 Podman 上有这个需求时,只能使用 --restart-policy=always
  2. 未设置默认 registry:缺点,默认只配置了常用镜像的 alias 来指向 docker.io 或 quay.io,可能会影响老脚本运行,需手动配置搜索镜像时访问的 registry 列表来优先引用 docker.io 的镜像。
  3. 多架构镜像支持不完善:缺点,缺少 buildkit 这种能利用模拟器构建多架构镜像的能力,需要使用老办法,在对应架构平台构建单架构镜像并推送到镜像仓库,然后使用 podman manifest create 命令引用多个单架构镜像创建多架构镜像,最后再推送到 registry。
  4. 支持挂载镜像:优点,执行 podman image mount imgID 即可挂载容器镜像,无需运行容器,对于利用容器镜像同步文件的场景很有用。
  5. 容器工具共享存储与配置:优点,/var/lib/contaners 目录下的容器和镜像存储,以及 /etc/containers 下的配置对 Red Hat 的容器工具 skopeo、buildah 等共享,它们也是无守护进程模式,现在看大概是利用共享存储和文件锁实现的,配置文件也对 CRI-O 共享,但可能需要重启才能生效。

Red Hat 容器工具对镜像的灵活操作在 skopeo 上体现的淋漓尽致,它支持在两个 registry 之间直接拷贝容器镜像(无需拉取到本地)、指定镜像 manifest 类型(oci、v2s1、v2s2)、保持镜像 digest 一致等,目前在市面上仅此一份,连 OpenShift Container Platform 中引用容器镜像时都使用 sha256 而非 tag。

3.3 镜像加速

Podman 的配置文件也使用 TOML 格式,可以参考:常用容器引擎-Podman & CRI-O