拓冰建站拓冰建站
首页 / 资讯中心 / 正文

containerd容器运行时命令实战:从基础操作到生产环境深度排查

1. 项目概述从Docker到containerd容器运行时命令的深度掌控如果你是从Docker开始接触容器技术的那么对docker ps、docker run这些命令一定不陌生。但你是否知道从Docker 1.11版本之后其底层容器运行时的重任已经逐渐移交给了containerd如今不仅是Docker整个Kubernetes生态也越来越多地直接使用containerd作为其默认的容器运行时。这意味着直接操作containerd不再只是“底层玩家”的专利而是每一位需要深入排查问题、优化性能或是在生产环境中进行精细控制的运维工程师和开发者的必备技能。掌握containerd命令操作就如同掌握了汽车的发动机维修手册当仪表盘Docker/K8s报警时你能直接诊断核心部件的状态。很多人对containerd的印象停留在“一个后台守护进程”通过Docker或K8s的API间接调用。然而当容器网络异常、镜像拉取失败、容器进程卡死等棘手问题出现时高层工具提供的日志可能语焉不详。这时直接使用containerd的命令行工具ctr或crictl就像用听诊器直接贴近心脏能获得最原始、最精准的诊断信息。本文将带你彻底搞懂containerd的核心命令操作不仅告诉你“怎么用”更深入剖析“为什么这么用”以及“用了之后能解决什么实际问题”让你在面对容器黑盒时拥有开盒检修的能力。2. containerd核心架构与命令行工具解析在直接敲命令之前我们需要先理解containerd的基本架构和它为我们提供的“手术刀”。containerd本身是一个守护进程containerd它通过gRPC API对外提供服务。而我们日常操作主要通过两个客户端工具ctr和crictl。2.1 ctr vs. crictl两把不同的手术刀ctr是containerd原生自带的命令行客户端功能强大且直接但它并不完全兼容Kubernetes的容器运行时接口CRI。你可以把它理解为containerd的“开发者模式”或“管理员模式”。通过ctr你能直接管理containerd内部的命名空间、镜像、容器等所有核心对象进行一些底层的调试和操作。crictl则是Kubernetes社区为了兼容CRI标准而提供的命令行工具。当Kubernetes通过CRI与containerd通信时crictl模拟了同样的接口因此它查看和操作的对象正是Kubernetes所看到和管理的容器与Pod。它更贴近于生产环境中K8s集群的运维视角。简单来说使用ctr当你需要直接对containerd守护进程进行底层操作、调试或者你的环境是纯Docker使用containerd作为运行时时。使用crictl当你的环境是Kubernetes并且需要排查K8s创建的Pod和容器问题时。这是目前生产环境更常见的场景。注意在默认安装下crictl需要额外配置才能连接containerd。通常需要设置端点endpoint例如在/etc/crictl.yaml中配置runtime-endpoint: unix:///run/containerd/containerd.sock。2.2 containerd的命名空间Namespace概念这是containerd中一个至关重要的概念也是容易让初学者困惑的地方。命名空间在这里主要用于资源隔离。不同的上层系统如Docker、Kubernetes会使用不同的命名空间来管理各自的资源防止互相干扰。defaultDocker引擎默认使用的命名空间。如果你用ctr命令时不指定-n默认操作的就是这里。运行ctr -n default container list你可能会看到通过docker run创建的容器。k8s.ioKubernetes默认使用的命名空间。K8s创建的所有Pod和容器都生活在这里。这也是crictl工具默认操作的空间。理解这一点至关重要。曾经我遇到一个故障通过docker ps看不到某个容器但系统资源明显被占用。最后用ctr -n k8s.io container list才发现原来是一个残留的Kubernetes Pod容器没有被彻底清理。如果不懂命名空间这个问题根本无法定位。3. 镜像管理拉取、查看与清理镜像是一切容器的源头。containerd的镜像管理命令与Docker有相似之处但更显简洁直接。3.1 拉取与查看镜像使用ctr拉取镜像必须显式指定完整的镜像地址和标签并且可以指定存放的命名空间。# 从Docker Hub拉取nginx镜像到default命名空间 ctr image pull docker.io/library/nginx:alpine # 从私有仓库拉取镜像可能需要先登录ctr本身不支持需配置containerd的credential helper ctr image pull myregistry.local:5000/myapp:v1.0 # 列出default命名空间下的所有镜像 ctr image list # 列出k8s.io命名空间下的所有镜像即K8s使用的镜像 ctr -n k8s.io image list实操心得ctr image list的输出默认比较简洁。如果你需要查看镜像的详细信息如Digest、大小等可以结合-q和ctr image info命令。例如先获取镜像的完整名称ctr image list -q然后ctr image info docker.io/library/nginx:alpine。3.2 镜像标签与导出导入有时我们需要给镜像打上新的标签或者将镜像导出为文件进行迁移。# 为镜像打上一个新的标签类似于 docker tag ctr image tag docker.io/library/nginx:alpine mynginx:latest # 将镜像导出为tar归档文件 ctr image export nginx.tar docker.io/library/nginx:alpine # 从tar文件导入镜像 ctr image import nginx.tar注意事项ctr image import导入的镜像会保留其原有的名称和标签。如果你需要修改可以在导入后使用tag命令。另外containerd的镜像存储基于内容寻址相同的层只会存储一份因此频繁tag操作并不会显著增加磁盘占用。3.3 镜像清理释放磁盘空间这是生产环境中经常需要进行的操作。随着版本迭代会积累大量无用的镜像层。# 删除指定的镜像 ctr image remove docker.io/library/nginx:alpine # 使用crictl删除K8s命名空间下的镜像更常用 crictl rmi myregistry.local:5000/myapp:v1.0深度排查技巧仅仅删除镜像可能无法立即释放磁盘空间因为containerd的镜像和容器数据存储在/var/lib/containerd目录下采用快照snapshot机制。当你删除一个容器时其对应的只读快照可能仍然被其他容器引用或者等待垃圾回收。彻底清理需要两步确保容器已删除使用ctr container list或crictl ps -a确认。手动触发垃圾回收# 告诉containerd清理未使用的资源 ctr content gc # 或者通过crictl如果配置了CRI端点 crictl rmi --prune更激进的方式是直接清理containerd的根目录但极其危险可能导致所有容器无法启动。通常建议定期设置containerd的GC配置而不是手动操作。4. 容器生命周期管理创建、运行与监控这是containerd命令操作的核心。我们将从创建一个简单的容器开始逐步深入到其完整生命周期。4.1 创建与启动容器在containerd中创建容器和启动容器是两步独立的操作这提供了更精细的控制。# 1. 创建容器类似于 docker create # 这里我们创建一个名为mynginx的容器使用之前拉取的nginx镜像并映射宿主机的8080端口到容器的80端口。 ctr container create \ --net-host \ # 使用主机网络模式简化演示。生产环境通常用CNI。 --mount typebind,src/tmp/nginx.conf,dst/etc/nginx/nginx.conf,optionsrbind:ro \ # 绑定挂载配置文件 docker.io/library/nginx:alpine \ mynginx # 2. 启动容器创建一个新的任务Task ctr task start mynginx # 也可以创建并立即启动更接近 docker run -d ctr run -d --net-host docker.io/library/nginx:alpine mynginx_running关键点解析容器Container是一个静态的配置对象包含了镜像、启动命令、资源限制、挂载点等所有配置信息。创建容器只是定义了“蓝图”。任务Task是容器“蓝图”的一个运行实例。一个容器可以没有任务已停止也可以有多个任务虽然不常见。ctr task start才是真正让容器进程跑起来的关键。4.2 查看容器与任务状态掌握如何查看不同维度的状态信息是排查问题的第一步。# 查看所有容器包括已停止的 ctr container list # 查看所有正在运行的任务 ctr task list # 使用crictl查看K8s视角的容器更符合生产环境 crictl ps # 运行中的容器 crictl ps -a # 所有容器包括已退出的 # 查看某个容器的详细信息配置信息 ctr container info mynginx # 查看某个任务的详细信息运行时信息如PID ctr task info mynginx一个经典排查场景假设一个Pod一直处于ContainerCreating状态。你用kubectl describe pod看到的事件信息很模糊。此时可以登录对应节点使用crictl ps -a | grep pod-name查看容器是否被创建。如果创建了但没启动再用crictl inspect container-id查看详细错误信息很可能是镜像拉取失败或挂载卷权限问题。这种直接对接运行时的排查方式效率远高于只在K8s层面打转。4.3 与容器内进程交互我们经常需要进入容器执行命令或者查看它的输出日志。# 在运行中的容器内执行命令类似于 docker exec ctr task exec --exec-id my_exec_1 -t mynginx sh # --exec-id 需要提供一个本次执行的唯一标识符任意字符串即可。 # -t 分配一个伪终端TTY这样你可以使用交互式shell。 # 暂停挂起一个任务 ctr task pause mynginx # 恢复一个被暂停的任务 ctr task resume mynginx日志查看containerd默认将容器的标准输出stdout和标准错误stderr日志收集起来可以通过ctr task logs查看。但请注意containerd默认的日志驱动是有限的它不像Docker那样有丰富的日志驱动如json-file, syslog, journald等。在生产环境中特别是Kubernetes环境下容器的日志通常由Kubelet通过CRI接口获取然后交给集群层面的日志收集方案如Fluentd、Filebeat处理。直接通过ctr task logs查看的只是当前会话的日志流。# 查看容器的标准输出日志 ctr task logs mynginx # 跟随日志输出类似于 tail -f ctr task logs -f mynginx4.4 停止与删除容器清理容器同样需要分两步先停止任务再删除容器。# 1. 停止任务发送SIGTERM信号等待优雅终止 ctr task kill mynginx # 强制停止任务发送SIGKILL信号 ctr task kill -s SIGKILL mynginx # 2. 删除任务必须先停止任务 ctr task delete mynginx # 3. 删除容器 ctr container delete mynginx # 使用crictl一键停止并删除K8s容器 crictl stop container-id crictl rm container-id避坑指南直接使用ctr container delete删除一个正在运行的容器会报错。你必须先kill再delete其任务。这虽然多了一步但保证了操作的明确性避免了误删运行中容器导致业务中断的风险。相比之下docker rm -f把这两步合并了方便但不够透明。5. 高级操作与生产环境问题排查掌握了基础的生命周期管理后我们来看一些更高级的操作和真实的生产环境排查案例。5.1 网络与端口映射上面例子中我们使用了--net-host这在实际生产环境中很少见因为失去了网络隔离。containerd自身不管理网络网络功能由CNIContainer Network Interface插件提供。当我们通过Kubernetes或Docker创建容器时它们会调用CNI插件来配置网络。对于使用ctr直接创建的容器如果想拥有独立的网络命名空间和IP需要更复杂的步骤通常涉及手动调用cni工具或使用nerdctl一个兼容Docker CLI但直接操作containerd的工具。对于绝大多数生产运维场景我们更多的是查看网络状态而非创建。# 查看容器的网络命名空间信息需要privileged权限 # 首先找到容器的PID ctr task info mynginx | grep PID # 假设PID是12345查看其网络命名空间 sudo nsenter -t 12345 -n ip addr show5.2 资源限制与cgroupscontainerd在创建容器时可以通过OCIOpen Container Initiative规范来设置cgroups限制。# 创建一个限制CPU和内存的容器示例实际参数更复杂 # 这通常通过一个config.json文件来定义然后传递给ctr # 简化演示使用ctr的--with-json参数需要提前准备好配置文件 ctr container create \ --config /path/to/config.json \ docker.io/library/stress:latest \ stressedconfig.json文件里可以定义linux.resources字段详细设置CPU份额、内存限制、设备访问等。但在Kubernetes环境中这些限制是通过Pod的resources.requests/limits来定义的由Kubelet翻译成OCI配置传递给containerd。因此直接使用ctr手动设置cgroups的场景多用于特定的性能测试或底层资源控制实验。5.3 生产环境问题排查实录案例一容器进程卡死无法正常停止现象通过crictl stop或kubectl delete pod --force --grace-period0都无法删除一个Pod状态一直是Terminating。排查步骤登录节点用crictl ps -a | grep pod找到问题容器ID。用crictl inspect container-id查看容器状态确认其PID。用ps aux | grep pid确认进程是否存在以及其状态是Z僵尸进程还是D不可中断睡眠。如果进程存在尝试向该进程直接发送信号sudo kill -9 pid。如果进程已变成僵尸其父进程通常是containerd-shim。此时需要重启containerd-shim进程。但注意重启shim会影响该节点上所有由该shim管理的容器。更安全的方法是重启整个containerd服务。这是最后的手段因为会导致节点上所有容器短暂重启。sudo systemctl restart containerd重启后再次用crictl rm清理残留的容器。案例二镜像拉取一直失败报错“unknown”现象Pod事件显示Failed to pull image但错误信息不明确。排查步骤首先用crictl pull手动拉取一下镜像看错误信息是否更详细。crictl pull myregistry.local:5000/myapp:v1.0如果提示认证失败检查containerd的认证配置。containerd的认证配置在/etc/containerd/config.toml中的[plugins.io.containerd.grpc.v1.cri.registry.configs]或[plugins.io.containerd.grpc.v1.cri.registry.auths]部分。也可以使用nerdctl login配置但ctr不直接支持。更底层地可以直接查看containerd的日志它记录了gRPC调用的详细错误。sudo journalctl -u containerd -f --since 5 minutes ago常见原因私有仓库证书不受信任。可以将仓库的CA证书放到/etc/containerd/certs.d/registry-host/目录下或者修改config.toml对特定仓库设置insecure_skip_verify true仅限测试环境。案例三磁盘空间不足containerd无法创建新容器现象新Pod调度到节点后容器创建失败日志提到“no space left on device”。排查步骤使用df -h检查/var/lib/containerd所在分区的使用情况。使用du -sh /var/lib/containerd/*查看哪个子目录占用最大。通常是io.containerd.content.v1.content镜像内容或io.containerd.snapshotter.v1.overlayfs容器快照。执行清理# 清理所有未使用的镜像、容器、快照 ctr content gc # 对于K8s环境清理所有未使用的镜像 crictl rmi --prune # 注意crictl的prune可能不会清理所有containerd内部的缓存有时需要结合两者。如果清理后空间仍然紧张可以考虑调整containerd的存储配置例如使用单独的、更大的数据盘并通过软链接挂载到/var/lib/containerd。6. 常用命令速查与最佳实践为了方便日常使用这里将最常用的命令整理成表。记住在Kubernetes环境下优先使用crictl在纯Docker或需要底层调试时使用ctr。6.1 命令速查表操作ctr 命令 (default/k8s.io 命名空间)crictl 命令 (k8s.io 命名空间)说明列出镜像ctr image list或ctr -n k8s.io image listcrictl imagescrictl默认只看k8s.io空间拉取镜像ctr image pull imagecrictl pull image删除镜像ctr image remove imagecrictl rmi image-id列出容器ctr container listcrictl ps(运行中) /crictl ps -a(全部)创建容器ctr container create ... image name(通常由K8s管理)启动容器ctr task start namecrictl start cid执行命令ctr task exec -t --exec-id id name shcrictl exec -it cid sh查看日志ctr task logs namecrictl logs cid停止容器ctr task kill namecrictl stop cid删除容器ctr container delete name(需先删task)crictl rm cid查看详情ctr container info name/ctr task info namecrictl inspect cid6.2 最佳实践与心得明确工具场景日常K8s节点运维把crictl配置好并熟练使用。需要深入containerd内部调试时再请出ctr。善用命名空间任何时候对容器或镜像操作感到困惑先想想它在哪个命名空间。default和k8s.io是两个完全独立的世界。日志是首要线索无论是journalctl -u containerd还是crictl logs遇到问题先看日志。containerd的日志级别可以在config.toml中调整排查复杂问题时可以临时设为debug。理解“任务”与“容器”这个概念区分是理解containerd操作逻辑的关键。删除一个容器前必须确保其任务已终止。谨慎操作存储/var/lib/containerd目录结构复杂不要手动删除里面的文件。清理空间使用内置的GC命令ctr content gc。配置调优生产环境务必根据实际情况调整/etc/containerd/config.toml特别是systemd_cgroup如果使用systemd、sandbox_imagePause镜像、registry镜像加速和认证等部分。掌握containerd的命令行操作就像是获得了容器引擎的“超级用户”权限。它剥去了Docker或Kubernetes这层“外壳”让你能直接与容器的“心脏”对话。这种能力在风平浪静时或许用不上但一旦系统出现深水区的故障它就是带你找到问题根源、恢复业务稳定的最强有力的工具。从今天起试着在测试环境中多用用ctr和crictl熟悉它们的输出和逻辑这份投入会在某个紧急的深夜给你带来丰厚的回报。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门