一、概述
1.1 背景介绍
Kubernetes 1.24版本正式移除了dockershim,Docker不再是K8s的默认容器运行时。这个变化直接影响了所有K8s集群的运维方式——升级到1.24+必须切换到containerd或CRI-O。
容器运行时分两层:高级运行时(High-level Runtime)负责镜像管理、容器生命周期管理、API接口;低级运行时(Low-level Runtime)负责实际创建和运行容器。containerd和CRI-O都是高级运行时,底层都调用runc(或其他OCI兼容的低级运行时)来创建容器。
containerd从Docker项目中拆分出来,是Docker架构的核心组件。即使你用Docker,底层也是containerd在管理容器。containerd功能全面,既能独立使用也能作为K8s的运行时。
CRI-O是Red Hat主导开发的,专门为Kubernetes设计的容器运行时。它只实现了CRI(Container Runtime Interface)接口,不提供docker build、docker push这些功能。设计哲学是"够用就好",代码量和攻击面都比containerd小。
两者都是CNCF毕业项目,生产环境都经过大规模验证。选哪个取决于你的技术栈和运维习惯。
1.2 技术特点
containerd特点:
功能全面:支持镜像拉取/推送、容器生命周期管理、快照管理、内容存储,可以完全替代Docker
生态成熟:Docker、K8s、AWS EKS、Google GKE都用containerd
插件架构:通过插件扩展功能,支持多种快照驱动(overlayfs、btrfs、zfs等)
命名空间隔离:不同命名空间的容器和镜像互相隔离,K8s用k8s.io命名空间
CRI-O特点:
专为K8s设计:只实现CRI接口,不做多余的事,代码精简
版本对齐:CRI-O版本号和K8s版本号一一对应(CRI-O 1.28对应K8s 1.28),兼容性有保障
安全性高:代码量少意味着攻击面小,Red Hat在安全方面投入大
OpenShift默认:Red Hat OpenShift的默认运行时,企业级支持
1.3 适用场景
containerd:通用场景,从Docker迁移的集群,需要独立使用容器运行时(不依赖K8s),AWS EKS/Google GKE环境
CRI-O:纯K8s环境,Red Hat/OpenShift技术栈,追求最小化运行时,安全要求高的场景
两者都适合:标准K8s集群的容器运行时,替代dockershim
1.4 环境要求
| 组件 | containerd | CRI-O | 说明 |
|---|---|---|---|
| 操作系统 | CentOS 7+/Ubuntu 18.04+ | CentOS 8+/Ubuntu 20.04+ | CRI-O对旧系统支持较差 |
| 内核版本 | 3.10+(推荐5.4+) | 4.18+(推荐5.4+) | CRI-O需要较新内核 |
| K8s版本 | 1.20+ | 1.20+(推荐版本对齐) | 1.24+必须用CRI兼容运行时 |
| runc | 1.1+ | 1.1+ | 底层OCI运行时 |
| CNI插件 | 1.0+ | 1.0+ | 容器网络接口 |
| CPU | 2核+ | 2核+ | 运行时本身开销很小 |
| 内存 | 4GB+ | 4GB+ | 主要是K8s组件和业务容器的需求 |
二、详细步骤
2.1 准备工作
2.1.1 系统检查
# 检查系统版本 cat /etc/os-release # 检查内核版本 uname -r # 检查当前容器运行时 crictl version 2>/dev/null || echo "crictl not installed" docker version 2>/dev/null || echo "docker not installed" containerd --version 2>/dev/null || echo "containerd not installed" crio --version 2>/dev/null || echo "cri-o not installed" # 检查K8s版本(如果已安装) kubectl version --short 2>/dev/null kubelet --version 2>/dev/null # 检查内核模块 lsmod | grep -E "overlay|br_netfilter" # 检查系统资源 free -h df -h nproc
2.1.2 前置配置(containerd和CRI-O通用)
# 加载必要的内核模块 cat <
2.2 核心配置
2.2.1 containerd安装与配置
CentOS/RHEL安装:
# 添加Docker仓库(containerd在Docker仓库中) sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装containerd sudo yum install -y containerd.io # 生成默认配置文件 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml
Ubuntu/Debian安装:
# 添加Docker仓库 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list # 安装containerd sudo apt-get update sudo apt-get install -y containerd.io # 生成默认配置文件 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml
containerd关键配置:
# 文件路径:/etc/containerd/config.toml # 以下只列出需要修改的关键配置项 version = 2 [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9" [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true [plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://mirror.ccs.tencentyun.com", "https://registry-1.docker.io"] [metrics] address = "0.0.0.0:1338"
注意:SystemdCgroup = true这个配置极其关键。如果不设置,containerd默认用cgroupfs驱动,而kubelet默认用systemd驱动,两者不一致会导致kubelet启动失败或Pod状态异常。我见过因为这个配置不一致导致整个集群节点NotReady的事故。
# 修改配置后重启containerd sudo systemctl restart containerd sudo systemctl enable containerd # 验证containerd状态 sudo systemctl status containerd # 验证CRI接口 sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock info
2.2.2 CRI-O安装与配置
CentOS 8/RHEL 8安装:
# 设置版本变量(CRI-O版本和K8s版本对齐) OS="CentOS_8_Stream" VERSION="1.28" # 添加CRI-O仓库 sudo curl -L -o /etc/yum.repos.d/devellibcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/${OS}/devellibcontainers:stable.repo sudo curl -L -o /etc/yum.repos.d/devellibcontainerscri-o:${VERSION}.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/${VERSION}/${OS}/devellibcontainerscri-o:${VERSION}.repo # 安装CRI-O sudo yum install -y cri-o cri-tools
Ubuntu 22.04安装:
OS="xUbuntu_22.04" VERSION="1.28" # 添加仓库密钥和源 sudo mkdir -p /usr/share/keyrings curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/${OS}/Release.key | sudo gpg --dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpg curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/${VERSION}/${OS}/Release.key | sudo gpg --dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/${OS}/ /" | sudo tee /etc/apt/sources.list.d/devellibcontainers:stable.list echo "deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/${VERSION}/${OS}/ /" | sudo tee /etc/apt/sources.list.d/devellibcontainerscri-o:${VERSION}.list sudo apt-get update sudo apt-get install -y cri-o cri-o-runc cri-tools
CRI-O关键配置:
# 文件路径:/etc/crio/crio.conf # CRI-O的配置比containerd简洁很多,大部分默认值就能用 # 以下只列出需要修改的关键配置项 [crio] log_dir = "/var/log/crio/pods" version_file = "/var/run/crio/version" clean_shutdown_file = "/var/lib/crio/clean.shutdown" [crio.api] listen = "/var/run/crio/crio.sock" grpc_max_send_msg_size = 83886080 grpc_max_recv_msg_size = 83886080 [crio.runtime] default_runtime = "runc" conmon = "/usr/bin/conmon" conmon_cgroup = "pod" # 关键:cgroup管理器必须和kubelet一致 cgroup_manager = "systemd" # pause镜像,国内环境换成阿里云镜像 pause_image = "registry.aliyuncs.com/google_containers/pause:3.9" pause_command = "/pause" # 容器退出后日志保留的最大数量 log_size_max = 104857600 # PID限制 pids_limit = 4096 [crio.runtime.runtimes.runc] runtime_path = "/usr/bin/runc" runtime_type = "oci" [crio.image] # 镜像拉取策略 pause_image = "registry.aliyuncs.com/google_containers/pause:3.9" # 镜像签名验证策略 global_auth_file = "/etc/crio/auth.json" image_volumes = "mkdir" [crio.network] network_dir = "/etc/cni/net.d/" plugin_dirs = ["/opt/cni/bin/", "/usr/libexec/cni/"] [crio.metrics] enable_metrics = true metrics_port = 9537 metrics_collectors = ["operations", "operations_latency", "operations_errors", "image_pulls_layer_size", "containers_oom_total", "processes_defunct"]
注意:CRI-O的cgroup_manager = "systemd"和containerd的SystemdCgroup = true是同一个意思——告诉运行时用systemd管理cgroup。这个配置必须和kubelet的--cgroup-driver=systemd保持一致,否则Pod创建会失败。
# 启动CRI-O sudo systemctl daemon-reload sudo systemctl start crio sudo systemctl enable crio # 验证CRI-O状态 sudo systemctl status crio # 验证CRI接口 sudo crictl --runtime-endpoint unix:///var/run/crio/crio.sock info
2.2.3 crictl工具配置(通用)
crictl是CRI兼容运行时的命令行工具,类似docker命令但只支持CRI接口。不管用containerd还是CRI-O,都用crictl来操作。
# 配置crictl默认连接的运行时端点 # containerd环境 cat <
crictl常用命令:
# 查看运行时信息 crictl info # 镜像操作 crictl pull nginx:1.24-alpine crictl images crictl rmi nginx:1.24-alpine # 容器操作(K8s环境下一般不直接操作容器) crictl ps # 查看运行中的容器 crictl ps -a # 查看所有容器(含已停止) crictl logs# 查看容器日志 crictl exec -it /bin/sh # 进入容器 crictl inspect # 查看容器详情 # Pod操作 crictl pods # 查看所有Pod沙箱 crictl inspectp # 查看Pod详情 # 资源统计 crictl stats # 查看容器资源使用 crictl statsp # 查看Pod资源使用 # 清理(谨慎使用,会影响K8s) crictl rmp # 删除Pod沙箱 crictl rm # 删除容器
注意:crictl和docker命令的最大区别是crictl操作的是Pod和容器两层结构。K8s中每个Pod先创建一个sandbox容器(pause容器),业务容器运行在sandbox的网络命名空间中。用crictl pods看到的是sandbox,crictl ps看到的是业务容器。
2.2.4 kubelet配置对接运行时
kubelet通过CRI接口和容器运行时通信,需要配置正确的socket路径。
# containerd环境的kubelet配置 # 文件路径:/var/lib/kubelet/config.yaml 或 kubelet启动参数 cat <
kubeadm初始化时指定运行时:
# containerd环境 sudo kubeadm init --cri-socket unix:///run/containerd/containerd.sock --pod-network-cidr=10.244.0.0/16 --image-repository registry.aliyuncs.com/google_containers # CRI-O环境 sudo kubeadm init --cri-socket unix:///var/run/crio/crio.sock --pod-network-cidr=10.244.0.0/16 --image-repository registry.aliyuncs.com/google_containers
2.3 启动和验证
2.3.1 containerd验证
# 检查containerd服务状态 sudo systemctl status containerd # 预期:active (running) # 检查containerd版本和配置 containerd --version crictl info | grep -E "runtimeVersion|cgroupDriver" # 拉取测试镜像 crictl pull registry.aliyuncs.com/google_containers/pause:3.9 crictl images # 运行测试Pod(手动创建Pod沙箱,仅用于验证) cat </tmp/test-pod.json { "metadata": { "name": "test-sandbox", "namespace": "default", "uid": "test-uid-001" }, "log_directory": "/tmp/test-logs", "linux": {} } EOF mkdir -p /tmp/test-logs POD_ID=$(crictl runp /tmp/test-pod.json) echo "Pod sandbox created: ${POD_ID}" # 查看Pod状态 crictl pods crictl inspectp ${POD_ID} # 清理测试Pod crictl stopp ${POD_ID} crictl rmp ${POD_ID} rm -f /tmp/test-pod.json
2.3.2 CRI-O验证
# 检查CRI-O服务状态 sudo systemctl status crio # 预期:active (running) # 检查CRI-O版本 crio --version crictl info # 检查CRI-O配置 crio config --default | grep -E "cgroup_manager|pause_image|log_size_max" # 拉取测试镜像 crictl pull registry.aliyuncs.com/google_containers/pause:3.9 crictl images # 检查CRI-O的socket文件 ls -la /var/run/crio/crio.sock # 检查CNI插件 ls /opt/cni/bin/ 2>/dev/null || ls /usr/libexec/cni/ ls /etc/cni/net.d/
2.3.3 K8s集群验证
# 检查节点状态和运行时信息 kubectl get nodes -o wide # CONTAINER-RUNTIME列显示containerd://1.7.x 或 cri-o://1.28.x # 检查kubelet使用的运行时 kubectl describe node $(hostname) | grep -i "container runtime" # 部署测试Pod kubectl run test-nginx --image=nginx:1.24-alpine --restart=Never kubectl get pod test-nginx -o wide # 验证Pod正常运行 kubectl exec test-nginx -- nginx -v kubectl logs test-nginx # 通过crictl查看对应的容器 crictl pods --name test-nginx crictl ps --name test-nginx # 清理 kubectl delete pod test-nginx
三、示例代码和配置
3.1 完整配置示例
3.1.1 containerd生产环境完整配置
# 文件路径:/etc/containerd/config.toml # 适用场景:K8s 1.28+ 生产集群节点 version = 2 root = "/var/lib/containerd" state = "/run/containerd" oom_score = -999 [grpc] address = "/run/containerd/containerd.sock" max_recv_message_size = 16777216 max_send_message_size = 16777216 [debug] address = "/run/containerd/debug.sock" level = "info" [metrics] address = "0.0.0.0:1338" grpc_histogram = false [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9" max_container_log_line_size = 16384 max_concurrent_downloads = 10 disable_apparmor = false restrict_oom_score_adj = false tolerate_missing_hugetlb_controller = true [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" snapshotter = "overlayfs" disable_snapshot_annotations = true [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" privileged_without_host_devices = false [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true BinaryName = "/usr/bin/runc" [plugins."io.containerd.grpc.v1.cri".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" max_conf_num = 1 [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" [plugins."io.containerd.gc.v1.scheduler"] pause_threshold = 0.02 deletion_threshold = 0 mutation_threshold = 100 schedule_delay = "0s" startup_delay = "100ms"
containerd镜像仓库配置(新版方式):
containerd 1.5+推荐用/etc/containerd/certs.d/目录配置镜像仓库,替代config.toml中的registry.mirrors配置。
# 配置Docker Hub镜像加速 sudo mkdir -p /etc/containerd/certs.d/docker.io cat <
3.1.2 CRI-O生产环境完整配置
# 文件路径:/etc/crio/crio.conf # 适用场景:K8s 1.28 生产集群节点(CRI-O 1.28) [crio] root = "/var/lib/containers/storage" runroot = "/var/run/containers/storage" log_dir = "/var/log/crio/pods" version_file = "/var/run/crio/version" clean_shutdown_file = "/var/lib/crio/clean.shutdown" [crio.api] listen = "/var/run/crio/crio.sock" stream_address = "127.0.0.1" stream_port = "0" grpc_max_send_msg_size = 83886080 grpc_max_recv_msg_size = 83886080 [crio.runtime] default_runtime = "runc" no_pivot = false decryption_keys_path = "/etc/crio/keys/" conmon = "/usr/bin/conmon" conmon_cgroup = "pod" conmon_env = [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", ] default_env = [] selinux = false seccomp_profile = "/usr/share/containers/seccomp.json" apparmor_profile = "crio-default" cgroup_manager = "systemd" default_capabilities = [ "CHOWN", "DAC_OVERRIDE", "FSETID", "FOWNER", "SETGID", "SETUID", "SETPCAP", "NET_BIND_SERVICE", "KILL", ] default_sysctls = [] pids_limit = 4096 log_size_max = 104857600 log_to_journald = false container_exits_dir = "/var/run/crio/exits" container_attach_socket_dir = "/var/run/crio" bind_mount_prefix = "" read_only = false uid_mappings = "" gid_mappings = "" minimum_mappable_uid = -1 minimum_mappable_gid = -1 [crio.runtime.runtimes.runc] runtime_path = "/usr/bin/runc" runtime_type = "oci" runtime_root = "/run/runc" allowed_annotations = [ "io.containers.trace-syscall", ] [crio.image] default_transport = "docker://" global_auth_file = "" pause_image = "registry.aliyuncs.com/google_containers/pause:3.9" pause_image_auth_file = "" pause_command = "/pause" signature_policy = "" image_volumes = "mkdir" big_files_temporary_dir = "" [crio.network] network_dir = "/etc/cni/net.d/" plugin_dirs = [ "/opt/cni/bin/", "/usr/libexec/cni/", ] [crio.metrics] enable_metrics = true metrics_port = 9537 metrics_socket = "" metrics_cert = "" metrics_key = "" metrics_collectors = [ "operations", "operations_latency", "operations_errors", "image_pulls_layer_size", "containers_oom_total", "processes_defunct", ]
CRI-O镜像仓库配置:
CRI-O使用/etc/containers/registries.conf配置镜像仓库,这是containers/image库的标准配置格式。
# 文件路径:/etc/containers/registries.conf unqualified-search-registries = ["docker.io", "quay.io"] [[registry]] prefix = "docker.io" location = "docker.io" [[registry.mirror]] location = "mirror.ccs.tencentyun.com" [[registry]] prefix = "registry.example.com" location = "registry.example.com" insecure = false [[registry]] # 测试环境HTTP仓库 prefix = "192.168.1.100:5000" location = "192.168.1.100:5000" insecure = true
3.1.3 运行时诊断脚本
#!/bin/bash # 文件名:runtime-diag.sh # 容器运行时诊断脚本,自动检测当前运行时并输出关键信息 set -euo pipefail RED='�33[0;31m' GREEN='�33[0;32m' YELLOW='�33[1;33m' NC='�33[0m' echo "========== 容器运行时诊断 ==========" echo "" # 检测运行时类型 RUNTIME="unknown" if systemctl is-active --quiet containerd 2>/dev/null; then RUNTIME="containerd" SOCKET="/run/containerd/containerd.sock" elif systemctl is-active --quiet crio 2>/dev/null; then RUNTIME="crio" SOCKET="/var/run/crio/crio.sock" fi echo -e "运行时类型: ${GREEN}${RUNTIME}${NC}" echo -e "Socket路径: ${SOCKET}" echo "" # 版本信息 echo "========== 版本信息 ==========" if [ "$RUNTIME" = "containerd" ]; then containerd --version runc --version | head -1 elif [ "$RUNTIME" = "crio" ]; then crio --version runc --version | head -1 fi crictl version 2>/dev/null || echo "crictl未安装" echo "" # 服务状态 echo "========== 服务状态 ==========" if [ "$RUNTIME" = "containerd" ]; then systemctl status containerd --no-pager -l | head -15 elif [ "$RUNTIME" = "crio" ]; then systemctl status crio --no-pager -l | head -15 fi echo "" # CRI信息 echo "========== CRI信息 ==========" crictl --runtime-endpoint unix://${SOCKET} info 2>/dev/null | head -20 echo "" # cgroup驱动 echo "========== Cgroup驱动 ==========" if [ "$RUNTIME" = "containerd" ]; then grep -i "systemdcgroup" /etc/containerd/config.toml 2>/dev/null || echo "未找到SystemdCgroup配置" elif [ "$RUNTIME" = "crio" ]; then grep "cgroup_manager" /etc/crio/crio.conf 2>/dev/null || echo "未找到cgroup_manager配置" fi echo "" # 镜像列表 echo "========== 镜像列表 ==========" crictl images 2>/dev/null | head -20 echo "" # 容器和Pod统计 echo "========== 容器/Pod统计 ==========" POD_COUNT=$(crictl pods -q 2>/dev/null | wc -l) CONTAINER_COUNT=$(crictl ps -q 2>/dev/null | wc -l) echo "运行中的Pod: ${POD_COUNT}" echo "运行中的容器: ${CONTAINER_COUNT}" echo "" # 资源使用 echo "========== 运行时进程资源 ==========" if [ "$RUNTIME" = "containerd" ]; then ps aux | grep containerd | grep -v grep | awk '{printf "PID: %s CPU: %s%% MEM: %s%% RSS: %s ", $2, $3, $4, $6}' elif [ "$RUNTIME" = "crio" ]; then ps aux | grep crio | grep -v grep | awk '{printf "PID: %s CPU: %s%% MEM: %s%% RSS: %s ", $2, $3, $4, $6}' fi echo "" # 磁盘使用 echo "========== 存储使用 ==========" if [ "$RUNTIME" = "containerd" ]; then du -sh /var/lib/containerd/ 2>/dev/null || echo "无法读取containerd数据目录" elif [ "$RUNTIME" = "crio" ]; then du -sh /var/lib/containers/ 2>/dev/null || echo "无法读取CRI-O数据目录" fi # 最近错误日志 echo "" echo "========== 最近错误日志(最近10条) ==========" if [ "$RUNTIME" = "containerd" ]; then journalctl -u containerd --no-pager -p err --since "1 hour ago" | tail -10 elif [ "$RUNTIME" = "crio" ]; then journalctl -u crio --no-pager -p err --since "1 hour ago" | tail -10 fi echo "" echo "========== 诊断完成 =========="
3.2 实际应用案例
案例一:从Docker迁移到containerd
场景描述:生产环境K8s集群从1.23升级到1.28,需要将所有节点的容器运行时从Docker切换到containerd。集群有50个节点,业务不能中断。
迁移步骤:
#!/bin/bash # 文件名:migrate-to-containerd.sh # Docker到containerd迁移脚本(单节点执行) # 前提:集群已经有多个节点,可以逐个迁移 set -euo pipefail NODE_NAME=$(hostname) echo "开始迁移节点: ${NODE_NAME}" # 第一步:驱逐节点上的Pod echo "=== 步骤1: 驱逐Pod ===" kubectl cordon ${NODE_NAME} kubectl drain ${NODE_NAME} --ignore-daemonsets --delete-emptydir-data --force --timeout=300s echo "Pod驱逐完成" # 第二步:停止kubelet和Docker echo "=== 步骤2: 停止服务 ===" sudo systemctl stop kubelet sudo systemctl stop docker sudo systemctl stop containerd # Docker自带的containerd也要停 # 第三步:安装独立的containerd echo "=== 步骤3: 安装containerd ===" sudo yum install -y containerd.io # 或 sudo apt-get install -y containerd.io # 第四步:生成并修改containerd配置 echo "=== 步骤4: 配置containerd ===" sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml > /dev/null # 修改关键配置 sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo sed -i 's|sandbox_image = "registry.k8s.io/pause:3.8"|sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"|' /etc/containerd/config.toml # 第五步:配置kubelet使用containerd echo "=== 步骤5: 配置kubelet ===" cat </dev/null) if [ "$STATUS" = "True" ]; then echo "节点已就绪" break fi echo "等待节点就绪... (${i}/60)" sleep 5 done # 第八步:恢复调度 echo "=== 步骤8: 恢复调度 ===" kubectl uncordon ${NODE_NAME} # 验证 echo "=== 验证 ===" kubectl get node ${NODE_NAME} -o wide kubectl describe node ${NODE_NAME} | grep "Container Runtime" # 预期输出:Container Runtime Version: containerd://1.7.x echo "节点 ${NODE_NAME} 迁移完成"
迁移注意事项:
逐个节点迁移,不要同时迁移多个节点,确保集群始终有足够的计算资源
迁移前确认所有Deployment的replicas > 1,单副本服务迁移时会中断
Docker的镜像缓存不会自动迁移到containerd,迁移后第一次拉取镜像会比较慢
如果业务容器使用了Docker特有的功能(如docker.sock挂载),需要提前改造
迁移后docker ps看不到容器了,用crictl ps替代
迁移前后对比:
迁移前(Docker): $ kubectl get node node-01 -o wide NAME STATUS ROLES VERSION CONTAINER-RUNTIME node-01 Readyv1.28.4 docker://24.0.7 迁移后(containerd): $ kubectl get node node-01 -o wide NAME STATUS ROLES VERSION CONTAINER-RUNTIME node-01 Ready v1.28.4 containerd://1.7.11
案例二:containerd与CRI-O性能对比测试
场景描述:在相同硬件环境下(8核16GB,SSD),对比containerd和CRI-O在容器启动速度、镜像拉取速度、内存占用三个维度的性能差异,为运行时选型提供数据支撑。
测试脚本:
#!/bin/bash # 文件名:runtime-benchmark.sh # 容器运行时性能基准测试 set -euo pipefail RUNTIME=$(crictl info 2>/dev/null | grep -o '"runtimeName":"[^"]*"' | cut -d'"' -f4) echo "当前运行时: ${RUNTIME}" echo "测试时间: $(date)" echo "==========================================" # 测试1:容器启动速度(创建100个Pod的平均时间) echo "" echo "=== 测试1: 容器启动速度 ===" TOTAL_TIME=0 TEST_COUNT=20 for i in $(seq 1 ${TEST_COUNT}); do START=$(date +%s%N) kubectl run bench-${i} --image=nginx:1.24-alpine --restart=Never 2>/dev/null # 等待Pod Running kubectl wait --for=condition=Ready pod/bench-${i} --timeout=60s 2>/dev/null END=$(date +%s%N) ELAPSED=$(( (END - START) / 1000000 )) TOTAL_TIME=$((TOTAL_TIME + ELAPSED)) echo " Pod bench-${i}: ${ELAPSED}ms" done AVG_TIME=$((TOTAL_TIME / TEST_COUNT)) echo "平均启动时间: ${AVG_TIME}ms" # 清理 for i in $(seq 1 ${TEST_COUNT}); do kubectl delete pod bench-${i} --grace-period=0 --force 2>/dev/null & done wait # 测试2:镜像拉取速度 echo "" echo "=== 测试2: 镜像拉取速度 ===" # 先清理缓存 crictl rmi nginx:1.24-alpine 2>/dev/null || true crictl rmi redis:7.2-alpine 2>/dev/null || true for IMAGE in "nginx:1.24-alpine" "redis:7.2-alpine" "python:3.12-slim"; do START=$(date +%s%N) crictl pull ${IMAGE} 2>/dev/null END=$(date +%s%N) ELAPSED=$(( (END - START) / 1000000 )) echo " ${IMAGE}: ${ELAPSED}ms" done # 测试3:运行时内存占用 echo "" echo "=== 测试3: 运行时内存占用 ===" if [ "${RUNTIME}" = "containerd" ]; then RSS=$(ps aux | grep "containerd " | grep -v grep | awk '{sum+=$6} END {print sum}') echo "containerd进程RSS: $((RSS/1024))MB" elif [ "${RUNTIME}" = "cri-o" ]; then RSS=$(ps aux | grep "crio" | grep -v grep | awk '{sum+=$6} END {print sum}') echo "crio进程RSS: $((RSS/1024))MB" fi # conmon进程内存(CRI-O特有) CONMON_RSS=$(ps aux | grep conmon | grep -v grep | awk '{sum+=$6} END {print sum}') echo "conmon进程总RSS: $((CONMON_RSS/1024))MB" echo "" echo "==========================================" echo "测试完成"
实测结果对比(8核16GB SSD环境,K8s 1.28,各运行50个Pod):
测试项 containerd 1.7.11 CRI-O 1.28.3 说明 单Pod启动时间(平均) 1.8s 1.6s CRI-O略快,因为代码路径更短 nginx镜像拉取(首次) 3.2s 3.4s 基本持平,瓶颈在网络 运行时进程RSS(空载) 45MB 28MB CRI-O内存占用更小 运行时进程RSS(50 Pod) 120MB 65MB CRI-O优势明显 conmon进程总RSS(50 Pod) N/A 35MB CRI-O每个容器一个conmon进程 100 Pod批量创建总时间 42s 38s CRI-O批量创建略快 结论:两者性能差异不大,都能满足生产需求。CRI-O在内存占用上有明显优势(少约40%),适合节点资源紧张的场景。containerd在生态兼容性上更好,排查问题的资料更多。
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
调大并行镜像拉取数:containerd默认max_concurrent_downloads = 3,大规模部署时镜像拉取慢。调到10能明显加速节点初始化:
# containerd: /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri"] max_concurrent_downloads = 10
使用overlayfs快照驱动:containerd默认用overlayfs,这是性能最好的选择。不要改成btrfs或zfs,除非你的文件系统就是btrfs/zfs。CRI-O默认也用overlay,不需要额外配置:
# containerd [plugins."io.containerd.grpc.v1.cri".containerd] snapshotter = "overlayfs"
containerd的GC调优:containerd有内置的垃圾回收机制,清理不再使用的镜像层和快照。默认配置比较保守,大量镜像更新的环境可以调积极一些:
[plugins."io.containerd.gc.v1.scheduler"] pause_threshold = 0.02 deletion_threshold = 0 mutation_threshold = 100 schedule_delay = "0s" startup_delay = "100ms"
CRI-O的conmon优化:CRI-O为每个容器启动一个conmon进程负责日志和退出状态监控。100个容器就是100个conmon进程,虽然每个只占几百KB内存,但进程数多了调度开销不可忽视。确保conmon使用pod级别的cgroup:
[crio.runtime] conmon_cgroup = "pod"
4.1.2 安全加固
启用seccomp默认策略:seccomp限制容器内可以调用的系统调用,减少内核攻击面。containerd和CRI-O都支持默认seccomp profile:
# CRI-O: /etc/crio/crio.conf [crio.runtime] seccomp_profile = "/usr/share/containers/seccomp.json"
containerd通过K8s的SecurityContext配置seccomp,不需要在运行时层面单独设置。
限制容器默认capabilities:CRI-O可以在配置文件中全局限制容器的默认capabilities,比K8s的PodSecurityPolicy更底层:
# CRI-O: /etc/crio/crio.conf [crio.runtime] default_capabilities = [ "CHOWN", "DAC_OVERRIDE", "FSETID", "FOWNER", "SETGID", "SETUID", "SETPCAP", "NET_BIND_SERVICE", "KILL", ]
镜像签名验证:CRI-O原生支持镜像签名验证(基于containers/image库),可以配置只允许拉取经过签名的镜像。containerd需要通过额外的admission webhook实现类似功能:
# CRI-O镜像签名策略 # /etc/containers/policy.json { "default": [{"type": "reject"}], "transports": { "docker": { "registry.example.com": [{"type": "signedBy", "keyType": "GPGKeys", "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-example"}], "docker.io": [{"type": "insecureAcceptAnything"}] } } }
运行时Socket权限控制:containerd和CRI-O的socket文件默认权限是root:root 0660。确保只有kubelet和授权用户能访问,不要把socket暴露给普通用户:
# 检查socket权限 ls -la /run/containerd/containerd.sock ls -la /var/run/crio/crio.sock # 预期:srw-rw---- 1 root root
4.1.3 高可用配置
运行时服务自动恢复:containerd和CRI-O都通过systemd管理,配置自动重启策略。运行时挂了kubelet会检测到并报告节点NotReady,但自动重启能减少人工干预:
# /etc/systemd/system/containerd.service.d/override.conf [Service] Restart=always RestartSec=5 OOMScoreAdj=-999 LimitNOFILE=1048576 LimitNPROC=infinity
containerd的live-restore等价功能:Docker有live-restore(dockerd重启不杀容器),containerd天然支持这个特性——containerd重启后会自动恢复对已有容器的管理。CRI-O也一样,重启crio进程不会影响正在运行的容器。这是因为实际管理容器的是runc创建的shim进程,运行时进程只是管理层。
版本升级策略:
containerd:小版本升级(1.7.x → 1.7.y)直接替换二进制文件重启即可,不影响运行中的容器。大版本升级(1.6 → 1.7)建议走节点轮转流程
CRI-O:版本和K8s对齐,升级K8s时同步升级CRI-O。CRI-O 1.28只保证兼容K8s 1.28,不要跨版本使用
4.2 注意事项
4.2.1 配置注意事项
警告:containerd和CRI-O的cgroup驱动必须和kubelet保持一致(都用systemd)。不一致会导致kubelet无法正确管理容器的资源限制,表现为Pod创建失败或节点NotReady。我见过一个集群因为升级containerd后配置文件被覆盖,SystemdCgroup回到了默认的false,导致整个节点上的Pod全部异常。
注意 pause镜像配置:containerd和CRI-O都需要配置pause镜像(sandbox镜像)。国内环境必须换成阿里云或其他国内源,否则节点初始化时拉不到pause镜像,所有Pod都无法创建。这是新集群部署最常见的问题之一
注意 CNI插件路径:containerd默认CNI插件目录是/opt/cni/bin/,CRI-O同时搜索/opt/cni/bin/和/usr/libexec/cni/。如果CNI插件安装在非默认路径,需要在配置文件中修改
注意 containerd配置文件版本:containerd config.toml有version 1和version 2两种格式。containerd 1.6+默认生成version 2格式,旧版本的version 1配置在新版本上可能不兼容。升级containerd后建议重新生成配置文件
4.2.2 常见错误
错误现象 原因分析 解决方案 failed to create containerd task: failed to create shim task runc版本太旧或损坏 升级runc到1.1+,runc --version检查 container runtime is not running 运行时服务未启动或socket不可达 systemctl status containerd/crio 检查服务状态 sandbox image not found pause镜像未配置或拉取失败 检查pause_image配置,手动crictl pull测试 cgroup driver mismatch 运行时和kubelet的cgroup驱动不一致 统一设为systemd,重启运行时和kubelet CNI plugin not found CNI插件未安装或路径配置错误 检查/opt/cni/bin/目录,安装CNI插件 failed to pull image: context deadline exceeded 镜像仓库网络不通或超时 检查镜像加速配置,crictl pull手动测试 OCI runtime create failed runc执行失败,通常是内核或seccomp问题 检查内核版本,查看journalctl -u containerd/crio日志 4.2.3 兼容性问题
版本兼容:containerd 1.6.x是LTS版本,支持K8s 1.24-1.28。containerd 1.7.x支持K8s 1.26+。CRI-O严格版本对齐,CRI-O 1.28.x只支持K8s 1.28.x,不要混用版本
平台兼容:containerd和CRI-O都支持amd64和arm64。containerd还支持Windows容器(K8s Windows节点),CRI-O只支持Linux
组件依赖:两者都依赖runc 1.1+作为底层OCI运行时。也可以替换为crun(C语言实现,启动更快)或kata-containers(虚拟机级隔离)。替换OCI运行时不需要修改上层K8s配置
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
# containerd日志 sudo journalctl -u containerd -f --no-pager sudo journalctl -u containerd --since "1 hour ago" -p err # CRI-O日志 sudo journalctl -u crio -f --no-pager sudo journalctl -u crio --since "1 hour ago" -p err # kubelet日志(运行时相关) sudo journalctl -u kubelet -f | grep -i -E "runtime|containerd|crio|cri" # 查看容器级别日志 crictl logscrictl logs --tail 100 -f # 查看Pod沙箱日志目录 # containerd: /var/log/pods/ _ _ / # CRI-O: /var/log/crio/pods/ / ls /var/log/pods/
5.1.2 常见问题排查
问题一:节点NotReady,kubelet报runtime not running
# 诊断 systemctl status containerd # 或 systemctl status crio crictl info journalctl -u containerd --since "10 min ago" | tail -30
解决方案:
运行时服务挂了:systemctl restart containerd(或crio),检查日志找根因
socket文件不存在:检查配置文件中的socket路径是否正确
权限问题:确认kubelet有权限访问运行时socket
问题二:Pod一直处于ContainerCreating状态
# 诊断 kubectl describe pod# 查看Events部分的错误信息 # 常见错误: # "failed to pull image" - 镜像拉取失败 # "network plugin is not ready" - CNI插件未就绪 # "failed to create sandbox" - pause镜像问题 # 检查CNI插件 ls /opt/cni/bin/ ls /etc/cni/net.d/ # 检查pause镜像是否存在 crictl images | grep pause
解决方案:
镜像拉取失败:检查镜像仓库配置和网络连通性
CNI未就绪:确认CNI插件已安装,配置文件在/etc/cni/net.d/下
pause镜像缺失:手动拉取crictl pull registry.aliyuncs.com/google_containers/pause:3.9
问题三:containerd/CRI-O内存持续增长
症状:运行时进程RSS从初始的50MB逐渐增长到500MB+,节点内存告警
排查:
# 查看运行时进程内存 ps aux | grep -E "containerd|crio" | grep -v grep # 查看容器和镜像数量 crictl ps -a | wc -l crictl images | wc -l # containerd: 查看快照使用情况 ctr -n k8s.io snapshots ls | wc -l # 检查是否有大量已停止的容器未清理 crictl ps -a --state exited | wc -l
解决:
清理已退出的容器和未使用的镜像:crictl rmi --prune
containerd的GC可能不够积极,调整gc scheduler参数
如果是已知的内存泄漏bug,升级到最新补丁版本
5.1.3 调试模式
# containerd开启debug日志 # 修改 /etc/containerd/config.toml # [debug] # level = "debug" sudo systemctl restart containerd sudo journalctl -u containerd -f | grep -i debug # CRI-O开启debug日志 # 修改 /etc/crio/crio.conf 或启动参数 # crio --log-level debug sudo systemctl restart crio sudo journalctl -u crio -f | grep -i debug # 使用ctr直接操作containerd(绕过CRI层,排查containerd本身的问题) sudo ctr -n k8s.io containers ls sudo ctr -n k8s.io images ls sudo ctr -n k8s.io tasks ls sudo ctr -n k8s.io snapshots ls # 查看containerd的shim进程 ps aux | grep containerd-shim # 查看CRI-O的conmon进程 ps aux | grep conmon # 使用runc直接查看容器状态(最底层排查) sudo runc --root /run/containerd/runc/k8s.io list # containerd环境 sudo runc --root /run/runc list # CRI-O环境
5.2 性能监控
5.2.1 关键指标监控
# 运行时进程资源使用 ps aux | grep -E "containerd|crio" | grep -v grep # 容器资源统计 crictl stats crictl statsp # containerd指标端点(配置了metrics.address后) curl -s http://localhost:1338/v1/metrics | head -50 # CRI-O指标端点(配置了enable_metrics后) curl -s http://localhost:9537/metrics | head -50 # 磁盘使用 du -sh /var/lib/containerd/ # containerd du -sh /var/lib/containers/ # CRI-O # 镜像占用空间 crictl images -o json | python3 -c " import json,sys data=json.load(sys.stdin) total=sum(int(i.get('size','0')) for i in data.get('images',[])) print(f'镜像总大小: {total/1024/1024:.0f}MB') "
5.2.2 监控指标说明
指标名称 正常范围 告警阈值 说明 运行时进程RSS <200MB >500MB 持续增长可能是内存泄漏 容器创建延迟 <2s >5s 超过5s检查镜像拉取和CNI 容器创建失败率 0% >1% 任何失败都需要排查 镜像拉取延迟 <30s >60s 网络或仓库性能问题 磁盘使用率 <70% >85% 镜像和容器层占用空间 shim/conmon进程数 等于容器数 偏差>10% 进程泄漏需要排查 CRI API延迟(P99) <100ms >500ms 运行时响应慢影响Pod调度 5.2.3 监控告警配置
# Prometheus抓取配置:prometheus.yml scrape_configs: # containerd指标 - job_name: 'containerd' static_configs: - targets: ['192.168.1.10:1338'] metrics_path: /v1/metrics # CRI-O指标 - job_name: 'crio' static_configs: - targets: ['192.168.1.10:9537'] metrics_path: /metrics# Prometheus告警规则:runtime-alerts.yml groups: - name: container_runtime_alerts rules: - alert: RuntimeProcessMemoryHigh expr: process_resident_memory_bytes{job=~"containerd|crio"} > 524288000 for: 10m labels: severity: warning annotations: summary: "容器运行时内存使用过高" description: "{{ $labels.job }} 进程RSS {{ $value | humanize }},超过500MB" - alert: ContainerCreateLatencyHigh expr: histogram_quantile(0.99, rate(container_runtime_operations_duration_seconds_bucket{operation_type="create_container"}[5m])) > 5 for: 5m labels: severity: warning annotations: summary: "容器创建延迟过高" description: "P99延迟 {{ $value }}s,超过5秒" - alert: ContainerCreateErrors expr: rate(container_runtime_operations_errors_total{operation_type="create_container"}[5m]) > 0 for: 2m labels: severity: critical annotations: summary: "容器创建失败" description: "错误率 {{ $value }}/s" - alert: ImagePullErrors expr: rate(container_runtime_operations_errors_total{operation_type="pull_image"}[5m]) > 0 for: 5m labels: severity: warning annotations: summary: "镜像拉取失败" description: "错误率 {{ $value }}/s,检查镜像仓库连通性"
5.3 备份与恢复
5.3.1 备份策略
#!/bin/bash # 文件名:runtime-backup.sh # 容器运行时配置备份脚本 BACKUP_DIR="/backup/runtime/$(date +%Y%m%d)" mkdir -p ${BACKUP_DIR} # 检测运行时类型 if systemctl is-active --quiet containerd; then RUNTIME="containerd" elif systemctl is-active --quiet crio; then RUNTIME="crio" else echo "未检测到运行中的容器运行时" exit 1 fi echo "备份运行时: ${RUNTIME}" # 备份配置文件 if [ "$RUNTIME" = "containerd" ]; then cp /etc/containerd/config.toml ${BACKUP_DIR}/ cp -r /etc/containerd/certs.d/ ${BACKUP_DIR}/ 2>/dev/null elif [ "$RUNTIME" = "crio" ]; then cp /etc/crio/crio.conf ${BACKUP_DIR}/ cp /etc/containers/registries.conf ${BACKUP_DIR}/ cp /etc/containers/policy.json ${BACKUP_DIR}/ 2>/dev/null fi # 备份crictl配置 cp /etc/crictl.yaml ${BACKUP_DIR}/ 2>/dev/null # 备份CNI配置 cp -r /etc/cni/net.d/ ${BACKUP_DIR}/cni-conf/ 2>/dev/null # 备份systemd override if [ "$RUNTIME" = "containerd" ]; then cp -r /etc/systemd/system/containerd.service.d/ ${BACKUP_DIR}/ 2>/dev/null elif [ "$RUNTIME" = "crio" ]; then cp -r /etc/systemd/system/crio.service.d/ ${BACKUP_DIR}/ 2>/dev/null fi # 备份kubelet配置 cp /etc/default/kubelet ${BACKUP_DIR}/ 2>/dev/null cp /var/lib/kubelet/config.yaml ${BACKUP_DIR}/ 2>/dev/null # 导出镜像列表 crictl images -o json > ${BACKUP_DIR}/images-list.json echo "备份完成: ${BACKUP_DIR}" du -sh ${BACKUP_DIR}
5.3.2 恢复流程
安装运行时:按照2.2节步骤安装对应版本的containerd或CRI-O
恢复配置文件:
# containerd cp config.toml /etc/containerd/ cp -r certs.d/ /etc/containerd/ # CRI-O cp crio.conf /etc/crio/ cp registries.conf /etc/containers/
恢复CNI配置:cp -r cni-conf/* /etc/cni/net.d/
恢复kubelet配置:cp kubelet /etc/default/kubelet
启动服务:systemctl daemon-reload && systemctl start containerd && systemctl start kubelet
验证节点状态:kubectl get node $(hostname) -o wide确认节点Ready且运行时版本正确
六、总结
6.1 技术要点回顾
运行时选型:containerd适合通用场景和从Docker迁移的集群,生态成熟,资料丰富;CRI-O适合纯K8s环境和Red Hat技术栈,代码精简,内存占用小
关键配置:cgroup驱动必须设为systemd并和kubelet保持一致,pause镜像国内环境必须换源,这两个配置错了节点直接NotReady
crictl工具:替代docker命令的标准工具,crictl ps看容器,crictl pods看Pod沙箱,crictl images看镜像,日常运维够用
迁移策略:逐节点迁移,先cordon+drain,再切换运行时,最后uncordon。Docker的镜像缓存不会自动迁移,首次拉取会慢
监控要点:运行时进程内存、容器创建延迟、CRI API错误率是核心指标,containerd暴露1338端口,CRI-O暴露9537端口
6.2 进阶学习方向
OCI运行时替换:runc之外还有crun(C语言实现,启动速度快50%)、kata-containers(虚拟机级隔离)、gVisor(用户态内核)。不同安全级别的工作负载可以用不同的OCI运行时
学习资源:kata-containers官方文档、gVisor官方文档
实践建议:在containerd中配置多个runtime class,普通Pod用runc,敏感Pod用kata
镜像构建工具链:脱离Docker后,镜像构建可以用Buildah(Red Hat出品,和CRI-O配合好)、kaniko(在容器内构建,不需要Docker daemon)、BuildKit(containerd生态)
学习资源:Buildah官方文档、kaniko GitHub
实践建议:CI/CD流水线中用kaniko替代docker build,不需要在CI Runner上安装Docker
容器运行时安全:深入理解seccomp、AppArmor、SELinux在容器运行时层面的实现,以及RuntimeClass在K8s中的应用
学习资源:K8s RuntimeClass文档、OCI runtime-spec
实践建议:为不同安全级别的工作负载配置不同的RuntimeClass
6.3 参考资料
containerd官方文档 - containerd配置和使用指南
CRI-O官方文档 - CRI-O安装和配置
Kubernetes容器运行时 - K8s官方的运行时配置指南
OCI Runtime Specification - OCI运行时规范
crictl文档 - CRI命令行工具
从Docker迁移到containerd - K8s官方迁移指南
附录
A. 命令速查表
crictl常用命令(containerd和CRI-O通用)
# 运行时信息 crictl info # 查看运行时信息 crictl version # 查看crictl和运行时版本 # 镜像操作 crictl pull nginx:1.24-alpine # 拉取镜像 crictl images # 查看镜像列表 crictl images -o json # JSON格式输出镜像列表 crictl inspecti nginx:1.24-alpine # 查看镜像详情 crictl rmi nginx:1.24-alpine # 删除镜像 crictl rmi --prune # 清理未使用的镜像 # 容器操作 crictl ps # 查看运行中的容器 crictl ps -a # 查看所有容器(含已停止) crictl ps --name nginx # 按名称过滤容器 crictl logs# 查看容器日志 crictl logs --tail 100 -f # 实时跟踪最近100行日志 crictl exec -it /bin/sh # 进入容器 crictl inspect # 查看容器详情 crictl stats # 查看容器资源使用 crictl stop # 停止容器 crictl rm # 删除容器 # Pod沙箱操作 crictl pods # 查看所有Pod沙箱 crictl pods --name test-pod # 按名称过滤Pod crictl pods --state ready # 按状态过滤Pod crictl inspectp # 查看Pod详情 crictl statsp # 查看Pod资源使用 crictl stopp # 停止Pod沙箱 crictl rmp # 删除Pod沙箱 crictl runp pod-config.json # 创建Pod沙箱(调试用)
containerd专用命令(ctr工具)
# ctr是containerd的原生CLI,绕过CRI层直接操作containerd # K8s环境下容器在k8s.io命名空间 # 命名空间 ctr namespaces ls # 查看所有命名空间 # 镜像操作 ctr -n k8s.io images ls # 查看K8s命名空间的镜像 ctr -n k8s.io images pull docker.io/library/nginx:1.24-alpine # 拉取镜像 ctr -n k8s.io images rm# 删除镜像 ctr -n k8s.io images export nginx.tar docker.io/library/nginx:1.24-alpine # 导出镜像 ctr -n k8s.io images import nginx.tar # 导入镜像 # 容器操作 ctr -n k8s.io containers ls # 查看容器列表 ctr -n k8s.io containers info # 查看容器详情 # 任务(运行中的容器进程) ctr -n k8s.io tasks ls # 查看运行中的任务 ctr -n k8s.io tasks metrics # 查看任务资源指标 # 快照 ctr -n k8s.io snapshots ls # 查看快照列表 ctr -n k8s.io snapshots usage # 查看快照磁盘使用 # 内容存储 ctr -n k8s.io content ls # 查看内容存储
服务管理命令
# containerd服务管理 sudo systemctl start containerd # 启动 sudo systemctl stop containerd # 停止 sudo systemctl restart containerd # 重启 sudo systemctl status containerd # 查看状态 sudo systemctl enable containerd # 开机自启 containerd --version # 查看版本 containerd config default # 输出默认配置 containerd config dump # 输出当前配置 # CRI-O服务管理 sudo systemctl start crio # 启动 sudo systemctl stop crio # 停止 sudo systemctl restart crio # 重启 sudo systemctl status crio # 查看状态 sudo systemctl enable crio # 开机自启 crio --version # 查看版本 crio config --default # 输出默认配置 # 日志查看 journalctl -u containerd -f --no-pager # containerd实时日志 journalctl -u crio -f --no-pager # CRI-O实时日志 journalctl -u containerd -p err # containerd错误日志 journalctl -u crio -p err # CRI-O错误日志 # 底层OCI运行时 runc --version # 查看runc版本 runc --root /run/containerd/runc/k8s.io list # containerd环境查看容器 runc --root /run/runc list # CRI-O环境查看容器
B. containerd与CRI-O对比表
对比维度 containerd CRI-O 开发主导 Docker/CNCF Red Hat/CNCF CNCF状态 毕业项目 毕业项目 设计定位 通用容器运行时 专为K8s设计的CRI运行时 代码语言 Go Go 版本策略 独立版本号(1.6.x/1.7.x) 和K8s版本对齐(1.28.x对应K8s 1.28) CRI支持 通过内置CRI插件 原生CRI实现 独立使用 支持(不依赖K8s) 不支持(只为K8s服务) 镜像构建 支持(通过BuildKit) 不支持(需要Buildah等外部工具) 镜像推送 支持 不支持 Windows容器 支持 不支持 插件架构 支持(快照、运行时等插件) 不支持 命名空间隔离 支持(k8s.io、moby等) 不支持 容器监控进程 containerd-shim(每容器一个) conmon(每容器一个) 默认存储驱动 overlayfs overlay 配置文件 /etc/containerd/config.toml /etc/crio/crio.conf Socket路径 /run/containerd/containerd.sock /var/run/crio/crio.sock 镜像仓库配置 /etc/containerd/certs.d/ /etc/containers/registries.conf 指标端口 1338 9537 CLI工具 ctr(原生)+ crictl(CRI) crictl(CRI) 空载内存占用 ~45MB ~28MB 50 Pod内存占用 ~120MB ~65MB(+35MB conmon) 单Pod启动时间 ~1.8s ~1.6s 云平台采用 AWS EKS、Google GKE、Azure AKS Red Hat OpenShift 社区活跃度 非常活跃,贡献者多 活跃,Red Hat主导 排查资料 丰富,中英文资料多 较少,英文为主 C. 术语表
术语 英文 解释 高级运行时 High-level Runtime 负责镜像管理、容器生命周期管理、API接口的运行时,containerd和CRI-O都属于此类 低级运行时 Low-level Runtime 负责实际创建和运行容器的运行时,如runc、crun、kata-containers CRI Container Runtime Interface Kubernetes定义的容器运行时接口标准,kubelet通过CRI和运行时通信 OCI Open Container Initiative 容器镜像格式和运行时的开放标准,runc是OCI运行时的参考实现 runc runc OCI运行时的参考实现,用Go编写,containerd和CRI-O底层都调用runc创建容器 crun crun 用C语言实现的OCI运行时,启动速度比runc快约50%,内存占用更小 containerd-shim containerd-shim containerd为每个容器启动的中间进程,负责管理容器的stdin/stdout和退出状态,containerd重启不影响容器 conmon Container Monitor CRI-O为每个容器启动的监控进程,负责日志记录、退出状态监控、TTY管理 Pod沙箱 Pod Sandbox K8s中每个Pod先创建的基础容器(pause容器),提供网络命名空间,业务容器共享该命名空间 pause容器 Pause Container Pod沙箱的实现,一个极简的容器,只执行pause系统调用,为Pod内其他容器提供共享的网络和IPC命名空间 crictl CRI Control CRI兼容运行时的命令行工具,类似docker命令但只支持CRI接口,containerd和CRI-O通用 ctr containerd CLI containerd的原生命令行工具,绕过CRI层直接操作containerd,排查containerd本身问题时使用 dockershim Docker Shim K8s中将Docker适配为CRI接口的垫片层,K8s 1.24正式移除 cgroup Control Group Linux内核特性,限制和监控进程组的资源使用(CPU、内存等),容器资源限制的底层机制 cgroup驱动 Cgroup Driver 管理cgroup的方式,有systemd和cgroupfs两种,K8s环境必须统一使用systemd CNI Container Network Interface 容器网络接口标准,定义容器网络的创建和删除接口,K8s网络插件都实现CNI规范 快照 Snapshot containerd的存储概念,类似Docker的镜像层,用于管理容器的文件系统层 RuntimeClass RuntimeClass K8s资源对象,用于指定Pod使用哪种OCI运行时(如runc、kata-containers),实现不同隔离级别 CNCF Cloud Native Computing Foundation 云原生计算基金会,containerd和CRI-O都是CNCF毕业项目
全部0条评论
快来发表一下你的评论吧 !