容器化与编排技术总结
概述
本文档是对容器化与编排技术的系统性总结,涵盖 Docker 核心与进阶、Docker Compose 生产实践、Kubernetes 核心概念与运维、Helm 包管理以及 Helmfile/Kustomize 选型对比。旨在帮助开发者建立完整的容器化知识体系,从开发到生产运维形成闭环理解。
一、Docker 核心回顾与进阶
1.1 Docker 生命周期:镜像 -> 容器 -> 仓库
Docker 的核心工作流围绕三个基本要素展开:镜像(Image)、容器(Container) 和 仓库(Registry)。
基本工作流:
# 1. 从 Dockerfile 构建镜像
docker build -t my-app:1.0.0 .
# 2. 将镜像推送到仓库
docker tag my-app:1.0.0 registry.example.com/my-app:1.0.0
docker push registry.example.com/my-app:1.0.0
# 3. 从仓库拉取镜像并运行容器
docker pull registry.example.com/my-app:1.0.0
docker run -d --name my-app -p 8080:8080 registry.example.com/my-app:1.0.0镜像构建原理:
Docker 镜像由多层只读层(Layer)堆叠而成,每一层对应 Dockerfile 中的一条指令。层被 Docker Daemon 缓存,当层内容未变化时直接复用缓存,加速构建。
Dockerfile 指令 镜像层
FROM base --> Layer 0: base OS
RUN apt install --> Layer 1: apt packages
COPY app.jar --> Layer 2: application
CMD ["java"] --> Layer 3: metadata (不占空间)
|
容器运行时在最上层增加可写层(容器层)容器的本质:
容器是运行在宿主机上的隔离进程,通过以下技术实现隔离:
- Namespace:隔离进程、网络、文件系统、用户、主机名等视图
- Cgroups:限制 CPU、内存、磁盘 IO、网络带宽等资源使用
- UnionFS(联合文件系统):将镜像多层叠加为统一视图,容器层记录变更
仓库的交互流程:
docker login --> 认证(生成 ~/.docker/config.json)
docker push --> 镜像分层上传(只推送不存在的层)
docker pull --> 镜像分层下载(仅下载缺失的层)镜像仓库支持 OCI(Open Container Initiative)标准格式,Harbor、Docker Hub、AWS ECR、阿里云 ACR 等均遵循该标准。
1.2 多阶段构建最佳实践
多阶段构建允许在一个 Dockerfile 中使用多个 FROM 语句,每个 FROM 开启一个新的构建阶段。只有最后一个阶段的产物保留在最终镜像中。
Java / Spring Boot 典型 Dockerfile
# 第一阶段:Maven 编译
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
# 第二阶段:使用 Spring Boot layertools 分离依赖(优化缓存)
FROM eclipse-temurin:21-jre-alpine AS extractor
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
# 第三阶段:最终运行镜像
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 分层复制,利用 Docker 层缓存
COPY --from=extractor /app/dependencies/ ./
COPY --from=extractor /app/spring-boot-loader/ ./
COPY --from=extractor /app/snapshot-dependencies/ ./
COPY --from=extractor /app/application/ ./
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -sf http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--spring.profiles.active=prod"]GraalVM 原生编译 Dockerfile
# 第一阶段:GraalVM 原生编译
FROM ghcr.io/graalvm/graalvm-ce:ol8-java17 AS builder
WORKDIR /build
COPY . .
RUN gu install native-image
RUN ./mvnw -Pnative native:compile -DskipTests
# 第二阶段:scratch 空基础镜像运行(极致精简)
FROM scratch
WORKDIR /app
COPY --from=builder /build/target/application .
EXPOSE 8080
ENTRYPOINT ["/app/application"]前端 Nginx 部署 Dockerfile
# 第一阶段:Node.js 构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:Nginx 运行
FROM nginx:1.25-alpine
# 从 builder 阶段复制构建产物到 Nginx 静态目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 自定义 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:80/ || exit 1Node.js 应用 Dockerfile
# 第一阶段:安装依赖并构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:运行(使用更小的基础镜像)
FROM node:20-alpine
WORKDIR /app
# 复制构建产物和 node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json .
# 非 root 运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]Python 应用 Dockerfile
# 第一阶段:安装依赖
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dirs -r requirements.txt
# 第二阶段:运行
FROM python:3.12-slim
WORKDIR /app
# 从 builder 阶段复制用户安装的包
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]1.3 镜像优化实践
基础镜像选型对比
| 镜像变体 | 基础大小 | libc | 包管理器 | 兼容性 | 适用场景 |
|---|---|---|---|---|---|
alpine | ~5MB | musl | apk | 部分原生库需适配 | 追求极致体积 |
slim (Debian) | ~30MB | glibc | apt | 兼容性好 | 平衡体积与兼容性 |
distroless | ~2MB (static) / ~25MB (java) | glibc | 无 | 仅运行应用 | 安全优先的生产镜像 |
nanoserver (Windows) | ~200MB | - | - | Windows 容器 | Windows 容器化 |
scratch | 0MB | - | - | 静态编译 | Go/C 静态二进制 |
| 标准版 (Debian/Ubuntu) | ~100MB+ | glibc | apt | 最好 | 需要完整工具链 |
Alpine 注意事项:
- 使用 musl libc 而非 glibc,某些原生扩展(如
psycopg2、cffi)需要编译 musl 版本 - DNS 解析行为与 glibc 不同,偶尔出现网络连接问题
- 调试时缺少 shell 工具(bash、curl、ping),需手动安装
Distroless 优势:
- 无 shell、无包管理器、无系统工具,攻击面极小
- 使用
gcr.io/distroless/java21-debian12等基础镜像 - 仅能运行应用,无法 ssh 进入容器,安全性高
- 调试可通过
kubectl debug注入临时容器
# Distroless Java 示例
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM gcr.io/distroless/java21-debian12
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]镜像优化综合实践
1. 多阶段构建减少最终镜像层数
构建阶段包含完整的 SDK 和构建工具,运行阶段仅包含 JRE 和产物,最终镜像体积可减少 60%-80%。
2. 依赖缓存清理
# apt 缓存清理
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
# apk 缓存清理
RUN apk add --no-cache curl
# pip 缓存清理
RUN pip install --no-cache-dir -r requirements.txt
# npm 缓存清理
RUN npm ci --only=production && npm cache clean --force3. 分离构建产物
# Spring Boot 分层构建(利用 layertools)
# 将依赖层与应用代码层分离,应用代码变化不影响依赖层缓存
COPY --from=extractor /app/dependencies/ ./
COPY --from=extractor /app/spring-boot-loader/ ./
COPY --from=extractor /app/snapshot-dependencies/ ./
COPY --from=extractor /app/application/ ./4. COPY 顺序利用缓存
# 正确的顺序:先复制不常变化的文件
COPY package.json package-lock.json ./ # 依赖定义
RUN npm ci # 安装依赖(仅 package.json 变化时重跑)
COPY . . # 复制源码(常变化)
RUN npm run build # 构建5. HEALTHCHECK 配置
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -sf http://localhost:8080/health || exit 1参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
--interval | 30s | 检查间隔 |
--timeout | 30s | 单次检查超时 |
--retries | 3 | 连续失败次数判为不健康 |
--start-period | 0s | 启动宽限期,期间失败不计入重试 |
6. 非 root 用户运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser在 Alpine 中使用 adduser/addgroup,在 Debian 中使用 useradd/groupadd。
1.4 Docker 网络进阶
网络驱动对比
| 驱动 | 作用域 | 容器间通信 | 外部访问 | 适用场景 |
|---|---|---|---|---|
| Bridge | 单机 | 同一网络下容器名互访 | 端口映射 -p | 默认模式,开发测试 |
| Host | 单机 | 共享宿主机网络栈 | 直接使用宿主机端口 | 追求网络性能(如 Redis) |
| Overlay | 跨主机 | 集群内容器互访 | 结合 ingress 网络 | Docker Swarm / K8s |
| Macvlan | 单机 | 容器拥有独立 MAC 地址 | 直接接入物理网络 | 遗留应用直接使用物理 IP |
| IPvlan | 单机 | 容器共享 MAC 但独立 IP | 同 Macvlan 但更高效 | 大规模容器部署 |
Bridge 网络工作原理:
宿主机
+----------------------------------------------------+
| docker0 (虚拟网桥, 172.17.0.1/16) |
| | |
| +--- veth0 ---> eth0@container1 (172.17.0.2) |
| +--- veth1 ---> eth0@container2 (172.17.0.3) |
| |
| iptables NAT 规则: |
| 宿主机 8080 -> 172.17.0.2:80 |
+----------------------------------------------------+容器间通信方式:
# 1. 默认 bridge 网络(通过 IP 通信)
docker network create my-network
docker run -d --name app1 --network my-network my-app
docker run -d --name app2 --network my-network my-app
# app1 可通过 app2 容器名访问(Docker 内置 DNS)
# 2. 自定义网络(推荐)
# 自定义 bridge 网络提供了:
# - 自动 DNS 解析(容器名 -> IP)
# - 网络隔离(不同网络无法直接通信)
# - 动态加入/退出
# 3. 容器连接到多个网络
docker network connect frontend-network app1
docker network connect backend-network app1CNM(Container Network Model)与 libnetwork:
Docker 的网络实现基于 CNM(Container Network Model),由 libnetwork 库实现:
- Sandbox:容器的网络栈(命名空间),包含网卡、路由表、DNS 配置
- Endpoint:Sandbox 与 Network 的连接点(veth 对的一端)
- Network:一组可以相互通信的 Endpoint 集合
# 查看容器的网络配置
docker inspect app1 --format '{{json .NetworkSettings}}'
# 查看网络详情
docker network inspect my-network网络隔离原理:
# docker-compose 中网络隔离示例
services:
frontend:
networks:
- frontend
- backend
backend:
networks:
- backend
db:
networks:
- backend # db 不接入 frontend,前端无法直接访问数据库
networks:
frontend:
backend:1.5 数据持久化
三种挂载方式对比
| 特性 | Volume(数据卷) | Bind Mount(绑定挂载) | tmpfs Mount(临时挂载) |
|---|---|---|---|
| 存储位置 | /var/lib/docker/volumes/ | 宿主机任意路径 | 内存(RAM) |
| 生命周期 | Docker 管理,独立于容器 | 依赖宿主机路径存在 | 容器停止后数据消失 |
| 备份迁移 | 通过 docker volume 命令 | 手动处理文件 | 不支持持久化 |
| 跨主机共享 | 需 Volume Driver(如 NFS) | 需共享文件系统 | 不支持 |
| 性能 | 与 Bind Mount 接近 | I/O 直通,性能高 | 极高性能 |
| 适用场景 | 数据库、配置文件持久化 | 开发热重载、日志输出 | 缓存、敏感信息(不落盘) |
Volume 生命周期管理:
# 创建数据卷
docker volume create --name app-data --driver local
docker volume create --name nfs-share --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/exported/path
# 使用数据卷
docker run -d -v app-data:/app/data --name my-app my-image
# 查看数据卷详情
docker volume inspect app-data
# 清理未使用的数据卷
docker volume prune
# 备份数据卷
docker run --rm -v app-data:/source -v $(pwd):/backup alpine \
tar czf /backup/app-data-backup.tar.gz -C /source .Bind Mount 主机路径依赖:
# 开发环境使用 bind mount 实现热重载
docker run -d \
-v /host/project/src:/app/src \
-v /host/project/config:/app/config:ro \
my-app
# 日志输出到宿主机
docker run -d \
-v /var/log/my-app:/app/logs \
my-appVolume Driver NFS 共享存储:
# 使用 NFS volume driver 实现跨主机共享
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw,nfsvers=4 \
--opt device=:/data/shared \
shared-volume
docker run -d -v shared-volume:/app/data my-app数据容器模式(已过时,但仍在使用):
# 数据容器模式(创建专门的数据容器,通过 --volumes-from 共享)
version: "3.9"
services:
data-container:
image: alpine
volumes:
- /var/lib/mysql
- /app/uploads
command: "echo Data Container"
app:
image: my-app
volumes_from:
- data-container推荐使用命名 Docker Volume 替代数据容器模式,Volume 语义更清晰,独立于容器生命周期。
1.6 Docker 安全
容器沙箱隔离
Docker 容器的安全性基于 Linux 内核的多种安全机制:
Namespace 隔离:
| Namespace 类型 | 隔离内容 | 系统调用参数 |
|---|---|---|
| PID | 进程号 | CLONE_NEWPID |
| Network | 网络栈(网卡、路由、iptables) | CLONE_NEWNET |
| Mount | 文件系统挂载点 | CLONE_NEWNS |
| UTS | 主机名与域名 | CLONE_NEWUTS |
| IPC | System V IPC / POSIX 消息队列 | CLONE_NEWIPC |
| User | 用户与用户组 ID 映射 | CLONE_NEWUSER |
| Cgroup | cgroup 根目录 | CLONE_NEWCGROUP |
Seccomp(安全计算模式):
Seccomp 限制容器可以使用的系统调用,Docker 默认提供一个白名单策略(约 300 个 syscall 允许,其他全部拒绝)。
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["accept", "bind", "connect", "listen", "read", "write", "open", "close", "exit", "exit_group"],
"action": "SCMP_ACT_ALLOW"
}
]
}# 运行容器时使用自定义 seccomp 策略
docker run --security-opt seccomp=./custom-profile.json my-app
# 禁用 seccomp(不推荐)
docker run --security-opt seccomp=unconfined my-appAppArmor / SELinux:
- AppArmor:使用路径名进行强制访问控制(MAC),Ubuntu 默认使用
- SELinux:使用安全上下文(标签)进行 MAC,CentOS/RHEL 默认使用
# AppArmor
docker run --security-opt apparmor=my-app-profile my-app
# SELinux
docker run --security-opt label:type:container_t my-appReadonly Rootfs:
将容器根文件系统设置为只读,防止恶意写入:
FROM alpine
RUN mkdir /data
VOLUME /datadocker run --read-only --tmpfs /tmp --tmpfs /var/run my-appCapability 最小化:
Docker 默认删除容器的所有特权能力,只保留白名单中的能力。生产环境应进一步裁剪:
# 查看容器默认能力
docker run --rm alpine grep CapPrm /proc/1/status
# 裁剪能力(删除 NET_RAW 防止容器内修改网络)
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-app
# 特权模式(仅调试时使用,绝对不要用于生产)
docker run --privileged my-app| 能力 | 说明 | 是否保留 |
|---|---|---|
| CHOWN | 修改文件所有者 | 需保留 |
| DAC_OVERRIDE | 绕过文件权限检查 | 需保留 |
| FOWNER | 绕过文件所有者检查 | 需保留 |
| KILL | 发送 SIGKILL | 需保留 |
| NET_BIND_SERVICE | 绑定 <1024 端口 | 按需保留 |
| NET_RAW | 原始套接字 | 建议删除 |
| SYS_ADMIN | 系统管理 | 建议删除 |
| NET_ADMIN | 网络管理 | 建议删除 |
安全扫描 Trivy:
# 安装 Trivy
# 下载二进制或使用 Docker 镜像
# 扫描镜像漏洞
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image my-app:1.0.0
# 扫描本地文件系统
trivy filesystem --severity=CRITICAL,HIGH /path/to/project
# 生成 SBOM(软件物料清单)
trivy image --format=spdx --output=my-app.spdx my-app:1.0.0Docker Bench Security:
Docker Bench Security 是一个用于检查 Docker 部署是否符合 CIS Docker Benchmark 安全标准的脚本:
# 运行 Docker Bench Security
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc:/host/etc:ro \
docker/docker-bench-security检查项包括:
| 检查类别 | 内容 |
|---|---|
| 主机配置 | 内核安全、Docker daemon 用户 |
| Docker daemon 配置 | TLS 加密、日志级别、仓库证书 |
| 容器运行时 | 权限、能力、只读文件系统 |
| 镜像构建 | 安全扫描、非 root 用户 |
| Docker Swarm | 加密通信、自动锁 |
二、Docker Compose 进阶
2.1 Compose 生产配置
Profiles(按环境启用服务)
Compose 3.8+ 支持 profiles,通过 profile 控制哪些服务在特定环境启动:
version: "3.9"
services:
app:
image: my-app:latest
ports:
- "8080:8080"
# 仅开发环境启用的服务
mailhog:
image: mailhog/mailhog
profiles:
- dev
ports:
- "8025:8025"
# 仅测试环境启用的服务
integration-test:
image: my-integration-test:latest
profiles:
- test
depends_on:
- app
# 生产监控服务
prometheus:
image: prom/prometheus
profiles:
- prod# 启动所有服务(不含 profile 服务)
docker compose up -d
# 启动 dev profile 服务
docker compose --profile dev up -d
# 启动多个 profile
docker compose --profile dev --profile test up -dExtends(服务继承)
extends 支持从其他服务或外部文件继承配置,减少重复:
# common.yml
services:
common-app:
image: my-app:latest
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3# docker-compose.yml
version: "3.9"
services:
user-service:
extends:
file: common.yml
service: common-app
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:mysql://db:3306/user_db
ports:
- "8081:8080"
order-service:
extends:
file: common.yml
service: common-app
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:mysql://db:3306/order_db
ports:
- "8082:8080"注意:
extends在 Compose V2 中推荐使用include替代(Docker Compose V2.20+),include更灵活且支持合并多个文件。
Deploy Resources(资源限制)
version: "3.9"
services:
app:
image: my-app:latest
deploy:
resources:
limits:
cpus: "1.0"
memory: "512M"
reservations:
cpus: "0.25"
memory: "128M"
# Docker Compose 非 Swarm 模式下,直接使用以下配置
mem_limit: 512m
mem_reservation: 128m
cpus: "1.0"Restart Policy
services:
app:
image: my-app:latest
restart: always # always / unless-stopped / on-failure / no
batch-worker:
image: my-worker:latest
restart: on-failure:3 # 最多重启 3 次
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120sHealthcheck 依赖检查
version: "3.9"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: mydb
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
app:
image: my-app:latest
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "8080:8080"depends_on 的 condition 支持三种值:
| 值 | 说明 |
|---|---|
service_started | 等待服务启动(默认行为) |
service_healthy | 等待健康检查通过 |
service_completed_successfully | 等待服务成功退出(一次性任务) |
2.2 多环境管理
Override 文件机制
Docker Compose 支持多个 -f 文件合并,默认自动加载 docker-compose.override.yml:
# docker-compose.yml(基础配置,所有环境通用)
version: "3.9"
services:
app:
image: my-app:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- app-logs:/app/logs# docker-compose.override.yml(开发环境覆盖,自动加载)
version: "3.9"
services:
app:
# 开发环境使用 build 替代 image
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8080:8080"
- "5005:5005" # JVM 远程调试
environment:
- SPRING_PROFILES_ACTIVE=dev
volumes:
- ./src:/app/src:ro # 源码热重载
- ./config:/app/config:ro# docker-compose.prod.yml(生产环境配置)
version: "3.9"
services:
app:
image: registry.example.com/my-app:${APP_VERSION:-latest}
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=${DB_URL}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
deploy:
resources:
limits:
memory: "1G"
reservations:
memory: "512M"
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"多环境启动命令:
# 开发环境(override.yml 自动生效)
docker compose up -d
# 生产环境(显式指定 -f 文件)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 测试环境
docker compose -f docker-compose.yml -f docker-compose.test.yml up -d
# 构建并启动
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build.env 环境变量文件
# .env 文件(Compose 自动加载)
APP_VERSION=1.2.0
DB_URL=jdbc:mysql://db:3306/mydb
DB_USERNAME=app_user
DB_PASSWORD=${DB_PASSWORD}
LOG_LEVEL=INFO# docker-compose.yml 中使用
services:
app:
image: my-app:${APP_VERSION:-latest}
environment:
- DB_URL=${DB_URL}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD:-default_pass}
- PROFILE=${PROFILE:-dev}变量替换语法:
| 语法 | 说明 |
|---|---|
${VAR} | 使用 VAR 值,未定义则警告 |
${VAR:-default} | 未定义时使用 default |
${VAR:?error} | 未定义时报错 "error" |
${VAR:+replacement} | 定义时使用 replacement,否则空 |
2.3 Compose 与服务发现
自动 DNS 解析
Compose 为自定义网络中的每个容器创建 DNS 记录,容器名作为主机名可互相解析:
version: "3.9"
services:
web:
image: nginx:alpine
depends_on:
- api
api:
image: my-api:latest
environment:
- DB_HOST=db # 自动解析到 db 容器 IP
- REDIS_HOST=redis
db:
image: mysql:8.0
redis:
image: redis:7-alpine容器名访问: 在 api 容器中,ping db 会自动解析为 db 容器的 IP 地址。Compose 使用 Docker 内置 DNS resolver,监听在 127.0.0.11:53。
Links vs Network
# 旧方式(links,已过时)
services:
web:
image: nginx
links:
- db:database # 创建别名,web 可通过 hostname "database" 访问 db
# 新方式(自定义 network,推荐)
services:
web:
image: nginx
networks:
- app-network
db:
image: mysql:8.0
networks:
- app-network
networks:
app-network:
driver: bridgelinks 的限制:
- 仅在单机 Compose 项目内有效
- 创建了环境变量(如
DB_PORT_3306_TCP_ADDR),增加安全隐患 - 不支持跨主机
network 的优势:
- 支持跨主机(使用 overlay 驱动)
- 自动 DNS 解析,无需别名声明
- 更好的网络隔离控制
- 支持网络级别的安全策略
Scale 扩展与负载均衡
version: "3.9"
services:
web:
image: nginx:alpine
ports:
- "8080"
networks:
- app-network
app:
image: my-app:latest
networks:
- app-network
networks:
app-network:# 扩展 web 服务到 3 个实例
docker compose up -d --scale web=3
# 扩展多个服务
docker compose up -d --scale app=5 --scale web=2Nginx 反向代理负载均衡配置(手动实现):
# nginx.conf
upstream app_backend {
server app:8080;
# Docker DNS 会自动轮询到多个 app 容器
}
server {
listen 80;
location / {
proxy_pass http://app_backend;
}
}注意:
docker compose up --scale不提供内置负载均衡,通常需要前面加一层 Nginx/Traefik 反向代理,或使用 Docker Swarm 模式的内置负载均衡。
使用 Traefik 做自动负载均衡:
version: "3.9"
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
app:
image: my-app:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`api.example.com`)"
- "traefik.http.services.app.loadbalancer.server.port=8080"三、Kubernetes 核心回顾
3.1 K8s 资源对象层次
Kubernetes 的资源对象体系构成了声明式基础设施的核心,以下是资源对象的分层全景:
Namespace(逻辑隔离空间)
|
+-- Workloads(工作负载)
| +-- Pod(最小调度单元)
| +-- ReplicaSet(Pod 副本管理)
| +-- Deployment(声明式更新,管理 ReplicaSet)
| +-- StatefulSet(有状态应用,稳定的网络标识和存储)
| +-- DaemonSet(每个节点运行一个 Pod)
| +-- Job(一次性任务)
| +-- CronJob(定时任务)
|
+-- Service & Networking(服务发现与网络)
| +-- Service(抽象 Pod 访问入口)
| | +-- ClusterIP(集群内访问)
| | +-- NodePort(节点端口暴露)
| | +-- LoadBalancer(云负载均衡器)
| | +-- ExternalName(外部服务别名)
| +-- Ingress(七层路由,HTTP/HTTPS 进入集群)
| +-- IngressClass(Ingress 控制器分类)
| +-- EndpointSlice(Service 后端 Pod 端点)
| +-- NetworkPolicy(网络访问策略)
|
+-- Configuration & Storage(配置与存储)
| +-- ConfigMap(非敏感配置)
| +-- Secret(敏感配置,Base64 编码 / 加密)
| +-- PersistentVolume(持久化存储资源)
| +-- PersistentVolumeClaim(存储资源请求)
| +-- StorageClass(动态存储供给模板)
| +-- CSI Driver(容器存储接口驱动)
|
+-- Security(安全)
| +-- ServiceAccount(Pod 身份标识)
| +-- Role / ClusterRole(权限集合)
| +-- RoleBinding / ClusterRoleBinding(权限绑定)
| +-- PodSecurityPolicy(已弃用,替代为 Pod Security Standards)
| +-- PodSecurityAdmission(PSA,内置准入控制器)
|
+-- Governance & Limits(治理与限制)
| +-- ResourceQuota(命名空间资源配额)
| +-- LimitRange(默认资源限制范围)
| +-- HorizontalPodAutoscaler(HPA,Pod 水平自动扩缩容)
| +-- VerticalPodAutoscaler(VPA,Pod 垂直自动扩缩容)
| +-- PodDisruptionBudget(PDB,Pod disruptions 预算)
| +-- PriorityClass(调度优先级)
|
+-- Cluster Infrastructure(集群基础设施)
| +-- Node(集群工作节点)
| +-- Namespace(命名空间)
| +-- APIService(API 聚合)
|
+-- Extensibility(扩展)
+-- CustomResourceDefinition(CRD,自定义资源类型)
+-- Operator(CRD + Controller)
+-- MutatingWebhookConfiguration(可变准入 Webhook)
+-- ValidatingWebhookConfiguration(校验准入 Webhook)核心工作负载对比:
| 资源 | Pod 标识 | 存储 | 更新策略 | 适用场景 |
|---|---|---|---|---|
| Deployment | 随机名称 | 无状态(临时) | 滚动更新/回滚 | 无状态应用(API、Web) |
| StatefulSet | 有序名称(-0, -1, -2) | 每 Pod 独立 PVC | 顺序滚动更新 | 数据库、消息队列 |
| DaemonSet | 每个节点一个 | 可选共享存储 | 节点感知更新 | 日志采集、监控 Agent |
| Job | 任务完成即退出 | 无需持久化 | 重试/并行 | 批处理、数据迁移 |
| CronJob | 定时触发的 Job | 无需持久化 | 历史限制 | 定时备份、清理 |
3.2 调度器
Kubernetes 调度器(kube-scheduler)负责将 Pod 绑定到合适的 Node 上,调度过程分为 过滤(Predicates) 和 打分(Priorities) 两个阶段。
调度两阶段
第一阶段:Predicates(预选)
过滤不满足 Pod 需求的节点。常见的预选策略:
| 预选策略 | 说明 |
|---|---|
| PodFitsResources | 节点剩余资源 >= Pod 请求资源 |
| PodFitsHostPorts | 节点上请求的端口未被占用 |
| PodMatchNodeSelector | 节点标签匹配 Pod 的 nodeSelector |
| PodToleratesNodeTaints | Pod 容忍节点的所有 Taints |
| CheckNodeMemoryPressure | 节点内存压力时,不调度 QoS 类为 BestEffort 的 Pod |
| CheckNodeDiskPressure | 节点磁盘压力时,不调度新 Pod |
| CheckNodePIDPressure | 节点 PID 压力时,不调度新 Pod |
第二阶段:Priorities(优选)
对通过预选的节点进行打分排序,选择最优节点。常见的优选策略:
| 优选策略 | 权重 | 说明 |
|---|---|---|
| LeastRequestedPriority | 1 | 优先调度到资源利用率低的节点(分散负载) |
| MostRequestedPriority | 1 | 优先调度到资源利用率高的节点(集中负载) |
| BalancedResourceAllocation | 1 | 优先调度到 CPU/内存利用率最均衡的节点 |
| NodeAffinityPriority | 2 | 节点亲和性匹配度 |
| TaintTolerationPriority | 1 | 根据 Pod 对节点 Taint 的容忍程度打分 |
| ImageLocalityPriority | 1 | 已有 Pod 所需镜像的节点优先 |
节点亲和性(nodeAffinity)
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/gpu
operator: Exists
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- us-east-1b
- weight: 20
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssdrequiredDuringSchedulingIgnoredDuringExecution:硬性要求,必须满足才能调度。IgnoredDuringExecution 表示运行时如果节点标签变化,已运行的 Pod 不会受到影响。
preferredDuringSchedulingIgnoredDuringExecution:软性偏好,调度器会尽量满足但不强制。
Pod 亲和性与反亲和性
spec:
affinity:
podAffinity:
# Pod 亲和性:与满足条件的 Pod 运行在同一拓扑域
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: cache
topologyKey: topology.kubernetes.io/hostname
podAntiAffinity:
# Pod 反亲和性:避免与满足条件的 Pod 在同一拓扑域
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: topology.kubernetes.io/hostnametopologyKey 常见取值:
| 值 | 含义 |
|---|---|
kubernetes.io/hostname | 节点级别(同一节点) |
topology.kubernetes.io/zone | 可用区级别 |
topology.kubernetes.io/region | 地域级别 |
Taints and Tolerations
Taint(污点) 标记节点的特殊属性,阻止普通 Pod 调度到该节点:
# 给节点添加污点
kubectl taint nodes node1 key=value:NoSchedule
kubectl taint nodes node1 key=value:PreferNoSchedule
kubectl taint nodes node1 key=value:NoExecute
# 查看节点污点
kubectl describe node node1 | grep TaintsToleration(容忍) 允许 Pod 调度到有特定污点的节点:
apiVersion: v1
kind: Pod
metadata:
name: with-toleration
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
- key: "key"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 3600 # 容忍 NoExecute 3600 秒后驱逐Taint 效果说明:
| 效果 | 说明 |
|---|---|
NoSchedule | 不调度新 Pod 到该节点(已有 Pod 不受影响) |
PreferNoSchedule | 尽量不调度,但不保证 |
NoExecute | 不调度新 Pod,并驱逐已有非容忍 Pod |
自定义调度器
# 部署自定义调度器(以 CRD 方式)
# 1. 编写调度器扩展(使用 scheduling.k8s.io/v1 API)
# 2. 部署为 Deployment
# 3. 在 Pod 中指定 schedulerNameapiVersion: v1
kind: Pod
metadata:
name: with-custom-scheduler
spec:
schedulerName: my-custom-scheduler # 指定自定义调度器
containers:
- name: app
image: my-app:latest调度器扩展机制:
- Extender:HTTP 扩展,在调度器预选/优选阶段调用外部服务
- Scheduler Framework:Kubernetes 1.19+ 推荐的插件化调度框架,通过实现
ScorePlugin、FilterPlugin等接口扩展 - 调度器 Webhook:通过 Mutating/Validating Webhook 修改 Pod 调度相关字段
3.3 存储体系
PV / PVC 生命周期
PV(PersistentVolume) 是集群中的存储资源,PVC(PersistentVolumeClaim) 是用户的存储资源请求。
PV 周期:
Available(可用)--> Bound(已绑定)--> Released(已释放)--> Available(回收后可用)
| |
+--> Failed(绑定失败/回收失败)<---------+PVC 与 PV 绑定规则:
- PVC 的
storageClassName必须与 PV 匹配(或为空时匹配默认 StorageClass) - PVC 的
accessModes必须包含 PV 的至少一种模式 - PVC 的
resources.requests.storage必须小于等于 PV 的容量 - 若
selector指定了标签,则 PV 必须匹配标签
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-1
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-storage
nfs:
server: 192.168.1.100
path: "/data/k8s-volumes"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: nfs-storageAccessModes:
| 模式 | 说明 |
|---|---|
| ReadWriteOnce(RWO) | 单节点读写(块存储) |
| ReadOnlyMany(ROX) | 多节点只读 |
| ReadWriteMany(RWX) | 多节点读写(文件/对象存储) |
| ReadWriteOncePod(RWOP) | 单 Pod 读写(1.22+) |
PV 回收策略:
| 策略 | 说明 |
|---|---|
| Retain | PVC 删除后 PV 保留,需手动清理 |
| Recycle | 已弃用,自动删除数据并恢复 Available |
| Delete | 自动删除底层存储资源(如 AWS EBS) |
StorageClass 动态供给
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
fsType: ext4
encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: fast-ssdvolumeBindingMode:
| 模式 | 说明 |
|---|---|
| Immediate | 立即创建 PV(默认),可能导致 PV 与 Pod 不在同一可用区 |
| WaitForFirstConsumer | 等待 Pod 调度后再创建 PV,确保 PV 与 Pod 在同一可用区 |
CSI 驱动与存储方案对比
CSI(Container Storage Interface)是 Kubernetes 存储的标准扩展接口,支持第三方存储系统以插件方式接入。
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
name: csi-hostpath
spec:
attachRequired: true
podInfoOnMount: true
volumeLifecycleModes:
- Persistent
- EphemeralNFS vs Longhorn vs Rook Ceph 对比:
| 特性 | NFS CSI Driver | Longhorn | Rook Ceph |
|---|---|---|---|
| 架构 | 中心化 NFS 服务 | 分布式,每节点存储 | 分布式 Ceph 集群 |
| 数据高可用 | 依赖 NFS 服务端 HA | 自动副本(2-3 副本) | 多副本 + EC |
| 性能 | 一般,NFS 协议开销 | 较好,本地优先写入 | 高,Ceph 成熟稳定 |
| 快照/备份 | 需外部工具 | 内置快照/备份 | Ceph RBD 快照 |
| 扩容 | 手动扩展 NFS | 在线扩容 | 在线扩容 |
| 运维复杂度 | 低 | 中等 | 高 |
| 适用场景 | 开发测试、简单文件共享 | 中小规模生产、边缘 | 大规模生产、高性能 |
# 安装 Longhorn
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
# 安装 Rook Ceph
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/crds.yaml
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/common.yaml
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/operator.yaml3.4 安全体系
RBAC(基于角色的访问控制)
RBAC 是 Kubernetes 的默认授权模式,通过四个资源对象实现权限管理:
# Role:定义命名空间级别的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "pods/status"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
---
# ClusterRole:定义集群级别的权限(或跨命名空间资源)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-reader
rules:
- apiGroups: [""]
resources: ["nodes", "namespaces", "persistentvolumes"]
verbs: ["get", "list", "watch"]
---
# RoleBinding:将 Role 绑定到用户/ServiceAccount(命名空间级别)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: production
name: read-pods
subjects:
- kind: ServiceAccount
name: jenkins
namespace: cicd
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
---
# ClusterRoleBinding:将 ClusterRole 绑定到用户/ServiceAccount(集群级别)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-reader-binding
subjects:
- kind: User
name: "ops-team@example.com"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-reader
apiGroup: rbac.authorization.k8s.ioRBAC 核心规则:
| 资源 | 作用域 | 说明 |
|---|---|---|
| Role | 命名空间 | 只对指定命名空间内的资源生效 |
| ClusterRole | 集群 | 对集群级资源(Nodes、PV)和所有命名空间资源生效 |
| RoleBinding | 命名空间 | 将 Role/ClusterRole 绑定到主体 |
| ClusterRoleBinding | 集群 | 将 ClusterRole 绑定到主体,范围覆盖整个集群 |
常见 verbs 说明:
| Verb | 对应 HTTP 方法 | 说明 |
|---|---|---|
| get | GET | 获取单个资源 |
| list | GET | 获取资源列表 |
| watch | GET | 监听资源变化 |
| create | POST | 创建资源 |
| update | PUT | 更新资源 |
| patch | PATCH | 部分更新资源 |
| delete | DELETE | 删除资源 |
| deletecollection | DELETE | 删除集合 |
ServiceAccount
ServiceAccount 为 Pod 提供身份标识,用于访问 Kubernetes API:
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
---
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: app-service-account
automountServiceAccountToken: true # 默认 true
containers:
- name: app
image: my-app:latest注意事项:
- 未指定
serviceAccountName的 Pod 使用命名空间的defaultServiceAccount - 建议每个应用创建独立的 ServiceAccount,避免权限过大
- 如果 Pod 不需要访问 API,设置
automountServiceAccountToken: false
Pod Security Standards(Pod 安全标准)
Kubernetes 1.23+ 弃用 PodSecurityPolicy(PSP),推荐使用内置的 Pod Security Admission(PSA):
# 命名空间级别设置 Pod 安全标准
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted # 强制 restricted 级别
pod-security.kubernetes.io/audit: baseline # 审计 baseline 违规
pod-security.kubernetes.io/warn: baseline # 警告 baseline 违规三个安全级别:
| 级别 | 说明 | 典型限制 |
|---|---|---|
| privileged | 无限制,完全信任 | 无额外限制 |
| baseline | 最低限制,POD 标准配置 | 禁止 privileged 容器、禁止 hostNetwork/hostPID/hostIPC、限制 SELinux/AppArmor/Seccomp |
| restricted | 严格限制,推荐生产 | 在 baseline 基础上:必须 non-root、只读 rootfs、禁止 ALL capabilities、限制 seccomp 为 RuntimeDefault |
# 满足 restricted 级别的 Pod 配置示例
apiVersion: v1
kind: Pod
metadata:
name: restricted-pod
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: my-app:latest
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
runAsUser: 1000
runAsGroup: 1000NetworkPolicy 隔离规则
NetworkPolicy 通过标签选择器定义 Pod 的网络访问规则:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
namespace: production
spec:
# 选择目标 Pod(应用于哪些 Pod)
podSelector:
matchLabels:
app: api-service
policyTypes:
- Ingress # 入站规则
- Egress # 出站规则
ingress:
# 允许来自 frontend Pod 的流量
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
# 允许来自 monitoring 命名空间的流量
- from:
- namespaceSelector:
matchLabels:
ns: monitoring
ports:
- protocol: TCP
port: 8080
egress:
# 允许访问数据库 Pod
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 3306
# 允许访问 kube-dns(需显式允许)
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53NetworkPolicy 注意事项:
- 如果 Namespace 中存在任何 NetworkPolicy,未被显式允许的流量将被拒绝(默认拒绝)
- 如果不创建 NetworkPolicy,默认允许所有流量
- NetworkPolicy 由 CNI 插件实现(Calico、Cilium、Weave Net 等),需确认集群 CNI 支持
podSelector: {}匹配命名空间中的所有 PodnamespaceSelector: {}匹配集群中的所有 Namespace