1. 概述
1.1 使用容器的目的
- 让应用更稳定、可靠的运行
- 降低服务部署和运维成本
1.2 应用开发部署方式的变迁
| 阶段 | 应用分发方式 | 应用运行方式 | 应用部署方式 | 应用开发方式 |
|---|---|---|---|---|
| 蛮荒时代 |
|
|
|
|
| 容器萌芽(过渡期) |
|
|
|
|
| 云原生时代 |
|
|
|
|
1.3 云原生
云原生指应用从设计之初就为云环境而构建。
CNCF关于云原生的定义:
云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。
这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
云原生计算基金会(CNCF)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。
四个关键特征及其带来的好处:
| 核心特征 | 核心实践 | 解决的问题 | 关键优势 |
|---|---|---|---|
| 微服务 | 将大型单体应用拆分为一组小型、独立的服务。 | 应用臃肿,变更困难,局部故障易导致系统整体瘫痪。 | 高内聚、低耦合,每个服务可独立开发、部署和扩展,提升灵活性和可维护性。 |
| 容器化 | 使用Docker等技术将应用及其依赖打包成标准化的镜像。 | 环境不一致(“在我这运行得好好的”),应用部署和迁移困难。 | 环境一致性和隔离性,实现应用的轻量、秒级启动和一次构建、随处运行。 |
| DevOps | 开发(Dev)和运维(Ops)团队紧密协作,并借助自动化工具链。 | 开发与运维脱节,软件交付流程缓慢且频繁出错。 | 持续交付(CI/CD),实现快速、频繁且可靠的软件发布,小步快跑,加速业务迭代。 |
| 动态管理 | 使用Kubernetes等容器编排系统自动管理容器集群。 | 当容器数量增多时,部署、调度、扩缩容和故障恢复变得极其复杂。 | 自动化运维,实现应用的弹性伸缩、故障自愈和资源的高效利用,无需人工干预。 |
2. 使用容器部署服务
2.1 容器概念
容器与虚拟机的主要区别:
| 对比维度 | 虚拟机 (VM) | 容器 (Container) |
|---|---|---|
| 核心架构 | 硬件级虚拟化。通过Hypervisor模拟一套完整的计算机硬件(CPU、内存等),每个虚拟机都需安装独立的客户操作系统。 | 操作系统级虚拟化。所有容器共享宿主机的操作系统内核,仅打包应用及其依赖库,本身不包含操作系统。 |
| 资源占用 | 重。每个虚拟机都运行完整的操作系统,需要分配固定的CPU、内存,镜像体积常以GB为单位,资源利用率较低。 | 轻。无需单独的操作系统开销,仅包含应用所需内容,镜像体积常为MB级,同一台宿主机可运行远多于虚拟机的实例。 |
| 启动速度 | 分钟级。需要经历完整的操作系统启动过程,类似于电脑开机 。 | 秒级甚至毫秒级。本质是启动一个被隔离的进程,无需启动操作系统,速度极快。 |
| 隔离性与安全性 | 强隔离。每个虚拟机拥有独立的内核和硬件抽象层,一个虚拟机的故障或安全威胁很难影响到其他虚拟机,安全性更高 。 | 进程级隔离。通过Linux的命名空间(Namespace) 和控制组(Cgroups) 实现隔离 。隔离性相对较弱,若宿主机内核存在漏洞,可能影响所有容器。 |
| 可移植性 | 一般。镜像与特定硬件配置和Hypervisor类型相关,迁移时可能遇到兼容性问题。 | 高。容器镜像通过Dockerfile等文件定义,封装了应用运行所需的一切,能确保在开发、测试、生产环境中的一致性,真正做到“一次构建,随处运行” 。 |
容器和虚拟机共存互补:
- 优先选择容器的情况:追求快速迭代、弹性伸缩的微服务架构;需要极高的资源利用率以节约成本;实践CI/CD(持续集成/持续部署)
- 优先选择虚拟机的情况:应用需要完整的操作系统功能或运行不同内核的操作系统(如在Linux服务器上运行Windows应用);对安全隔离性有极高要求(如金融、政务等多租户场景)
2.1.1 容器实现
CNCF容器运行时项目列表:
业界常用容器运行时工具:
| 名称 | 类型/定位 | 核心架构与隔离机制 | 主要优势与特点 |
|---|---|---|---|
| Docker | 全功能容器平台(High-level) | C/S架构,依赖Docker Daemon守护进程。使用containerd和runc,隔离基于Linux命名空间和cgroups。 | 生态完整,工具链丰富,开发者体验友好。但随着Kubernetes弃用其作为运行时,在生产集群中地位下降。 |
| containerd | 行业标准容器运行时(High-level) | 轻量级守护进程。作为Docker的核心组件被剥离,现为CNCF毕业项目。通过插件支持CRI,是Kubernetes默认运行时。 | 性能优异、稳定性高、资源消耗低。架构精简,是生产环境Kubernetes集群的流行选择。 |
| CRI-O | 专为Kubernetes设计的运行时(High-level) | 轻量级守护进程。专门为实现Kubernetes CRI接口而生,结构简洁。 | 极度轻量,与Kubernetes集成紧密。是追求纯粹Kubernetes原生体验和轻量化的选择。其代码库只专注于Kubernetes的需求,攻击面更小,被认为是更安全的选择 |
| Kata Containers | 安全容器运行时(Low-level) | 使用硬件虚拟化技术,每个Pod运行在一个独立的轻量级虚拟机中,拥有自己的内核。当被调用时(通常通过containerd的kata-shim),它会启动一个包含自己专用内核的微型虚拟机(VM)。 | 提供虚拟机级别的强安全隔离,有效防止容器逃逸。适用于多租户、安全敏感型场景,但会有一定的性能开销。为不可信或多租户工作负载提供最高级别的安全隔离。它兼容OCI规范,使得Kubernetes可以通过RuntimeClass资源来指定某些Pod使用Kata运行,实现与普通容器的混合部署 |
| Podman | 无守护进程容器引擎(High-level) | 无守护进程(Daemonless),直接通过OCI运行时(如runc或crun运行容器。支持rootless模式。 | 安全性高,资源占用少。CLI命令与Docker高度兼容,迁移成本低。特别适合开发机和CI/CD环境。 |
| Lima | macOS上的Linux虚拟机管理工具 | 不是容器运行时本身,而是在macOS上启动Linux虚拟机的工具(通常使用QEMU)。用于在虚拟机内运行Docker或Podman等容器引擎。 | 让macOS用户能够方便地运行Linux环境及容器,是Docker Desktop的一种开源替代方案组成部分。 |
| LXD | 系统容器管理器 | 基于LXC的下一代系统容器管理器。它本身是一个守护进程,提供REST API管理系统级容器。 | 容器内运行完整的操作系统和初始化进程(如systemd),体验更接近轻量级虚拟机,隔离性优于应用容器。 |
2.1.2 Docker容器架构
Docker没有发明新技术,而是把 Linux 已有的 namespace、cgroup、chroot 和 unionfs 机制组合起来,用来管理容器的生命周期、镜像的生命周期。
- docker(客户端):是一个可执行的二进制文件,接收并解析用户输入的命令后发送给dockerd
- dockerd:是一个由 systemd 管理的服务端守护进程,主要功能如下:
- dockerd 自己并不直接创建容器,它会将这项任务委托给更底层的 containerd,containerd 再通过 containerd-shim 调用 runc 来实际创建容器
- 生命周期管理:根据指令创建、启动、停止、删除容器。
- 镜像管理:构建镜像,或从仓库(如 Docker Hub)拉取和推送镜像
- 资源管理管理容器的网络连接和数据存储卷等
2.1.3 Docker容器模型
业务进程在容器中运行原理如图所示:
- Linux Namespace:Namespace 技术实际上修改了应用进程看待整个计算机“视图”,即它的“视线”被操作系统做了限制,只能“看到”某些指定的内容
- Linux Cgroup:限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等
- 文件系统
2.2 容器镜像
容器镜像的应用场景:
- 镜像是应用及其环境的静态视图。
- 镜像根据Dockerfile中的配置构建的按照一定标准组织起来的一系列文件。
容器镜像的要点:
- 联合文件系统(Union File System):将多个不同位置的目录联合挂载(union mount)到同一个目录下
- 从镜像仓库拉取到的“镜像”,实际上就是一个操作系统的 rootfs,它的内容是操作系统的所有文件和目录。
- 镜像分层
以一个golang镜像为例,执行 docker image inspect golang:1.20.5 可以得到镜像元数据:
- GraphDriver 存储驱动信息:
GraphDriver部分显示了该镜像使用 overlay2 存储驱动时的具体目录结构- LowerDir:包含多个以冒号分隔的目录路径,这些是镜像的只读层,按从底层到顶层的顺序排列。每个目录对应一个镜像层,存储着该层的文件变更。
- UpperDir:是可写层目录,当基于该镜像创建容器时,容器的所有写操作都会记录在此处。
- MergedDir:是 LowerDir 和 UpperDir 合并后的视图,也就是容器实际看到的统一文件系统。
- WorkDir:是 overlay2 驱动内部使用的工作目录。
- RootFS 根文件系统信息:
RootFS部分描述了镜像的文件系统层次结构:- Type: “layers” 表明这是一个分层文件系统。
- Layers:数组中的每个元素都是一个 SHA256 哈希值,唯一标识一个镜像层。这些哈希值与
LowerDir中的层是一一对应的关系。
- 镜像层结构分析:该镜像由 6 个层组成(对应 Layers 数组中的 6 个哈希值)。每个层都包含文件系统的变更集,Docker 利用这种分层结构实现了镜像的共享和高效存储。当运行容器时,overlay2 驱动会将这些层与一个可写层(UpperDir)组合,通过 MergedDir 为容器提供统一的文件系统视图。
2.2.1 OCI镜像规范
| 作用 | |
|---|---|
| 镜像索引 ImageIndex | ImageIndex 指向不同平台的 Manifest 文件,确保一个镜像可以跨平台使用,每个平台拥有不同的 Manifest 文件。 |
| 镜像清单 ImageManifest | 包含镜像的配置和层文件以及镜像的各种元数据信息,是组成一个容器镜像所需的所有组件的集合。 |
| 镜像层 ImageLayer | 是以Layer保存的文件系统,是镜像的主要内容。
一般是压缩后的二进制数据文件格式。 一个镜像有一个或多个Layer文件,每个Layer文件保存了与上层相比变化的部分。 |
| 镜像配置 ImageConfiguration | 保存容器 rootfs 文件系统的层级信息,以及容器运行时需要的信息(环境变量、工作目录、命令参数、mount列表等) |
2.2.2 Dockerfile
通常通过 Dockerfile 构建镜像。
- Dockefile详情见官网文档。
- Dockerfile 最佳实践:https://docs.docker.com/build/building/best-practices/
简单Dockerfile示例:
# 使用官方的轻量级 Python 运行时作为父镜像FROM python:3.9-slim# 设置镜像的维护者信息(新版推荐使用 LABEL)LABEL maintainer="your-email@example.com"LABEL version="1.0" description="示例应用"# 设置一个环境变量,用于防止 Python 在容器中生成 .pyc 文件ENV PYTHONDONTWRITEBYTECODE 1# 设置另一个环境变量,确保 Python 输出直接发送到终端而不被缓冲ENV PYTHONUNBUFFERED 1# 设置应用的工作目录路径ENV APP_HOME /app# 在镜像中创建工作目录WORKDIR $APP_HOME# 将当前目录(构建上下文)下的 requirements.txt 文件复制到容器的 /app 目录下COPY requirements.txt .# 运行命令,安装项目依赖# 将更新软件源列表、安装依赖、清理缓存合并到一层,以减少镜像层数和体积RUN apt-get update \&& apt-get install -y --no-install-recommends gcc \&& pip install --no-cache-dir -r requirements.txt \&& apt-get purge -y --auto-remove gcc \&& rm -rf /var/lib/apt/lists/*# 将当前目录下的所有文件复制到容器的工作目录中COPY . .# 声明容器在运行时将监听的端口号(这只是一个文档说明,实际映射需要在运行时指定)EXPOSE 8000# 在容器内创建一个卷挂载点,用于持久化数据或与宿主机共享数据VOLUME /app/data# 创建一个非 root 用户并切换,以增强容器运行时的安全性<cite data-id='190018'>190018</cite>RUN groupadd -r appuser && useradd -r -g appuser appuserUSER appuser# 定义一个健康检查命令,Docker 会定期执行 `curl -f http://localhost:8000/health/` 来检查应用状态HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \CMD curl -f http://localhost:8000/health/ || exit 1# 配置容器启动时运行的命令(使用 exec 格式)# 如果 Dockerfile 中有 ENTRYPOINT,则 CMD 作为其默认参数CMD
构建过程解析:
| 指令 | 主要作用 | 核心要点与区别 |
|---|---|---|
| FROM | 指定构建镜像的基础镜像。 | 必须是第一条指令。建议使用官方、特定版本标签的镜像(如 python:3.9-slim),而非默认的 latest,以保证构建的一致性 |
| COPY vs ADD | 将文件/目录从主机复制到镜像中。 | 优先使用 COPY,因为它行为更清晰透明。ADD 虽然支持自动解压和从 URL 下载,但功能复杂,容易造成非预期行为 |
| RUN | 在构建过程中执行命令。 | 将多个命令用 && 连接并在最后清理临时文件,可以减少镜像层数,让镜像更小。 |
| CMD vs ENTRYPOINT | 定义容器启动时执行的命令。 | CMD 设定的命令可以被 docker run 后面的参数覆盖,通常用于定义默认命令。ENTRYPOINT 则更像一个固定的可执行文件,CMD 的内容会作为它的参数。两者结合使用非常常见 |
| EXPOSE | 声明容器运行时监听的网络端口。 | 这仅仅是一个文档说明,并不会自动进行端口映射。实际的端口映射需要在运行容器时通过 -p 参数完成。 |
多阶段构建Dockerfile示例:
# 第一阶段:构建阶段FROM node:18 AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build# 第二阶段:生产阶段,使用更小的基础镜像FROM node:18-alpineWORKDIR /app# 从 builder 阶段只复制构建好的结果,不复制源代码和 node_modulesCOPY --from=builder /app/dist ./distCOPY --from=builder /app/package*.json ./RUN npm ci --omit=devUSER nodeEXPOSE 3000CMD ["node", "dist/index.js"]
注意要点:
- 使用 .dockerignore 文件:类似于 .gitignore,它可以排除不需要打入镜像的文件(如 node_modules, .git, .env 等),加速构建过程并减小镜像体积。
- 优化指令顺序:将变化频率最低的指令(如安装依赖 COPY requirements.txt / RUN pip install)放在 Dockerfile 的前面,变化频率高的指令(如复制源代码 COPY . .)放在后面。这样可以充分利用 Docker 的构建缓存,极大提升构建速度。
- 尽量保证镜像的轻量精简,不要增加不必要的文件。
2.2.3 制作镜像
使用 docker build 制作和所在环境CPU架构和操作系统类型一致的镜像:
docker build -t domain.io/demo:v1.0 -f Dockerfile --network=host .
手动创建多CPU架构的镜像:
- 在各自CPU架构的环境通过
docker build构建tag带后缀的镜像 - 通过
docker manifest create命令手动构建支持多架构的镜像 - 通过
docker manifest push命令推送多架构镜像到镜像仓库
docker manifest create domain.io/demo:v1.0 domain.io/demo:v1.0-amd64 domain.io/demo:v1.0-arm64 --insecuredocker manifest push domain.io/demo:v1.0
使用 docker buildx 构建多CPU架构的镜像:
# 安装 buildx 后docker buildx create --use --name mybuilder --bootstrapdocker buildx build \--platform linux/amd64,linux/arm64,linux/arm/v7 \--push \-t domain.io/demo:v1.0 .
执行docker manifest inspect domain.io/demo:v1.0查看镜像Manifest配置:
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json","manifests": [{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 1791,"digest": "sha256:d6b022ac81f2367b0b8d560e5139c298ca0d0fb5b1e93a5f7d9f4096c19f0535","platform": {"architecture": "amd64","os": "linux"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 1791,"digest": "sha256:6caaeb33c0a0de9f72b261ed38449b9244ebe52aaa50c1d76cc7a8cacfee4db1","platform": {"architecture": "arm64","os": "linux"}}]}
在一个安装 docker 的环境中,
- 执行
docker image ls或docker images可以查看本地镜像列表 - 执行
docker pull或docker image pull从镜像仓库拉取镜像 - 执行
docker push或docker image push向镜像仓库推送镜像 - 执行
docker image inspect {image id}可以查看镜像详情 - 执行
docker image save image:tag -o file.tar可以将镜像image.tag导出保存到 file.tag 文件中 - 执行
docker image load -i file.tar可以将 file.tar 文件中保存的镜像导入
在一个安装了 containerd 的环境中,
- 执行
ctr i ls可以查看当前环境中由 containerd 管理的镜像列表
在一个安装了 crictl 的环境中,
- 执行
crictl image可以查看当前环境中的 OCI 镜像列表 - 执行
crictl pull image:tag可以拉取镜像 image:tag 到本地 - 执行
crictl inspecti image:tag可以查看镜像 image:tag 的详细信息
在一台服务器上启动多个容器时,运行状态如下:
例如,Docker容器有如下五种网络模式:
| 网络模式 | 工作原理简述 | 核心特点 | 典型应用场景 |
|---|---|---|---|
| Bridge(桥接) | Docker 创建一个虚拟网桥(默认 docker0),容器通过 veth pair 连接到该网桥 | 默认模式;容器有独立IP,需要通过端口映射 (-p) 与宿主机外部通信;同主机内容器可通过IP或容器名(自定义网络)互通 | 单机部署、需要网络隔离的多容器应用 |
| Container(容器) | 新创建的容器与一个已存在的容器共享网络命名空间,而非与宿主机共享 | 容器间通过 localhost 直接通信,网络配置完全一致 | 需要紧密协作的容器组(如日志收集器与主应用)。 |
| Host(主机) | 容器直接使用宿主机的网络命名空间,与宿主机共享IP和端口 | 高性能;无需端口映射;容器网络与宿主机完全无隔离,易端口冲突 | 对网络性能要求极高的场景,如高频交易、负载测试。 |
| None(无网络) | 容器拥有自己的网络命名空间,但内部不做任何网络配置,只有回环接口 (lo) | 完全网络隔离;安全性最高;需要手动配置网络栈。 | 安全敏感场景、需要完全自定义网络配置的离线任务。 |
| Macvlan | 为容器分配一个物理网络接口的虚拟子接口,使其在物理网络上像一台拥有独立MAC和IP地址的真实主机 | 容器直接使用物理网络IP,无需NAT;性能好;但需要网络管理员分配IP段 | 需要容器直接暴露在物理二层网络中的场景,如继承老旧网络策略的应用 |
containerd 本身不直接提供像 Docker 那样丰富的“网络模式”选项,而是通过插件机制来管理容器网络containerd 的网络能力主要由 CNI (Container Network Interface) 插件提供。这意味着,containerd 支持的网络类型取决于你安装和配置了哪些 CNI 插件。
| 网络类型 / 插件示例 | 核心工作原理与特点 |
|---|---|
| Bridge (如 bridge 插件) | 这是最常用的模式,类似 Docker 的默认 bridge 模式。会在宿主机上创建一个虚拟网桥(非 docker0),容器通过 veth pair 连接到网桥,并分配私有 IP。容器间通过网桥通信,与外网通信需通过宿主机进行 NAT。 |
| Host | 容器直接共享使用宿主机的网络命名空间(Network Namespace),使用宿主机 IP 和端口。网络性能高,但隔离性最差,容易引发端口冲突 |
| MACvLAN / IPvLAN | 允许为容器分配一个真实的 MAC 或 IP 地址,使其在物理网络上显示为一台真实的设备。非常适合需要容器直接暴露在物理网络中的场景。 |
| Overlay (如 Flannel, Calico, Weave) | 用于构建跨多个主机的容器网络。通过封装网络报文(如 VXLAN)在不同主机的容器间创建虚拟网络,是实现 Kubernetes 等容器编排平台网络的基础 |
想了解更多干货,可通过下方扫码关注

可扫码添加上智启元官方客服微信👇

17认证网








