从Docker到nerdctl:轻量级容器管理工具实战指南

📅 2026/8/16 22:59:55
从Docker到nerdctl:轻量级容器管理工具实战指南
1. 项目概述为什么我们需要一个更轻量的容器工具如果你和我一样长期在容器化领域摸爬滚打那么对docker命令的熟悉程度可能已经刻进了肌肉记忆。然而随着容器生态的演进尤其是在云原生和边缘计算场景下一个更轻量、更专注、与 containerd 原生集成的工具逐渐进入了我们的视野——它就是nerdctl。简单来说nerdctl是一个与 Docker CLI 兼容的容器命令行工具但它直接与 containerd 守护进程通信而不是 Docker Engine。这意味着在那些已经使用 containerd 作为底层容器运行时的环境中比如标准的 Kubernetes 节点你可以直接使用nerdctl来管理容器、镜像和网络而无需安装庞大的 Docker 套件。这对于追求极致轻量化的生产环境、资源受限的边缘设备或者只是想更深入理解容器运行时层的人来说是一个非常得力的工具。这份操作手册的目的不是要取代 Docker而是为你提供另一种选择并深入解析nerdctl在命令设计、功能特性上与 Docker 的异同。我们将从最基础的安装配置讲起覆盖镜像、容器、网络、数据卷等核心资源的日常操作并重点探讨那些nerdctl独有的或表现不同的功能点。无论你是运维工程师、开发人员还是技术爱好者掌握nerdctl都能让你在容器世界里多一份从容和深入。2. 环境准备与安装配置详解在开始挥舞nerdctl命令之前一个正确且高效的环境是基石。与 Docker 一键安装包不同nerdctl的安装通常需要我们先确保其依赖的运行时环境就绪。2.1 核心依赖containerd 与 runcnerdctl本身只是一个客户端工具它的“大脑”和“执行器”分别是 containerd 和 runc或其他符合 OCI 标准的运行时。因此安装的第一步是确保你的系统上已经运行着一个正确配置的 containerd。对于大多数 Linux 发行版你可以通过包管理器安装# 对于 Ubuntu/Debian sudo apt-get update sudo apt-get install -y containerd # 对于 CentOS/RHEL/Fedora sudo yum install -y containerd # 或使用 dnf (Fedora/RHEL8) sudo dnf install -y containerd安装后需要启动并启用 containerd 服务sudo systemctl enable --now containerd注意许多 Kubernetes 发行版如 kubeadm在初始化时已经安装了 containerd。你可以通过systemctl status containerd或ctr version命令来验证其是否正在运行。如果已经存在请跳过此步避免版本冲突。至于 runc它通常作为 containerd 的依赖被自动安装。如果需要单独安装或升级可以从开源项目的发布页面获取二进制文件。2.2 nerdctl 的多种安装方式nerdctl提供了灵活的安装方式你可以根据你的使用场景和偏好来选择。方式一直接下载二进制文件最通用这是最推荐的方式尤其是对于生产环境因为它不依赖特定的包管理器易于版本管理和分发。前往 nerdctl 的 GitHub Release 页面 根据你的操作系统和架构如linux-amd64下载对应的压缩包。# 示例下载特定版本并安装 export NERDCTL_VERSION1.5.0 wget https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz sudo tar Cxzvvf /usr/local/bin nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz方式二使用包管理器如 macOS 的 Homebrew对于 macOS 用户安装体验非常顺畅brew install nerdctl方式三从源码构建如果你需要最新的功能或特定的修改可以克隆源码仓库进行构建。这需要 Go 语言环境。git clone https://github.com/containerd/nerdctl.git cd nerdctl make sudo make install安装完成后通过nerdctl version验证安装是否成功。这个命令会同时输出nerdctl客户端和containerd服务端的版本信息这是检查两者连通性的好方法。2.3 关键配置namespace 与 rootless 模式安装好就能用了吗对于简单试用是的。但对于生产或进阶使用有两个核心配置概念必须理解。1. 命名空间 (Namespace)这是nerdctl与 Docker 一个重要的设计哲学差异。nerdctl默认操作的是名为default的 containerd 命名空间。你可以通过--namespace或-n参数来指定。Kubernetes 使用的容器通常在不同的命名空间里如k8s.io。这实现了很好的隔离性。# 查看默认命名空间下的容器 nerdctl ps # 查看 Kubernetes 使用的命名空间下的容器 nerdctl -n k8s.io ps2. Rootless 模式无根模式这是安全性的巨大飞跃。Rootless 模式允许非特权用户运行容器极大地减少了潜在的安全风险。启用它需要一些额外的设置主要是为用户配置 subuid/subgid。# 1. 安装 rootlesskit 等依赖通常包含在 nerdctl 完整包中 # 2. 为用户配置 subuid 和 subgid sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami) # 3. 重新登录后即可使用 rootless 模式 nerdctl run --rm -it alpine:latest echo Hello, Rootless!实操心得在个人开发环境或对安全有要求的共享环境中我强烈建议从一开始就习惯使用 rootless 模式。虽然初期配置有一点点麻烦但它能从根本上避免“容器逃逸”导致主机 root 权限泄露的风险。对于生产环境这是安全基线之一。3. 镜像管理拉取、构建与维护镜像是一切容器运行的起点。nerdctl在镜像管理方面的命令与docker高度相似这降低了学习成本但在一些细节和底层实现上有所不同。3.1 拉取与查看镜像拉取镜像的命令与 Docker 完全一致nerdctl pull nginx:alpine nerdctl pull ubuntu:22.04你可以使用nerdctl images来列出本地镜像。这里有一个小技巧结合--format参数使用 Go 模板可以输出更简洁或定制化的信息这在编写脚本时特别有用。# 只显示镜像ID和标签 nerdctl images --format “table {{.ID}}\t{{.Repository}}:{{.Tag}}” # 以纯JSON格式输出便于用 jq 等工具解析 nerdctl images --format json | jq .与 Docker 的差异点nerdctl的镜像默认存储在 containerd 的内容存储区路径通常为/var/lib/containerd/。而 Docker 使用的是/var/lib/docker/。这意味着两者之间的本地镜像默认不共享。如果你需要迁移可以使用nerdctl save和nerdctl load命令或者直接操作底层的存储目录不推荐新手操作。3.2 构建镜像深入 Dockerfile 与 BuildKit使用nerdctl build构建镜像是核心操作。它默认集成了BuildKit这是下一代镜像构建工具提供了更快的构建速度、更高效的缓存管理和更安全的构建流程。# 基本构建命令 nerdctl build -t my-app:latest . # 指定 Dockerfile 路径和构建上下文 nerdctl build -f ./docker/Dockerfile.prod -t my-app:prod ./dockerBuildKit 高级特性实践缓存管理BuildKit 的缓存更加精细。你可以使用--cache-from来指定缓存源甚至使用远程缓存如注册表缓存来加速 CI/CD 流水线中的构建。nerdctl build --cache-from typeregistry,refmy-registry.com/my-app:buildcache -t my-app:latest .多阶段构建的优化BuildKit 能更好地处理多阶段构建只拉取和导出必需的层。安全构建 (--secret和--ssh)这是非常强大的功能允许你在构建过程中安全地使用密钥或 SSH 代理而不会将秘密信息留在最终镜像层中。# 从文件传递密钥构建时可用镜像中不可见 nerdctl build --secret idmysecret,src./api-key.txt -t my-app:secure .在 Dockerfile 中你可以通过RUN --mounttypesecret,idmysecret ...来临时挂载并使用这个秘密。注意事项虽然nerdctl build兼容 Dockerfile 语法但如果你使用了非常新的、只有 Docker Buildx 才支持的 Dockerfile 指令如RUN --mounttypebind的某些高级形式可能会遇到兼容性问题。在涉及复杂构建时建议先在标准 Docker 环境下测试 Dockerfile 的可行性。3.3 镜像的导入、导出与清理与 Docker 一样你可以使用save和load来导出导入镜像包。# 导出镜像为 tar 包 nerdctl save -o my-app.tar my-app:latest # 从 tar 包导入镜像 nerdctl load -i my-app.tar镜像清理是维护系统存储空间健康的重要环节。除了使用nerdctl rmi删除指定镜像外更常用的是清理悬虚镜像那些没有标签且未被任何容器引用的中间层镜像。# 删除所有悬虚镜像 nerdctl image prune # 强制删除所有未被使用的镜像包括有标签但未被容器引用的 nerdctl image prune -a一个常见的坑在 containerd 中镜像的删除有时可能因为被其他命名空间引用而失败或者删除后磁盘空间没有立即释放。这是因为 containerd 采用内容寻址存储只有当一块内容layer完全没有任何镜像或容器引用时才会被垃圾回收。你可以手动触发垃圾回收nerdctl system prune -a -f # 类似于 docker system prune # 或者直接调用 containerd 的 ctr 工具进行更底层的清理 sudo ctr content gc4. 容器生命周期管理从创建到销毁管理容器的创建、运行、监控和停止是日常最频繁的操作。nerdctl run是这一切的起点它的参数与docker run大同小异但理解其与 containerd 的交互方式能帮你更好地排错。4.1 创建与运行容器一个典型的运行命令如下nerdctl run -d \ --name my-nginx \ -p 8080:80 \ -v /host/path:/container/path:ro \ --restart unless-stopped \ nginx:alpine让我们拆解几个关键参数在nerdctl上下文下的细节-d后台运行。背后是 containerd 创建一个常驻的 task任务。--name为容器命名。在同一个 containerd 命名空间内名称必须唯一。-p端口映射。底层是通过iptables或nftables规则实现与 Docker 无异。-v卷挂载。这里的行为高度一致。但要注意 rootless 模式下挂载主机路径可能需要先正确配置用户映射subuid/subgid否则会遇到权限错误。--restart重启策略。nerdctl通过一个内置的、简单的重启管理器来实现对于always和unless-stopped策略它会在容器退出后重新创建并运行。交互式容器与调试 对于需要交互的容器比如运行一个临时调试工具使用-it参数nerdctl run -it --rm alpine:latest /bin/sh这里的--rm参数表示容器退出后自动删除非常适合临时任务。4.2 容器状态监控与日志查看查看容器状态使用nerdctl ps。添加-a查看所有容器包括已停止的。# 查看运行中的容器 nerdctl ps # 查看所有容器并显示完整的容器ID和状态 nerdctl ps -a --format “table {{.ID}}\t{{.Names}}\t{{.Status}}”查看容器日志是排查问题的首要步骤# 查看最新日志 nerdctl logs my-nginx # 跟踪实时日志类似 tail -f nerdctl logs -f my-nginx # 查看最近10行日志 nerdctl logs --tail 10 my-nginx # 查看特定时间戳之后的日志 nerdctl logs --since 2023-10-27T10:00:00 my-nginx实操心得nerdctl logs默认显示的是容器标准输出stdout和标准错误stderr。如果容器内的应用将日志写入文件而非标准流logs命令将看不到内容。在这种情况下你需要进入容器查看文件或者更佳实践是配置容器内的应用将日志输出到标准流或者使用volumes将日志文件挂载到主机然后用主机的日志工具如journald、syslog或loki收集。4.3 容器的停止、删除与资源清理停止容器使用nerdctl stop它会向容器内的主进程发送 SIGTERM 信号等待一段时间默认为10秒后如果进程仍未终止则发送 SIGKILL 强制终止。nerdctl stop my-nginx # 等待5秒后强制停止 nerdctl stop -t 5 my-nginx删除已停止的容器使用nerdctl rm。如果要删除正在运行的容器需要加-f参数。# 删除已停止的容器 nerdctl rm my-nginx # 强制删除运行中的容器 nerdctl rm -f my-nginx批量清理所有已停止的容器是一个好习惯nerdctl container prune5. 数据持久化与网络配置实战容器本身是无状态的数据和网络是连接容器与外部世界、保证服务可用的桥梁。5.1 数据卷Volumes与绑定挂载Bind Mountsnerdctl支持两种主要的数据持久化方式概念与 Docker 一致。1. 命名卷Named Volumes由nerdctl/containerd 管理生命周期是持久化数据的推荐方式尤其适合数据库数据。# 创建卷 nerdctl volume create my-data # 运行容器并使用卷 nerdctl run -d -v my-data:/var/lib/mysql --name mysql-db mysql:8.0 # 查看卷信息 nerdctl volume inspect my-data # 删除卷确保没有容器在使用它 nerdctl volume rm my-data2. 绑定挂载Bind Mounts将主机上的一个特定目录或文件挂载到容器中。这适用于配置文件、代码开发等场景。# 挂载主机目录 nerdctl run -v /home/user/app:/app -it node:18 /bin/bash # 挂载单个文件例如配置文件 nerdctl run -v /host/nginx.conf:/etc/nginx/nginx.conf:ro nginx:alpine重要提示在 rootless 模式下使用绑定挂载时权限问题是最常见的“坑”。因为容器内的 root 用户实际上被映射到了主机的一个高编号的非特权用户如 uid100000。如果主机上的目录对于这个映射用户没有读写权限容器内就会访问失败。解决方法通常是确保主机目录对“其他用户”others有适当权限如755或者更精细地配置/etc/subuid和/etc/subgid。5.2 网络管理从基础桥接到CNI插件网络是nerdctl与 Docker 差异较大的一个领域。Docker 有自己的网络驱动bridge, host, overlay等而nerdctl则主要依赖CNIContainer Network Interface插件来提供网络功能这与 Kubernetes 的网络模型一脉相承。1. 默认的“桥接”网络安装nerdctl时它通常会创建一个名为nerdctl的默认桥接网络其底层由bridgeCNI 插件实现。# 列出网络 nerdctl network ls # 创建一个新的自定义桥接网络 nerdctl network create my-net --subnet 172.20.0.0/16 # 在指定网络中运行容器 nerdctl run -d --name app1 --network my-net nginx:alpine nerdctl run -d --name app2 --network my-net nginx:alpine # 此时 app1 和 app2 可以通过容器名互相访问2. 使用更强大的 CNI 插件nerdctl的真正威力在于可以轻松集成各种 CNI 插件实现复杂的网络策略如 Calico网络策略、FlannelOverlay网络、Macvlan让容器获得宿主机网络段的独立IP等。首先你需要将 CNI 插件的二进制文件和配置文件放到正确的位置通常是/opt/cni/bin和/etc/cni/net.d。然后nerdctl就能自动识别并使用它们。# 假设你已经配置了 macvlan CNI # 创建一个 macvlan 网络 nerdctl network create my-macvlan --driver macvlan \ --subnet 192.168.1.0/24 \ --gateway 192.168.1.1 \ -o parenteth0 # 运行容器并接入 macvlan 网络容器将获得一个与主机同网段的独立IP nerdctl run -d --name hosted-service --network my-macvlan nginx:alpine3. 端口映射的底层原理当使用-p参数时nerdctl默认会使用portmapCNI 插件它通过配置 iptables 规则来实现端口转发。你可以通过iptables -t nat -L查看相关的 DNAT 规则。6. 高级功能与组合命令应用掌握了基础操作后一些高级功能和命令组合能极大提升效率。6.1 容器内执行命令与调试当容器在后台运行时我们经常需要进入容器内部执行命令。# 在运行中的容器内执行一次性命令 nerdctl exec my-nginx cat /etc/nginx/nginx.conf # 以交互模式进入容器类似 ssh nerdctl exec -it my-nginx /bin/sh排查技巧如果容器启动失败或者镜像没有常驻进程如alpine你可以尝试以“调试模式”运行它让它启动一个 shell 而不是默认命令这样你就能进去查看环境。nerdctl run -it --rm --entrypoint /bin/sh my-broken-image:latest6.2 系统级信息与资源管理nerdctl system命令提供了系统层面的信息和管理功能。# 查看系统级信息包括容器、镜像、构建缓存的数量和磁盘使用情况 nerdctl system df # 一键清理所有未使用的资源镜像、容器、网络、构建缓存 nerdctl system prune -a -f6.3 与 Docker Compose 的兼容性nerdctl compose这是nerdctl生态中一个极其重要的功能。你可以使用nerdctl compose来直接运行 Docker Compose 文件这对于从 Docker 生态迁移过来的项目非常友好。# 假设当前目录有 docker-compose.yml 文件 nerdctl compose up -d nerdctl compose ps nerdctl compose logs -f nerdctl compose downnerdctl compose命令会解析你的docker-compose.yml并在 containerd 中创建对应的容器、网络和卷。这意味着你几乎不需要修改现有的 Compose 文件就能运行起来。一个潜在的兼容性问题Compose 文件中如果使用了 Docker 特有的build上下文扩展如dockerfile:和context:的某些组合或者依赖 Docker 特有的网络驱动如driver: overlay可能会在nerdctl compose下失败。对于绝大多数标准定义的微服务栈它都能完美工作。7. 常见问题排查与性能调优指南即使工具再成熟在实际操作中也会遇到各种问题。这里记录了一些典型场景和解决思路。7.1 容器启动失败快速诊断流程检查镜像是否存在nerdctl images | grep image-name。查看详细错误信息去掉-d参数直接在前台运行容器错误信息会直接打印到终端。nerdctl run --rm my-image检查端口冲突nerdctl run时报错address already in use。使用ss -tulnp | grep port或lsof -i:port查看哪个进程占用了端口。检查 rootless 模式下的权限如果是在 rootless 模式下遇到“权限被拒绝”首先检查挂载的数据卷或绑定目录的权限确保主机目录对映射的用户可访问。可以尝试在容器内以非 root 用户运行Dockerfile 中使用USER指令。查看 containerd 日志容器引擎本身的日志是终极武器。使用journalctl -u containerd或查看/var/log/containerd/containerd.log来获取底层错误。7.2 网络不通问题排查表现象可能原因排查命令/步骤容器无法访问外网1. 主机网络问题2. CNI 插件配置错误3.nerdctl默认网络故障1.nerdctl exec container ping 8.8.8.82.cat /etc/cni/net.d/*.conf3. 重启nerdctl网络nerdctl network prune后重建容器间无法通过容器名互访未使用自定义网络或不在同一网络1.nerdctl network inspect network-name2. 确保容器都连接到同一个用户自定义网络主机无法访问容器映射端口1. 防火墙iptables/nftables规则被清空或阻止2. 端口映射参数写错1.sudo iptables -t nat -L -n查看 DNAT 规则2.nerdctl port container确认映射关系使用 Macvlan 网络容器无法与宿主机通信Macvlan 的设计使然这是 Macvlan 的“特性”通常需要额外配置一个 Macvlan 子接口给宿主机或改用 Ipvaln 模式。7.3 性能调优与资源限制对于生产环境为容器设置资源限制是必须的。# 限制容器使用最多 1 个 CPU 核心和 512MB 内存 nerdctl run -d \ --name limited-app \ --cpus 1.0 \ --memory 512m \ --memory-swap 1g \ # 总内存交换分区限制为1G nginx:alpine--cpus可以指定小数如0.5表示半个 CPU 核心。--memory硬性内存限制容器尝试超额使用会被 OOM Killer 终止。--memory-swap内存和交换分区总和限制。设置为-1表示不限制交换分区危险通常设置为内存的两倍或等于内存值--memory-swap等于--memory时容器无法使用交换分区。监控容器资源使用情况 虽然nerdctl没有原生的stats命令像 Docker 那样直观但我们可以利用crictl如果安装了或更通用的cadvisor、prometheus等监控工具。一个简单的替代方法是直接使用ctr命令查看容器的 cgroup 信息或者使用systemd-cgtop来查看按容器分组资源消耗如果容器是通过 systemd 管理的。