Docker vs Podman:从零开始搭建你的第一个容器应用(附实战对比)

📅 发布时间:2026/7/5 20:58:15 👁️ 浏览次数:
Docker vs Podman:从零开始搭建你的第一个容器应用(附实战对比)
Docker vs Podman从零开始搭建你的第一个容器应用附实战对比最近几年容器技术彻底改变了我们构建、分发和运行软件的方式。如果你是一名开发者尤其是刚接触这个领域的新手可能会被各种术语和工具搞得眼花缭乱。Docker 无疑是那个让容器技术走向大众的“明星”但你是否听说过 Podman它被许多人称为“Docker 的无守护进程替代品”听起来很酷但实际用起来到底怎么样这篇文章就是为你准备的。我们不打算空谈理论而是直接动手。我们将从一个最经典的场景开始搭建一个简单的 Web 服务。我会手把手地带你分别使用 Docker 和 Podman 走完全程。在这个过程中你会清晰地看到两者在命令、权限、资源消耗乃至设计哲学上的微妙差异。无论你是想快速上手容器还是正在为团队技术选型而犹豫这篇基于实战的对比都能给你带来最直观的参考。1. 环境准备与工具安装在开始任何容器之旅前确保你的“工具箱”是齐全的。对于新手来说安装过程往往是第一个门槛。Docker 和 Podman 的安装体验某种程度上也预示了它们后续的使用风格。Docker Desktop一站式的便捷体验对于大多数个人开发者尤其是在 Windows 或 macOS 上Docker Desktop 是最直接的选择。它不仅仅是一个命令行工具更是一个包含了 Docker 引擎、图形化管理界面、Kubernetes 集群等组件的完整套件。安装完成后你通常需要启动 Docker Desktop 应用。这时你会在系统托盘区看到那个熟悉的鲸鱼图标。一个关键点在于Docker 采用的是客户端-服务器架构。你输入的docker命令客户端会与后台运行的dockerd守护进程通信由它来真正执行创建容器等操作。这意味着如果 Docker Desktop 没有运行你的所有docker命令都会失败。注意在 Linux 上安装 Docker Engine 后通常需要手动启动并启用docker.service这个守护进程并且需要将你的用户加入docker用户组以避免每次命令都加sudo。这个操作本身涉及权限变更需要谨慎。Podman更“原生”的 Linux 体验Podman 的安装则显得更为“轻量”和传统。在大多数 Linux 发行版上你可以直接通过包管理器安装。例如在基于 RHEL/CentOS/Fedora 的系统上# Fedora / RHEL 8 sudo dnf install podman # Ubuntu / Debian sudo apt-get update sudo apt-get install podmanPodman 的一个核心设计是无守护进程。它直接利用 Linux 内核的特性如 user namespaces, cgroups来运行容器不需要一个常驻后台的中央守护进程。这带来的一个直接好处是你可以更容易地以普通用户身份运行容器Rootless Containers而无需将用户加入任何特殊组。安装完成后理论上你就可以直接使用podman命令了。为了让你对两者的基础架构有个快速认识这里有一个简单的对比特性DockerPodman核心架构客户端-守护进程 (C/S)无守护进程 (Fork-Exec)默认权限需要 root 或 docker 组权限支持 rootless普通用户直接运行安装包通常为集成套件 (Docker Desktop) 或引擎CLI独立的命令行工具后台服务必须运行dockerd无需常驻后台服务安装完毕后建议都运行一下各自的“hello-world”命令来验证安装是否成功docker run hello-worldpodman run hello-world如果看到欢迎信息恭喜你环境准备就绪。2. 实战第一步构建一个简单的 Web 应用镜像现在让我们进入正题。我们的目标是运行一个能显示自定义信息的简单网页。我们将创建一个使用 Nginx 作为 Web 服务器的容器。第一步就是为这个应用制作一个“模板”也就是容器镜像。创建应用文件首先在你的工作目录下创建一个新文件夹比如my-web-app。进入该文件夹创建两个文件index.html这是我们网站的主页。!DOCTYPE html html head title我的第一个容器应用/title style body { font-family: sans-serif; text-align: center; padding-top: 50px; } .container { max-width: 800px; margin: 0 auto; } .tech { color: #2496ed; font-weight: bold; } /* Docker 蓝 */ .tech-alt { color: #cc181e; font-weight: bold; } /* Podman 红 */ /style /head body div classcontainer h1 你好容器世界/h1 p这个页面运行在 span classtechNginx/span 容器中。/p p构建工具 span classtechDocker/span 或 span classtech-altPodman/span。 /p p当前时间span idtime/span/p /div script document.getElementById(time).textContent new Date().toLocaleString(); /script /body /htmlDockerfile注意对于 Podman 同样适用这是构建镜像的“食谱”。# 使用官方的 Nginx 镜像作为基础 FROM nginx:alpine # 将我们本地的网页文件复制到容器内的 Nginx 默认站点目录 COPY index.html /usr/share/nginx/html/ # 暴露 80 端口这是一个声明实际映射在运行时决定 EXPOSE 80这个Dockerfile非常简单它做了三件事指定基础镜像、复制文件、声明端口。alpine版本的镜像非常小巧适合作为演示。使用 Docker Build在包含Dockerfile和index.html的目录下打开终端运行 Docker 构建命令docker build -t my-web-app:v1 .这个命令的分解docker build启动构建过程。-t my-web-app:v1为构建成功的镜像打上一个标签Tag名称是my-web-app版本是v1。.指定构建上下文为当前目录。Docker 客户端会将这个目录下的所有文件受.dockerignore影响发送给守护进程进行构建。你会看到 Docker 逐层执行Dockerfile中的指令拉取基础镜像如果本地没有创建新的镜像层。最终使用docker images命令你应该能看到一个名为my-web-app、标签为v1的镜像。使用 Podman Build现在让我们用 Podman 做同样的事情。命令几乎一模一样podman build -t my-web-app:v1 .是的命令格式完全兼容。这是 Podman 的一大优势降低了学习成本。执行后Podman 也会开始构建。你可以用podman images来查看构建好的镜像。构建过程的底层差异虽然命令相同但背后的机制有所不同Dockerdocker build命令将构建上下文发送给dockerd守护进程由守护进程在后台完成所有工作。这有时会导致上下文目录过大时传输耗时。Podmanpodman build默认使用Buildah库在本地直接构建无需与守护进程通信。整个过程更贴近直接在主机上执行命令。你可以通过podman build --help看到它还有很多构建选项比如--layers,--squash等用于优化镜像层。提示无论是 Docker 还是 Podman构建的镜像都遵循 OCI开放容器倡议标准。这意味着你用 Docker 构建的镜像通常可以直接用 Podman 运行反之亦然。这为技术选型提供了灵活性。3. 运行与访问启动你的容器应用镜像构建好了就像拥有了一个软件安装包。接下来我们要把它运行起来成为一个正在提供服务的容器实例。使用 Docker Run运行一个 Docker 容器最常用的命令如下docker run -d --name docker-web -p 8080:80 my-web-app:v1让我们解析一下这个命令的关键参数-d让容器在后台运行detached mode。--name docker-web给这个容器实例起一个名字方便后续管理。-p 8080:80这是端口映射至关重要。它将主机你的电脑的8080端口映射到容器内部的80端口Nginx 默认监听端口。my-web-app:v1指定要运行的镜像及其标签。执行后容器就在后台启动了。你可以通过docker ps查看运行中的容器确认其状态为 “Up”。现在打开你的浏览器访问http://localhost:8080你应该能看到我们之前编写的那个 HTML 页面了。使用 Podman RunPodman 的运行命令在形式上与 Docker 完全一致podman run -d --name podman-web -p 8080:80 my-web-app:v1执行它。然后用podman ps查看。同样地访问http://localhost:8080页面应该也能正常显示。一个关键的冲突与解决方法细心的你可能发现了问题我们让 Docker 和 Podman 都把容器的 80 端口映射到了主机的同一个端口8080。如果两个容器同时运行后启动的那个会失败因为 8080 端口已经被占用了。这引出了容器网络的一个基础概念端口是主机层面的资源。为了解决这个问题我们需要为其中一个容器映射到不同的主机端口。比如让 Podman 的容器使用 8081 端口# 先停止并删除之前可能冲突的容器 podman stop podman-web podman rm podman-web # 使用新的端口映射运行 podman run -d --name podman-web -p 8081:80 my-web-app:v1现在你可以同时运行两个容器了Docker 容器http://localhost:8080Podman 容器http://localhost:8081深入对比运行时的权限与隔离这是体验差异最明显的地方之一。尝试执行以下命令查看容器进程# 查看 Docker 容器内的进程 docker exec docker-web ps aux # 查看 Podman 容器内的进程以 rootless 模式运行时 podman exec podman-web ps aux在以Rootless 模式即用普通用户直接运行运行的 Podman 容器中你可能会注意到一个有趣的现象容器内的root用户实际上被映射到了主机上的一个高编号的非特权用户通过 user namespace 实现。这意味着即使容器被攻破攻击者在主机上获得的权限也极其有限。而默认安装的 Docker虽然可以通过将用户加入docker组来免sudo但容器内的 root 在主机上依然拥有相当于 root 的权限取决于守护进程的权限这在安全敏感的环境中被认为风险更高。你可以通过一个简单的命令来感受这种权限差异。尝试在主机上查看这两个容器进程对应的主机进程# 查看进程注意 USER 和 PID 字段 ps aux | grep -E (nginx|httpd) | grep -v grep你会发现Podman 运行的容器进程其主机上的执行用户很可能就是你当前的登录用户而不是 root。4. 日常管理与进阶操作对比应用跑起来了但容器的生命周期管理远不止于此。日常的运维操作如查看日志、进入容器调试、管理数据、连接网络等才是开发者最常打交道的部分。基础生命周期管理两者的命令几乎可以互换这大大减轻了迁移成本。操作Docker 命令Podman 命令说明列出运行中容器docker pspodman ps默认只显示运行中的列出所有容器docker ps -apodman ps -a包括已停止的停止容器docker stop namepodman stop name发送 SIGTERM 信号启动已停止容器docker start namepodman start name重新启动重启容器docker restart namepodman restart name删除已停止容器docker rm namepodman rm name运行中的需加-f查看容器日志docker logs namepodman logs name查看标准输出实时跟踪日志docker logs -f namepodman logs -f name类似tail -f进入容器 Shelldocker exec -it name shpodman exec -it name sh交互式终端数据持久化挂载卷Volume容器本身是无状态的。当容器被删除其内部产生的所有数据也会消失。为了持久化数据如数据库文件、配置文件、日志我们需要使用卷挂载。假设我们想修改 Nginx 的默认配置文件并使其持久化。首先在主机上准备一个自定义的default.conf文件。然后使用-v或--mount参数将其挂载到容器内覆盖默认配置。# Docker 方式 docker run -d --name docker-web-v2 -p 8082:80 \ -v $(pwd)/custom.conf:/etc/nginx/conf.d/default.conf:ro \ my-web-app:v1 # Podman 方式 podman run -d --name podman-web-v2 -p 8083:80 \ -v $(pwd)/custom.conf:/etc/nginx/conf.d/default.conf:ro \ my-web-app:v1这里的:ro表示以只读read-only方式挂载防止容器内进程意外修改主机文件。对于需要容器写入数据的场景如日志目录、数据库目录可以创建命名卷或绑定目录两者命令也基本一致。资源限制与监控在本地开发时你可能不常限制资源但在生产环境或资源有限的机器上这是必须的。两者都支持在运行时限制 CPU 和内存。# 限制容器最多使用 512MB 内存和 1 个 CPU 核的 50% docker run -d --name limited-docker -m 512m --cpus0.5 my-web-app:v1 podman run -d --name limited-podman -m 512m --cpus0.5 my-web-app:v1监控资源使用情况Dockerdocker stats命令提供了一个实时更新的资源使用情况仪表板。Podmanpodman stats命令功能类似。此外由于 Podman 容器就是普通的 Linux 进程你完全可以利用top,htop或systemd-cgtop等主机工具来监控这更符合 Linux 原生管理习惯。网络管理两者都允许你创建自定义的桥接网络实现容器间的隔离通信。# 创建网络 docker network create my-net podman network create my-net # 将容器连接到指定网络运行 docker run -d --name web1 --network my-net my-web-app:v1 podman run -d --name web2 --network my-net my-web-app:v1在同一个自定义网络中的容器可以通过容器名直接互相访问自动 DNS 解析这对于微服务应用的本地模拟非常方便。5. 设计哲学与选型思考何时用 Docker何时考虑 Podman经过一番实战操作你应该对两者在命令行层面的相似性和底层实现的差异性有了切身感受。现在让我们跳出具体命令从更高维度看看它们背后的设计理念这能帮助你做出更合适的技术选型。Docker生态的王者与便捷性的标杆Docker 的成功很大程度上在于它早期降低了容器的使用门槛并构建了一个极其繁荣的生态。一体化体验Docker Desktop 提供了从安装、运行、图形化管理到 Kubernetes 集成的无缝体验对新手和桌面开发者极其友好。强大的生态系统Docker Hub 是最大的公共镜像仓库有海量的官方和社区镜像。docker-compose工具通过一个 YAML 文件定义和运行多容器应用已成为本地开发环境的事实标准其简洁性深受喜爱。成熟的 CI/CD 集成几乎所有的云平台和 CI/CD 工具如 GitHub Actions, GitLab CI, Jenkins都对 Docker 提供了原生、稳定的支持。然而它的守护进程架构也带来了一些固有特点安全顾虑拥有docker组权限的用户等同于在主机上拥有了 root 权限。守护进程本身作为一个高权限、常驻的进程也被认为是潜在的攻击面。单点故障如果dockerd守护进程崩溃所有由它管理的容器都会失去控制虽然可能仍在运行直到守护进程恢复。系统集成度Docker 创建了自己的网络、存储驱动和进程管理方式有时会与系统自带的工具如systemd,firewalld产生微妙的冲突或重叠。Podman云原生时代的原生公民Podman 诞生于 Red Hat其设计紧密贴合 Linux 内核特性和云原生生态尤其是 Kubernetes的理念。无守护进程与 Rootless这是 Podman 最核心的卖点。它直接利用runc运行容器就像运行普通进程一样。Rootless 模式从根本上提升了安全性是严格安全策略环境如金融、政府的首选。与 systemd 深度集成你可以用podman generate systemd命令为一个容器生成 systemd 服务文件然后像管理其他系统服务一样用systemctl来启动、停止、启用自启容器。这让容器化应用能更好地融入现有的 Linux 运维体系。原生 Pod 概念Podman 的名字就来源于 “Pod Manager”。它原生支持创建和管理Pod一组共享网络、IPC 等命名空间的容器集合这与 Kubernetes 的 Pod 概念完全一致。这对于开发和测试 Kubernetes 应用工作负载非常有用。# 创建一个 Pod并运行两个容器 podman pod create --name mypod -p 8080:80 podman run -d --pod mypod --name nginx nginx podman run -d --pod mypod --name redis redis兼容性与可替代性命令与 Docker CLI 高度兼容还提供了podman-docker包可以创建一个名为docker的别名指向podman让一些依赖 Docker 命令的旧脚本无需修改即可运行。Podman 的挑战桌面体验在 macOS 和 Windows 上Podman 需要通过虚拟机如 Fedora CoreOS来运行其体验不如 Docker Desktop 那样原生和一体化。虽然也有 Podman Desktop 项目在努力改善但成熟度仍有差距。生态工具podman-compose的功能和稳定性目前与docker-compose相比还有差距。对于重度依赖 Compose 的复杂项目迁移可能需要额外工作。学习资源虽然命令兼容但遇到 Podman 特有的问题尤其是 rootless 模式下的权限、用户命名空间问题时能找到的社区解答和文档丰富度仍不及 Docker。如何选择一些个人经验在我自己的开发和工作流中选择通常基于场景个人开发机macOS/Windows我倾向于使用Docker Desktop。它的图形界面、一键启停、以及无缝的docker-compose体验能让我专注于应用开发本身而不是折腾环境。特别是需要快速拉起一个包含数据库、缓存、消息队列的完整后端环境时一个docker-compose up就能解决所有问题。Linux 服务器或生产环境我会认真考虑Podman。尤其是当我对安全有更高要求或者希望容器服务能像系统服务一样被systemd管理时。在 RHEL/CentOS/Fedora 等系统中Podman 甚至是默认或推荐的容器工具。它的 rootless 特性让我在部署时安心不少。学习和理解容器本质如果你想深入理解容器是如何作为进程在 Linux 上运行的Podman是一个更透明的工具。它没有守护进程这个“黑盒”很多行为更符合你对 Linux 的预期。最后别忘了containerd。无论是 Docker 还是 Podman在 Linux 底层它们最终都可能通过containerd这个行业标准的容器运行时来运行容器。像 Kubernetes 这类编排系统也早已直接使用containerd或CRI-O另一个运行时与 Podman 系出同门。作为应用开发者Docker 和 Podman 都是优秀的“交互界面”你可以根据团队习惯、安全要求和平台支持来灵活选择甚至混合使用。毕竟它们创建的镜像和容器在 OCI 标准下是相通的。掌握其中一个切换到另一个的成本并不高。真正的价值在于理解容器技术本身以及如何用它来解决你的实际问题。