ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【Kubernetes从入门到精通】第05篇:Docker退役了?containerd和CRI的前世今生

【Kubernetes从入门到精通】第05篇:Docker退役了?containerd和CRI的前世今生 上一篇【第04篇】云原生是什么鬼——CNCF技术版图漫游指南下一篇【第06篇】第一个K8s应用——3分钟部署你的Hello World摘要2020 年底K8s 社区扔了颗炸弹宣布从 v1.20 开始弃用 dockershimv1.24 正式移除。一时间“K8s 不支持 Docker 了”Docker 要凉了的标题党满天飞。论坛上人心惶惶很多刚学会 Docker 的同学吓得连夜发帖“我刚学完 Docker 就要被淘汰了”别慌我来告诉你真相K8s 废弃的只是 dockershim一个适配层不是你手里的 Docker。你照样可以docker build构建镜像Docker Hub 照样有上百万镜像docker run照样好用。本文把这件事的来龙去脉讲清楚K8s 为什么这么干、CRI 是什么标准、containerd 和 CRI-O 有什么区别、对你有什么实际影响——帮你把这块知识彻底吃透。一、事件复盘——K8s 到底干掉了什么先把事实摆清楚。K8s 从 v1.24 起不再内置 dockershim 组件一个让 K8s 能和 Docker 通信的适配层。媒体和自媒体的标题是K8s drops Docker support听起来像是离婚声明。但事情的真相用一个比喻就懂了【dockershim 是什么——翻译官的比喻】 K8s 只会说CRI 语言 Docker 只会说自己的语言 ┌──────────────┐ ┌──────────────┐ │ kubelet │ │ Docker │ │ CRI Request│ │ daemon │ │ Please │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ 这俩语言不通没法直接沟通 │ │ │ │ ┌───────────────┐ │ └────────►│ dockershim │◄─────────┘ │ (翻译官) │ │ │ │ CRI ←→ Docker│ │ API 互译 │ └───────────────┘ 问题是这位翻译官住在 K8s 代码仓库里 ── 每次 K8s 发新版本翻译官也得跟着更新 ── Docker 底层改了翻译官也要改 ── 维护成本高还容易出 bug要点K8s 废弃 dockershim就像公司把外包翻译辞退了——不是因为翻译不好而是因为人家内部已经有了更直接的沟通方式。Docker 自己拆出了一个叫 containerd 的组件这个组件原生就说 CRI 语言根本不再需要翻译。事件时间线时间事件2016.12K8s 引入 CRIContainer Runtime Interface标准同时内置 dockershim 作为 Docker 的适配层2017Docker 将 containerd 捐给 CNCF成为独立项目2017-2019CRI-O 和 containerd 逐渐成熟开始原生支持 CRI2020.12K8s 宣布 dockershim 进入弃用倒计时2021.04K8s v1.21 dockershim 开始输出弃用警告2022.05K8s v1.24 dockershim 正式移除至今containerd 和 CRI-O 成为两大主流 CRI 运行时二、CRI是什么——K8s的操作系统接口CRIContainer Runtime Interface是 K8s 定义的一套标准接口规定了kubelet 和容器运行时之间怎么通信。你可以把它理解成 K8s 世界的POSIX 标准——只要你的容器运行时实现了 CRI 接口就能被 K8s 使用。【CRI 标准架构】 ┌────────────────────────────────────────────────────┐ │ Kubernetes │ │ │ │ ┌────────────────────────────────────────────────┐ │ │ │ kubelet │ │ │ │ (每个节点上的工头负责管理本节点的容器) │ │ │ └────────────────────┬───────────────────────────┘ │ │ │ gRPC (CRI 协议) │ │ ▼ │ │ ┌────────────────────────────────────────────────┐ │ │ │ CRI gRPC Server │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ RuntimeService │ │ │ │ │ │ ┌─────────────────┐ │ │ │ │ │ │ │ RunPodSandbox │ │ ◄── 创建Pod沙箱 │ │ │ │ │ │ CreateContainer │ │ ◄── 创建容器 │ │ │ │ │ │ StartContainer │ │ ◄── 启动容器 │ │ │ │ │ │ StopContainer │ │ ◄── 停止容器 │ │ │ │ │ │ ListContainers │ │ ◄── 列出容器 │ │ │ │ │ │ ContainerStatus │ │ ◄── 容器状态 │ │ │ │ │ └─────────────────┘ │ │ │ │ │ └──────────────────────┘ │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ ImageService │ │ │ │ │ │ ┌─────────────────┐ │ │ │ │ │ │ │ PullImage │ │ ◄── 拉取镜像 │ │ │ │ │ │ ListImages │ │ ◄── 列出镜像 │ │ │ │ │ │ RemoveImage │ │ ◄── 删除镜像 │ │ │ │ │ └─────────────────┘ │ │ │ │ │ └──────────────────────┘ │ │ │ └────────────────────────────────────────────────┘ │ │ │ │ 任何实现了这两个 gRPC Service 的运行时 │ │ 都可以无缝接入 K8s │ └────────────────────────────────────────────────────┘要点CRI 的核心价值是解耦。K8s 不关心你底层用的是 containerd 还是 CRI-O 还是什么新的运行时——只要你的 gRPC 接口符合 CRI 规范kubelet 就能指挥你干活。就像 Linux 上可以跑 ext4、XFS、Btrfs 等多种文件系统一样K8s 上也可以跑多种容器运行时。CRI 定义的两种 gRPC ServiceService职责关键方法RuntimeService管理 Pod 和容器的生命周期RunPodSandbox, CreateContainer, StartContainer, StopContainer, RemoveContainerImageService管理容器镜像PullImage, ListImages, RemoveImage, ImageStatuskubelet 通过调用这两个 gRPC Service完成 Pod 和容器的所有操作。当然底层还要配合 CNIContainer Network Interface管理网络以及 CSIContainer Storage Interface管理存储——这是 K8s 的三驾马车接口标准。三、containerd vs CRI-O——两大运行时正面对比K8s v1.24 之后主流选择就是 containerd 和 CRI-O。它们都原生实现了 CRI 接口都经过了大规模生产验证。【containerd vs CRI-O 架构对比】 containerd CRI-O ┌─────────────────┐ ┌─────────────────┐ │ kubelet │ │ kubelet │ └────────┬────────┘ └────────┬────────┘ │ CRI │ CRI ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ containerd │ │ CRI-O │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │CRI Plugin │ │ │ │CRI Server │ │ │ └───────────┘ │ │ └───────────┘ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Content │ │ │ │ Storage │ │ │ │ Store │ │ │ │ (镜像存储) │ │ │ └───────────┘ │ │ └───────────┘ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Snapshot│ │ │ │ Container │ │ │ │ (文件系统)│ │ │ │ (容器管理) │ │ │ └───────────┘ │ │ └───────────┘ │ └────────┬────────┘ └────────┬────────┘ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ runc │ │ runc │ │ (OCI 运行时) │ │ (OCI 运行时) │ └─────────────────┘ └─────────────────┘ 来源从 Docker 拆出 来源Red Hat 主导 背景K8s 社区推荐 背景为 K8s 量身打造详细对比对比维度containerdCRI-O出身从 Docker 中拆分出来的独立运行时Red Hat 主导专为 K8s 设计的运行时设计理念通用容器运行时不仅服务于 K8s纯 K8s 运行时只做 K8s 需要的事CRI 实现内置 CRI 插件原生 CRI 实现更纯粹OCI 兼容✅ 完全兼容✅ 完全兼容镜像管理完整的镜像拉取/存储/管理聚焦 K8s 镜像管理Docker 镜像兼容✅ 完全兼容本身就是 Docker 底层✅ 兼容都是 OCI 格式社区维护CNCF 毕业项目社区庞大CNCF 孵化项目Red Hat 主要维护默认使用场景K8s 官方推荐kind/minikube 默认OpenShift 默认Red Hat 生态学习成本低Docker 用户几乎无感中概念更 K8s 原生安全特性支持 seccomp/AppArmor/SELinux原生集成 SELinuxRed Hat 强项性能优秀优秀略有差异化场景兼容性Docker CLI 可直连docker -H有 crictl 工具ctl 工具ctr低层/nerdctlDocker 兼容crictlK8s 调试专用要点对于学 K8s 来说选 containerd 还是 CRI-O 真不关键——两者都原生实现 CRI对上层 K8s 完全透明。就像你不会关心你的 Linux 用 ext4 还是 XFS 文件系统一样。kind 默认用 containerdminikube 两者都支持n个发行版也各有所好——不管哪个你的kubectl命令都一样。crictl——K8s 调试专用容器工具Docker 被干掉以后你没法在 K8s 节点上docker ps看容器了。这时你需要的是crictl——K8s 社区提供的 CRI 调试工具# crictl 常用命令如果你不装问题也不大crictlps# 查看运行中的容器docker ps 等效crictl pods# 查看所有 Podcrictl images# 查看节点上的镜像crictl logscontainer-id# 查看容器日志crictlexec-itcontainer-idbash# 进入容器# 注意crictl 需要配置 runtime-endpoint# containerd: unix:///var/run/containerd/containerd.sock# CRI-O: unix:///var/run/crio/crio.sock# 配置方法以 containerd 为例cat/etc/crictl.yamlEOF runtime-endpoint: unix:///var/run/containerd/containerd.sock image-endpoint: unix:///var/run/containerd/containerd.sock timeout: 10 debug: false EOF四、对你有什么影响——答案几乎没影响这是很多人最关心的问题我直接用表格列出来操作影响说明docker build✅ 照常用Docker 构建镜像和 K8s 运行时选择无关仍然是最主流的镜像构建方式docker run本地测试✅ 照常用你还是可以在本地用 Docker 启动容器做开发测试docker push推送镜像✅ 照常用你构建的镜像仍然推送到 Docker Hub 或其他 RegistryK8s 从那里拉取Dockerfile 写法✅ 不用改构建出来的镜像是 OCI 标准格式containerd 和 CRI-O 都能拉取运行docker-compose✅ 照常用本地开发环境依然可以用 compose 编排多容器Docker Desktop✅ 照常用Docker Desktop 内置的 K8s 已经切换为 containerddocker ps在 K8s 节点上⚠️ 不灵了K8s 节点用了 containerd需要用crictl ps或nerdctl psK8s YAML 写法✅ 不改容器镜像字段写镜像名就行完全不变【镜像构建 vs 容器运行的分离】 构建镜像Development 运行容器Production ┌─────────────────┐ ┌─────────────────┐ │ Docker CLI │ │ K8s (kubelet) │ │ docker build │ │ │ │ │ docker push │ │ │ CRI │ └────────┬────────┘ │ ▼ │ │ │ ┌───────────┐ │ │ push │ │containerd │ │ ▼ │ │ or CRI-O │ │ ┌─────────────────┐ │ └───────────┘ │ │ Repository │ pull └─────────────────┘ │ (Docker Hub / │◄───────────────── │ Harbor / ECR) │ └─────────────────┘ docker build 从来就不是 K8s 的一部分 它是构建工具不是运行工具。 就好比你把菜做好docker build送进冰箱Registry K8s 只是负责从冰箱取菜的人——它不关心菜是谁做的。要点Docker 是一个全家桶——它包含了 CLI、API、build、run、push、pull、compose……而 K8s 只需要其中的run能力。K8s 废弃 dockershim相当于说我不要你全家桶了我只要里面那个叫 containerd 的组件。但你作为开发者该用全家桶还接着用——构建和运行本来就可以分开。五、K8s v1.24如何检查你的容器运行时如果你想确认自己 K8s 集群用的是什么容器运行时# 方法一查看节点详情kubectl get nodes-owide# 最后一列 CONTAINER-RUNTIME 会显示# 方法二查看节点详细信息kubectl describenodenode-name|grepContainer Runtime Version# Container Runtime Version: containerd://1.7.15# 方法三直接进节点看# 如果是 kind 集群dockerexeccontrol-plane-containercrictlps# 如果是 minikubeminikubesshcrictlps# 查看 containerd 的 K8s 相关配置cat/etc/containerd/config.toml|grep-A10plugins.io.containerd.grpc.v1.crinerdctl——Docker 用户最无感的 containerd 命令如果你习惯了 Docker CLI 的用法又想在只用 containerd 的环境中操作容器nerdctl几乎完美替代 Docker CLI# nerdctl 安装# macOS:brewinstallnerdctl# Linux:wgethttps://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gztarxzf nerdctl-*.tar.gz-C/usr/local/bin/# 用法几乎和 Docker CLI 一模一样nerdctl run-d--namenginx-p8080:80 nginx:alpine nerdctlpsnerdctl images nerdctl build-tmyapp.nerdctl compose up-d# 甚至支持 compose!要点nerdctl 是由 containerd 社区维护的 Docker CLI 兼容工具。它的命令格式、参数名称、行为都与 Docker CLI 高度一致甚至支持nerdctl compose。如果你只是想在 containerd 环境里用熟悉的命令操作容器装 nerdctl 就行了几乎零学习成本。六、容器运行时的演进路线——一张图看懂历史【容器运行时演进史】 2013 2016-2017 2020-2022 现在 │ │ │ │ ▼ ▼ ▼ ▼ Docker 横空出世 K8s 推出 CRI 标准 dockershim 被废 三足鼎立 ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Docker │ │ dockershim│ │ dockershim│ │containerd│ │ 全家桶 │ │ 翻译官 │ │ ❌ 移除 │ │✅ 原生CRI │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │containerd│ │containerd│ │ CRI-O │ │ │ │ 新秀登场 │ │ 日渐成熟 │ │✅ 原生CRI │ │ │ └──────────┘ └──────────┘ └──────────┘ └────────┘ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ CRI-O │ │ CRI-O │ │ Docker │ ┌────────┐ │ Red Hat搞│ │ 用于OpenShift│ │(构建工具)│ │ rkt │ └──────────┘ └──────────┘ │ ✅ 继续用 │ │CoreOS搞 │ └──────────┘ └────────┘ ┌──────────┐ (已弃用) │ runc │ ┌──────────┐ ┌──────────┐ │ OCI标准 │ │ 所有运行 │ │ OCI 标准 │ │ 参考实现 │ │ 时底层 │ │ 统一天下 │ └──────────┘ │ 都是 runc│ └──────────┘ └──────────┘ 关键趋势所有容器运行时底层都统一到 OCI 标准runc/crun/youki 上层通过 CRI 接口对接 K8s 中间层的选择containerd 还是 CRI-O对用户透明要点容器运行时的演进趋势非常清晰——标准化是不可逆转的。OCI 定义了镜像和运行时的标准CRI 定义了 K8s 和运行时的接口。只要你的镜像符合 OCI 标准任何实现了 CRI 接口的运行时都能跑。这个设计让整个生态充满了可替换性——没有任何一个组件是不可替代的包括 K8s 本身。本篇小结让我们把几个关键结论钉在墙上K8s 废弃的不是 Docker是 dockershim——一个为了兼容 Docker 内核 API 而存在的适配层。Docker 本身活得好好的你照样用它 build/push/run。CRI 是一套标准接口——K8s 通过它告诉容器运行时创建 Pod“启动容器”“拉取镜像”。只要你的运行时实现了 CRI就能被 K8s 用。containerd 和 CRI-O 是两大主流 CRI 运行时——containerd 出身于 Docker更通用CRI-O 由 Red Hat 主导更聚焦 K8s。对学 K8s 来说选哪个没区别。对你的实际影响接近于零——docker build 照用Dockerfile 不改K8s YAML 不变。唯一的小变化是 K8s 节点上不能用docker ps用crictl替代就行。下一篇我们趁热打铁——刚才搭好的 kind 集群还热乎着吧让我们一起把第一个 K8s 应用跑起来从一句 YAML 到浏览器看到 Hello World全程不超过 3 分钟。上一篇【第04篇】云原生是什么鬼——CNCF技术版图漫游指南下一篇【第06篇】第一个K8s应用——3分钟部署你的Hello World
返回列表