ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/8/16 23:12:21 拓冰建站 浏览量
从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 与 runc

nerdctl本身只是一个客户端工具,它的“大脑”和“执行器”分别是 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 containerdctr version命令来验证其是否正在运行。如果已经存在,请跳过此步,避免版本冲突。

至于 runc,它通常作为 containerd 的依赖被自动安装。如果需要单独安装或升级,可以从开源项目的发布页面获取二进制文件。

2.2 nerdctl 的多种安装方式

nerdctl提供了灵活的安装方式,你可以根据你的使用场景和偏好来选择。

方式一:直接下载二进制文件(最通用)这是最推荐的方式,尤其是对于生产环境,因为它不依赖特定的包管理器,易于版本管理和分发。前往 nerdctl 的 GitHub Release 页面 ,根据你的操作系统和架构(如linux-amd64)下载对应的压缩包。

# 示例:下载特定版本并安装 export NERDCTL_VERSION="1.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 ps

2. 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 savenerdctl 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 ./docker

BuildKit 高级特性实践

  1. 缓存管理:BuildKit 的缓存更加精细。你可以使用--cache-from来指定缓存源,甚至使用远程缓存(如注册表缓存)来加速 CI/CD 流水线中的构建。
    nerdctl build --cache-from type=registry,ref=my-registry.com/my-app:buildcache -t my-app:latest .
  2. 多阶段构建的优化:BuildKit 能更好地处理多阶段构建,只拉取和导出必需的层。
  3. 安全构建 (--secret--ssh):这是非常强大的功能,允许你在构建过程中安全地使用密钥或 SSH 代理,而不会将秘密信息留在最终镜像层中。
    # 从文件传递密钥(构建时可用,镜像中不可见) nerdctl build --secret id=mysecret,src=./api-key.txt -t my-app:secure .
    在 Dockerfile 中,你可以通过RUN --mount=type=secret,id=mysecret ...来临时挂载并使用这个秘密。

注意事项:虽然nerdctl build兼容 Dockerfile 语法,但如果你使用了非常新的、只有 Docker Buildx 才支持的 Dockerfile 指令(如RUN --mount=type=bind的某些高级形式),可能会遇到兼容性问题。在涉及复杂构建时,建议先在标准 Docker 环境下测试 Dockerfile 的可行性。

3.3 镜像的导入、导出与清理

与 Docker 一样,你可以使用saveload来导出导入镜像包。

# 导出镜像为 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 gc

4. 容器生命周期管理:从创建到销毁

管理容器的创建、运行、监控和停止,是日常最频繁的操作。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:端口映射。底层是通过iptablesnftables规则实现,与 Docker 无异。
  • -v:卷挂载。这里的行为高度一致。但要注意 rootless 模式下,挂载主机路径可能需要先正确配置用户映射(subuid/subgid),否则会遇到权限错误。
  • --restart:重启策略。nerdctl通过一个内置的、简单的重启管理器来实现,对于alwaysunless-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将日志文件挂载到主机,然后用主机的日志工具(如journaldsyslogloki)收集。

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 prune

5. 数据持久化与网络配置实战

容器本身是无状态的,数据和网络是连接容器与外部世界、保证服务可用的桥梁。

5.1 数据卷(Volumes)与绑定挂载(Bind Mounts)

nerdctl支持两种主要的数据持久化方式,概念与 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-data

2. 绑定挂载(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 用户实际上被映射到了主机的一个高编号的非特权用户(如 uid=100000)。如果主机上的目录对于这个映射用户没有读写权限,容器内就会访问失败。解决方法通常是确保主机目录对“其他用户”(others)有适当权限(如755),或者更精细地配置/etc/subuid/etc/subgid

5.2 网络管理:从基础桥接到CNI插件

网络是nerdctl与 Docker 差异较大的一个领域。Docker 有自己的网络驱动(bridge, host, overlay等),而nerdctl则主要依赖CNI(Container 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(网络策略)、Flannel(Overlay网络)、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 parent=eth0 # 运行容器并接入 macvlan 网络,容器将获得一个与主机同网段的独立IP nerdctl run -d --name hosted-service --network my-macvlan nginx:alpine

3. 端口映射的底层原理当使用-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:latest

6.2 系统级信息与资源管理

nerdctl system命令提供了系统层面的信息和管理功能。

# 查看系统级信息,包括容器、镜像、构建缓存的数量和磁盘使用情况 nerdctl system df # 一键清理所有未使用的资源(镜像、容器、网络、构建缓存) nerdctl system prune -a -f

6.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 down

nerdctl compose命令会解析你的docker-compose.yml,并在 containerd 中创建对应的容器、网络和卷。这意味着你几乎不需要修改现有的 Compose 文件就能运行起来。

一个潜在的兼容性问题:Compose 文件中如果使用了 Docker 特有的build上下文扩展(如dockerfile:context:的某些组合),或者依赖 Docker 特有的网络驱动(如driver: overlay),可能会在nerdctl compose下失败。对于绝大多数标准定义的微服务栈,它都能完美工作。

7. 常见问题排查与性能调优指南

即使工具再成熟,在实际操作中也会遇到各种问题。这里记录了一些典型场景和解决思路。

7.1 容器启动失败:快速诊断流程

  1. 检查镜像是否存在nerdctl images | grep <image-name>
  2. 查看详细错误信息:去掉-d参数,直接在前台运行容器,错误信息会直接打印到终端。
    nerdctl run --rm my-image
  3. 检查端口冲突nerdctl run时报错address already in use。使用ss -tulnp | grep <port>lsof -i:<port>查看哪个进程占用了端口。
  4. 检查 rootless 模式下的权限:如果是在 rootless 模式下遇到“权限被拒绝”,首先检查挂载的数据卷或绑定目录的权限,确保主机目录对映射的用户可访问。可以尝试在容器内以非 root 用户运行(Dockerfile 中使用USER指令)。
  5. 查看 containerd 日志:容器引擎本身的日志是终极武器。使用journalctl -u containerd或查看/var/log/containerd/containerd.log来获取底层错误。

7.2 网络不通问题排查表

现象可能原因排查命令/步骤
容器无法访问外网1. 主机网络问题
2. CNI 插件配置错误
3.nerdctl默认网络故障
1.nerdctl exec <container> ping 8.8.8.8
2.cat /etc/cni/net.d/*.conf
3. 重启nerdctl网络:nerdctl network prune后重建
容器间无法通过容器名互访未使用自定义网络,或不在同一网络1.nerdctl network inspect <network-name>
2. 确保容器都连接到同一个用户自定义网络
主机无法访问容器映射端口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(如果安装了)或更通用的cadvisorprometheus等监控工具。一个简单的替代方法是直接使用ctr命令查看容器的 cgroup 信息,或者使用systemd-cgtop来查看按容器分组资源消耗(如果容器是通过 systemd 管理的)。