跳转到内容

container-deep-dive

概念描述
容器 (Container)应用程序及其运行时环境的 隔离、独立 的实例。它共享主机的操作系统内核,但拥有自己的文件系统、进程空间和网络接口。
容器镜像 (Image)包含应用程序代码、运行时、系统工具、系统库和配置等 不可变 内容的静态模板。它是容器的构建基础。
容器运行时 (Runtime)负责真正执行容器操作(如创建、启动、停止)的软件,它遵循 OCI 标准。例如 runc
注册中心 (Registry)存储和分发容器镜像的中心仓库,例如 Docker Hub 或 Harbor。
命名空间 (Namespaces)Linux 内核提供的技术,用于隔离容器内的进程视图。例如,PID 命名空间隔离进程列表;Net 命名空间隔离网络堆栈。
控制组 (Cgroups)Linux 内核提供的技术,用于限制和分配容器可以使用的资源(CPU、内存、I/O 等)。

容器技术的设计旨在解决“在我的机器上能跑,在其他机器上却不能跑”的环境差异问题,其核心理念是:

  • 环境一致性 (Consistency):将应用及其依赖一起打包,确保从开发、测试到生产环境的 运行结果完全一致
  • 轻量级与快速启动 (Lightweight & Fast Startup):容器共享主机内核,避免了启动完整操作系统的开销,因此启动速度极快,资源占用极小。
  • 隔离性 (Isolation):通过 Linux 命名空间(Namespaces) 技术,为每个容器提供独立的进程树、网络、文件系统等,实现进程级隔离。
  • 不可变基础设施 (Immutability):容器镜像一旦构建,就 不可更改。任何更新都需要创建一个新的镜像,这极大简化了回滚和部署逻辑。
  • 可移植性 (Portability):容器镜像遵循 OCI 标准,可以在任何支持该标准的平台上运行(无论是物理机、虚拟机还是公有云)。

典型的容器架构(以 Docker 为例)采用 客户端-服务器(Client-Server) 模型:

  1. 客户端 (CLI):用户与容器系统交互的命令行工具(如 dockerpodman)。
  2. 守护进程 (Daemon):在主机上运行的后台服务(如 Docker Daemon),负责接收客户端请求,并执行构建镜像、管理容器、网络配置等核心任务。
  3. OCI 运行时 (OCI Runtime):负责与操作系统内核直接交互,创建和管理容器的底层进程。最常见的是 runc。它实现了 OCI Runtime Specification。
  4. 注册中心 (Registry):外部存储库,用于推送和拉取镜像。

容器运行的本质是利用 Linux 内核提供的两大核心技术:NamespacesCgroups

  1. Namespaces (隔离):当运行一个容器时,内核会为容器进程创建新的命名空间副本:
    • PID Namespace:容器内进程看到的 PID 1 是容器的启动进程。
    • Net Namespace:容器拥有独立的网络堆栈、IP 地址和端口。
    • Mount Namespace:容器拥有独立的根文件系统视图。
    • User Namespace:提供 Rootless 容器的基础,隔离用户和用户组 ID。
  2. Cgroups (资源限制):内核通过 Cgroups 机制限制容器可以使用的 CPU、内存、块 I/O 等资源,防止单个容器耗尽主机资源。
  3. 联合文件系统 (Union File System):容器镜像采用分层结构,只读层堆叠,最上层是 可写层。容器运行时,在这个可写层进行操作,实现了高效的存储共享和快速启动。

容器的完整生命周期涉及 构建、拉取/推送、运行、停止/删除 四个主要阶段:

阶段核心操作描述
构建 (Build)docker build根据 Dockerfile 文件,创建并打包一个不可变的容器镜像。
存储/分发 (Store/Distribute)docker push/pull将构建好的镜像 推送到 注册中心,或从注册中心 拉取 到本地。
运行 (Run)docker run运行时通过 NamespacesCgroups 启动一个隔离的进程,创建一个容器实例。
管理 (Manage)docker ps, stop, exec查看运行中的容器、停止容器、进入容器执行命令。
终止/清理 (Terminate/Clean)docker stop/rm停止容器进程,然后删除容器实例。

与容器技术交互主要通过以下两种方式:

  1. 命令行界面 (CLI):这是最直接的单机级交互方式。例如:

    • 镜像操作docker pull centos (拉取镜像),docker push myapp:v1 (推送镜像)。
    • 容器操作docker run -d --name webapp -p 8080:80 myapp:v1 (后台运行容器并进行端口映射)。
    • 日志和状态docker logs webapp (查看日志),docker ps (查看运行中的容器)。
  2. 容器编排 (Orchestration):在生产环境中,需要管理海量容器和复杂的分布式应用。容器编排系统(如 Kubernetes / K8s)负责:

    • 调度:将容器部署到合适的集群节点。
    • 弹性扩缩容:根据资源负载自动增减容器副本数量。
    • 服务发现与负载均衡:负责容器之间的网络通信与流量分发。
    • 健康检查与自愈:监控容器状态并在故障时自动拉起新容器。

底层运行时是 真正 与 Linux 内核交互,负责创建、启动、停止和管理容器进程的程序。它们遵循 OCI 运行时规范 (OCI Runtime Specification)

运行时描述主要特点
runc最流行且默认 of OCI 运行时。它是 Docker 捐赠给 OCI 的核心组件,由 Go 语言实现。标准容器:基于 Linux Namespaces 和 Cgroups 的标准隔离容器。
crun另一个 OCI 运行时规范的实现,由 C 语言编写。速度和效率:通常比 runc 更快,内存占用更少,是 Podman 的一个推荐选项。
Kata Containers由 Clear Containers 和 runV 合并而来。安全容器/Hypervisor 隔离:在容器和主机之间增加一层轻量级虚拟机 (VM) 隔离(如 KVM),提供更高的安全性(适用于多租户或运行非受信工作负载的场景)。
gVisorGoogle 开源的用户空间内核,用 Go 语言编写。沙箱容器:在容器和主机内核之间引入沙箱,拦截系统调用,提供比标准容器更高的安全性。

高级运行时管理整个容器生命周期(如镜像拉取、存储、网络设置),并调用底层运行时来执行容器。在 Kubernetes 环境中,它们必须实现 CRI(Container Runtime Interface)

运行时描述架构与用途
Containerd从 Docker 核心功能中剥离出来的高级运行时,现已成为 CNCF 毕业项目。CRI 兼容:主流选择,直接实现 CRI,并依赖 runc 等底层运行时。它是目前 DockerKubernetes 的默认底层引擎。
CRI-O专为 Kubernetes CRI 设计的高级运行时。Kubernetes 优化:专注于与 Kubelet 通过 CRI 通信,避免了其它高级运行时的额外复杂性,依赖 runccrun
Docker (Daemon/Engine)包含 CLI、守护进程、镜像构建和高级 API 的完整平台。一体化平台:Docker Daemon 实际上是 Containerd 的 上层封装。虽然 Kubernetes 已经弃用了对 Docker Daemon 的直接支持(dockershim),但其在开发环境依然被广泛使用。

容器运行时的层次结构可以简化为如下关系:

层次组件示例遵循标准职责
编排层Kubernetes (Kubelet)CRI请求创建 Pod 和容器
高级运行时Containerd / CRI-OCRI & OCI镜像管理、网络设置、调用底层运行时
底层运行时runc / crun / KataOCI 运行时规范与 Linux 内核交互,创建隔离 of 容器进程
操作系统Linux 内核N/A提供 NamespacesCgroups 机制

在当前的云计算和 Kubernetes 生态中,最主流且推荐的运行时组合是:

  1. Containerd + runc:这是最常见的生产环境组合,Containerd 负责高级管理和 CRI 兼容,runc 负责底层执行。
  2. CRI-O + crun/runc:主要用于注重 Kubernetes 极致优化和轻量级的主机环境(如 Red Hat OpenShift)。
  3. Kata ContainersgVisor:常用于需要增强安全隔离(如公共云或多租户环境)的场景,它们作为底层 OCI 运行时被 Containerd 或 CRI-O 调用。