容器运行时是真正让容器“跑”起来的底层引擎,镜像则是一份只读的打包模板;两者一个管执行、一个管交付,通过OCI规范协作,缺一不可。
容器运行时和镜像的区别在哪
容器运行时到底是什么
把容器运行时当成厨房里的“掌勺师傅”,把所有镜像当成“菜谱”,菜谱写清了食材切法和烹饪步骤,但最终做出来的菜必须由厨子动手,容器运行时的职责类似:负责读取镜像内容和配置,申请宿主机的内核资源,并最终把文件系统变成一台隔离的进程,业内专家指出,这个角色在整个容器生态里一直承担最底层的执行任务,少了它,镜像就只是一堆安静的压缩文件。
容器运行时和镜像的区别
| 对比项 | 镜像 | 容器运行时 |
|---|---|---|
| 本质 | 只读文件快照,包含程序、依赖、环境变量 | 一个可执行程序,负责启动和管理容器进程 |
| 生命周期 | 静态存在,可传输、可共享、不可修改 | 动态运行,通过与内核交互创建和销毁进程 |
| 存储位置 | 镜像仓库和本地磁盘 | 宿主机系统环境下 |
| 核心接口 | 遵循OCI Image Spec打包格式 | 遵循OCI Runtime Spec定义行为标准 |
举一个实际场景来理解这组名词:在服务器上执行docker pull nginx:alpine,下载下来的是一个镜像;再执行docker run时,运行时才接过这个镜像文件,解压、挂载、创建隔离环境,最终返回一个可以访问的Nginx进程。
容器运行时有哪些主流选择
容器运行时有哪些主流选择,是刚开始接触云原生的朋友常问的问题,按层级大致分为两类:
- 低级运行时:直接调内核系统调用,典型代表是runC
- 高级运行时:负责镜像管理、解压和调用低级运行时,典型代表有containerd、CRI-O
实际使用中,Docker自带一套完整工具,底层默认用containerd和runC组合;Kubernetes节点则可以直接装containerd,配合crictl命令行管理容器进程,CRI-O在OpenShift和特定安全场景下也有不少忠实用户。
镜像和容器运行时如何协作
docker run背后发生了什么
容器和镜像之间如何协作,执行docker run就能看得一清二楚,完整过程大约经过五个环节:
- 客户端向dockerd发出运行请求,dockerd在本地镜像缓存里找目标镜像
- 本地找不到时,运行时负责从镜像仓库拉取,并按OCI格式解析
- containerd把镜像挂载为只读层,再叠加上一层可读写层
- runC调用Linux内核的Namespace和Cgroup能力,创建隔离进程
- 进程启动后,通过shim进程维持连接,保证容器日志回传和状态同步
从结果倒推,镜像本身是静态资产,整个过程里它始终没变,变的是运行时围绕它搭建出来的运行环境。
写时复制:镜像层与可写层的配合机制
这里有一个整套协作机制中特别巧妙的设计:写时复制,镜像内部按层组织,每一层都只读,运行时在顶层附上一个可读写层,容器内所有增删改都发生在这一层,底层镜像不受任何影响。
使用overlay2驱动时,宿主机存储目录大概长这样:
/var/lib/docker/overlay2/
├── <镜像层哈希>/diff/ # 只读层数据
├── <容器层哈希>/merged/ # 容器视角的完整文件系统
└── <容器层哈希>/diff/ # 可写层实际落盘内容
这个配合方式带来的收益很明显,多个容器共用同一份镜像层,不必各自完整复制一份,在批量启动或扩容上百个实例时,磁盘空间和启动速度都受益。
containerd和docker有什么区别
容器运行时与镜像的关系弄明白后,很多人会把containerd和Docker搞混,两者不是一个层级的东西,Docker是完整容器管理工具链:有CLI、有守护进程、有镜像构建、有网络卷管理,containerd则只专注做一件事
把镜像变成运行中的容器,行业共识认为,Docker是平台,containerd是内核组件。
Kubernetes在2020年底宣布废弃dockershim后,生产集群直接对接containerd成为主流路径,很多公有云托管集群的控制台里,节点运行时一栏默认显示的就是containerd,而不是Docker,这是一个明显趋势:分层职责越来越清晰,镜像构建与容器执行逐渐解耦。
容器运行时怎么选
容器运行时怎么选:结合生产场景判断
容器运行时怎么选,取决于你所在的场景,如果只是本地开发,Docker Desktop或裸Docker最顺手,因为构建、调试、网络、挂载都打磨得很成熟,如果是Kubernetes生产集群,建议直接用containerd,少一层Docker自带的守护进程,内存占用更低,故障排查路径也更短。
对安全合规敏感的场景,比如金融内网或政企项目,CRI-O这类轻量运行时更受青睐,它砍掉了Docker的额外组件,攻击面更小,多架构和GPU调度配合层面,containerd对特殊硬件设备的适配速度也更快,总体选择优先级可以按这个思路排列:
- 本地开发:Docker
- 生产K8s:containerd优先
- 安全加固:CRI-O
- 底层定制:直接调用runC
器化部署落地时,运行时配置思路
器化部署落地时,更需要先确认操作系统和架构,在CentOS 7.9环境里,配置containerd的常见步骤是:
- 配置yum源,安装
containerd.io - 修改
/etc/containerd/config.toml,启用SystemdCgroup - 执行
systemctl enable --now containerd - 安装crictl,在
/etc/crictl.yaml里指定endpoint
镜像仓库这块,国内团队普遍会用简米云容器镜像服务或Harbor做中转,拉公网镜像速度不稳时,在运行时侧配置mirror地址是关键一步,公有云托管的Kubernetes大多已经替用户处理好了运行时调度,控制台上只需选好containerd版本。
动手验证运行时的实操入口
给一套可复现的验证命令,便于你亲手摸清现状:
docker version查看本地Docker所使用的runtime信息containerd config default输出当前containerd默认配置crictl info查看运行时与存储驱动runc --version查看低级运行时的具体版本
看到这几条命令输出,就基本摸清了当前环境里镜像与运行时协作的底座。
结束语
一句话回到主题:镜像负责“定义”,运行时负责“执行”,没有执行者的定义永远只是一堆静态文件,理解这条协作链路,才算真正摸清了容器技术最底层的运转逻辑。
关于容器运行时与镜像协作的常见问题
容器运行时和容器引擎是一回事吗
不是一回事,容器引擎是完整工具链,包含客户端、构建器、网络管理、镜像管理等模块;容器运行时只负责进程的创建、隔离和销毁,Docker引擎因为自带运行时,让两者边界在表面看起来模糊,实际拆开后,Docker的可替换部分恰好只有运行时这一块,这也是Kubernetes能甩开Docker,直接对接containerd的根本原因。
容器化部署中,镜像分层越多是不是越差
镜像分层多不必然代表运行效率低,但分层越细,传输和落盘时要处理的元数据条目就越多,构建阶段适当合并RUN指令、清理中间产物,能显著减少镜像层数量,从运行侧来看,启动容器时只关心最外层可写层,镜像分层的多寡主要通过拉取和存储空间影响体验。
直接只用runC能不能跑业务容器
能跑,但不建议,runC本身只负责启动容器进程,镜像拉取、存储挂载、网络配置、日志追踪这些能力全部缺失,生产环境至少需要containerd或CRI-O这类高级运行时把周边能力补齐,否则手动拼装整个容器生命周期会耗费极大精力,底层设计上,runC是手段,完整运行时才是产品。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/640975.html




