原文链接:https://zhuanlan.zhihu.com/p/22886116297
自从卸载Docker Desktop后,我一直在使用Colima。过去这三年中,它一直稳定可靠。但随着时间推移,我也逐渐发现了一些问题:Colima依然依赖Docker作为容器引擎,而且它基于第三方的Lima虚拟化方案,显得有些冗余和陈旧。偶然间我看到了Podman,它充满着新鲜感:
独立引擎:与Colima依赖Docker引擎不同,Podman采用了自己的开源容器引擎,完全脱离Docker生态。这对我来说是全新的尝试。
Daemonless:Podman的无守护进程设计节省了系统资源,性能更好。
更简单的工具链:相比于Colima需要借助Lima来实现虚拟化,Podman使用自己的Podman Machine工具。
兼容性:Podman与Docker CLI完全兼容,能够无缝接入现有工作流程。
说干就干。不过动手之前,我们先深入了解一下Podman与Colima在底层实现上的区别。
提示:本文阅读时长15分钟。
架构对比
首先,所有的容器方案,都是基于Linux,所以在macOS上需要运行虚拟机。最常见的虚拟机有QEMU、Apple原生的vz和applehv。vz是macOS原生的虚拟化技术Virtualization Framework。目前Lima和Docker Desktop都默认使用vz。而applehv是Apple hypervisor,据说是vz的底层技术,Podman使用applehv。
在虚拟机里运行容器引擎,Docker使用守护进程dockerd,通过容器运行时containerd调用更低级别容器运行时runc。Podman则直接调用自家的容器运行时crun(也支持runc),据说crun比runc快50%,对资源的要求也更低。这些容器运行时都符合OCI标准,所以都可以运行Docker镜像。
此外,还有专门管理虚拟机环境的工具,用来下载Linux镜像、启动虚拟机。Podman方案使用Podman Machine,而Colima方案依赖Lima。
还有命令行工具,Podman方案采用的是Podman Remote,通过SSH调用虚拟机内的Podman。而Colima使用Docker CLI。Podman CLI特意模仿Docker CLI,所以迁移非常顺利。
综上所述,Colima方案通过Lima启动基于vz技术的虚拟机,在macOS上使用Docker CLI与虚拟机内的dockerd交互,由dockerd调用containerd和runc控制容器;而Podman方案则使用Podman Machine基于applehv启动虚拟机,用户通过Podman Remote调用虚拟机内的Podman,后者直接调用crun。
安装上手
首先是安装Podman:
$ brew install podman
安装好以后初始化虚拟机。这会下载Fedora CoreOS镜像运行Linux。
$ podman machine init
现在就可以看到创建的虚拟机:
$ podman machine list
NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE
podman-machine-default* applehv 4 days ago 2 seconds ago 7 2GiB 100GiB
然后就是启动虚拟机:
$ podman machine start
然后就可以正常使用了:
$ podman run --rm hello-world
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull quay.io/podman/hello:latest...
Getting image source signatures
Copying blob sha256:1ff9adeff4443b503b304e7aa4c37bb90762947125f4a522b370162a7492ff47
Copying config sha256:83fc7ce1224f5ed3885f6aaec0bb001c0bbb2a308e3250d7408804a720c72a32
Writing manifest to image destination
!... Hello Podman World ...!
.--"--.
/ - - \
/ (O) (O) \
~~~| -=(,Y,)=- |
.---. /` \ |~~
~/ o o \~~~~.----. ~~
| =(X)= |~ / (O (O) \
~~~~~~~ ~| =(Y_)=- |
~~~~ ~~~| U |~~
Project: https://github.com/containers/podman
Website: https://podman.io
Desktop: https://podman-desktop.io
Documents: https://docs.podman.io
YouTube: https://youtube.com/@Podman
X/Twitter: @Podman_io
Mastodon: @Podman_io@fosstodon.org
哈哈哈哈这怎么和正版的不一样?这是什么小可爱?
不过先不用在意这些细节哈,我们继续。现在就可以使用Podman代替Docker了:
$ alias docker=podman
如果想要自动补全,可以安装docker-completion:
$ brew install docker-completion
使用完后,就可以关闭虚拟机,节约资源:
$ podman machine stop
Podman的镜像
在pull或者run的时候,需要指定镜像名字。如果使用一个简单的名字,例如podman pull mysql(这种被称为unqualified image name)。Podman会先查找Short-Name Aliasing配置:
$ podman machine ssh cat /etc/containers/registries.conf.d/000-shortnames.conf
[aliases]
...
# centos
"centos" = "quay.io/centos/centos"
# containers
"hello-world" = "quay.io/podman/hello"
# docker
...
# Ubuntu
"ubuntu" = "docker.io/library/ubuntu"
...
我们前面运行run hello-world的时候会看见小海豹,就是因为在别名里配置了"hello-world" = "quay.io/podman/hello",所以实际上运行的是quay.io/podman/hello镜像。
如果没有在别名配置里找到,Podman就会到配置的仓库里查找,默认配置的仓库就是Docker Hub(http://docker.io):
$ podman machine ssh cat /etc/containers/registries.conf.d/999-podman-machine.conf
...
unqualified-search-registries=["docker.io"]
这个行为就和Docker一样了。
如果要运行正版的hello-world怎么办呢?可以使用镜像全名(fully-qualified image):
$ podman run --rm docker.io/library/hello-world
...
Hello from Docker!
...
PS:hello-world是Docker的官方镜像,所以Podman里对应的镜像全名就是docker.io/library/hello-world,参考Normalization of docker.io references。如果是非官方的镜像,例如在Docker Hub的镜像pytorch/pytorch,对应的Podman里的全名就是docker.io/pytorch/pytorch。
搜索
还可以通过命令行方便的搜索镜像,不需要打开Docker Hub:
$ podman search mysql --filter=is-official
NAME DESCRIPTION
docker.io/library/mysql MySQL is a widely used, open-source relation...
这里会到unqualified-search-registries配置的仓库里进行搜索。
还可以查询某个镜像的tag,注意这里需要使用全名:
$ podman search docker.io/library/mysql --list-tags --limit 10000 | grep -i lts
docker.io/library/mysql lts
docker.io/library/mysql lts-oracle
docker.io/library/mysql lts-oraclelinux8
docker.io/library/mysql lts-oraclelinux9
也可以指定在某个仓库里搜索,格式是[仓库名字]/[search term]:
$ podman search quay.io/centos
NAME DESCRIPTION
...
quay.io/centos/centos The official CentOS base containers.
...
host-gateway问题
使用过程中我也遇到了一些问题。在使用Open WebUI的时候,按照文档说明运行会报错:
$ podman run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
报错信息:Error: failed to create new hosts file: unable to replace "host-gateway" of host entry "host.docker.internal:host-gateway": host containers internal IP address is empty.
看上去问题出现在--add-host=host.docker.internal:host-gateway。这个参数是什么意思呢?我查了资料,这个参数的含义如下:
在Docker环境下,host.docker.internal是一个特殊的主机名,用于解析为宿主机在容器内部的访问地址。这样容器内的应用便可以通过该地址访问宿主机提供的服务。
host-gateway则是一个特殊的关键字,用于在容器启动时自动解析为宿主机的IP地址,以便动态设置host.docker.internal的值。
然而,Podman默认已经提供了对host.docker.internal的解析,无需额外配置。所以解决方案是直接去掉 --add-host=host.docker.internal:host-gateway参数:
$ podman run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
Docker Compose
Docker Compose是容器化开发中常用的功能,Podman也通过external compose provider支持这一功能。external compose provider包括podman-compose和docker-compose两个选项。
一开始我想彻底摆脱Docker,所以使用podman-compose。不过在使用过程中遇到了一些问题,后来切换成docker-compose后问题就解决了。
Spring Boot Docker Compose Support可以让我们在运行测试或者本地服务时,不需要额外写代码就能自动启动容器。当我将Docker/Colima替换成Podman后,运行的时候Spring Boot Docker Compose Support就报错了:Cannot run program "docker": error=2, No such file or directory。
这是因为Java使用ProcessBuilder执行外部命令时,并不会读取Shell中的别名(alias)。解决这个问题,可以创建一个符号链接:
$ ln -s `brew --prefix`/bin/podman `brew --prefix`/bin/docker
然而继续运行时,报了另外一个错误,显示podman compose --ansi never config --format=json执行出错。
经过分析发现,podman-compose和docker-compose虽然在命令行上尽量保持一致,但在某些功能和参数实现上存在差异。例如,上述命令中--ansi never与--format=json参数在podman-compose下未能正确处理,导致兼容性问题。最终,我还是安装了docker-compose来解决该问题:
$ brew install docker-compose
清理Colima
现在可以正常使用Podman来完成功能了。如果你和我一样有“强迫症”,那你肯定想彻底删除Colima。
首先记得迁移Docker的镜像和Volume。然后删除Colima和Docker:
$ brew services stop colima
$ colima delete
$ brew uninstall docker colima qemu
PS:这里看到brew services stop colima,不得不说,Podman还有个缺点,就是不能通过brew services自动启动,期望以后会改进。
Colima使用的配置文件和pid文件,在~/.colima目录下。同时Docker的配置文件在~/.docker目录下。
删除不需要的配置文件:
$ rm -rf ~/.colima ~/.docker
PS:另外可以顺便检查下还有哪些工具是不再使用的:
$ brew leaves
既然到这里了,我们也看看Podman用了哪些文件:
~/.config/containers Podman的配置,包括和虚拟机的连接信息,例如podman-connections.json
~/.local/share/containers 虚拟机运行文件目录
这就是我从Docker/Colima迁移到Podman的经验总结。期间为了弄清各种技术细节,我把Podman官网翻了个底朝天,终于弄明白了它的优势,还理清了各概念之间的关系。整个过程充满挑战,却也别有一番乐趣。