尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从Docker到nerdctl:容器CLI工具演进与K8s环境实战指南

从Docker到nerdctl:容器CLI工具演进与K8s环境实战指南 1. 从Docker到nerdctl为什么我们需要一个新的容器CLI工具如果你和我一样习惯了用docker ps查看容器用docker run启动服务那么第一次看到nerdctl这个命令时可能会有点懵。这玩意儿是啥又一个Docker的替代品命令还长得差不多。其实nerdctl的出现背后是容器生态一次静默但重要的演进。简单来说你可以把它理解为专门为containerd这个容器运行时设计的“Docker CLI”。Docker本身是一个庞大的体系包含了客户端Docker CLI、容器运行时containerd、镜像构建工具等一系列组件。而nerdctl的目标就是提供一个功能丰富、体验接近Docker CLI的工具但直接与更底层、更标准的containerd运行时对话。为什么这很重要在实际的生产和开发环境中KubernetesK8s早已成为容器编排的事实标准。而K8s在v1.24版本之后移除了对Docker作为默认运行时的直接支持转向了更轻量、更专注的containerd和CRI-O。这意味着如果你在K8s节点上想直接操作容器比如排查问题、临时执行命令、导入导出镜像传统的docker命令可能不再可用或不是最佳选择。nerdctl填补了这个空白它让你在纯containerd的环境中依然能获得类似Docker的便捷操作体验。我最初接触它就是在一次K8s集群的故障排查中节点上只有containerddocker命令报错“找不到守护进程”而nerdctl成了我的救命稻草。所以这份“操作手册”的目的不是简单罗列命令而是帮你快速建立对nerdctl的直觉让你知道在哪些场景下该用它以及如何用它高效地完成日常工作。它的命令设计很大程度上兼容了Docker所以学习成本很低但也有一些独有的特性和细微差别这正是我们需要重点关注的地方。2. nerdctl基础安装、配置与初体验在深入具体命令之前我们得先把环境搭起来。nerdctl的安装方式很灵活你可以通过包管理器、下载二进制文件或者从源码编译。2.1 安装与快速配置对于大多数Linux系统最方便的方法是使用其项目提供的安装脚本或者从GitHub Releases页面下载对应架构的二进制文件。这里以直接下载二进制文件为例因为它通用性最强。# 假设我们是在一个x86_64的Linux服务器上操作 # 前往GitHub Releases页面找到最新版本例如 v1.5.0 # 下载压缩包 wget https://github.com/containerd/nerdctl/releases/download/v1.5.0/nerdctl-1.5.0-linux-amd64.tar.gz # 解压 tar -xzf nerdctl-1.5.0-linux-amd64.tar.gz # 将二进制文件移动到系统PATH目录例如 /usr/local/bin/ sudo mv nerdctl /usr/local/bin/ # 验证安装 nerdctl --version安装完成后一个关键配置是命名空间namespace。containerd支持多租户隔离不同的命名空间就像不同的“抽屉”镜像和容器默认是分开的。nerdctl默认使用default命名空间。如果你需要操作K8s创建的容器它们通常在k8s.io命名空间就需要在命令中指定。# 查看当前有哪些命名空间需要containerd的ctr命令 sudo ctr namespace ls # 使用nerdctl操作k8s.io命名空间下的容器 nerdctl --namespacek8s.io ps -a为了方便你可以设置环境变量NERDCTL_NAMESPACE来避免每次输入--namespace。export NERDCTL_NAMESPACEk8s.io # 或者写入 ~/.bashrc 持久化 echo export NERDCTL_NAMESPACEk8s.io ~/.bashrc source ~/.bashrc注意操作k8s.io命名空间下的资源通常需要root权限因为K8s组件是以高权限运行的。普通用户可能无法看到或操作这些容器。2.2 第一个命令验证与帮助系统安装配置好后我们先跑几个最简单的命令来感受一下。# 查看版本确认安装成功 nerdctl version # 查看系统信息类似于 docker info nerdctl info # 强大的帮助系统任何命令记不清了就用 --help nerdctl --help nerdctl run --help # 查看run子命令的详细用法你会发现nerdctl info输出的信息比docker info更“底层”一些它会直接显示containerd的运行时和快照器snapshotter信息。快照器是一个关键概念它决定了容器镜像层在磁盘上的存储方式。常见的如overlayfs、stargz等nerdctl对此有很好的支持我们后面会提到。3. 镜像管理拉取、查看、构建与清理镜像操作是容器工作的起点。nerdctl的镜像命令与Docker高度相似但也有一些增强功能。3.1 拉取与查看镜像拉取镜像使用nerdctl pull。默认情况下它会从Docker Hub拉取。# 拉取一个nginx镜像 nerdctl pull nginx:alpine # 拉取时指定完整仓库地址 nerdctl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9 # 查看本地镜像列表 nerdctl images # 输出格式和docker几乎一致仓库、标签、镜像ID、创建时间、大小 # 使用更丰富的格式输出例如只显示镜像ID和仓库名 nerdctl images --format table {{.ID}}\t{{.Repository}} # 查看镜像的详细信息Inspect nerdctl image inspect nginx:alpine | jq . # 配合jq工具美化JSON输出这里有个实操心得在拉取一些大型镜像时可能会遇到速度慢的问题。nerdctl本身没有内置的镜像加速器配置但你可以通过配置containerd的镜像仓库镜像registry mirror来实现加速这需要修改/etc/containerd/config.toml文件。另一种更直接的方式是先把镜像拉取到有Docker且配置了加速器的环境然后用docker save导出再用nerdctl load导入到目标机器。3.2 构建镜像兼容Dockerfile与高级特性nerdctl build命令用于构建镜像它完全兼容标准的Dockerfile语法。# 在当前目录包含Dockerfile构建镜像 nerdctl build -t my-app:latest . # 指定构建上下文路径和Dockerfile路径 nerdctl build -t my-app:v1 -f ./Dockerfile.prod ./context-dirnerdctl在构建方面的一个亮点是它对BuildKit的深度集成。BuildKit是下一代镜像构建工具支持更快的构建、更高效的缓存和更多高级功能。你可以通过--builder参数指定使用BuildKit。# 使用BuildKit后端进行构建需要containerd的buildkitd服务在运行 nerdctl build --builder buildkit -t my-app:buildkit .更强大的是nerdctl支持一种名为“Stargz”的可延迟拉取镜像格式。这种镜像格式允许容器在拉取完元数据后立即启动而镜像层数据则在需要时按需拉取非常适合边缘计算或网络带宽受限的场景。构建Stargz格式的镜像需要额外的工具链但nerdctl提供了实验性支持。3.3 镜像的导入、导出与清理日常运维中经常需要在离线环境或不同机器间迁移镜像。# 将镜像保存为tar归档文件 nerdctl save -o nginx-alpine.tar nginx:alpine # 从tar归档文件加载镜像 nerdctl load -i nginx-alpine.tar # 给镜像打上新的标签 nerdctl tag nginx:alpine my-registry.local:5000/nginx:my-tag # 推送镜像到私有仓库需要先登录 nerdctl push my-registry.local:5000/nginx:my-tag镜像清理是维护系统磁盘空间的重要环节。nerdctl提供了比原生Docker更灵活的镜像删除命令。# 删除指定镜像 nerdctl rmi nginx:alpine # 强制删除即使有容器引用 nerdctl rmi -f nginx:alpine # 删除所有未被任何容器引用的悬空镜像dangling images nerdctl image prune # 交互式删除列出所有镜像并询问是否删除 nerdctl images | tail -n 2 | awk {print $1:$2} | fzf -m | xargs -r nerdctl rmi # 这个命令组合使用了fzf一个命令行模糊查找器可以让你方便地选择多个镜像进行删除非常高效。注意nerdctl rmi在删除被多个标签引用的镜像时行为与Docker一致它只是删除该标签的引用直到最后一个标签被删除时才会真正删除镜像层数据。4. 容器生命周期管理从创建到销毁这是最核心的部分我们每天都要和这些命令打交道。nerdctl run命令的参数与docker run兼容度极高你可以几乎无缝迁移。4.1 创建与启动容器# 最基本的运行启动一个交互式容器并进入shell nerdctl run -it --rm alpine:latest /bin/sh # -it: 分配一个交互式终端并保持打开 # --rm: 容器退出后自动删除 # 退出shell后容器自动停止并被删除 # 在后台运行一个守护进程容器 nerdctl run -d --name my-nginx -p 8080:80 nginx:alpine # -d: 后台运行 # --name: 指定容器名称 # -p: 端口映射主机端口:容器端口 # 运行容器并执行一条命令后退出 nerdctl run --rm alpine:latest echo Hello, nerdctl! # 挂载主机目录到容器内 nerdctl run -v /host/data:/container/data -it alpine:latest sh # -v: 卷挂载格式为 主机路径:容器路径[:选项] # 设置环境变量 nerdctl run -e MY_ENVvalue -e JAVA_OPTS-Xmx512m my-app:latest # 限制容器资源 nerdctl run -d --name limited-app \ --cpus 1.5 \ # 限制使用1.5个CPU核心 --memory 512m \ # 限制内存为512MB --memory-swap 1g \ # 限制内存交换分区为1GB my-app:latest一个重要的细节是网络。nerdctl默认会为容器创建一个新的网络命名空间并使用CNIContainer Network Interface插件来配置网络。这与Docker默认的bridge驱动不同。你可以使用nerdctl network ls查看现有的CNI网络。如果你需要容器使用主机网络例如运行一个网络监控工具可以使用--nethost参数。4.2 查看、进入与操作运行中的容器容器跑起来之后我们需要观察和管理它们。# 列出正在运行的容器 nerdctl ps # 列出所有容器包括已停止的 nerdctl ps -a # 使用自定义格式输出例如只显示ID、名称、状态和镜像 nerdctl ps -a --format table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}} # 查看容器日志 nerdctl logs my-nginx # 实时跟踪日志输出 nerdctl logs -f my-nginx # 查看最近10行日志 nerdctl logs --tail 10 my-nginx # 进入一个正在运行的后台容器类似 docker exec -it nerdctl exec -it my-nginx /bin/sh # 在容器内执行单条命令 nerdctl exec my-nginx nginx -t # 测试nginx配置 # 查看容器内进程 nerdctl top my-nginx # 查看容器的详细信息Inspect获取所有配置和状态 nerdctl inspect my-nginx | jq .[0].NetworkSettings.Networks # 这个命令可以帮你找到容器在CNI网络中的IP地址对于网络排查非常有用。4.3 停止、重启与删除容器# 停止一个运行中的容器 nerdctl stop my-nginx # 强制停止发送SIGKILL nerdctl kill my-nginx # 启动一个已停止的容器 nerdctl start my-nginx # 重启容器 nerdctl restart my-nginx # 暂停容器冻结进程 nerdctl pause my-nginx # 恢复被暂停的容器 nerdctl unpause my-nginx # 删除已停止的容器 nerdctl rm my-nginx # 强制删除一个运行中的容器 nerdctl rm -f my-nginx # 删除所有已停止的容器清理空间 nerdctl container prune # 交互式清理类似镜像删除可以用fzf组合命令这里有一个踩坑点在K8s环境下使用nerdctl --namespacek8s.io操作由K8s创建的容器时极其不推荐直接使用nerdctl rm -f强制删除。因为这可能会破坏K8s对Pod的管理状态导致不可预知的行为。正确的做法是通过kubectl delete pod来让K8s以受控的方式清理容器。5. 数据与网络管理持久化与连通性容器本身是无状态的数据和网络是让它变得有用的关键。5.1 数据卷Volume管理nerdctl也支持类似Docker的数据卷管理但底层机制略有不同。# 创建一个命名卷 nerdctl volume create my-data # 列出所有卷 nerdctl volume ls # 使用命名卷运行容器 nerdctl run -v my-data:/app/data -it alpine:latest sh # 查看卷的详细信息 nerdctl volume inspect my-data # 你会看到卷在主机上的实际存储路径通常位于 /var/lib/containerd/... 下。 # 删除未使用的卷 nerdctl volume prune注意与Docker不同nerdctl创建的卷是containerd管理的与Docker的卷不互通。如果你需要迁移可能需要手动拷贝数据。对于主机目录绑定挂载-v /host/path:/container/path行为与Docker一致是最简单直接的持久化方式。在K8s的k8s.io命名空间下你可能会看到很多由K8s自动创建的、名称很长的卷这些通常对应着Pod中定义的hostPath或emptyDir不要随意删除它们。5.2 网络管理如前所述nerdctl默认使用CNI插件管理网络。这给了它更强大的网络能力特别是在多主机和K8s集成场景下。# 列出所有CNI网络 nerdctl network ls # 你可能会看到类似 bridge natter 等网络。 # 创建一个新的CNI桥接网络 nerdctl network create my-net --subnet 10.15.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可以通过容器名直接互相通信因为CNI插件通常会集成DNS。 # 将容器连接到另一个网络 nerdctl network connect my-net existing-container # 断开容器与网络的连接 nerdctl network disconnect my-net existing-container # 查看网络详情 nerdctl network inspect my-net # 删除网络必须没有任何容器连接 nerdctl network rm my-net一个高级技巧是使用--ip参数为容器指定静态IP地址前提是网络支持。这在一些需要固定IP的测试场景中很有用。nerdctl run -d --name static-ip-app --network my-net --ip 10.15.0.100 nginx:alpine6. 组合应用与系统管理对于复杂的多容器应用我们通常使用Docker Compose。nerdctl通过nerdctl compose子命令提供了对Compose文件的原生支持这是一个巨大的便利。6.1 使用Compose部署应用首先你需要一个docker-compose.yml文件语法与Docker Compose完全兼容。# docker-compose.yml version: 3.8 services: web: image: nginx:alpine ports: - 80:80 volumes: - ./html:/usr/share/nginx/html db: image: postgres:15-alpine environment: POSTGRES_PASSWORD: secretpassword volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data:然后使用nerdctl compose命令来管理这个应用栈。# 在包含docker-compose.yml的目录下启动所有服务 nerdctl compose up -d # -d 表示后台运行 # 查看Compose项目下的容器状态 nerdctl compose ps # 查看特定服务的日志 nerdctl compose logs web nerdctl compose logs -f db # 跟踪日志 # 在运行中的服务内执行命令 nerdctl compose exec web nginx -t # 停止并移除所有服务、网络、卷但会保留命名卷 nerdctl compose down # 停止并移除所有服务、网络、卷包括命名卷数据会丢失 nerdctl compose down -v实操心得nerdctl compose命令会默认使用当前目录名作为项目名称project name用于隔离不同项目的网络和容器。你可以通过-p参数指定项目名。在K8s节点上使用Compose时务必注意命名空间避免与K8s管理的资源冲突。6.2 系统级查看与资源清理除了管理单个容器和镜像nerdctl也提供了一些系统视角的命令。# 查看容器、镜像、卷、网络的数量和磁盘使用情况类似 docker system df nerdctl system df # 这个命令能快速帮你定位是镜像还是容器占用了大量磁盘空间。 # 查看containerd的事件流类似 docker events nerdctl events # 在一个终端运行这个命令在另一个终端操作容器可以实时看到所有事件pull, create, start, die等对于调试非常有用。 # 全面的系统信息 nerdctl info定期清理对于维护一个健康的系统至关重要。我通常会写一个简单的清理脚本结合nerdctl和系统命令。#!/bin/bash # cleanup.sh echo Cleaning up unused containers... nerdctl container prune -f echo Cleaning up unused images... nerdctl image prune -f echo Cleaning up unused volumes... nerdctl volume prune -f echo Cleaning up unused networks... nerdctl network prune -f echo Cleanup complete. # 可以把这个脚本加入cron定时任务例如每周日凌晨执行一次。7. 进阶特性与排错指南掌握了基础命令我们来看看nerdctl一些独有的进阶功能以及遇到问题时如何排查。7.1 容器检查点Checkpoint与恢复这是一个非常强大的功能允许你将一个运行中容器的状态包括内存、CPU寄存器等保存到磁盘然后在之后恢复运行。这对于调试复杂的内存状态问题或者进行容器迁移很有帮助。这个功能需要安装额外的工具criu并启用containerd的相应配置。# 为运行中的容器创建一个检查点 nerdctl checkpoint create my-running-container my-checkpoint # 从检查点恢复容器可以恢复到原容器或新容器 nerdctl start --checkpoint my-checkpoint my-new-container7.2 使用非默认快照器Snapshotter快照器决定了镜像层如何存储和叠加。overlayfs是最常见的。但nerdctl还支持如stargz、nydus等高级快照器它们针对镜像启动速度或空间效率做了优化。你可以在运行或构建时指定。# 使用stargz快照器运行容器需要提前准备stargz格式的镜像 nerdctl run --snapshotter stargz -it your-stargz-image:latest sh7.3 常见问题排查问题一执行nerdctl命令报错connect: permission denied或failed to dial。这通常是因为当前用户没有权限访问 containerd 的套接字文件默认是/run/containerd/containerd.sock。解决方案使用sudo执行命令sudo nerdctl ps。将用户加入docker或containerd组如果存在然后重新登录。sudo usermod -aG docker $USER # 或 containerd 组直接修改套接字文件的权限不推荐用于生产环境sudo chmod 666 /run/containerd/containerd.sock问题二nerdctl compose up失败提示网络相关错误。这通常是因为CNI插件配置问题或网络冲突。排查步骤检查CNI插件是否安装ls /opt/cni/bin/。检查CNI配置文件ls /etc/cni/net.d/。尝试使用一个更简单的网络驱动或者检查Compose文件中定义的网络子网是否与现有网络冲突。查看containerd日志获取更详细信息sudo journalctl -u containerd -f。问题三在k8s.io命名空间下看不到预期的容器。可能原因容器可能已经退出并被清理。使用nerdctl --namespacek8s.io ps -a查看所有容器。你可能没有使用root权限。尝试sudo nerdctl --namespacek8s.io ps。containerd的k8s.io命名空间可能被禁用或配置不同。检查/etc/containerd/config.toml中[plugins.io.containerd.grpc.v1.cri.containerd]下的sandbox_image和snapshotter配置。问题四磁盘空间不足但nerdctl system df显示占用不大。containerd的镜像和容器数据默认存储在/var/lib/containerd。除了nerdctl管理的内容还可能存在一些旧的、未清理的临时文件或快照。深度清理命令谨慎使用# 停止containerd服务 sudo systemctl stop containerd # 使用containerd自带的清理工具最安全 sudo containerd config dump | grep root # 查看数据根目录 # 通常可以手动清理根目录下的 io.containerd.snapshotter.v1.* 子目录中的陈旧内容但需要专业知识。 # 重启containerd sudo systemctl start containerd更安全的方法是定期重启节点并确保K8s的垃圾回收配置合理。我个人在从Docker转向nerdctl的过程中最大的体会就是它“既熟悉又陌生”。熟悉的是命令语法陌生的是它背后更贴近云原生标准containerd CNI的架构。把它当作一个在纯containerd环境下的“瑞士军刀”特别是在处理K8s节点运维、CI/CD流水线构建等场景时你会发现它不可或缺。刚开始可能会纠结于一些命令参数的细微差别但多用几次肌肉记忆自然就形成了。关键是要理解它和Docker在底层模型上的不同这样遇到问题时你的排查思路才会更清晰。
返回列表