云计算(四)云原生与容器技术
Chen Kai BOSS

云原生技术正在重塑现代软件开发和部署的方式。从单体应用到微服务,从虚拟机到容器,从手动部署到自动化编排,这一系列转变背后是云原生理念的推动。理解容器技术和 Kubernetes 编排系统,已经成为现代 DevOps 工程师的必备技能。

本文将从云原生的核心理念出发,深入探讨 Docker 容器技术的实现细节,然后系统介绍 Kubernetes 集群的部署与管理,最后通过实战案例展示如何构建完整的云原生应用栈。无论你是刚开始接触容器技术,还是希望深入理解 Kubernetes 的底层机制,都能在本文中找到实用的内容。

云原生概念与 12 要素应用

什么是云原生

云原生( Cloud Native)是一种构建和运行应用程序的方法,充分利用云计算的优势。 CNCF( Cloud Native Computing Foundation)将其定义为:云原生技术使组织能够在现代动态环境(如公有云、私有云和混合云)中构建和运行可扩展的应用程序

云原生的核心特征包括:

  • 容器化:应用打包在容器中,实现环境一致性
  • 微服务架构:应用拆分为小型、独立的服务
  • 动态编排:通过 Kubernetes 等工具自动管理容器生命周期
  • DevOps 文化:开发与运维紧密协作,实现持续交付

12 要素应用( 12-Factor App)

12 要素应用是一套构建 SaaS 应用的最佳实践,这些原则同样适用于云原生应用:

  1. 代码库( Codebase):一个代码库对应多个部署环境
  2. 依赖( Dependencies):显式声明并隔离依赖
  3. 配置( Config):将配置存储在环境变量中
  4. 后端服务( Backing Services):将数据库、消息队列等视为附加资源
  5. 构建、发布、运行( Build, Release, Run):严格分离构建和运行阶段
  6. 进程( Processes):应用作为无状态进程运行
  7. 端口绑定( Port Binding):应用通过端口绑定提供服务
  8. 并发( Concurrency):通过进程模型进行扩展
  9. 易处理( Disposability):快速启动和优雅关闭
  10. 开发/生产环境等价( Dev/Prod Parity):保持开发和生产环境尽可能相似
  11. 日志( Logs):将日志视为事件流
  12. 管理进程( Admin Processes):将管理任务作为一次性进程运行

这些原则指导我们设计可扩展、可维护的云原生应用。

Docker 容器技术详解

Docker 架构与核心概念

Docker 采用客户端-服务器架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────┐
│ Docker CLI │ ← 客户端
└──────┬──────┘

│ API 调用

┌─────────────────┐
│ Docker Daemon │ ← 服务端( dockerd)
│ - 镜像管理 │
│ - 容器管理 │
│ - 网络管理 │
│ - 存储管理 │
└─────────────────┘


┌─────────────────┐
│ Linux Kernel │
│ - Namespaces │
│ - Cgroups │
│ - Union FS │
└─────────────────┘

核心组件

  • 镜像( Image):只读的模板,用于创建容器
  • 容器( Container):镜像的运行实例,包含应用和运行时环境
  • 仓库( Registry):存储镜像的地方,如 Docker Hub
  • Dockerfile:用于构建镜像的文本文件

镜像管理

镜像采用分层存储结构,每一层都是只读的。多个容器可以共享相同的镜像层,节省存储空间。

查看镜像

1
2
3
4
5
6
7
8
# 列出本地镜像
docker images

# 查看镜像详细信息
docker inspect nginx:latest

# 查看镜像历史(各层信息)
docker history nginx:latest

拉取和推送镜像

1
2
3
4
5
6
7
8
9
# 从 Docker Hub 拉取镜像
docker pull nginx:1.21

# 从私有仓库拉取
docker pull registry.example.com/myapp:v1.0

# 推送镜像到仓库
docker tag myapp:v1.0 registry.example.com/myapp:v1.0
docker push registry.example.com/myapp:v1.0

镜像构建

1
2
3
4
5
6
7
8
# 使用 Dockerfile 构建
docker build -t myapp:v1.0 .

# 指定 Dockerfile 路径
docker build -f Dockerfile.prod -t myapp:prod .

# 构建时传递构建参数
docker build --build-arg VERSION=1.0 -t myapp:v1.0 .

容器管理

容器是镜像的运行实例,具有独立的文件系统、网络和进程空间。

容器生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建并启动容器
docker run -d --name myapp nginx:latest

# 启动已停止的容器
docker start myapp

# 停止容器
docker stop myapp

# 重启容器
docker restart myapp

# 删除容器
docker rm myapp

# 强制删除运行中的容器
docker rm -f myapp

容器交互

1
2
3
4
5
6
7
8
9
10
11
# 进入运行中的容器
docker exec -it myapp /bin/bash

# 在容器中执行命令
docker exec myapp ls /var/log

# 查看容器日志
docker logs -f myapp

# 查看容器资源使用情况
docker stats myapp

容器导出和导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 导出容器为 tar 文件
docker export myapp > myapp.tar

# 从 tar 文件导入为镜像
docker import myapp.tar myapp:v1.0

# 保存镜像为 tar 文件(包含所有层)
docker save myapp:v1.0 > myapp.tar

# 从 tar 文件加载镜像
docker load < myapp.tar

# 批量导出多个镜像
docker save myapp:v1.0 myapp:v2.0 nginx:latest > all-images.tar

容器监控和调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 实时查看容器资源使用
docker stats myapp

# 查看特定容器的资源使用
docker stats --no-stream myapp

# 查看容器进程
docker top myapp

# 查看容器详细信息
docker inspect myapp

# 查看容器配置的特定字段
docker inspect -f '{{ .NetworkSettings.IPAddress }}' myapp
docker inspect -f '{{ .State.Status }}' myapp

# 查看容器事件
docker events --filter container=myapp

# 查看容器日志(带时间戳)
docker logs -f --timestamps myapp

# 查看最近 100 行日志
docker logs --tail 100 myapp

# 查看指定时间范围的日志
docker logs --since 2025-01-02T10:00:00 myapp
docker logs --until 2025-01-02T12:00:00 myapp

容器故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 检查容器健康状态
docker ps --filter "health=unhealthy"

# 查看容器退出代码
docker inspect -f '{{ .State.ExitCode }}' myapp

# 查看容器启动失败原因
docker inspect myapp | grep -A 10 "State"

# 进入容器调试(容器必须运行)
docker exec -it myapp /bin/bash

# 如果容器没有 bash,使用 sh
docker exec -it myapp /bin/sh

# 在容器中执行命令
docker exec myapp ps aux
docker exec myapp netstat -tlnp
docker exec myapp env

# 复制文件到容器
docker cp local-file.txt myapp:/app/

# 从容器复制文件
docker cp myapp:/app/log.txt ./log.txt

# 查看容器网络配置
docker network inspect bridge | grep -A 10 myapp

# 测试容器网络连通性
docker exec myapp ping -c 3 8.8.8.8
docker exec myapp curl http://another-container:8080

容器批量操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 停止所有运行中的容器
docker stop $(docker ps -q)

# 删除所有停止的容器
docker rm $(docker ps -aq)

# 删除所有容器(包括运行中的)
docker rm -f $(docker ps -aq)

# 清理未使用的容器
docker container prune

# 清理所有未使用的资源
docker system prune -a

# 查看容器资源使用情况
docker stats --no-stream --format "table {{ .Container }}\t{{ .CPUPerc }}\t{{ .MemUsage }}"

容器性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 限制容器资源使用
docker run -d --name myapp \
--memory="512m" \
--cpus="1.0" \
--cpu-shares=1024 \
myapp:latest

# 设置容器重启策略
docker run -d --name myapp \
--restart=always \
myapp:latest

# restart 选项:
# no - 不自动重启(默认)
# on-failure - 失败时重启
# always - 总是重启
# unless-stopped - 除非手动停止,否则总是重启

# 设置容器优先级
docker run -d --name myapp \
--oom-kill-disable \
--memory="512m" \
myapp:latest

Docker 网络详解

Docker 提供了多种网络模式,满足不同的网络需求。理解这些网络模式对于构建复杂的容器化应用至关重要。

网络模式概览

  1. bridge(桥接):默认模式,容器通过虚拟网桥连接
  2. host(主机):容器直接使用主机网络
  3. none(无):容器没有网络接口
  4. overlay(覆盖):用于多主机网络,常用于 Swarm 或 Kubernetes
  5. macvlan:容器获得 MAC 地址,直接连接到物理网络

Bridge 网络详解

Bridge 网络是 Docker 的默认网络模式,每个容器都连接到名为 docker0 的虚拟网桥。容器之间可以通过容器名称或 IP 地址通信。

Bridge 网络工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────┐         ┌─────────────┐
│ Container1 │ │ Container2 │
│ 172.17.0.2 │ │ 172.17.0.3 │
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬───────────┘

┌─────▼─────┐
│ docker0 │
│ 172.17.0.1 │
└─────┬─────┘

┌─────▼─────┐
│ Host │
│ Interface │
└───────────┘

创建自定义 Bridge 网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建基础 bridge 网络
docker network create mynetwork

# 创建带子网和网关的网络
docker network create --driver bridge \
--subnet=172.20.0.0/16 \
--gateway=172.20.0.1 \
--ip-range=172.20.240.0/20 \
mynetwork

# 创建带 DNS 配置的网络
docker network create --driver bridge \
--subnet=172.20.0.0/16 \
--gateway=172.20.0.1 \
--dns=8.8.8.8 \
--dns=8.8.4.4 \
mynetwork

# 创建隔离网络(容器无法访问外部)
docker network create --driver bridge \
--internal \
isolated-network

Bridge 网络高级配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 查看网络详细信息
docker network inspect mynetwork

# 输出示例:
# [
# {
# "Name": "mynetwork",
# "Id": "abc123...",
# "Created": "2025-01-02T10:00:00Z",
# "Scope": "local",
# "Driver": "bridge",
# "IPAM": {
# "Driver": "default",
# "Config": [
# {
# "Subnet": "172.20.0.0/16",
# "Gateway": "172.20.0.1"
# }
# ]
# },
# "Containers": {
# "container-id": {
# "Name": "myapp",
# "EndpointID": "...",
# "MacAddress": "02:42:ac:14:00:02",
# "IPv4Address": "172.20.0.2/16"
# }
# }
# }
# ]

# 连接容器到网络
docker network connect mynetwork myapp

# 连接时指定 IP 地址
docker network connect --ip=172.20.0.100 mynetwork myapp

# 断开连接
docker network disconnect mynetwork myapp

# 删除网络(需要先断开所有容器)
docker network rm mynetwork

# 强制删除网络
docker network rm -f mynetwork

Bridge 网络容器间通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建网络
docker network create app-network

# 启动数据库容器
docker run -d --name mysql \
--network app-network \
--network-alias db \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0

# 启动应用容器,通过容器名或别名访问数据库
docker run -d --name app \
--network app-network \
-e DB_HOST=mysql \
-e DB_HOST_ALT=db \
myapp:latest

# 测试容器间连通性
docker exec app ping -c 3 mysql
docker exec app ping -c 3 db

Host 网络模式

Host 网络模式下,容器直接使用主机的网络栈,没有网络隔离。性能最好,但安全性较低。

使用 Host 网络

1
2
3
4
5
6
7
# 使用 host 网络模式运行容器
docker run -d --name nginx \
--network host \
nginx:latest

# 容器可以直接访问主机的所有网络接口
# 端口映射选项会被忽略(-p 无效)

Host 网络适用场景

  • 需要最佳网络性能的应用
  • 需要直接访问主机网络服务的场景
  • 网络密集型应用(如负载均衡器)

Host 网络注意事项

  • 容器端口直接绑定到主机,可能造成端口冲突
  • 无法在同一主机上运行多个使用相同端口的容器
  • 安全性较低,容器可以直接访问主机网络

Overlay 网络详解

Overlay 网络用于跨多个 Docker 主机连接容器,常用于 Docker Swarm 或 Kubernetes 集群。

创建 Overlay 网络( Swarm 模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 初始化 Swarm
docker swarm init

# 创建 overlay 网络
docker network create \
--driver overlay \
--subnet=10.0.0.0/24 \
--attachable \
my-overlay-network

# 在服务中使用 overlay 网络
docker service create \
--name web \
--network my-overlay-network \
--replicas 3 \
nginx:latest

Overlay 网络特性

  • 跨主机容器通信
  • 自动服务发现
  • 加密通信(可选)
  • 支持多子网

Overlay 网络配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建加密的 overlay 网络
docker network create \
--driver overlay \
--opt encrypted=true \
secure-overlay

# 创建带自定义子网的 overlay 网络
docker network create \
--driver overlay \
--subnet=10.1.0.0/24 \
--gateway=10.1.0.1 \
--ip-range=10.1.0.0/25 \
custom-overlay

Macvlan 网络

Macvlan 允许容器直接连接到物理网络,每个容器都有独立的 MAC 地址。

创建 Macvlan 网络

1
2
3
4
5
6
7
8
9
10
11
12
# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
macvlan-net

# 运行容器使用 macvlan 网络
docker run -d --name app \
--network macvlan-net \
--ip=192.168.1.100 \
myapp:latest

Macvlan 网络管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有网络
docker network ls

# 查看网络详细信息
docker network inspect mynetwork

# 查看网络中的容器
docker network inspect mynetwork --format '{{ range .Containers }}{{ .Name }} {{ end }}'

# 清理未使用的网络
docker network prune

# 清理所有未使用的网络(包括未使用的)
docker network prune -a

网络故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检查容器网络配置
docker inspect <container-name> | grep -A 20 "NetworkSettings"

# 测试容器间连通性
docker exec <container1> ping <container2-ip>

# 查看容器 DNS 配置
docker exec <container> cat /etc/resolv.conf

# 查看网络接口
docker exec <container> ip addr show

# 跟踪网络数据包(需要特权容器)
docker run --rm --privileged --network host \
nicolaka/netshoot tcpdump -i eth0

Docker 存储驱动与卷管理

Docker 提供了多种数据持久化方案和存储驱动,理解这些机制对于优化容器性能和确保数据安全至关重要。

存储驱动详解

存储驱动控制 Docker 如何在宿主机上存储和管理镜像层。选择合适的存储驱动可以显著影响性能。

Docker 支持的存储驱动

  • overlay2:推荐使用,性能好,支持最多 128 层,适合大多数场景
  • devicemapper:基于设备映射,适合生产环境,但性能不如 overlay2
  • aufs:早期版本使用,现在较少使用,兼容性较好
  • btrfs:需要 btrfs 文件系统支持,支持快照和压缩
  • zfs:需要 ZFS 文件系统支持,功能强大但配置复杂

查看和配置存储驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看当前存储驱动
docker info | grep "Storage Driver"

# 查看详细的存储信息
docker system df -v

# 查看存储驱动配置
cat /etc/docker/daemon.json

# 配置存储驱动(需要重启 Docker)
# /etc/docker/daemon.json
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}

Overlay2 存储驱动详解

Overlay2 是推荐的存储驱动,使用 Linux 内核的 OverlayFS 文件系统。

工作原理

1
2
3
4
5
6
7
8
9
10
11
镜像层结构:
┌─────────────────┐
│ Container │ ← 可写层(容器层)
├─────────────────┤
│ Upper Dir │ ← 容器修改的文件
├─────────────────┤
│ Lower Dirs │ ← 只读镜像层(多个)
│ (镜像层 1) │
│ (镜像层 2) │
│ (镜像层 3) │
└─────────────────┘

Overlay2 优势

  • 性能优异,接近原生文件系统
  • 支持最多 128 层
  • 支持页缓存共享,节省内存
  • 支持硬链接,节省磁盘空间

检查 Overlay2 状态

1
2
3
4
5
6
7
8
# 查看 overlay2 挂载信息
mount | grep overlay

# 查看存储使用情况
docker system df

# 清理未使用的数据
docker system prune -a --volumes

数据卷( Volume)管理

数据卷是 Docker 推荐的数据持久化方式,由 Docker 管理,独立于容器生命周期。

Volume 基础操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建数据卷
docker volume create mydata

# 创建带标签的卷
docker volume create --label env=prod \
--label app=myapp \
prod-data

# 列出所有数据卷
docker volume ls

# 查看数据卷详细信息
docker volume inspect mydata

# 输出示例:
# [
# {
# "CreatedAt": "2025-01-02T10:00:00Z",
# "Driver": "local",
# "Labels": {},
# "Mountpoint": "/var/lib/docker/volumes/mydata/_data",
# "Name": "mydata",
# "Options": {},
# "Scope": "local"
# }
# ]

# 删除数据卷
docker volume rm mydata

# 删除未使用的数据卷
docker volume prune

Volume 高级用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 创建带驱动选项的卷
docker volume create \
--driver local \
--opt type=none \
--opt device=/host/path \
--opt o=bind \
myvolume

# 使用卷运行容器
docker run -d --name app \
-v mydata:/var/lib/data:ro \
myapp:latest

# 挂载选项说明:
# :ro - 只读挂载
# :rw - 读写挂载(默认)
# :z - SELinux 共享标签
# :Z - SELinux 私有标签

# 多个卷挂载
docker run -d --name app \
-v data1:/app/data1 \
-v data2:/app/data2 \
-v data3:/app/data3 \
myapp:latest

Volume 备份与恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 备份卷数据
docker run --rm \
-v mydata:/data \
-v $(pwd):/backup \
alpine tar czf /backup/mydata-backup.tar.gz -C /data .

# 恢复卷数据
docker run --rm \
-v mydata:/data \
-v $(pwd):/backup \
alpine sh -c "cd /data && tar xzf /backup/mydata-backup.tar.gz"

# 卷迁移示例
docker run --rm \
-v source-volume:/source \
-v target-volume:/target \
alpine sh -c "cp -a /source/. /target/"

绑定挂载( Bind Mount)

绑定挂载将主机文件系统路径直接挂载到容器,适合开发环境和配置文件管理。

绑定挂载用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 基本绑定挂载
docker run -d --name app \
-v /host/path:/container/path \
myapp:latest

# 只读绑定挂载
docker run -d --name app \
-v /host/config:/app/config:ro \
myapp:latest

# 挂载单个文件
docker run -d --name app \
-v /host/config.json:/app/config.json:ro \
myapp:latest

# 使用绝对路径(推荐)
docker run -d --name app \
-v $(pwd)/config:/app/config \
myapp:latest

绑定挂载注意事项

  • 路径必须存在,否则 Docker 会创建目录
  • 文件挂载时,如果文件不存在, Docker 会创建空文件
  • 权限问题:容器内进程的 UID/GID 需要匹配文件权限
  • 性能:绑定挂载性能略低于 Volume

权限问题处理

1
2
3
4
5
6
7
8
9
10
11
# 查看文件权限
ls -la /host/path

# 使用特定用户运行容器
docker run -d --name app \
--user 1000:1000 \
-v /host/path:/container/path \
myapp:latest

# 在 Dockerfile 中设置用户
# USER 1000:1000

临时文件系统( tmpfs)

tmpfs 将数据存储在内存中,适合临时数据和缓存。

tmpfs 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 使用 tmpfs 挂载
docker run -d --name app \
--tmpfs /tmp \
myapp:latest

# 指定 tmpfs 大小和选项
docker run -d --name app \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
myapp:latest

# tmpfs 选项说明:
# rw - 读写(默认)
# ro - 只读
# noexec - 禁止执行
# nosuid - 禁止 setuid
# size - 大小限制(如 100m, 1g)

# 多个 tmpfs 挂载
docker run -d --name app \
--tmpfs /tmp:size=100m \
--tmpfs /cache:size=200m \
myapp:latest

tmpfs 适用场景

  • 临时文件存储
  • 缓存数据(如编译缓存)
  • 敏感数据(容器停止后自动清除)
  • 高性能临时存储需求

存储最佳实践

1. 选择合适的存储方式

1
2
3
4
5
6
7
8
# 生产环境:使用 Volume
docker run -v db-data:/var/lib/mysql mysql:8.0

# 开发环境:使用绑定挂载
docker run -v $(pwd):/app myapp:dev

# 临时数据:使用 tmpfs
docker run --tmpfs /tmp myapp:latest

2. 数据卷命名规范

1
2
3
4
5
6
7
8
9
# 使用有意义的名称
docker volume create app-db-data
docker volume create app-cache-data

# 使用标签组织
docker volume create \
--label project=myapp \
--label env=production \
prod-db-data

3. 存储清理策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看存储使用情况
docker system df

# 清理未使用的数据
docker system prune

# 清理包括卷的数据(谨慎使用)
docker system prune -a --volumes

# 定期清理脚本
#!/bin/bash
# 清理超过 7 天未使用的容器
docker container prune -f --filter "until=168h"

# 清理未使用的镜像
docker image prune -a -f --filter "until=168h"

# 清理未使用的卷
docker volume prune -f

4. 存储监控

1
2
3
4
5
6
7
8
# 监控卷使用情况
docker system df -v | grep -A 10 "VOLUME NAME"

# 检查卷大小
du -sh /var/lib/docker/volumes/*

# 查找大文件
docker exec <container> find /data -type f -size +100M

Dockerfile 最佳实践与多阶段构建

Dockerfile 基础语法

Dockerfile 由一系列指令组成,每条指令创建一个新的镜像层。

常用指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 基础镜像
FROM ubuntu:20.04

# 维护者信息
LABEL maintainer="your-email@example.com"

# 设置工作目录
WORKDIR /app

# 复制文件
COPY requirements.txt .
COPY . .

# 添加文件(支持 URL 和解压)
ADD https://example.com/file.tar.gz /tmp/

# 运行命令
RUN apt-get update && apt-get install -y python3

# 设置环境变量
ENV PYTHONUNBUFFERED=1
ENV APP_VERSION=1.0

# 暴露端口
EXPOSE 8080

# 设置入口点
ENTRYPOINT ["python3", "app.py"]

# 设置默认命令
CMD ["--help"]

Dockerfile 最佳实践

1. 使用多阶段构建减少镜像大小

Docker 多阶段构建:从 2GB 到 20MB 的镜像瘦身实战

多阶段构建( Multi-stage Build)是 Docker 17.05 引入的特性,可以在一个 Dockerfile 中使用多个 FROM 指令,每个 FROM 指令开始一个新的构建阶段。通过在最终阶段只复制需要的构建产物,可以将镜像大小从 GB 级别压缩到 MB 级别,同时确保生产镜像不包含编译工具和源代码,提升安全性。

问题背景:传统单阶段构建的问题: - 镜像臃肿:包含编译工具链( gcc 、 make 、 SDK)和源代码,导致镜像体积巨大。例如 Go 应用,包含 golang 镜像(~800MB)+ 源代码(~200MB)+ 依赖(~1GB)= 2GB+ - 安全风险:生产镜像包含编译工具和源代码,增加攻击面。攻击者可以利用编译工具在容器内编译恶意程序 - 构建缓慢:每次修改代码都需要重新下载依赖和编译, CI/CD 流水线耗时长

解决思路: 1. 构建阶段( builder):使用完整的 SDK 镜像(如 golang:1.19-alpine)编译应用,生成可执行文件 2. 运行阶段( production):使用最小化的基础镜像(如 alpine 、 distroless),只复制可执行文件和运行时依赖 3. 依赖隔离:第一阶段安装所有依赖,第二阶段只复制必要文件 4. 缓存优化:合理组织 COPY 指令顺序,最大化利用 Docker 层缓存

设计考虑: - 基础镜像选择: alpine( 5MB)最小但可能有兼容性问题, distroless( 20MB)更安全但调试困难, debian-slim( 100MB)兼容性最好但体积较大 - 构建时间 vs 镜像大小:多阶段构建增加构建时间(需要构建多个阶段),但大幅减少镜像大小和传输时间。对于频繁部署的应用,镜像大小优化更重要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# ========== 构建阶段( Stage 1: Builder) ==========
# 使用完整的 Golang SDK 镜像作为构建环境
# golang:1.19-alpine:包含 Go 编译器和工具链,基于 Alpine Linux(轻量级)
# AS builder:为这个阶段命名为 builder,后续阶段可以引用
# 为什么使用 alpine?体积小(~300MB vs debian 的~800MB),包含必要的构建工具
FROM golang:1.19-alpine AS builder

# 设置工作目录
# 所有后续命令( COPY 、 RUN)都在此目录下执行
WORKDIR /build

# 复制 Go 模块依赖文件( go.mod 和 go.sum)
# 先复制依赖文件,利用 Docker 层缓存机制:
# 只有当依赖文件变化时,才会重新下载依赖
# 如果只修改了代码,依赖层会使用缓存,加速构建
# COPY go.mod go.sum ./ # 对于 Go 项目,建议添加这两行
# RUN go mod download

# 复制应用源代码
# .(本地)→ .(容器内/build 目录)
# 注意:此时复制所有文件,包括.git 、测试文件等(后续会清理)
COPY . .

# 编译应用
# go build:编译 Go 应用生成可执行文件
# -o app:输出文件名为 app
# -ldflags="-w -s":去除调试信息和符号表,减小可执行文件大小(约 30%)
# -w:去除 DWARF 调试信息,-s:去除符号表
# CGO_ENABLED=0:禁用 CGO,生成静态链接的可执行文件(可以在 alpine 等最小镜像运行)
# 编译结果:生成/build/app 可执行文件(~10-20MB)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o app .

# ========== 运行阶段( Stage 2: Production) ==========
# 使用最小化的 Alpine Linux 镜像作为运行环境
# alpine:latest:只有~5MB,只包含最基础的 C 库和工具
# 为什么不使用 scratch(空镜像)?因为需要 ca-certificates( HTTPS 通信)和时区数据
FROM alpine:latest

# 安装运行时依赖(可选)
# ca-certificates: HTTPS 通信必需的根证书(调用外部 API 时需要)
# tzdata:时区数据,确保日志时间正确
# --no-cache:不缓存 apk 索引,减小镜像大小
RUN apk --no-cache add ca-certificates tzdata

# 设置工作目录
WORKDIR /root/

# 从构建阶段复制可执行文件
# --from=builder:引用 builder 阶段
# /build/app: builder 阶段生成的可执行文件
# .(当前目录,即/root/):目标路径
# 关键点:只复制可执行文件,不复制源代码、编译工具、依赖
# 这一步将镜像从~800MB( golang 镜像)压缩到~20MB( alpine + 可执行文件)
COPY --from=builder /build/app .

# (可选)创建非 root 用户运行应用,提升安全性
# RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# USER appuser

# 暴露应用端口(文档作用,不实际开放端口)
EXPOSE 8080

# 启动应用
# CMD:容器启动时执行的命令
# ["./app"]:执行/root/app 可执行文件
# 注意:使用 exec 形式( JSON 数组)而非 shell 形式,避免 shell 包裹进程
CMD ["./app"]

深入解读

关键点解释

  1. 多阶段构建的优势
    • 镜像大小优化:从 2GB+(包含 golang 镜像)压缩到 20MB( alpine + 可执行文件),减少 95%+
    • 安全性提升:生产镜像不包含编译工具( gcc 、 go)和源代码,攻击者无法在容器内编译恶意程序
    • 传输加速:镜像大小从 2GB 减少到 20MB,拉取镜像从分钟级别降低到秒级
    • 层次清晰:构建阶段和运行阶段分离,职责明确,便于维护
  2. CGO_ENABLED=0 的作用
    • CGO: Go 的 C 语言互操作功能,允许 Go 调用 C 代码
    • CGO_ENABLED=0:禁用 CGO,生成静态链接的可执行文件
    • 为什么禁用:启用 CGO 会生成动态链接的可执行文件,依赖 glibc 等 C 库。 alpine 使用 musl libc(与 glibc 不兼容),会导致运行失败
    • 静态链接的优势:可执行文件包含所有依赖,可以在任何 Linux 发行版运行(甚至 scratch 空镜像)
  3. -ldflags="-w -s" 的作用
    • -ldflags:传递给链接器的参数
    • -w:去除 DWARF 调试信息(用于 gdb 调试),减小文件大小约 10-15%
    • -s:去除符号表(函数名、变量名),减小文件大小约 10-15%
    • 总体效果:可执行文件大小减少约 30%,但无法使用 gdb 调试
    • 生产建议:生产环境使用-w -s(不需要调试),开发环境不使用(保留调试信息)
  4. 为什么不使用 scratch 镜像
    • scratch: Docker 的空镜像( 0 字节),不包含任何文件
    • 优势:镜像极小(只有可执行文件大小)
    • 劣势:没有 shell(无法 exec 进入容器调试),没有 ca-certificates( HTTPS 失败),没有时区数据(时间错误)
    • 选择:对于纯静态链接且不需要调试的应用,使用 scratch;其他情况使用 alpine

设计权衡

基础镜像 大小 优势 劣势 适用场景
scratch 0 MB 极小,最安全 无 shell,无调试工具,无证书 纯静态链接的 Go/Rust 应用
alpine 5-10 MB 小,包含基础工具 musl libc 兼容性问题 大多数 Go/Rust 应用
distroless 20-30 MB 更安全(无 shell), glibc 兼容 无调试工具,无包管理器 安全要求高的生产环境
debian-slim 100 MB 兼容性最好,调试方便 体积较大 需要调试的应用

常见问题与解决方案

问题 原因 解决方案
运行阶段找不到共享库 动态链接的可执行文件缺少依赖 使用 CGO_ENABLED=0 生成静态链接,或使用 debian-slim 镜像
HTTPS 请求失败 alpine 缺少 ca-certificates 添加RUN apk add ca-certificates
时区不正确 alpine 缺少时区数据 添加RUN apk add tzdata,设置ENV TZ=Asia/Shanghai
镜像构建缓存失效 COPY 顺序不当,每次都重新下载依赖 先 COPY 依赖文件( go.mod),后 COPY 代码
无法 exec 进入容器调试 scratch 或 distroless 没有 shell 改用 alpine 或 debian-slim,或使用临时调试镜像
可执行文件无法运行 musl/glibc 不兼容 使用 CGO_ENABLED=0 静态链接,或改用 debian-slim

生产实践建议

  1. 依赖缓存优化:先复制依赖文件,再复制代码,最大化利用 Docker 层缓存:

    1
    2
    3
    4
    5
    6
    7
    # 先复制依赖文件( go.mod 、 go.sum),利用缓存
    COPY go.mod go.sum ./
    RUN go mod download

    # 再复制代码(代码变更频繁,但依赖层仍使用缓存)
    COPY . .
    RUN go build -o app .

  2. 使用.dockerignore 减少上下文大小

    1
    2
    3
    4
    5
    6
    .git
    .env
    *.log
    .DS_Store
    node_modules
    dist

  3. 镜像大小监控:在 CI/CD 流程中监控镜像大小,防止镜像膨胀:

    1
    2
    3
    4
    # 检查镜像大小
    docker images myapp:latest --format "{{ .Size }}"

    # 如果超过阈值(如 100MB),告警并阻止部署

  4. 多架构支持:使用 docker buildx 构建多架构镜像( amd64 、 arm64):

    1
    docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

  5. 安全扫描:使用 trivy 扫描镜像漏洞:

    1
    trivy image myapp:latest

2. 合理利用缓存层

1
2
3
4
5
6
# 先复制依赖文件,利用缓存
COPY requirements.txt .
RUN pip install -r requirements.txt

# 再复制应用代码(代码变更频繁)
COPY . .

3. 使用 .dockerignore

创建 .dockerignore 文件:

1
2
3
4
5
node_modules
.git
.env
*.log
dist

4. 使用非 root 用户运行

1
2
3
4
5
6
FROM node:16-alpine
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
WORKDIR /home/appuser
COPY --chown=appuser:appuser . .

5. 健康检查

1
2
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1

多阶段构建实战

示例:构建 Python Flask 应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 阶段 1:构建依赖
FROM python:3.9-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# 阶段 2:运行环境
FROM python:3.9-slim
WORKDIR /app

# 从构建阶段复制已安装的包
COPY --from=builder /root/.local /root/.local

# 复制应用代码
COPY . .

# 确保使用本地安装的包
ENV PATH=/root/.local/bin:$PATH

# 暴露端口
EXPOSE 5000

# 运行应用
CMD ["python", "app.py"]

示例:构建 Node.js 应用

1
2
3
4
5
6
7
8
9
10
11
12
13
# 阶段 1:构建
FROM node:16-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production

# 阶段 2:运行
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /build/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Docker Compose 编排

Docker Compose 用于定义和运行多容器 Docker 应用。通过 YAML 文件配置服务、网络和卷。

Compose 文件结构

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
version: '3.8'

services:
web:
build: .
ports:

- "5000:5000"
environment:

- FLASK_ENV=production
depends_on:

- db
- redis

db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:

- db_data:/var/lib/postgresql/data

redis:
image: redis:6-alpine
ports:

- "6379:6379"

volumes:
db_data:

常用配置项

服务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
services:
app:
# 构建配置
build:
context: .
dockerfile: Dockerfile.prod
args:
VERSION: 1.0

# 镜像
image: myapp:v1.0

# 命令覆盖
command: python app.py --debug

# 环境变量
environment:

- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/mydb
env_file:

- .env
- .env.production

# 端口映射
ports:

- "8080:80"
- "8443:443"

# 卷挂载
volumes:

- ./data:/app/data
- app_cache:/app/cache

# 网络
networks:

- frontend
- backend

# 依赖服务
depends_on:

- db
- cache

# 重启策略
restart: unless-stopped

# 资源限制
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M

# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

网络配置

1
2
3
4
5
6
7
8
9
10
11
networks:
frontend:
driver: bridge
ipam:
config:

- subnet: 172.20.0.0/16

backend:
driver: bridge
internal: true

卷配置

1
2
3
4
5
6
7
volumes:
db_data:
driver: local
driver_opts:
type: none
o: bind
device: /path/to/data

Compose 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 启动服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs -f web

# 停止服务
docker-compose stop

# 停止并删除容器
docker-compose down

# 停止并删除容器、网络、卷
docker-compose down -v

# 重新构建并启动
docker-compose up -d --build

# 扩展服务实例
docker-compose up -d --scale web=3

# 执行命令
docker-compose exec web python manage.py migrate

实战案例:微服务应用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
version: '3.8'

services:
# Nginx 反向代理
nginx:
image: nginx:alpine
ports:

- "80:80"
- "443:443"
volumes:

- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:

- api
- frontend
networks:

- frontend

# 前端应用
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
environment:

- API_URL=http://api:3000
networks:

- frontend

# API 服务
api:
build:
context: ./api
dockerfile: Dockerfile
environment:

- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:

- db
- redis
networks:

- frontend
- backend

# 数据库
db:
image: postgres:13
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:

- db_data:/var/lib/postgresql/data
networks:

- backend

# Redis 缓存
redis:
image: redis:6-alpine
command: redis-server --appendonly yes
volumes:

- redis_data:/data
networks:

- backend

volumes:
db_data:
redis_data:

networks:
frontend:
driver: bridge
backend:
driver: bridge

Kubernetes 核心概念

Kubernetes( K8s)是 Google 开源的容器编排平台,用于自动化部署、扩展和管理容器化应用。

集群架构

Kubernetes 集群由控制平面( Control Plane)和工作节点( Node)组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────┐
│ Control Plane │
│ ┌──────────┐ ┌──────────┐ │
│ │ API │ │ etcd │ │
│ │ Server │ │ │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Scheduler │ │ Controller │ │
│ │ │ │ Manager │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────┘


┌─────────┴─────────┐
│ │
┌───▼────┐ ┌───▼────┐
│ Node 1 │ │ Node 2 │
│ ┌────┐ │ │ ┌────┐ │
│ │ Pod │ │ │ │ Pod │ │
│ └────┘ │ │ └────┘ │
│ ┌────┐ │ │ ┌────┐ │
│ │ Pod │ │ │ │ Pod │ │
│ └────┘ │ │ └────┘ │
└────────┘ └────────┘

控制平面组件

  • API Server:集群的前端接口,处理所有 REST 请求
  • etcd:分布式键值存储,保存集群状态
  • Scheduler:调度器,决定 Pod 运行在哪个节点
  • Controller Manager:运行各种控制器,维护集群状态

节点组件

  • kubelet:节点代理,与 API Server 通信
  • kube-proxy:网络代理,实现服务发现和负载均衡
  • 容器运行时: Docker 、 containerd 等

Pod

Pod 是 Kubernetes 的最小调度单元,包含一个或多个容器,共享网络和存储。

Pod 定义示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
env: production
spec:
containers:

- name: nginx
image: nginx:1.21
ports:

- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
volumeMounts:

- name: config
mountPath: /etc/nginx/conf.d
volumes:

- name: config
configMap:
name: nginx-config

Pod 生命周期

  1. Pending: Pod 已被创建,但容器尚未启动
  2. Running: Pod 已绑定到节点,所有容器已创建
  3. Succeeded:所有容器成功终止
  4. Failed:至少一个容器终止失败
  5. Unknown:无法获取 Pod 状态

Service

Service 为 Pod 提供稳定的网络访问,实现服务发现和负载均衡。

Service 类型

  1. ClusterIP:默认类型,在集群内部访问
  2. NodePort:通过节点端口暴露服务
  3. LoadBalancer:使用云提供商的负载均衡器
  4. ExternalName:将服务映射到外部 DNS 名称

Service 定义示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP
selector:
app: nginx
ports:

- protocol: TCP
port: 80
targetPort: 8080
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800

NodePort Service

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:

- port: 80
targetPort: 8080
nodePort: 30080

Deployment

Deployment 管理 Pod 的副本集,提供声明式更新、回滚和扩缩容功能。

Deployment 生产级配置完全指南

Kubernetes Deployment:高可用应用部署的最佳实践

Deployment 是 Kubernetes 中管理无状态应用的核心资源对象,它通过 ReplicaSet 控制 Pod 副本数量,提供声明式更新、滚动发布、自动回滚和弹性伸缩能力。理解 Deployment 的配置细节和最佳实践,是构建生产级 Kubernetes 应用的基础。

问题背景:手动管理 Pod 存在诸多问题: - 高可用性: Pod 故障后无法自动重建,导致服务中断 - 滚动更新:手动更新需要逐个停止/启动 Pod,过程复杂且易错 - 版本回滚:更新失败后无法快速回滚到上一个稳定版本 - 弹性伸缩:流量高峰时无法快速扩容,流量低谷时浪费资源 Deployment 正是为解决这些问题而设计的。

解决思路: 1. 副本管理( replicas):声明期望的 Pod 副本数, Kubernetes 自动维护(故障自愈、自动重建) 2. 滚动更新( RollingUpdate):逐步替换旧版本 Pod,确保服务零中断 3. 健康检查( Probes): livenessProbe 检测应用存活, readinessProbe 检测应用就绪,自动剔除故障 Pod 4. 资源配额( Resources): requests 保证最小资源, limits 限制最大资源,防止资源抢占 5. 更新策略( Strategy):控制更新过程, maxSurge 允许超出副本数, maxUnavailable 限制不可用副本数

设计考虑: - 副本数选择:生产环境建议至少 3 个副本(高可用),关键服务 5 个以上。副本数应为节点数的倍数,确保均匀分布 - 资源配额: requests 过低导致 Pod 被驱逐(节点资源不足), limits 过高导致资源浪费。建议: requests = 平均使用量, limits = 峰值使用量 × 1.5 - 健康检查: initialDelaySeconds 过短导致应用未启动就被杀死, periodSeconds 过长导致故障检测延迟。建议: initialDelaySeconds = 应用启动时间 + 5s - 更新策略: maxUnavailable = 0 确保零停机,但更新较慢; maxUnavailable = 1 加速更新,但短暂影响服务能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# ========== Deployment 元数据配置 ==========
# apiVersion: Kubernetes API 版本
# apps/v1: Deployment 、 StatefulSet 、 DaemonSet 使用的 API 组
# 注意: v1beta1 、 v1beta2 已废弃,生产环境必须使用 apps/v1
apiVersion: apps/v1

# kind:资源类型
# Deployment:管理无状态应用( Web 服务、 API 服务、缓存等)
# 有状态应用使用 StatefulSet(数据库、消息队列等)
kind: Deployment

# metadata: Deployment 的元数据
metadata:
# Deployment 名称(集群内唯一)
# 命名规范:<应用名>-deployment,如 nginx-deployment
name: nginx-deployment

# 标签( Labels):键值对,用于标识和选择资源
# 用途:通过 kubectl get deployment -l app=nginx 筛选资源
# Service 通过 selector 选择 Deployment 创建的 Pod
labels:
app: nginx
# 建议添加更多标签,便于管理和监控
# environment: production
# version: "1.21"
# team: platform

# ========== Deployment 规格配置 ==========
spec:
# replicas:期望的 Pod 副本数
# Kubernetes 会自动维护该数量的 Pod(故障自愈、自动重建)
# 生产环境建议:至少 3 个副本(高可用),关键服务 5-10 个
# 副本数选择:考虑节点数、资源限制、流量需求
# 注意:副本数可以通过 kubectl scale 或 HPA(水平自动伸缩)动态调整
replicas: 3

# selector:选择器,定义 Deployment 管理哪些 Pod
# matchLabels:标签匹配规则,必须与 template.metadata.labels 一致
# 这是 Deployment 关联 Pod 的核心机制:通过标签选择器匹配 Pod
# 注意: selector 一旦创建不可修改( immutable),修改需要删除重建 Deployment
selector:
matchLabels:
app: nginx

# template: Pod 模板,定义 Pod 的规格
# Deployment 会根据此模板创建 Pod
# 修改 template 会触发滚动更新(逐步替换旧 Pod)
template:
# Pod 的元数据
metadata:
# Pod 的标签(必须与 selector.matchLabels 一致)
# Deployment 通过标签识别和管理 Pod
labels:
app: nginx
# 建议添加版本标签,便于监控和回滚
# version: "1.21"

# Pod 的规格
spec:
# containers:容器列表(一个 Pod 可以包含多个容器)
containers:
# 容器名称( Pod 内唯一)
- name: nginx
# 容器镜像(必需)
# 格式:<仓库>/<镜像名>:<标签>
# 生产环境建议:使用完整的镜像名(包含仓库域名和 SHA256 摘要)
# 如: registry.example.com/nginx@sha256:abc123...(不可变,防止镜像被篡改)
# 注意:避免使用 latest 标签(不确定版本,导致不可预测的行为)
image: nginx:1.21

# imagePullPolicy:镜像拉取策略
# Always:每次都拉取镜像(适合开发环境,确保使用最新镜像)
# IfNotPresent:本地不存在时拉取(默认,适合生产环境)
# Never:只使用本地镜像(适合离线环境)
# imagePullPolicy: IfNotPresent

# ports:容器暴露的端口(文档作用,不实际限制端口)
ports:
- containerPort: 80
# name:端口名称, Service 可以通过名称引用端口
# protocol: TCP(默认)或 UDP
name: http
protocol: TCP

# ========== 资源配额( Resources)==========
# 资源配额控制容器的 CPU 和内存使用,防止资源抢占和节点过载
# requests:最小资源保证,调度器根据 requests 选择节点
# limits:最大资源限制,超过 limits 会被 OOM(内存)或 Throttle( CPU)
resources:
# requests:最小资源保证
# Kubernetes 调度器会确保节点有足够的资源( requests)才会调度 Pod
# 设置过低: Pod 可能被驱逐(节点资源不足时,优先驱逐 requests 低的 Pod)
# 设置过高:浪费资源,降低节点利用率
# 建议: requests = 应用平均资源使用量
requests:
# 内存: 64Mi = 64 Mebibytes = 67,108,864 bytes
# 单位: Ki( kibibyte)、 Mi( mebibyte)、 Gi( gibibyte)
memory: "64Mi"

# CPU: 250m = 0.25 核心 = 25%的 CPU 时间
# 单位: m(毫核), 1000m = 1 核心
# 注意: CPU 是可压缩资源(超限会被限流),内存是不可压缩资源(超限会被 OOM 杀死)
cpu: "250m"

# limits:最大资源限制
# 容器使用资源超过 limits 时:
# - CPU:被 Throttle(限流),性能下降但不会被杀死
# - 内存:被 OOM Kill(杀死并重启)
# 设置过低:应用频繁被 OOM 或限流,性能差
# 设置过高:资源浪费,无法有效限制异常进程
# 建议: limits = 峰值资源使用量 × 1.5(留有余量)
limits:
memory: "128Mi"
cpu: "500m"

# ========== 存活探针( Liveness Probe)==========
# livenessProbe:检测容器是否存活(是否需要重启)
# 如果 livenessProbe 失败, Kubernetes 会杀死容器并重启
# 用途:自动恢复死锁、无响应的应用(如内存泄漏导致应用卡死)
# 注意:不要把 livenessProbe 设置得太严格,否则会频繁重启健康的容器
livenessProbe:
# httpGet: HTTP GET 请求探测(最常用)
# 其他方式: tcpSocket( TCP 连接探测)、 exec(执行命令探测)
httpGet:
# path: HTTP 请求路径
# 建议:创建专用的健康检查端点(/healthz 、/health)
# 该端点应检查应用核心功能(数据库连接、缓存可用性等)
path: /

# port: HTTP 请求端口(可以是端口号或端口名称)
port: 80

# httpHeaders:自定义 HTTP 头(可选)
# httpHeaders:
# - name: Custom-Header
# value: HealthCheck

# initialDelaySeconds:容器启动后延迟多久开始探测
# 设置过短:应用未启动就被探测失败,导致无限重启
# 设置过长:故障检测延迟,影响恢复时间
# 建议: initialDelaySeconds = 应用启动时间 + 5 秒(留有余量)
initialDelaySeconds: 30

# periodSeconds:探测间隔(每隔多久探测一次)
# 默认: 10 秒
# 建议: 10-30 秒(过短增加负载,过长延迟故障检测)
periodSeconds: 10

# timeoutSeconds:探测超时时间
# 默认: 1 秒
# 建议: 1-5 秒(根据应用响应时间调整)
# timeoutSeconds: 1

# successThreshold:连续成功多少次才认为探测成功
# 默认: 1( liveness 只能是 1)
# successThreshold: 1

# failureThreshold:连续失败多少次才认为探测失败
# 默认: 3
# 建议: 3-5 次(避免偶发故障导致重启)
failureThreshold: 3

# ========== 就绪探针( Readiness Probe)==========
# readinessProbe:检测容器是否就绪(是否可以接收流量)
# 如果 readinessProbe 失败, Kubernetes 会将 Pod 从 Service 的 Endpoints 中移除
# 用途:防止未就绪的 Pod 接收流量(如应用启动中、数据库连接失败)
# 区别: livenessProbe 失败会重启容器, readinessProbe 失败只是不转发流量
readinessProbe:
httpGet:
path: /
port: 80

# initialDelaySeconds:容器启动后延迟多久开始探测
# readinessProbe 的 initialDelaySeconds 通常比 livenessProbe 短
# 因为需要尽快检测应用是否就绪,开始接收流量
initialDelaySeconds: 5

# periodSeconds:探测间隔
# readinessProbe 的 periodSeconds 通常比 livenessProbe 短
# 因为需要快速检测应用状态变化(如依赖服务故障)
periodSeconds: 5

# timeoutSeconds: 1
# successThreshold: 1 # readiness 可以大于 1,表示连续成功多次才就绪
# failureThreshold: 3

# ========== 环境变量(可选)==========
# env:容器的环境变量
# 用途:传递配置、凭证、特性开关等
# 生产建议:使用 ConfigMap 和 Secret,不要硬编码敏感信息
# env:
# - name: ENV
# value: "production"
# - name: DB_HOST
# valueFrom:
# configMapKeyRef:
# name: app-config
# key: database.host
# - name: DB_PASSWORD
# valueFrom:
# secretKeyRef:
# name: db-secret
# key: password

# ========== 卷挂载(可选)==========
# volumeMounts:挂载卷到容器
# 用途:持久化数据、共享配置文件
# volumeMounts:
# - name: config
# mountPath: /etc/nginx/nginx.conf
# subPath: nginx.conf
# - name: logs
# mountPath: /var/log/nginx

# ========== Pod 级别配置 ==========
# volumes: Pod 的卷定义(与 volumeMounts 配合使用)
# volumes:
# - name: config
# configMap:
# name: nginx-config
# - name: logs
# emptyDir: {}

# restartPolicy: Pod 重启策略
# Always(默认):容器退出时总是重启
# OnFailure:容器异常退出(退出码非 0)时重启
# Never:容器退出时不重启
# Deployment 必须使用 Always(保证服务持续运行)
# restartPolicy: Always

# nodeSelector:节点选择器(可选)
# 用途:将 Pod 调度到特定节点(如 GPU 节点、高性能节点)
# nodeSelector:
# disktype: ssd

# affinity:亲和性(可选)
# 用途:高级调度策略( Pod 亲和、 Pod 反亲和、节点亲和)
# 示例:将 Pod 调度到不同节点(反亲和),提高可用性
# affinity:
# podAntiAffinity:
# preferredDuringSchedulingIgnoredDuringExecution:
# - weight: 100
# podAffinityTerm:
# labelSelector:
# matchExpressions:
# - key: app
# operator: In
# values:
# - nginx
# topologyKey: kubernetes.io/hostname

# tolerations:容忍度(可选)
# 用途:允许 Pod 调度到有污点( Taint)的节点
# 示例:允许 Pod 调度到专用节点
# tolerations:
# - key: "dedicated"
# operator: "Equal"
# value: "nginx"
# effect: "NoSchedule"

# ========== 更新策略( Strategy)==========
# strategy: Deployment 更新策略
# 控制更新过程,确保服务零中断
strategy:
# type:更新类型
# RollingUpdate(默认):滚动更新,逐步替换 Pod
# Recreate:先删除所有旧 Pod,再创建新 Pod(会中断服务,适合不支持多版本共存的应用)
type: RollingUpdate

# rollingUpdate:滚动更新配置(仅 RollingUpdate 类型有效)
rollingUpdate:
# maxSurge:更新过程中允许超出 replicas 的最大 Pod 数量
# 可以是绝对数量(如 2)或百分比(如 25%)
# maxSurge = 1:更新时最多有 replicas + 1 个 Pod(如 3 个副本,更新时最多 4 个 Pod)
# 优势:加速更新(同时创建新 Pod 和删除旧 Pod)
# 劣势:短暂增加资源使用(需要额外的 CPU/内存)
# 建议:生产环境设置为 1 或 25%,加速更新
maxSurge: 1

# maxUnavailable:更新过程中允许不可用的最大 Pod 数量
# 可以是绝对数量(如 1)或百分比(如 25%)
# maxUnavailable = 0:更新时始终保持 replicas 个可用 Pod(零停机)
# 优势:服务零中断,始终有足够的 Pod 处理流量
# 劣势:更新较慢(必须先创建新 Pod,等待就绪,再删除旧 Pod)
# 建议:关键服务设置为 0(零停机),非关键服务设置为 1(加速更新)
maxUnavailable: 0

# 注意: maxSurge 和 maxUnavailable 不能同时为 0
# 建议组合:
# - 零停机更新: maxSurge=1, maxUnavailable=0(推荐)
# - 快速更新: maxSurge=25%, maxUnavailable=25%(非关键服务)
# - 保守更新: maxSurge=0, maxUnavailable=1(资源受限)

# ========== 其他配置 ==========
# revisionHistoryLimit:保留的历史版本数量(用于回滚)
# 默认: 10
# 建议: 10-20(保留足够的历史版本,但不占用过多 etcd 空间)
# revisionHistoryLimit: 10

# progressDeadlineSeconds:更新超时时间(秒)
# 如果更新超过此时间未完成,标记为失败
# 默认: 600 秒( 10 分钟)
# 建议:根据应用启动时间调整(如 1800 秒 = 30 分钟)
# progressDeadlineSeconds: 600

# minReadySeconds: Pod 就绪后等待多久才认为可用
# 用途:防止新 Pod 快速失败(如启动后立即崩溃)
# 默认: 0(就绪后立即可用)
# 建议: 10-30 秒(留有观察期)
# minReadySeconds: 10

深入解读

关键点解释

  1. 副本数( replicas)与高可用性
    • 为什么至少 3 个副本: 2 个副本时,如果 1 个 Pod 故障,剩余 1 个 Pod 可能无法承载全部流量。 3 个副本时,即使 1 个 Pod 故障,剩余 2 个 Pod 仍可提供服务
    • 副本数与节点数:如果集群有 4 个节点,副本数应该是 4 的倍数(如 4 、 8),确保 Pod 均匀分布
    • PodAntiAffinity:使用 Pod 反亲和性,强制将副本调度到不同节点,避免单点故障
  2. 资源配额( Resources)的重要性
    • requests:调度器根据 requests 选择节点。如果 requests 过低, Pod 可能被调度到资源不足的节点,导致 OOM 或限流
    • limits:限制容器最大资源使用。没有设置 limits 的 Pod 可能耗尽节点资源,影响其他 Pod
    • QoS 等级: Kubernetes 根据 requests 和 limits 将 Pod 分为三个等级:
      • Guaranteed( requests = limits):最高优先级,不会被驱逐
      • Burstable( requests < limits):中等优先级,资源不足时可能被驱逐
      • BestEffort(无 requests/limits):最低优先级,首先被驱逐
    • 生产建议:关键服务使用 Guaranteed( requests = limits),非关键服务使用 Burstable
  3. 健康检查( Probes)的最佳实践
    • livenessProbe vs readinessProbe
      • livenessProbe:检测应用是否存活(死锁、无响应),失败会重启容器
      • readinessProbe:检测应用是否就绪(启动中、依赖服务故障),失败会移除 Pod from Service
    • 探测端点设计:不要只检查 HTTP 200,应该检查应用核心功能(如数据库连接、缓存可用性)
    • 避免频繁重启: livenessProbe 的 initialDelaySeconds 和 failureThreshold 应该足够宽松,避免偶发故障导致重启
  4. 滚动更新策略( RollingUpdate)
    • maxSurge = 1, maxUnavailable = 0:零停机更新策略,更新过程中始终保持至少 replicas 个可用 Pod
    • 更新流程
      1. 创建 1 个新 Pod( maxSurge=1)
      2. 等待新 Pod 就绪( readinessProbe 成功)
      3. 删除 1 个旧 Pod
      4. 重复步骤 1-3,直到所有旧 Pod 被替换
    • 更新速度:取决于 Pod 启动时间和 readinessProbe 配置。如果 Pod 启动慢,更新会很慢

设计权衡

配置项 选项 优势 劣势 适用场景
replicas 1 资源占用最小 无高可用,故障会中断服务 开发/测试环境
3 高可用,容忍 1 个副本故障 资源占用中等 生产环境(推荐)
5+ 高可用,容忍多个副本故障 资源占用大 关键服务
maxUnavailable 0 零停机,始终有足够副本 更新较慢 关键服务(推荐)
1 更新较快 短暂影响服务能力 非关键服务
QoS 等级 Guaranteed 最高优先级,不会被驱逐 资源利用率低 关键服务
Burstable 资源利用率高 可能被驱逐 非关键服务

常见问题与解决方案

问题 原因 解决方案
Pod 频繁重启 livenessProbe 配置过严 增加 initialDelaySeconds 和 failureThreshold
Pod 无法就绪 readinessProbe 失败或应用启动慢 检查应用日志,调整 initialDelaySeconds
更新卡住( Progressing) 新 Pod 无法就绪或资源不足 检查 Pod 状态( kubectl describe pod),回滚更新
OOMKilled 内存 limits 过低 增加 memory limits,或优化应用内存使用
CPU Throttle CPU limits 过低 增加 cpu limits,或优化应用 CPU 使用
单节点故障导致服务中断 Pod 都调度到同一节点 使用 PodAntiAffinity,强制 Pod 分布到不同节点
更新过程中服务不可用 maxUnavailable 过大 设置 maxUnavailable=0,确保零停机

生产实践建议

  1. 使用 HPA( Horizontal Pod Autoscaler)自动伸缩

    1
    kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=3 --max=10

  2. 监控 Deployment 状态

    1
    2
    3
    4
    5
    6
    7
    8
    # 查看 Deployment 状态
    kubectl get deployment nginx-deployment

    # 查看 Pod 状态
    kubectl get pods -l app=nginx

    # 查看事件(排查问题)
    kubectl describe deployment nginx-deployment

  3. 金丝雀发布( Canary Deployment): 创建两个 Deployment( stable 和 canary), canary 先发布新版本,验证无误后更新 stable 。

  4. 使用 PodDisruptionBudget( PDB)保证可用性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: policy/v1
    kind: PodDisruptionBudget
    metadata:
    name: nginx-pdb
    spec:
    minAvailable: 2 # 始终保持至少 2 个 Pod 可用
    selector:
    matchLabels:
    app: nginx

  5. 定期清理旧 ReplicaSet

    1
    2
    # 清理旧的 ReplicaSet(保留最近 10 个)
    kubectl delete replicaset --all --field-selector status.replicas=0

Deployment 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建 Deployment
kubectl apply -f deployment.yaml

# 查看 Deployment
kubectl get deployments

# 扩缩容
kubectl scale deployment nginx-deployment --replicas=5

# 更新镜像
kubectl set image deployment/nginx-deployment nginx=nginx:1.22

# 查看更新历史
kubectl rollout history deployment/nginx-deployment

# 回滚
kubectl rollout undo deployment/nginx-deployment

# 回滚到特定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=2

# 查看更新状态
kubectl rollout status deployment/nginx-deployment

StatefulSet

StatefulSet 用于管理有状态应用,提供稳定的网络标识和有序部署。

StatefulSet 特性

  • 稳定的网络标识: Pod 名称和 DNS 名称保持不变
  • 有序部署和扩展:按顺序创建和删除 Pod
  • 有序更新:按顺序更新 Pod
  • 持久存储:每个 Pod 有独立的存储卷

StatefulSet 定义示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-statefulset
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:

- name: mysql
image: mysql:8.0
env:

- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:

- containerPort: 3306
volumeMounts:

- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:

- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi

StatefulSet 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建 StatefulSet
kubectl apply -f statefulset.yaml

# 查看 StatefulSet
kubectl get statefulset

# 查看 Pod(按顺序命名)
kubectl get pods -l app=mysql

# 扩展 StatefulSet
kubectl scale statefulset mysql-statefulset --replicas=5

# 删除 StatefulSet(需要先删除 Pod)
kubectl delete statefulset mysql-statefulset

K8s 集群部署

kubeadm 部署(推荐)

kubeadm 是 Kubernetes 官方提供的集群部署工具,适合快速搭建测试和生产环境。

前置准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 在所有节点上执行

# 1. 关闭 swap
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

# 2. 配置内核参数
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

# 3. 安装容器运行时( containerd)
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd

# 4. 安装 kubeadm 、 kubelet 、 kubectl
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

初始化控制平面节点

1
2
3
4
5
6
7
8
9
10
11
# 在主节点执行
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=192.168.1.100 \
--control-plane-endpoint=192.168.1.100:6443 \
--upload-certs

# 配置 kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装网络插件( Flannel)

1
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

加入工作节点

1
2
3
4
# 在工作节点执行(使用初始化时输出的 join 命令)
sudo kubeadm join 192.168.1.100:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>

验证集群

1
2
3
4
5
6
7
8
# 查看节点
kubectl get nodes

# 查看所有 Pod
kubectl get pods --all-namespaces

# 查看集群信息
kubectl cluster-info

二进制部署

二进制部署提供更多控制,适合需要自定义配置的场景。

下载二进制文件

1
2
3
4
5
6
7
# 下载 Kubernetes 组件
wget https://dl.k8s.io/v1.28.0/kubernetes-server-linux-amd64.tar.gz
tar -xzf kubernetes-server-linux-amd64.tar.gz

# 下载 etcd
wget https://github.com/etcd-io/etcd/releases/download/v3.5.9/etcd-v3.5.9-linux-amd64.tar.gz
tar -xzf etcd-v3.5.9-linux-amd64.tar.gz

配置 etcd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建 etcd 配置
cat > /etc/etcd/etcd.conf <<EOF
ETCD_NAME=etcd1
ETCD_DATA_DIR=/var/lib/etcd
ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS=http://192.168.1.100:2379
EOF

# 创建 systemd 服务
cat > /etc/systemd/system/etcd.service <<EOF
[Unit]
Description=etcd
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.conf
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable etcd
systemctl start etcd

配置 API Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建 API Server 配置
cat > /etc/kubernetes/apiserver.conf <<EOF
KUBE_API_ARGS="--etcd-servers=http://127.0.0.1:2379 \
--service-cluster-ip-range=10.96.0.0/12 \
--advertise-address=192.168.1.100 \
--allow-privileged=true \
--authorization-mode=Node,RBAC \
--enable-admission-plugins=NodeRestriction"
EOF

# 创建 systemd 服务
cat > /etc/systemd/system/kube-apiserver.service <<EOF
[Unit]
Description=Kubernetes API Server
After=etcd.service

[Service]
ExecStart=/usr/local/bin/kube-apiserver \$KUBE_API_ARGS
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-apiserver
systemctl start kube-apiserver

高可用部署

高可用集群需要多个控制平面节点,使用负载均衡器分发请求。

架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                    ┌─────────────┐
│ Load │
│ Balancer │
│ (HAProxy) │
└──────┬──────┘

┌──────────────────┼──────────────────┐
│ │ │
┌───────▼──────┐ ┌────────▼──────┐ ┌────────▼──────┐
│ Master Node 1 │ │ Master Node 2 │ │ Master Node 3 │
│ │ │ │ │ │
│ API Server │ │ API Server │ │ API Server │
│ etcd │ │ etcd │ │ etcd │
│ Scheduler │ │ Scheduler │ │ Scheduler │
│ Controller │ │ Controller │ │ Controller │
└──────────────┘ └───────────────┘ └───────────────┘

使用 kubeadm 部署高可用集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 1. 配置负载均衡器( HAProxy)
cat > /etc/haproxy/haproxy.cfg <<EOF
global
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon

defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

frontend kubernetes-frontend
bind *:6443
mode tcp
option tcplog
default_backend kubernetes-backend

backend kubernetes-backend
mode tcp
balance roundrobin
option tcp-check
server master1 192.168.1.101:6443 check
server master2 192.168.1.102:6443 check
server master3 192.168.1.103:6443 check
EOF

systemctl restart haproxy

# 2. 初始化第一个控制平面节点
sudo kubeadm init \
--control-plane-endpoint=192.168.1.100:6443 \
--upload-certs \
--pod-network-cidr=10.244.0.0/16

# 3. 在其他控制平面节点执行 join
sudo kubeadm join 192.168.1.100:6443 \
--control-plane \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--certificate-key <certificate-key>

K8s 网络与存储

Kubernetes 网络模型

Kubernetes 网络遵循以下原则:

  1. 每个 Pod 都有独立的 IP 地址
  2. Pod 之间可以直接通信,无需 NAT
  3. 节点上的 Pod 可以与所有节点上的 Pod 通信

网络插件

  • Flannel:简单易用,使用 VXLAN 或 host-gw
  • Calico:功能强大,支持网络策略和 BGP
  • Weave Net:自动网络发现,支持加密
  • Cilium:基于 eBPF,高性能

Flannel 配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-system
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}

Service 网络

ClusterIP Service

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: myapp
ports:

- port: 80
targetPort: 8080

Headless Service(用于 StatefulSet):

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:

- port: 3306

Ingress 详解

Ingress 提供 HTTP/HTTPS 路由,支持基于域名和路径的路由,是 Kubernetes 中暴露服务到集群外部的主要方式。

Ingress 基础概念

Ingress 需要 Ingress Controller 才能工作,常见的 Controller 包括:

  • Nginx Ingress Controller:功能丰富,使用最广泛
  • Traefik:自动服务发现,配置简单
  • HAProxy Ingress:高性能,适合大规模场景
  • Istio Gateway:与服务网格集成

安装 Nginx Ingress Controller

1
2
3
4
5
6
7
8
9
10
# 使用官方 YAML 安装
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml

# 或使用 Helm 安装
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx

# 验证安装
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

Ingress 基本配置

简单 Ingress 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

PathType 说明

  • Exact:精确匹配路径
  • Prefix:前缀匹配(最常用)
  • ImplementationSpecific:由 Ingress Controller 决定

Ingress 高级配置

多路径路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:

- host: example.com
http:
paths:
# API 路由
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
# 前端路由
- path: /app(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: frontend-service
port:
number: 80
# 默认路由
- path: /
pathType: Prefix
backend:
service:
name: default-service
port:
number: 80

TLS/HTTPS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: nginx
tls:

- hosts:
- api.example.com
- app.example.com
secretName: tls-secret
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

- host: app.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80

创建 TLS Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 使用现有证书创建 Secret
kubectl create secret tls tls-secret \
--cert=path/to/cert.crt \
--key=path/to/cert.key

# 使用 Let's Encrypt(需要 cert-manager)
# 安装 cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# 创建 ClusterIssuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:

- http01:
ingress:
class: nginx

使用 cert-manager 自动 TLS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:

- hosts:
- api.example.com
secretName: api-tls-secret
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

Nginx Ingress 常用注解

限流配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rate-limit-ingress
annotations:
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "10"
spec:
ingressClassName: nginx
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

CORS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cors-ingress
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
spec:
ingressClassName: nginx
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

SSL 重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ssl-redirect-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:

- hosts:
- api.example.com
secretName: tls-secret
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

自定义 Nginx 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: custom-config-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/client-max-body-size: "10m"
spec:
ingressClassName: nginx
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

基于 Cookie 的会话保持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: session-affinity-ingress
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
spec:
ingressClassName: nginx
rules:

- host: app.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80

Ingress 故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看 Ingress 状态
kubectl get ingress

# 查看 Ingress 详细信息
kubectl describe ingress my-ingress

# 查看 Ingress Controller 日志
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller

# 测试 Ingress 路由
curl -H "Host: api.example.com" http://<ingress-ip>/

# 查看 Ingress Controller 配置
kubectl exec -n ingress-nginx deployment/ingress-nginx-controller -- cat /etc/nginx/nginx.conf

存储卷

PersistentVolume( PV)和 PersistentVolumeClaim( PVC)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 定义 PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 20Gi
accessModes:

- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
hostPath:
path: /data/mysql

---
# 定义 PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:

- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-storage

---
# 在 Pod 中使用
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:

- name: mysql
image: mysql:8.0
volumeMounts:

- name: mysql-storage
mountPath: /var/lib/mysql
volumes:

- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc

StorageClass(动态 provisioning)

1
2
3
4
5
6
7
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

ConfigMap 和 Secret 管理

ConfigMap 和 Secret 是 Kubernetes 中管理配置和敏感信息的重要资源。

ConfigMap 详解

ConfigMap 用于存储非敏感的配置数据,如配置文件、环境变量、命令行参数等。

创建 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 方式 1:从字面量创建
kubectl create configmap my-config \
--from-literal=key1=value1 \
--from-literal=key2=value2

# 方式 2:从文件创建
kubectl create configmap my-config \
--from-file=config.properties

# 方式 3:从目录创建
kubectl create configmap my-config \
--from-file=/path/to/config/dir

# 方式 4:从环境文件创建
kubectl create configmap my-config \
--from-env-file=config.env

YAML 方式定义 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
# 键值对形式
database.host: "mysql-service"
database.port: "3306"
cache.host: "redis-service"
cache.port: "6379"

# 文件形式(多行内容)
nginx.conf: |
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
}
}

application.properties: |
spring.datasource.url=jdbc:mysql://mysql-service:3306/mydb
spring.redis.host=redis-service
spring.redis.port=6379

在 Pod 中使用 ConfigMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:

- name: app
image: myapp:latest
# 方式 1:作为环境变量
env:

- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host

- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: database.port

# 方式 2:使用 envFrom 导入所有键值对
envFrom:

- configMapRef:
name: app-config

# 方式 3:作为文件挂载
volumeMounts:

- name: config-volume
mountPath: /etc/config

- name: nginx-config-volume
mountPath: /etc/nginx/conf.d
readOnly: true

volumes:
# 挂载整个 ConfigMap
- name: config-volume
configMap:
name: app-config

# 挂载特定键
- name: nginx-config-volume
configMap:
name: app-config
items:

- key: nginx.conf
path: nginx.conf

ConfigMap 更新和热重载

1
2
3
4
5
6
7
8
9
10
11
12
13
# 更新 ConfigMap
kubectl edit configmap app-config

# 或通过文件更新
kubectl create configmap app-config \
--from-file=config.properties \
--dry-run=client -o yaml | kubectl apply -f -

# 查看 ConfigMap
kubectl get configmap app-config -o yaml

# 删除 ConfigMap
kubectl delete configmap app-config

ConfigMap 使用最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1. 使用子路径避免覆盖目录
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:

- name: app
image: myapp:latest
volumeMounts:

- name: config-volume
mountPath: /etc/config/application.properties
subPath: application.properties
volumes:

- name: config-volume
configMap:
name: app-config

# 2. 设置默认值和可选配置
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:

- name: app
image: myapp:latest
env:

- name: DB_HOST
value: "localhost" # 默认值

- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: database.port
optional: true # 可选,不存在时不报错

Secret 详解

Secret 用于存储敏感信息,如密码、令牌、密钥等。 Secret 数据会被 Base64 编码(不是加密),需要额外的加密措施保护。

创建 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 方式 1:从字面量创建
kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=password=secret123

# 方式 2:从文件创建
kubectl create secret generic my-secret \
--from-file=username.txt \
--from-file=password.txt

# 方式 3:从 Docker 配置创建(用于镜像仓库认证)
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password=secret123 \
--docker-email=admin@example.com

# 方式 4:从 TLS 证书创建
kubectl create secret tls tls-secret \
--cert=path/to/cert.crt \
--key=path/to/cert.key

YAML 方式定义 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
# Base64 编码的值
username: YWRtaW4= # admin
password: c2VjcmV0MTIz # secret123

# 使用 stringData(自动 Base64 编码)
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
stringData:
username: admin
password: secret123

Secret 类型

  • Opaque:用户定义的任意数据(默认)
  • kubernetes.io/dockerconfigjson: Docker 镜像仓库认证
  • kubernetes.io/tls: TLS 证书和密钥
  • kubernetes.io/service-account-token: Service Account 令牌

在 Pod 中使用 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:

- name: app
image: myapp:latest
# 方式 1:作为环境变量
env:

- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: my-secret
key: username

- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: password

# 方式 2:使用 envFrom 导入所有键值对
envFrom:

- secretRef:
name: my-secret

# 方式 3:作为文件挂载
volumeMounts:

- name: secret-volume
mountPath: /etc/secrets
readOnly: true

volumes:

- name: secret-volume
secret:
secretName: my-secret
# 可选:设置文件权限
defaultMode: 0400
items:

- key: username
path: db-username

- key: password
path: db-password
mode: 0400

镜像拉取 Secret

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: private-reg-pod
spec:
imagePullSecrets:

- name: regcred
containers:

- name: app
image: registry.example.com/myapp:latest

Secret 安全最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 使用外部 Secret 管理工具(如 Vault 、 Sealed Secrets)
# 2. 启用 Secret 加密(需要配置 EncryptionConfiguration)
# 3. 限制 Secret 访问权限( RBAC)
# 4. 定期轮换 Secret

# 查看 Secret(注意:密码会显示)
kubectl get secret my-secret -o yaml

# 解码 Secret 值
kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 -d

# 更新 Secret
kubectl create secret generic my-secret \
--from-literal=password=newpassword \
--dry-run=client -o yaml | kubectl apply -f -

# 删除 Secret
kubectl delete secret my-secret

使用 Sealed Secrets 加密 Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装 kubeseal
brew install kubeseal # macOS
# 或从 GitHub 下载

# 创建 SealedSecret
kubectl create secret generic my-secret \
--from-literal=password=secret123 \
--dry-run=client -o yaml | \
kubeseal -o yaml > sealed-secret.yaml

# 应用 SealedSecret
kubectl apply -f sealed-secret.yaml

# Kubernetes 会自动解密并创建 Secret

Kubernetes 资源限制与 QoS

Kubernetes 通过资源请求( requests)和限制( limits)来管理 Pod 的资源使用,并根据这些设置分配 QoS 等级。

资源请求和限制

CPU 和内存资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:

- name: app
image: myapp:latest
resources:
# 请求资源(调度时保证)
requests:
memory: "64Mi"
cpu: "250m" # 250 millicores = 0.25 CPU
# 资源限制(运行时限制)
limits:
memory: "128Mi"
cpu: "500m" # 500 millicores = 0.5 CPU

CPU 单位说明

  • 1000m = 1 = 1 个 CPU 核心
  • 500m = 0.5 = 0.5 个 CPU 核心
  • 100m = 0.1 = 0.1 个 CPU 核心

内存单位说明

  • Mi = Mebibyte (1024^2 bytes)
  • Gi = Gibibyte (1024^3 bytes)
  • M = Megabyte (1000^2 bytes)
  • G = Gigabyte (1000^3 bytes)

扩展资源(如 GPU)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:

- name: gpu-app
image: gpu-app:latest
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1

QoS 等级

Kubernetes 根据资源请求和限制分配三种 QoS 等级:

1. Guaranteed(保证)

  • 所有容器都设置了 requests 和 limits
  • requests 和 limits 相等
  • 优先级最高,不会被驱逐(除非节点资源不足)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:

- name: app
image: myapp:latest
resources:
requests:
memory: "128Mi"
cpu: "500m"
limits:
memory: "128Mi"
cpu: "500m"

2. Burstable(可突发)

  • 至少有一个容器设置了 requests,但不满足 Guaranteed 条件
  • 可以使用超出 requests 的资源(在 limits 范围内)
  • 中等优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:

- name: app
image: myapp:latest
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "1000m"

3. BestEffort(尽力而为)

  • 所有容器都没有设置 requests 和 limits
  • 优先级最低,资源不足时首先被驱逐
  • 可以使用节点的所有可用资源
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
spec:
containers:

- name: app
image: myapp:latest
# 没有设置 resources

资源配额( ResourceQuota)

ResourceQuota 用于限制命名空间的资源使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: production
spec:
hard:
# CPU 和内存配额
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi

# Pod 数量限制
pods: "10"

# 持久卷限制
persistentvolumeclaims: "4"
requests.storage: 100Gi

# ConfigMap 和 Secret 限制
configmaps: "10"
secrets: "10"

查看资源配额

1
2
3
4
5
# 查看命名空间的配额
kubectl get resourcequota -n production

# 查看配额详情
kubectl describe resourcequota compute-quota -n production

限制范围( LimitRange)

LimitRange 用于设置命名空间中 Pod 和容器的默认资源限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
namespace: production
spec:
limits:
# 容器默认限制
- default:
memory: "512Mi"
cpu: "500m"
defaultRequest:
memory: "256Mi"
cpu: "250m"
type: Container

# Pod 级别限制
- max:
memory: "1Gi"
cpu: "1000m"
min:
memory: "128Mi"
cpu: "100m"
type: Pod

资源限制最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 1. 始终设置 requests 和 limits
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:

- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

# 2. 根据应用特性设置合理的比例
# CPU 密集型: limits.cpu / requests.cpu = 2-4
# 内存密集型: limits.memory / requests.memory = 1.5-2
# 一般应用:比例在 2 左右

# 3. 使用 HPA 自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:

- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80

资源监控和调优

1
2
3
4
5
6
7
8
9
10
11
12
# 查看 Pod 资源使用情况
kubectl top pod <pod-name>

# 查看节点资源使用情况
kubectl top node

# 查看 Pod 资源请求和限制
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# 分析资源使用情况
kubectl get pods --all-namespaces -o json | \
jq '.items[] | {name: .metadata.name, namespace: .metadata.namespace, requests: .spec.containers[].resources.requests, limits: .spec.containers[].resources.limits}'

Helm 包管理

Helm 是 Kubernetes 的包管理器,类似于 apt/yum,用于管理 Kubernetes 应用。

Helm 基础

安装 Helm

1
2
3
4
5
6
# 下载 Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 或使用包管理器
brew install helm # macOS
apt-get install helm # Ubuntu

Helm 概念

  • Chart: Helm 包,包含应用的所有资源定义
  • Repository: Chart 仓库,存储 Chart
  • Release: Chart 的部署实例
  • Values:配置参数,用于定制 Chart

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 搜索 Chart
helm search repo nginx

# 安装 Chart
helm install my-nginx bitnami/nginx

# 查看 Release
helm list

# 查看 Release 详情
helm status my-nginx

# 升级 Release
helm upgrade my-nginx bitnami/nginx --set service.type=NodePort

# 卸载 Release
helm uninstall my-nginx

创建自定义 Chart

Chart 结构

1
2
3
4
5
6
7
8
mychart/
├── Chart.yaml # Chart 元数据
├── values.yaml # 默认配置值
├── templates/ # 模板文件
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl # 辅助模板
└── charts/ # 依赖 Chart

Chart.yaml

1
2
3
4
5
6
apiVersion: v2
name: myapp
description: A Helm chart for my application
type: application
version: 0.1.0
appVersion: "1.0.0"

values.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
replicaCount: 3

image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.21"

service:
type: ClusterIP
port: 80

ingress:
enabled: false
className: "nginx"
annotations: {}
hosts:

- host: chart-example.local
paths:

- path: /
pathType: Prefix
tls: []

resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi

templates/deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{ - include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{ - include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{ - include "myapp.selectorLabels" . | nindent 8 }}
spec:
containers:

- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:

- containerPort: 80
resources:
{{ - toYaml .Values.resources | nindent 10 }}

**templates/_helpers.tpl**:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{{ - define "myapp.name" - }}
{{ - default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{ - end }}

{{ - define "myapp.fullname" - }}
{{ - if .Values.nameOverride }}
{{ - .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{ - else }}
{{ - printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{ - end }}
{{ - end }}

{{ - define "myapp.labels" - }}
helm.sh/chart: {{ include "myapp.chart" . }}
{{ include "myapp.selectorLabels" . }}
{{ - if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{ - end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ - end }}

{{ - define "myapp.selectorLabels" - }}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{ - end }}

打包和安装

1
2
3
4
5
6
7
8
9
10
11
# 打包 Chart
helm package mychart

# 安装本地 Chart
helm install myapp ./mychart

# 使用自定义 values
helm install myapp ./mychart -f my-values.yaml

# 使用 --set 覆盖值
helm install myapp ./mychart --set replicaCount=5

服务网格( Istio)深入

服务网格( Service Mesh)是处理服务间通信的基础设施层,提供服务发现、负载均衡、故障恢复、指标收集、安全策略等功能,无需修改应用代码。

Istio 架构详解

Istio 由数据平面和控制平面组成:

数据平面

  • Envoy 代理:以 Sidecar 形式部署在每个 Pod 中,拦截所有服务间通信
  • 处理流量路由、负载均衡、健康检查、故障注入等

控制平面

  • Istiod:统一管理和配置所有 Envoy 代理
  • 包含 Pilot(流量管理)、 Citadel(安全)、 Galley(配置管理)

架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─────────────────────────────────────┐
│ Control Plane (Istiod) │
│ ┌──────────┐ ┌──────────┐ │
│ │ Pilot │ │ Citadel │ │
│ │ (Traffic)│ │ (Security)│ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ │
│ │ Galley │ │
│ │ (Config)│ │
│ └──────────┘ │
└─────────────────────────────────────┘

│ 配置下发

┌─────────────────────────────────────┐
│ Data Plane (Envoy) │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Pod1 │ │ Pod2 │ │ Pod3 │ │
│ │ ┌──┐ │ │ ┌──┐ │ │ ┌──┐ │ │
│ │ │ App │ │ │ │ App │ │ │ │ App │ │ │
│ │ └┬─┘ │ │ └┬─┘ │ │ └┬─┘ │ │
│ │ ┌▼─┐ │ │ ┌▼─┐ │ │ ┌▼─┐ │ │
│ │ │ Env │ │ │ │ Env │ │ │ │ Env │ │ │
│ │ │ oy │ │ │ │ oy │ │ │ │ oy │ │ │
│ │ └──┘ │ │ └──┘ │ │ └──┘ │ │
│ └──────┘ └──────┘ └──────┘ │
└─────────────────────────────────────┘

安装 Istio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 下载 Istio
curl -L https://istio.io/downloadIstio | sh -
cd istio-*

# 将 istioctl 添加到 PATH
export PATH=$PWD/bin:$PATH

# 安装到集群(使用 default profile)
istioctl install --set profile=default

# 验证安装
istioctl verify-install

# 启用 sidecar 自动注入
kubectl label namespace default istio-injection=enabled

# 或使用 istioctl 注入
istioctl kube-inject -f deployment.yaml | kubectl apply -f -

Istio 安装配置选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自定义安装配置
istioctl install --set profile=default \
--set values.global.proxy.logLevel=debug \
--set values.telemetry.v2.prometheus.enabled=true \
--set values.pilot.traceSampling=100.0

# 查看安装配置
istioctl profile dump default

# 升级 Istio
istioctl upgrade

# 卸载 Istio
istioctl uninstall --purge

流量管理深入

VirtualService 详解

VirtualService 定义路由规则,控制流量如何路由到服务。

基础路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- route:
- destination:
host: reviews
subset: v1
weight: 50

- destination:
host: reviews
subset: v2
weight: 50

基于 Header 的路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- match:
- headers:
end-user:
exact: jason
uri:
prefix: "/api/v2"
route:

- destination:
host: reviews
subset: v2

- match:
- headers:
x-api-version:
regex: "^v[2-9]"
route:

- destination:
host: reviews
subset: v2

- route:
- destination:
host: reviews
subset: v1
weight: 75

- destination:
host: reviews
subset: v3
weight: 25

基于 URI 的路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api
spec:
hosts:

- api.example.com
http:

- match:
- uri:
exact: /api/v1/users
route:

- destination:
host: user-service
port:
number: 8080

- match:
- uri:
prefix: /api/v2
rewrite:
uri: /api/v1
route:

- destination:
host: api-service
port:
number: 8080

- match:
- uri:
regex: "^/api/.*"
route:

- destination:
host: api-service
port:
number: 8080

故障注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- fault:
delay:
percentage:
value: 10
fixedDelay: 5s
abort:
percentage:
value: 5
httpStatus: 500
route:

- destination:
host: reviews
subset: v1

超时和重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- route:
- destination:
host: reviews
subset: v1
timeout: 3s
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx,gateway-error,connect-failure

DestinationRule 详解

DestinationRule 定义服务的流量策略,如负载均衡、连接池、熔断等。

负载均衡策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
loadBalancer:
simple: LEAST_CONN # ROUND_ROBIN, LEAST_CONN, RANDOM, PASSTHROUGH
connectionPool:
tcp:
maxConnections: 100
connectTimeout: 30s
http:
http1MaxPendingRequests: 10
http2MaxRequests: 100
maxRequestsPerConnection: 2
maxRetries: 3
idleTimeout: 90s
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
minHealthPercent: 50
subsets:

- name: v1
labels:
version: v1
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN

- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: LEAST_CONN

TLS 策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
tls:
mode: ISTIO_MUTUAL # DISABLE, SIMPLE, MUTUAL, ISTIO_MUTUAL
sni: reviews.example.com
subjectAltNames:

- reviews.example.com
subsets:

- name: v1
labels:
version: v1
trafficPolicy:
tls:
mode: SIMPLE

Gateway 详解

Gateway 用于管理入口流量,类似于 Kubernetes Ingress 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
spec:
selector:
istio: ingressgateway
servers:

- port:
number: 80
name: http
protocol: HTTP
hosts:

- api.example.com
- app.example.com
tls:
httpsRedirect: true

- port:
number: 443
name: https
protocol: HTTPS
hosts:

- api.example.com
- app.example.com
tls:
mode: SIMPLE
credentialName: tls-secret

Gateway 与 VirtualService 配合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Gateway 定义入口
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: api-gateway
spec:
selector:
istio: ingressgateway
servers:

- port:
number: 80
name: http
protocol: HTTP
hosts:

- api.example.com

---
# VirtualService 定义路由规则
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api
spec:
hosts:

- api.example.com
gateways:

- api-gateway
http:

- route:
- destination:
host: api-service
port:
number: 8080

安全策略深入

mTLS 配置

PeerAuthentication(对等认证)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 命名空间级别 mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # DISABLE, PERMISSIVE, STRICT

---
# 全局 mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
selector:
matchLabels:
app: myapp

mTLS 模式说明

  • DISABLE:禁用 mTLS
  • PERMISSIVE:允许明文和 mTLS 流量(迁移阶段使用)
  • STRICT:只允许 mTLS 流量(生产环境推荐)

授权策略

AuthorizationPolicy 详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 允许所有请求
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-all
spec:
action: ALLOW
rules:

- {}

---
# 拒绝所有请求
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
spec:
action: DENY
rules:

- {}

---
# 基于 IP 的访问控制
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: ip-based-policy
spec:
selector:
matchLabels:
app: api
action: ALLOW
rules:

- from:
- source:
ipBlocks: ["192.168.1.0/24"]
to:

- operation:
methods: ["GET", "POST"]

---
# 基于 JWT 的访问控制
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: jwt-based-policy
spec:
selector:
matchLabels:
app: api
action: ALLOW
rules:

- from:
- source:
requestPrincipals: ["*"]
to:

- operation:
methods: ["GET"]

- from:
- source:
requestPrincipals: ["cluster.local/ns/default/sa/admin"]
to:

- operation:
methods: ["GET", "POST", "PUT", "DELETE"]

可观测性

指标收集( Prometheus)

1
2
3
4
5
6
7
8
9
10
11
# 启用 Prometheus 指标
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: control-plane
spec:
values:
telemetry:
v2:
prometheus:
enabled: true

分布式追踪( Jaeger)

1
2
3
4
5
6
7
8
9
10
# 配置追踪采样率
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: control-plane
spec:
meshConfig:
defaultConfig:
tracing:
sampling: 100.0 # 100% 采样率

访问追踪数据

1
2
3
4
# 端口转发到 Jaeger UI
kubectl port-forward -n istio-system svc/jaeger-query 16686:16686

# 访问 http://localhost:16686

服务网格最佳实践

1. 渐进式采用

  • 先在非关键服务上启用
  • 使用 PERMISSIVE mTLS 模式进行迁移
  • 逐步启用 STRICT mTLS

2. 性能优化

  • 合理设置连接池大小
  • 使用适当的负载均衡算法
  • 监控 Sidecar 资源使用

3. 安全配置

  • 启用 mTLS
  • 使用 AuthorizationPolicy 限制访问
  • 定期轮换证书

4. 监控和告警

  • 监控服务指标
  • 设置 SLA 告警
  • 追踪错误率

微服务架构设计模式

API 网关模式

API 网关作为所有客户端请求的单一入口点,提供路由、认证、限流等功能。

使用 Kong 作为 API 网关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Service
metadata:
name: kong-proxy
spec:
type: LoadBalancer
ports:

- port: 80
targetPort: 8000
protocol: TCP
name: proxy

- port: 443
targetPort: 8443
protocol: TCP
name: proxy-ssl
selector:
app: kong

服务发现模式

使用 Kubernetes Service 进行服务发现

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:

- port: 80
targetPort: 8080

应用通过服务名称访问:http://user-service:80

配置中心模式

使用 ConfigMap 存储配置

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database.host: "mysql-service"
database.port: "3306"
cache.host: "redis-service"
cache.port: "6379"

在 Pod 中使用

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
spec:
containers:

- name: app
image: myapp:latest
envFrom:

- configMapRef:
name: app-config

熔断器模式( Circuit Breaker)

熔断器模式用于防止级联故障,当服务失败率达到阈值时,快速失败而不是等待超时。

使用 Istio 实现熔断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100 # 最大连接数
connectTimeout: 30s # 连接超时
http:
http1MaxPendingRequests: 10 # 最大等待请求数
http2MaxRequests: 100 # HTTP/2 最大请求数
maxRequestsPerConnection: 2 # 每个连接最大请求数
maxRetries: 3 # 最大重试次数
outlierDetection:
consecutiveErrors: 5 # 连续错误次数
interval: 30s # 检测间隔
baseEjectionTime: 30s # 基础驱逐时间
maxEjectionPercent: 50 # 最大驱逐百分比
minHealthPercent: 50 # 最小健康百分比

熔断器状态

  • Closed(关闭):正常状态,请求正常通过
  • Open(打开):失败率超过阈值,直接拒绝请求
  • Half-Open(半开):尝试恢复,允许少量请求通过

使用 Spring Cloud 实现熔断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 Hystrix
@HystrixCommand(fallbackMethod = "fallbackMethod")
public String callRemoteService() {
return restTemplate.getForObject("http://remote-service/api", String.class);
}

public String fallbackMethod() {
return "Service temporarily unavailable";
}

// 使用 Resilience4j
@CircuitBreaker(name = "remoteService", fallbackMethod = "fallbackMethod")
public String callRemoteService() {
return restTemplate.getForObject("http://remote-service/api", String.class);
}

限流模式( Rate Limiting)

限流模式用于控制服务的请求速率,防止系统过载。

使用 Istio 实现限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: networking.istio.io/v1beta1
kind: EnvoyFilter
metadata:
name: rate-limit
spec:
configPatches:

- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100
tokens_per_fill: 100
fill_interval: 60s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value:
numerator: 100
denominator: HUNDRED

使用 Nginx Ingress 限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rate-limit-ingress
annotations:
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "10"
nginx.ingress.kubernetes.io/limit-rpm: "1000"
spec:
ingressClassName: nginx
rules:

- host: api.example.com
http:
paths:

- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80

使用 Redis 实现分布式限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import redis
import time

class RateLimiter:
def __init__(self, redis_client, key, max_requests, window_seconds):
self.redis = redis_client
self.key = key
self.max_requests = max_requests
self.window_seconds = window_seconds

def is_allowed(self):
now = time.time()
window_start = now - self.window_seconds

# 使用滑动窗口
pipe = self.redis.pipeline()
pipe.zremrangebyscore(self.key, 0, window_start)
pipe.zcard(self.key)
pipe.zadd(self.key, {str(now): now})
pipe.expire(self.key, self.window_seconds)
results = pipe.execute()

current_requests = results[1]
return current_requests < self.max_requests

降级模式( Degradation)

降级模式在系统负载过高或部分服务不可用时,提供简化版功能或返回缓存数据。

降级策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 使用 Istio 实现降级
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- match:
- headers:
x-degrade:
exact: "true"
route:

- destination:
host: reviews
subset: v1-cache # 使用缓存版本
weight: 100

- route:
- destination:
host: reviews
subset: v1
weight: 80

- destination:
host: reviews
subset: v2
weight: 20
fault:
delay:
percentage:
value: 10
fixedDelay: 5s

降级实现示例( Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class ProductService {

@Autowired
private CacheService cacheService;

@Autowired
private RemoteService remoteService;

@Degrade(fallbackMethod = "getProductFromCache")
public Product getProduct(String id) {
return remoteService.getProduct(id);
}

public Product getProductFromCache(String id) {
// 降级:从缓存获取
Product product = cacheService.get(id);
if (product == null) {
// 返回默认值
return Product.defaultProduct();
}
return product;
}
}

重试模式( Retry)

重试模式用于处理临时性故障,通过重试提高请求成功率。

使用 Istio 实现重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- route:
- destination:
host: reviews
subset: v1
retries:
attempts: 3 # 重试次数
perTryTimeout: 2s # 每次重试超时
retryOn: 5xx,gateway-error,connect-failure,refused-stream
retryRemoteLocalities: false

指数退避重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import time
import random

def exponential_backoff_retry(func, max_retries=3, base_delay=1):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise

# 指数退避 + 抖动
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)

return None

超时模式( Timeout)

超时模式用于防止请求无限等待,及时释放资源。

使用 Istio 配置超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:

- reviews
http:

- route:
- destination:
host: reviews
subset: v1
timeout: 3s # 请求超时时间

超时配置最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 不同路径设置不同超时
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api
spec:
hosts:

- api.example.com
http:

- match:
- uri:
prefix: "/api/fast"
route:

- destination:
host: api-service
timeout: 1s # 快速接口短超时

- match:
- uri:
prefix: "/api/slow"
route:

- destination:
host: api-service
timeout: 30s # 慢速接口长超时

服务发现模式扩展

使用 Consul 进行服务发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: ConfigMap
metadata:
name: consul-config
data:
consul.json: |
{
"service": {
"name": "myapp",
"tags": ["web", "v1"],
"port": 8080,
"check": {
"http": "http://localhost:8080/health",
"interval": "10s"
}
}
}

使用 Eureka 进行服务发现

1
2
3
4
5
6
7
8
9
10
11
12
13
# Spring Cloud Eureka Client 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: eureka-config
data:
application.yml: |
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka/
instance:
prefer-ip-address: true

分布式追踪模式

使用 Jaeger 进行分布式追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-tracing
spec:
template:
spec:
containers:

- name: app
image: myapp:latest
env:

- name: JAEGER_AGENT_HOST
value: jaeger-agent

- name: JAEGER_AGENT_PORT
value: "6831"

- name: JAEGER_SAMPLER_TYPE
value: "const"

- name: JAEGER_SAMPLER_PARAM
value: "1"

使用 OpenTelemetry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-otel
spec:
template:
spec:
containers:

- name: app
image: myapp:latest
env:

- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4317"

- name: OTEL_SERVICE_NAME
value: "myapp"

- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.name=myapp,service.version=1.0"

CI/CD 流水线实战

Jenkins Pipeline

Jenkinsfile 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
pipeline {
agent any

environment {
DOCKER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
KUBERNETES_NAMESPACE = 'production'
}

stages {
stage('Checkout') {
steps {
checkout scm
}
}

stage('Build') {
steps {
sh 'docker build -t ${IMAGE_NAME}:${BUILD_NUMBER} .'
sh 'docker tag ${IMAGE_NAME}:${BUILD_NUMBER} ${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}'
}
}

stage('Test') {
steps {
sh 'docker run --rm ${IMAGE_NAME}:${BUILD_NUMBER} npm test'
}
}

stage('Push') {
steps {
withCredentials([usernamePassword(credentialsId: 'docker-registry', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh 'echo $PASSWORD | docker login -u $USERNAME --password-stdin ${DOCKER_REGISTRY}'
sh 'docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}'
}
}
}

stage('Deploy') {
steps {
sh '''
kubectl set image deployment/myapp myapp=${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER} -n ${KUBERNETES_NAMESPACE}
kubectl rollout status deployment/myapp -n ${KUBERNETES_NAMESPACE}
'''
}
}
}

post {
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed!'
}
}
}

GitLab CI/CD

.gitlab-ci.yml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
stages:

- build
- test
- deploy

variables:
DOCKER_REGISTRY: registry.gitlab.com
IMAGE_NAME: $CI_REGISTRY_IMAGE
KUBERNETES_NAMESPACE: production

build:
stage: build
script:

- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
only:

- main
- develop

test:
stage: test
script:

- docker run --rm $IMAGE_NAME:$CI_COMMIT_SHA npm test
only:

- main
- develop

deploy:
stage: deploy
script:

- kubectl set image deployment/myapp myapp=$IMAGE_NAME:$CI_COMMIT_SHA -n $KUBERNETES_NAMESPACE
- kubectl rollout status deployment/myapp -n $KUBERNETES_NAMESPACE
only:

- main
environment:
name: production
url: https://myapp.example.com

GitHub Actions

.github/workflows/ci-cd.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
name: CI/CD Pipeline

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:

- uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
namespace: production

实战案例

案例一:部署 WordPress 应用栈

架构: WordPress + MySQL + Redis

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
version: '3.8'

services:
wordpress:
image: wordpress:php8.0-apache
ports:

- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:

- wp_data:/var/www/html
depends_on:

- db
- redis
networks:

- app-network

db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: rootpassword
volumes:

- db_data:/var/lib/mysql
networks:

- app-network

redis:
image: redis:6-alpine
command: redis-server --appendonly yes
volumes:

- redis_data:/data
networks:

- app-network

volumes:
wp_data:
db_data:
redis_data:

networks:
app-network:
driver: bridge

Kubernetes 部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:

- name: mysql
image: mysql:8.0
env:

- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password

- name: MYSQL_DATABASE
value: wordpress

- name: MYSQL_USER
value: wordpress

- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:

- containerPort: 3306
volumeMounts:

- name: mysql-storage
mountPath: /var/lib/mysql
volumes:

- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc

---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:

- port: 3306

---
# wordpress-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 3
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:

- name: wordpress
image: wordpress:php8.0-apache
ports:

- containerPort: 80
env:

- name: WORDPRESS_DB_HOST
value: mysql

- name: WORDPRESS_DB_USER
value: wordpress

- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password

- name: WORDPRESS_DB_NAME
value: wordpress
volumeMounts:

- name: wp-storage
mountPath: /var/www/html
volumes:

- name: wp-storage
persistentVolumeClaim:
claimName: wp-pvc

---
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
type: LoadBalancer
selector:
app: wordpress
ports:

- port: 80
targetPort: 80

案例二:构建微服务电商系统

服务架构

  • API Gateway( Kong)
  • 用户服务( User Service)
  • 商品服务( Product Service)
  • 订单服务( Order Service)
  • 支付服务( Payment Service)
  • 数据库( PostgreSQL)
  • 消息队列( RabbitMQ)

Kong API Gateway 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: v1
kind: ConfigMap
metadata:
name: kong-config
data:
kong.yml: |
_format_version: "1.1"
services:

- name: user-service
url: http://user-service:8080
routes:

- name: user-route
paths:

- /api/users
- name: product-service
url: http://product-service:8080
routes:

- name: product-route
paths:

- /api/products
- name: order-service
url: http://order-service:8080
routes:

- name: order-route
paths:

- /api/orders

用户服务 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:

- name: user-service
image: myregistry/user-service:v1.0
ports:

- containerPort: 8080
env:

- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url

- name: RABBITMQ_URL
value: amqp://rabbitmq:5672
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

案例三:高可用 Redis 集群

Redis Sentinel 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# redis-master.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-master
spec:
serviceName: redis
replicas: 1
selector:
matchLabels:
app: redis
role: master
template:
metadata:
labels:
app: redis
role: master
spec:
containers:

- name: redis
image: redis:6-alpine
command:

- redis-server
- /etc/redis/redis.conf
ports:

- containerPort: 6379
volumeMounts:

- name: redis-data
mountPath: /data

- name: redis-config
mountPath: /etc/redis
volumes:

- name: redis-config
configMap:
name: redis-config

---
# redis-sentinel.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-sentinel
spec:
serviceName: redis-sentinel
replicas: 3
selector:
matchLabels:
app: redis-sentinel
template:
metadata:
labels:
app: redis-sentinel
spec:
containers:

- name: sentinel
image: redis:6-alpine
command:

- redis-sentinel
- /etc/sentinel/sentinel.conf
ports:

- containerPort: 26379
volumeMounts:

- name: sentinel-config
mountPath: /etc/sentinel
volumes:

- name: sentinel-config
configMap:
name: sentinel-config

❓ Q&A: 云原生与容器常见问题

Q1: Docker 容器和虚拟机的区别是什么?

A: Docker 容器和虚拟机的主要区别在于:

  1. 资源占用:容器共享主机操作系统内核,比虚拟机更轻量,启动更快
  2. 隔离级别:虚拟机提供硬件级隔离,容器提供进程级隔离
  3. 性能:容器性能接近原生应用,虚拟机有虚拟化开销
  4. 可移植性:容器镜像更小,更容易在不同环境间迁移

容器适合微服务、 CI/CD 等场景,虚拟机适合需要完整操作系统隔离的场景。

Q2: 如何选择 Kubernetes 的网络插件?

A: 选择网络插件需要考虑以下因素:

  • Flannel:简单易用,适合中小型集群,性能良好
  • Calico:功能强大,支持网络策略,适合需要细粒度安全控制的场景
  • Weave Net:自动网络发现,支持加密,适合多租户环境
  • Cilium:基于 eBPF,高性能,适合大规模集群

对于大多数场景, Flannel 或 Calico 是不错的选择。

Q3: Pod 和容器的关系是什么?

A: Pod 是 Kubernetes 的最小调度单元,可以包含一个或多个容器。 Pod 内的容器:

  • 共享网络命名空间(同一 IP 地址)
  • 共享存储卷
  • 可以通过 localhost 通信
  • 生命周期一致(一起创建、删除)

单容器 Pod 最常见,多容器 Pod 通常用于:

  • Sidecar 模式(如日志收集、代理)
  • 初始化容器(准备主容器运行环境)

Q4: 如何实现 Kubernetes 的滚动更新?

A: Deployment 默认使用 RollingUpdate 策略:

1
2
3
4
5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 可以超出期望副本数的 Pod 数量
maxUnavailable: 0 # 更新时不可用的 Pod 数量

更新过程: 1. 创建新版本 Pod 2. 等待新 Pod 就绪 3. 删除旧版本 Pod 4. 重复直到所有 Pod 更新完成

可以通过 kubectl rollout 命令管理更新:

  • kubectl rollout status 查看状态
  • kubectl rollout pause 暂停更新
  • kubectl rollout undo 回滚

Q5: StatefulSet 和 Deployment 的区别?

A: 主要区别:

特性 Deployment StatefulSet
网络标识 随机名称 稳定名称( pod-0, pod-1)
存储 共享存储 每个 Pod 独立存储
部署顺序 并行 有序( 0, 1, 2...)
删除顺序 并行 逆序( 2, 1, 0)
适用场景 无状态应用 有状态应用(数据库、消息队列)

StatefulSet 适合需要稳定网络标识和持久存储的应用。

Q6: 如何调试 Kubernetes Pod 问题?

A: 调试步骤和常用命令:

1. 查看 Pod 状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看所有 Pod
kubectl get pods

# 查看特定命名空间的 Pod
kubectl get pods -n production

# 查看 Pod 详细信息
kubectl describe pod <pod-name>

# 查看 Pod 的 YAML 配置
kubectl get pod <pod-name> -o yaml

# 查看 Pod 的 JSON 格式
kubectl get pod <pod-name> -o json

# 查看 Pod 标签
kubectl get pods --show-labels

# 根据标签筛选 Pod
kubectl get pods -l app=myapp

2. 查看日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看 Pod 日志
kubectl logs <pod-name>

# 多容器 Pod 查看特定容器日志
kubectl logs <pod-name> -c <container-name>

# 实时查看日志
kubectl logs -f <pod-name>

# 查看最近 100 行日志
kubectl logs <pod-name> --tail=100

# 查看指定时间范围的日志
kubectl logs <pod-name> --since=1h
kubectl logs <pod-name> --since-time=2025-01-02T10:00:00Z

# 查看之前容器的日志( Pod 重启后)
kubectl logs <pod-name> --previous

# 查看所有容器的日志
kubectl logs <pod-name> --all-containers=true

3. 进入容器调试

1
2
3
4
5
6
7
8
9
10
11
12
13
# 进入容器
kubectl exec -it <pod-name> -- /bin/bash

# 如果容器没有 bash,使用 sh
kubectl exec -it <pod-name> -- /bin/sh

# 在容器中执行命令
kubectl exec <pod-name> -- ps aux
kubectl exec <pod-name> -- env
kubectl exec <pod-name> -- netstat -tlnp

# 多容器 Pod 进入特定容器
kubectl exec -it <pod-name> -c <container-name> -- /bin/bash

4. 查看事件和资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看所有事件(按时间排序)
kubectl get events --sort-by=.metadata.creationTimestamp

# 查看特定命名空间的事件
kubectl get events -n production

# 查看 Pod 资源使用情况
kubectl top pod <pod-name>

# 查看节点资源使用情况
kubectl top node

# 查看 Pod 的 IP 地址
kubectl get pod <pod-name> -o wide

# 查看 Pod 的节点信息
kubectl get pod <pod-name> -o jsonpath='{.spec.nodeName}'

5. 常见问题排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Pod 一直 Pending
kubectl describe pod <pod-name> | grep -A 5 "Events"
kubectl get nodes
kubectl describe node <node-name>

# Pod 一直 CrashLoopBackOff
kubectl logs <pod-name> --previous
kubectl describe pod <pod-name> | grep -A 10 "State"

# Pod 无法启动
kubectl get pod <pod-name> -o yaml | grep -A 20 "containerStatuses"
kubectl describe pod <pod-name> | grep -A 10 "Warning\|Error"

# 检查镜像拉取问题
kubectl describe pod <pod-name> | grep -i "image\|pull"

# 检查资源限制
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# 检查存储卷挂载
kubectl describe pod <pod-name> | grep -A 10 "Volumes\|Mounts"

6. 网络问题排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看 Service 和 Endpoints
kubectl get svc <service-name>
kubectl get endpoints <service-name>

# 测试 Service 连通性
kubectl run -it --rm debug --image=busybox --restart=Never -- sh
# 在 debug pod 中: wget -qO- http://<service-name>:<port>

# 查看网络策略
kubectl get networkpolicy
kubectl describe networkpolicy <policy-name>

# 查看 Pod 的 DNS 配置
kubectl exec <pod-name> -- cat /etc/resolv.conf

# 测试 DNS 解析
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup <service-name>

7. 使用临时调试容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建临时 Pod 进行调试
kubectl run -it --rm debug \
--image=busybox \
--restart=Never \
-- sh

# 使用 netshoot 工具(网络调试)
kubectl run -it --rm netshoot \
--image=nicolaka/netshoot \
--restart=Never \
-- bash

# 使用 curl 测试服务
kubectl run -it --rm curl-test \
--image=curlimages/curl \
--restart=Never \
-- curl http://<service-name>:<port>

Q7: Helm Chart 的依赖管理如何工作?

A: Helm 支持 Chart 依赖,通过 Chart.yamldependencies 字段定义:

1
2
3
4
5
6
7
8
9
dependencies:

- name: mysql
version: 8.8.0
repository: https://charts.bitnami.com/bitnami

- name: redis
version: 17.0.0
repository: https://charts.bitnami.com/bitnami

管理依赖:

1
2
3
4
5
6
7
8
# 更新依赖
helm dependency update

# 构建包含依赖的 Chart
helm package .

# 安装时自动安装依赖
helm install myapp .

依赖 Chart 会被下载到 charts/ 目录,可以通过 values.yaml 覆盖依赖的配置。

Q8: 如何实现 Kubernetes 的自动扩缩容?

A: Kubernetes 提供 HPA( Horizontal Pod Autoscaler):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:

- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80

HPA 会根据 CPU/内存使用率自动调整 Pod 数量。需要安装 metrics-server:

1
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Q9: Docker 镜像层缓存机制如何优化构建速度?

A: Docker 使用层缓存机制,优化策略:

  1. 合理排序 Dockerfile 指令

    1
    2
    3
    4
    5
    6
    # 先复制依赖文件(变更频率低)
    COPY requirements.txt .
    RUN pip install -r requirements.txt

    # 再复制应用代码(变更频率高)
    COPY . .

  2. 合并 RUN 指令

    1
    2
    3
    4
    5
    6
    # 好的做法
    RUN apt-get update && \
    apt-get install -y python3 && \
    rm -rf /var/lib/apt/lists/*

    # 避免多个 RUN(每层都会增加镜像大小)

  3. 使用 .dockerignore:排除不需要的文件

  4. 多阶段构建:减少最终镜像大小

Q10: 如何保障 Kubernetes 集群的安全性?

A: 安全最佳实践:

  1. RBAC 权限控制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: pod-reader
    rules:

    - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "watch", "list"]

  2. 网络策略

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: deny-all
    spec:
    podSelector: {}
    policyTypes:

    - Ingress
    - Egress

  3. Secret 管理:使用 Secret 存储敏感信息,避免硬编码

  4. 镜像安全:扫描镜像漏洞,使用可信镜像源

  5. Pod 安全策略:限制 Pod 权限,禁止特权容器

  6. TLS 加密:启用 API Server TLS,使用加密存储

定期更新 Kubernetes 版本,及时修复安全漏洞。

Q11: Docker 镜像构建缓慢,如何优化?

A: 优化 Docker 镜像构建速度的方法:

1. 利用构建缓存

1
2
3
4
5
6
# 将变更频率低的层放在前面
COPY requirements.txt .
RUN pip install -r requirements.txt

# 变更频率高的代码放在后面
COPY . .

2. 使用多阶段构建

1
2
3
4
5
6
7
8
9
10
# 构建阶段
FROM node:16-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production

# 运行阶段
FROM node:16-alpine
COPY --from=builder /build/node_modules ./node_modules
COPY . .

3. 使用 .dockerignore

1
2
3
4
5
6
node_modules
.git
.env
*.log
dist
.DS_Store

4. 使用 BuildKit

1
2
3
4
5
# 启用 BuildKit
export DOCKER_BUILDKIT=1

# 使用 BuildKit 构建
docker build --progress=plain -t myapp .

5. 并行构建多个阶段

1
2
3
4
5
FROM alpine AS deps
RUN apk add --no-cache python3

FROM alpine AS runtime
RUN apk add --no-cache python3

6. 使用缓存挂载

1
2
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt

Q12: Kubernetes Pod 一直处于 Pending 状态怎么办?

A: Pod Pending 状态的常见原因和解决方法:

1. 检查节点资源

1
2
3
4
5
6
7
# 查看节点资源
kubectl describe node <node-name>

# 查看 Pod 调度事件
kubectl describe pod <pod-name>

# 常见原因:节点资源不足( CPU/内存)

2. 检查节点选择器

1
2
3
4
# Pod 有 nodeSelector,但节点不匹配
spec:
nodeSelector:
disktype: ssd

3. 检查污点和容忍度

1
2
3
4
5
6
7
8
9
10
11
# 查看节点污点
kubectl describe node <node-name> | grep Taints

# Pod 需要匹配容忍度
spec:
tolerations:

- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"

4. 检查持久卷声明

1
2
3
4
5
6
# 查看 PVC 状态
kubectl get pvc

# 如果 PVC 未绑定,检查 StorageClass 和 PV
kubectl get storageclass
kubectl get pv

5. 检查资源配额

1
2
3
4
# 查看命名空间配额
kubectl describe resourcequota -n <namespace>

# 如果超出配额,需要增加配额或删除其他资源

6. 查看调度器日志

1
2
# 查看调度器 Pod 日志
kubectl logs -n kube-system <scheduler-pod-name>

Q13: Docker 容器无法访问外部网络怎么办?

A: 容器网络问题的排查步骤:

1. 检查容器网络模式

1
2
3
4
5
# 查看容器网络配置
docker inspect <container-name> | grep -A 20 "NetworkSettings"

# 检查网络连接
docker exec <container-name> ping -c 3 8.8.8.8

2. 检查 DNS 配置

1
2
3
4
5
# 查看容器 DNS 配置
docker exec <container-name> cat /etc/resolv.conf

# 使用自定义 DNS
docker run --dns=8.8.8.8 --dns=8.8.4.4 myapp

3. 检查防火墙规则

1
2
3
4
5
# 检查 iptables 规则
sudo iptables -L -n

# 检查 Docker 网络
docker network inspect bridge

4. 检查代理设置

1
2
3
4
# 如果使用代理,配置代理环境变量
docker run -e HTTP_PROXY=http://proxy:8080 \
-e HTTPS_PROXY=http://proxy:8080 \
myapp

5. 重启 Docker 服务

1
2
3
4
5
# 重启 Docker daemon
sudo systemctl restart docker

# 或重启 Docker 网络
sudo systemctl restart docker-network

Q14: Kubernetes Service 无法访问,如何排查?

A: Service 访问问题的排查方法:

1. 检查 Service 和 Endpoints

1
2
3
4
5
6
7
8
# 查看 Service
kubectl get svc <service-name> -o yaml

# 查看 Endpoints(确保有后端 Pod)
kubectl get endpoints <service-name>

# 如果没有 Endpoints,检查 Pod 标签是否匹配
kubectl get pods --show-labels

2. 检查 Pod 标签和 Selector

1
2
3
4
5
6
7
8
9
10
11
12
# Service selector 必须匹配 Pod labels
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: myapp # 必须与 Pod labels 匹配
ports:

- port: 80
targetPort: 8080

3. 检查 Pod 端口

1
2
3
4
5
# 确保 Pod 监听的端口与 targetPort 一致
kubectl exec <pod-name> -- netstat -tlnp

# 测试 Pod 端口
kubectl exec <pod-name> -- curl localhost:8080

4. 检查网络策略

1
2
3
4
# 查看 NetworkPolicy
kubectl get networkpolicy

# NetworkPolicy 可能阻止流量

5. 从 Pod 内部测试 Service

1
2
3
4
5
6
7
8
# 进入 Pod
kubectl exec -it <pod-name> -- /bin/sh

# 测试 Service DNS
nslookup my-service

# 测试 Service IP
curl http://my-service:80

6. 检查 kube-proxy

1
2
3
4
5
# 查看 kube-proxy 日志
kubectl logs -n kube-system <kube-proxy-pod>

# 检查 iptables 规则
sudo iptables -t nat -L | grep <service-name>

Q15: Docker 和 Kubernetes 的资源消耗对比?

A: 资源消耗对比表:

资源类型 Docker Kubernetes (单节点) Kubernetes (3 节点)
CPU 低(仅容器运行时) 中等(控制平面+节点组件) 高( 3 个控制平面+节点)
内存 低(~100MB) 中等(~2-4GB) 高(~6-12GB)
磁盘 低(镜像和容器) 中等(+etcd 数据) 高(+多节点数据)
网络 低(仅容器网络) 中等(+Service 网络) 高(+跨节点通信)

详细说明

Docker

  • CPU:容器运行时开销约 1-5%
  • 内存: Docker daemon 约 50-200MB,每个容器约 10-50MB 开销
  • 磁盘:镜像存储,可共享层节省空间
  • 网络: bridge 网络开销很小

Kubernetes(单节点)

  • CPU:控制平面组件( API Server 、 etcd 、 Scheduler 、 Controller Manager)约 500m-2 CPU
  • 内存:控制平面约 1-2GB,节点组件( kubelet 、 kube-proxy)约 200-500MB
  • 磁盘: etcd 数据约 1-10GB(取决于集群规模)
  • 网络: CNI 插件额外开销

Kubernetes(多节点)

  • 控制平面资源乘以节点数(高可用部署)
  • 跨节点网络通信开销
  • 分布式存储开销

优化建议: 1. 小规模应用:使用 Docker Compose 2. 中等规模:单节点 Kubernetes 或轻量级发行版( k3s 、 k0s) 3. 大规模生产:完整 Kubernetes 集群

Q16: 如何实现 Kubernetes 的蓝绿部署?

A: 蓝绿部署实现方法:

方法 1:使用两个 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 蓝色版本(当前生产)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:

- name: app
image: myapp:v1.0

---
# 绿色版本(新版本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:

- name: app
image: myapp:v2.0

---
# Service 指向蓝色版本
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: myapp
version: blue # 切换到 green 实现切换
ports:

- port: 80
targetPort: 8080

切换脚本

1
2
3
4
5
6
7
8
9
#!/bin/bash
# 切换到绿色版本
kubectl patch service app-service -p '{"spec":{"selector":{"version":"green" }}}'

# 验证切换
kubectl get endpoints app-service

# 回滚到蓝色版本
kubectl patch service app-service -p '{"spec":{"selector":{"version":"blue" }}}'

方法 2:使用 Istio VirtualService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app
spec:
hosts:

- app.example.com
http:

- route:
- destination:
host: app-service
subset: blue
weight: 100

- destination:
host: app-service
subset: green
weight: 0

---
# 切换到绿色(修改 weight)
# blue: 0, green: 100

Q17: Docker 容器日志管理最佳实践?

A: 容器日志管理策略:

1. 配置日志驱动

1
2
3
4
5
6
7
8
9
10
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"labels": "production",
"env": "os,customer"
}
}

2. 容器级别日志配置

1
2
3
4
5
6
7
8
9
# 限制日志大小
docker run --log-opt max-size=10m \
--log-opt max-file=3 \
myapp

# 使用 syslog 驱动
docker run --log-driver=syslog \
--log-opt syslog-address=udp://localhost:514 \
myapp

3. 使用日志收集工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Docker Compose 配置 Fluentd
version: '3.8'
services:
app:
image: myapp
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: docker.{{ .Name }}

fluentd:
image: fluent/fluentd
volumes:

- ./fluentd.conf:/fluentd/etc/fluent.conf
ports:

- "24224:24224"

4. Kubernetes 日志管理

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:

- name: app
image: myapp
# 日志轮转(需要日志驱动支持)

5. 日志清理脚本

1
2
3
4
5
6
7
#!/bin/bash
# 清理所有容器的旧日志
docker system prune -f

# 清理特定容器的日志
docker logs <container-name> > /dev/null 2>&1
truncate -s 0 $(docker inspect <container-name> | grep LogPath | cut -d '"' -f 4)

Q18: Kubernetes 中如何实现服务间认证?

A: 服务间认证的几种方式:

1. 使用 ServiceAccount 和 Token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa

---
# Pod 使用 ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
serviceAccountName: app-sa
containers:

- name: app
image: myapp
env:

- name: KUBERNETES_SERVICE_HOST
value: "kubernetes.default.svc"

2. 使用 Istio mTLS

1
2
3
4
5
6
7
8
# 启用 mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT

3. 使用 OAuth2/OIDC

1
2
3
4
5
6
7
8
# 配置 OIDC
apiVersion: v1
kind: ConfigMap
metadata:
name: oidc-config
data:
oidc-issuer-url: "https://auth.example.com"
oidc-client-id: "kubernetes"

4. 使用 cert-manager 管理证书

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: service-cert
spec:
secretName: service-tls
issuerRef:
name: ca-issuer
kind: ClusterIssuer
dnsNames:

- service.example.com

Q19: Docker 镜像安全扫描和漏洞管理?

A: 镜像安全最佳实践:

1. 使用 Docker Scout

1
2
3
4
5
6
7
8
# 扫描镜像
docker scout cves myapp:latest

# 查看详细报告
docker scout cves --format json myapp:latest

# 持续监控
docker scout watch myapp:latest

2. 使用 Trivy

1
2
3
4
5
6
7
8
# 安装 Trivy
brew install trivy

# 扫描镜像
trivy image myapp:latest

# 扫描并生成报告
trivy image -f json -o report.json myapp:latest

3. 使用 Clair

1
2
# 使用 Clair 扫描
clair-scanner --ip <clair-server-ip> myapp:latest

4. CI/CD 集成

1
2
3
4
5
6
7
# GitHub Actions 示例
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:latest
format: 'sarif'
output: 'trivy-results.sarif'

5. 使用基础镜像安全扫描

1
2
3
4
5
# 使用官方、维护良好的基础镜像
FROM node:18-alpine # Alpine 镜像更小、更安全

# 定期更新基础镜像
# docker pull node:18-alpine

Q20: Kubernetes 集群备份和恢复策略?

A: 集群备份和恢复方法:

1. etcd 备份

1
2
3
4
5
6
7
8
9
10
11
# 备份 etcd
ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db

# 恢复 etcd
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
--data-dir=/var/lib/etcd-backup

2. 资源备份脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# 备份所有资源
NAMESPACE=production
BACKUP_DIR=/backup/k8s-$(date +%Y%m%d)

mkdir -p $BACKUP_DIR

# 备份所有资源
kubectl get all -n $NAMESPACE -o yaml > $BACKUP_DIR/all-resources.yaml

# 备份 ConfigMap 和 Secret
kubectl get configmap -n $NAMESPACE -o yaml > $BACKUP_DIR/configmaps.yaml
kubectl get secret -n $NAMESPACE -o yaml > $BACKUP_DIR/secrets.yaml

# 备份 PVC
kubectl get pvc -n $NAMESPACE -o yaml > $BACKUP_DIR/pvcs.yaml

3. 使用 Velero

1
2
3
4
5
6
7
8
9
10
11
12
# 安装 Velero
velero install \
--provider aws \
--bucket my-backup-bucket \
--secret-file ./credentials-velero

# 备份整个命名空间
velero backup create production-backup \
--include-namespaces production

# 恢复备份
velero restore create --from-backup production-backup

4. 定期备份策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# CronJob 自动备份
apiVersion: batch/v1
kind: CronJob
metadata:
name: etcd-backup
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
jobTemplate:
spec:
template:
spec:
containers:

- name: etcd-backup
image: bitnami/etcd:latest
command:

- /bin/sh
- -c
- |
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd.db
# 上传到云存储
volumes:

- name: backup
hostPath:
path: /backup
restartPolicy: OnFailure

云原生技术正在快速发展,掌握容器技术和 Kubernetes 编排能力,对于现代软件开发和运维至关重要。通过本文的介绍,希望你能建立起对云原生技术的全面理解,并在实际项目中灵活运用这些工具和模式。持续学习,不断实践,才能在这个快速变化的领域中保持竞争力。

  • 本文标题:云计算(四)云原生与容器技术
  • 本文作者:Chen Kai
  • 创建时间:2023-02-05 15:30:00
  • 本文链接:https://www.chenk.top/cloud-computing-cloud-native-containers/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论