nerdctl 容器部署与 Compose 网络问题排查

先执行 nerdctl info,确认 nerdctl 已经连上 containerd;如果这一步失败,先处理 PATH、socket、权限和服务状态,不要急着修改 Compose YAML。连通后再用下面这条最短链路验证镜像、容器和端口:

command -v nerdctl
nerdctl version
nerdctl info

nerdctl pull nginx:alpine
nerdctl run -d --name web -p 8080:80 nginx:alpine
nerdctl ps
nerdctl logs web
curl -I http://127.0.0.1:8080

root 模式下,后续命令应始终使用同一权限,例如全部通过 sudo nerdctl 执行;rootless 模式则应始终使用安装 rootless containerd 的普通用户,不要在同一排错链路里混用 sudo nerdctl 和普通用户 nerdctl。这两种模式默认连接的 socket、用户服务和网络环境不同,混用时很容易出现“命令能执行,但连接的不是同一个 containerd”。

本文从 CSDN《【云原生】nerdctl 部署及使用指南》提到的部署与使用问题切入,但不沿用其中的历史版本号作为当前版本。正文统一使用 <VERSION>、<ARCH> 等占位符;唯一原样保留的终端告警来自参考资料,其余故障均通过可复查的检查命令定位。

先分清安装包、权限和 containerd 连接

nerdctl 本身是 containerd 的客户端,主要适用于 Linux。开始安装前,先确定采用 root 还是 rootless 模式:root 模式连接系统级 containerd 服务,rootless 模式连接普通用户自己的 containerd 服务。两者的安装脚本、服务启动方式和 socket 地址不同,装好后再切换模式,通常意味着要重新检查环境变量和权限。

官方发布包含 minimal 和 full 两种形态。minimal 主要提供 nerdctl 二进制及 rootless 辅助脚本;full 还包含 containerd、CNI、runc、BuildKit 等组件。全新主机没有 containerd 时可以选择 full,主机已经运行 containerd 时则应选择 minimal,避免覆盖现有二进制、服务配置或数据目录。

全新主机可以按当前 Release 页面提供的文件名安装 full 包:

sudo tar -xf "nerdctl-full-<VERSION>-linux-<ARCH>.tar.gz" -C /usr/local/
sudo systemctl daemon-reload
sudo systemctl enable --now containerd

这里的 enable 会让 containerd 随系统启动,--now 则会立即启动服务。如果解压后提示找不到服务单元,不要反复执行 enable,应先检查发行包实际包含的 systemd unit,再按对应版本的 containerd 安装文档补齐:

sudo systemctl cat containerd
sudo systemctl status containerd --no-pager

已有 containerd 时只安装 nerdctl:

tar -xf "nerdctl-<VERSION>-linux-<ARCH>.tar.gz"
sudo install -m 0755 nerdctl /usr/local/bin/nerdctl

参考资料中出现过 v1.2.1、v1.5.0 等历史版本,但它们只能说明当时的安装方式,不能据此判断当前最新版。下载前应从官方 Release 页面确认文件名、版本号、CPU 架构以及校验信息。

rootless 模式允许普通用户运行容器,安装脚本和 socket 地址需要按当期 rootless 文档配置。常见流程如下:

containerd-rootless-setuptool.sh install
systemctl --user status containerd --no-pager
systemctl --user start containerd

安装脚本如果已经启动并配置了用户服务,不需要重复执行安装。安装后重新打开登录 shell,再检查当前用户服务与 CONTAINERD_ADDRESS:

systemctl --user status containerd --no-pager
journalctl --user -u containerd -n 200 --no-pager
printf 'CONTAINERD_ADDRESS=%s\n' "${CONTAINERD_ADDRESS:-<unset>}"

rootless 环境不要再默认连接 /run/containerd/containerd.sock。如果环境变量没有进入当前 shell,应按官方 rootless 配置重新设置,而不是手工改成系统级 socket。

安装完成后,分别检查二进制是否进入 PATH、nerdctl 客户端能否运行,以及 containerd daemon 是否可以访问。

command -v nerdctl
nerdctl version
nerdctl info

如果提示 nerdctl: command not found,先检查文件和 PATH,不要因此下载第二份不同版本的二进制:

command -v nerdctl
ls -l /usr/local/bin/nerdctl
printf '%s\n' "$PATH"

如果二进制存在,但 nerdctl info 报连接失败,root 模式检查系统服务和默认 socket:

sudo systemctl status containerd --no-pager
sudo journalctl -u containerd -n 200 --no-pager
sudo ls -l /run/containerd/containerd.sock
sudo nerdctl info

rootless 模式则检查用户服务及当前环境变量:

systemctl --user status containerd --no-pager
journalctl --user -u containerd -n 200 --no-pager
printf 'CONTAINERD_ADDRESS=%s\n' "${CONTAINERD_ADDRESS:-<unset>}"

需要显式指定地址时,可以把全局参数放在子命令前:

nerdctl --address /run/containerd/containerd.sock info
nerdctl --address "$CONTAINERD_ADDRESS" info

第二条只适用于已经正确导出 CONTAINERD_ADDRESS 的 rootless 环境。

权限问题可以先用下面两条命令对照:

nerdctl info
sudo nerdctl info

如果只有 sudo nerdctl info 成功,说明当前用户没有访问目标 socket 的权限。此后 root 模式的拉取、运行、Compose 和网络命令都应保持相同权限,不要只给某一条命令加 sudo。如果 nerdctl info 已经成功,但创建容器时提示运行时不可用,还要确认 runc 是否存在及其版本是否兼容:

command -v runc
nerdctl info

minimal 包并不等于完整运行时环境。已有 containerd 的主机还可能缺少 runc、CNI 或 BuildKit,这些组件缺失通常属于主机 provisioning 问题,而不是 Compose 文件问题。

跑起第一个容器,再单独处理构建链路

第一个容器不需要复杂参数,显式设置容器名、端口和重启策略,后续定位会更直接:

nerdctl pull nginx:alpine
nerdctl run -d \
  --name web \
  -p 8080:80 \
  --restart=always \
  nginx:alpine

这种后台运行、命名容器和发布端口的方式与常见的 Docker CLI 操作接近,也便于按常见习惯检查运行状态、日志和端口。如果提示容器名已经存在,换一个名称,或者先检查并清理旧容器,不要重复启动同名实例。

容器级验证按下面顺序执行:

nerdctl ps
nerdctl ps -a
nerdctl logs web
nerdctl exec -it web sh
nerdctl inspect web
nerdctl port web

nerdctl ps 中看不到容器时,先用 ps -a 确认它是不是已经退出。容器提前退出时,先看日志和 inspect 中的退出状态、启动命令,再检查 containerd 日志。进程自身启动失败、配置错误或端口冲突,都可能让容器在 daemon 已经正常工作时立即退出,不能直接归因于 containerd 服务异常。

端口不可达也要分层检查。先确认发布关系,再看宿主监听地址和端口占用:

nerdctl port web
ss -lntp
curl -I http://127.0.0.1:8080

如果 nerdctl port 没有显示预期映射,应检查 run 时的 -p 参数。如果映射存在但 ss 中没有对应监听,继续检查容器状态;如果容器已经退出,端口自然不会监听。如果本机 curl 成功、远程访问失败,则重点检查绑定地址和宿主防火墙,而不是重新构建镜像。

端口映射会真实占用宿主的 8080 端口。启动前可以用 ss -lntp 确认它是否已被其他进程使用。--restart=always 也会改变容器的恢复行为:daemon 或主机重启后,nerdctl 会按重启策略尝试恢复容器,因此它适合长期服务,但不适合临时排障容器。

镜像构建是另一条链路。nerdctl build 并不只是读取 Dockerfile,它依赖 buildctl 客户端以及可用的 buildkitd worker;手工安装 BuildKit 时,只放一个 buildctl 二进制并不够,服务端和 worker 同样需要配置。full 安装包已经包含 BuildKit 相关组件。

执行构建:

nerdctl build -t app:test .

参考资料中的真实告警是:

WARN[0000] unable to determine buildctl version: exec: "buildctl": executable file not found in $PATH

这条告警表示构建客户端无法从 PATH 找到 buildctl。它不是业务代码报错,也不能单凭这条告警断定 Dockerfile 有语法问题;应继续看构建命令的最终退出状态和后续错误。如果后面明确因找不到 buildctl 而失败,再处理 PATH 或安装问题。

可以依次检查客户端、服务端和 worker:

command -v buildctl
buildctl --version
systemctl status buildkitd --no-pager
buildctl debug workers

systemctl status buildkitd 只适用于由 systemd 管理的 buildkitd;如果是手工启动或放在容器中运行,需要按该部署方式检查进程、socket 和 worker 地址。若 command -v buildctl 有结果,但 worker 列表为空,问题更可能在 buildkitd 服务端或其 worker 配置,而不是 PATH。

排障结束后,如果不再需要测试容器,再显式停止并删除:

nerdctl stop web
nerdctl rm web

这不会自动处理所有镜像、卷或网络。尤其是数据卷,应在确认数据不再需要后单独删除,不要把容器清理和存储清理混成一个动作。

Compose 与网络必须使用同一个 containerd 上下文

nerdctl 从 0.8 起兼容 Docker Compose 语法,但不包含 Swarm,其 Compose 实现遵循 Compose Specification,而不是把 Docker Swarm 编排能力搬到 containerd 上。因此,应准备兼容的 Compose v2;它不提供 docker stack deploy 一类功能。

先确认 Compose 命令和 nerdctl Compose 子命令都可用:

command -v docker-compose
docker-compose version
nerdctl compose version

这里的 docker-compose 指的是当前发行方式提供的独立 Compose v2 可执行文件。如果机器使用的是 Docker CLI 插件,也可以检查:

docker compose version

不同 nerdctl 版本查找 Compose 二进制或插件的方式可能不同,最终应按对应版本的官方 Compose 文档确认,不能只看到某个 docker-compose 二进制就认为组合一定兼容。

一个最小 Compose 文件如下:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    networks:
      - app

  api:
    image: your-api-image:tag
    networks:
      - app

networks:
  app:
    name: app-net

your-api-image:tag 必须替换成实际存在且当前节点可以拉取的镜像。验证配置时先不要直接启动:

nerdctl compose config

配置解析通过后再启动:

nerdctl compose up -d
nerdctl compose ps
nerdctl compose logs -f
nerdctl compose exec web sh

停止并清理项目资源:

nerdctl compose down

down 会处理该 Compose 项目管理的容器和项目网络,但默认不会删除命名卷。卷需要单独确认后删除:

nerdctl volume ls
nerdctl volume rm <VOLUME_NAME>

<VOLUME_NAME> 必须来自实际列表,不要把占位符直接执行。

Compose 报错可以按层次处理。找不到 Compose 时检查可执行文件和 PATH;配置解析失败时先运行 nerdctl compose config;连接 containerd 失败时检查当前用户、CONTAINERD_ADDRESS 以及 root/rootless 上下文;构建失败时回到 BuildKit 链路检查 buildctl 和 buildkitd,不要把 buildctl 缺失归因于 YAML。

网络问题还要区分默认 bridge、自定义 bridge 和 host 三种模式。普通单容器可以使用默认 bridge;需要稳定服务名解析的多容器环境,应使用同一个自定义 bridge 网络。Compose 自己会创建和管理项目网络,本文示例中的 app-net 已经被 Compose 声明,因此不要在执行 compose up 前再手动创建同名网络。

直接使用 nerdctl 启动容器时,自定义网络需要显式创建:

nerdctl network create app-net
nerdctl network ls
nerdctl network inspect app-net

然后把需要互通的容器加入同一个网络:

nerdctl run -d \
  --name peer \
  --network app-net \
  nginx:alpine

容器加入同一个网络,只是满足了互通的前提;还要确认服务使用的是预期名称。Compose 中应优先使用 web、api 这样的服务名,而不是依赖 Compose 自动生成的容器名称。服务启动后检查:

nerdctl compose ps
nerdctl inspect "$(nerdctl compose ps -q web)"
nerdctl inspect "$(nerdctl compose ps -q api)"
nerdctl network inspect app-net

如果 api 无法通过服务名访问,依次确认两点:第一,两个服务是否都加入了同一个项目网络;第二,访问使用的是不是 Compose 服务名。只检查容器是否处于 running 状态,并不能证明 DNS 和网络附件已经正确。

host 网络只适合支持该模式的 Linux 环境,而且容器直接使用宿主的网络空间。此时不要再依赖 -p 做端口发布:

nerdctl run -d \
  --name web-host \
  --network host \
  nginx:alpine

host 模式下,容器内服务监听的端口就是宿主端口,因此也更容易与宿主现有服务发生端口冲突。rootless 环境是否支持 host 网络,以及支持到什么程度,需要按当期官方文档和当前内核环境验证。

端口绑定还要区分“容器端口”和“宿主监听地址”。未指定宿主 IP 时,发布端口通常会绑定到所有可用宿主地址,但最终应以本机实际输出为准:

nerdctl port web
ss -lntp

如果只需要本机访问,可以明确绑定回环地址:

nerdctl run -d \
  --name web-loopback \
  -p 127.0.0.1:8080:80 \
  nginx:alpine

这种配置下,远程主机无法直接访问 8080,即使容器和端口映射本身都正常。若 web-loopback 已经存在,应换名或先清理旧容器。

自定义 bridge 网络依赖 CNI 组件。full 包包含 CNI 相关文件;如果使用 minimal 包,或者主机已有 containerd,需要另外确认 CNI 插件文件、containerd 使用的插件目录以及网络配置是否一致。如果 nerdctl network create 失败,应检查:

nerdctl info
nerdctl network ls
sudo systemctl cat containerd
sudo journalctl -u containerd -n 200 --no-pager

这类失败通常属于 CNI 插件缺失、插件目录不一致或网络配置没有初始化。不要反复创建随机网络名来规避这个错误,因为后续 Compose 仍可能因为找不到预期网络而失败。

rootless 下的端口发布、host 网络和 CNI 驱动支持情况可能随 nerdctl、BuildKit、内核及 cgroup 配置变化。rootless 排查应继续使用普通用户上下文和 journalctl --user,不要为了看日志临时切到 root socket 后继续执行其他命令。

排障时按下面的顺序检查:

nerdctl info
nerdctl network ls
nerdctl network inspect app-net
nerdctl compose ps
nerdctl port web
ss -lntp

先确认连接的是哪一个 containerd,再确认网络是否存在、容器是否加入网络,最后检查端口发布和宿主防火墙。服务名解析、容器互通和外部端口可达是三个不同问题,混在一起排查时很容易把 DNS、路由和端口绑定重复检查很多遍。

nerdctl 是 containerd 的客户端,不是 containerd 的替代品;minimal 安装仍然要求主机具备兼容的 containerd、运行时和网络组件。本文涉及的 Compose 只覆盖单机 Compose Specification 场景,不包含 Swarm,也不能据此推断具备 Kubernetes 编排能力。

尚未统一覆盖的部分包括 rootless 的 host 网络与端口细节、不同内核和 cgroup 配置、CNI 插件版本、存储驱动,以及 macOS、Windows 等非原生 Linux 环境。发布或迁移前,应以官方 Release、rootless 和 Compose 文档复核 <VERSION>、<ARCH>、Compose v2 的安装方式以及具体参数;资料中的 v1.2.1、v1.5.0 等历史版本不应再被当作当前最新版。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容