Docker生产环境部署与Kubernetes入门

本文是Docker专栏的收官之作,将系统讲解Docker在生产环境中的部署实践,并带领读者从Docker平滑过渡到Kubernetes容器编排世界。全文涵盖Docker Swarm集群、CI/CD流水线、Kubernetes核心概念与实战、从Compose到K8s的迁移指南、生产环境运维等内容,适合有一定Docker基础的开发者和运维人员进阶学习。


引言

在前面九篇文章中,我们从Docker的基础概念出发,逐步学习了镜像构建、容器管理、网络与存储、Docker Compose编排等核心技能。相信读者已经能够熟练地在开发环境中使用Docker来构建、运行和管理容器化应用。然而,当应用从开发环境走向生产环境时,我们面临的挑战将截然不同——高可用、可扩展、可观测、安全合规等生产级要求,远非单机Docker所能满足。

生产环境需要的是一个能够跨多台服务器调度容器、自动故障恢复、弹性伸缩、滚动更新的容器编排平台。在当今的云原生生态中,Kubernetes(K8s)已经成为事实标准,但Docker自带的Swarm模式仍然是轻量级集群方案的有力选择。理解从Docker到Kubernetes的演进路径,掌握两者的核心概念与最佳实践,是每一位云原生工程师的必修课。

本文作为Docker专栏的完结篇,将完成从"会用Docker"到"能在生产环境部署和运维容器化应用"的关键跨越。让我们开始这段旅程。


第一章 生产环境部署概述

1.1 从开发到生产的差距

很多团队在开发环境中使用Docker如鱼得水,但一到生产环境就问题频发。这种"开发能跑,生产就崩"的现象,根源在于开发环境与生产环境之间存在巨大的差距。让我们系统地分析这些差距。

环境一致性差距

在开发环境中,开发者通常在单台机器上运行所有容器,使用docker-compose up一条命令启动整个应用栈。但在生产环境中,应用需要分布在多台服务器上运行,这就引入了跨主机通信、数据同步、状态一致性等复杂问题。

# 开发环境:一条命令搞定
docker-compose up -d

# 生产环境:需要考虑的问题远不止启动
# - 容器分布在哪些节点?
# - 某个节点宕机后容器如何迁移?
# - 流量如何负载均衡到多个实例?
# - 配置变更如何滚动更新?
# - 日志如何集中收集?
# - 监控告警如何配置?

规模差距

开发环境通常只有少量数据、少量用户、低并发。生产环境则可能面对百万级用户、TB级数据、高并发请求。这种规模差异要求我们在架构设计时就考虑水平扩展能力。

维度开发环境生产环境
节点数量单机多机集群
实例数量单实例多副本
数据规模小量测试数据TB级生产数据
并发量高并发
可用性要求可随时重启99.9%+可用性
安全要求基本无严格合规
监控告警无或简单全链路监控
日志管理本地日志集中日志平台

可靠性差距

开发环境中容器挂了重启即可,数据丢了重新生成。但在生产环境中,每一次宕机都意味着业务损失,数据丢失可能是灾难性的。生产环境要求:

  • 故障自动恢复:容器崩溃后自动重启,节点宕机后容器自动迁移
  • 零停机部署:新版本上线不影响正在进行的业务
  • 数据持久化:数据库等有状态应用的数据不能丢失
  • 优雅降级:部分组件故障时系统仍能提供核心服务

1.2 生产环境的核心要求

生产环境的部署需要满足四大核心要求:高可用、可扩展、可观测、安全。

高可用(High Availability)

高可用是指系统在面临硬件故障、网络中断、软件Bug等异常情况时,仍能持续提供服务的能力。通常用"几个9"来衡量:

可用性等级:
- 99%     = 2个9 = 年宕机87.6小时
- 99.9%   = 3个9 = 年宕机8.76小时
- 99.99%  = 4个9 = 年宕机52.6分钟
- 99.999% = 5个9 = 年宕机5.26分钟

实现高可用的关键策略:

  • 多副本部署:每个服务至少运行2个以上副本,分布在不同的节点上
  • 健康检查:实时检测容器健康状态,不健康的容器自动重启
  • 故障转移:节点故障时,该节点上的容器自动调度到健康节点
  • 负载均衡:流量均匀分配到多个副本,避免单点过载

可扩展(Scalability)

可扩展性是指系统通过增加或减少资源来应对流量变化的能力。分为垂直扩展(Scale Up)和水平扩展(Scale Out):

# 垂直扩展:增加单机资源配置(CPU、内存)
# 适用于单机Docker环境
docker run --cpus=4 --memory=8g myapp

# 水平扩展:增加实例数量
# 适用于集群环境
docker service scale myapp=10  # Swarm
kubectl scale deployment myapp --replicas=10  # K8s

水平扩展是容器化应用的首选方案,因为容器本身是轻量级的,可以快速启动和销毁。但水平扩展要求应用本身是无状态的,状态信息需要外置到数据库或缓存中。

可观测(Observability)

可观测性是指能够通过外部输出理解系统内部状态的能力,包含三大支柱:

  1. 日志(Logging):记录系统中发生的事件,用于故障排查和审计
  2. 指标(Metrics):量化系统的运行状态,如CPU使用率、请求延迟等
  3. 追踪(Tracing):追踪一个请求在分布式系统中的完整调用链路
可观测性三大支柱:

  Logging          Metrics          Tracing
  ┌──────┐        ┌──────┐        ┌──────┐
  │ ELK  │        │Prom  │        │Jaeger│
  │Stack │        │etheus│        │      │
  └──┬───┘        └──┬───┘        └──┬───┘
     │               │               │
     └───────────────┼───────────────┘
                     │
              ┌──────┴──────┐
              │   Grafana   │
              │  (可视化层)  │
              └─────────────┘

安全(Security)

生产环境的安全涉及多个层面:

  • 镜像安全:使用可信基础镜像,定期扫描漏洞,最小化镜像体积
  • 运行时安全:以非root用户运行容器,限制容器权限,只读根文件系统
  • 网络安全:网络隔离,限制容器间通信,加密传输
  • 密钥管理:敏感信息(密码、证书、API Key)不能硬编码在镜像中
  • 访问控制:基于角色的访问控制(RBAC),最小权限原则

1.3 Docker在生产环境的定位

Docker在生产环境中扮演着"容器运行时"和"镜像构建工具"的双重角色。但随着容器生态的演进,Docker的角色正在发生变化。

Docker的定位变迁

2013-2015: Docker = 容器运行时 + 编排工具 + 镜像构建
           ↓
2016-2018: Docker = 容器运行时 + 镜像构建 (编排交给K8s)
           ↓
2019-至今: Docker = 镜像构建工具 + 开发环境运行时
           生产环境运行时 → containerd / CRI-O
           编排 → Kubernetes

在当前的生产环境中,Docker的典型使用场景是:

  1. 开发环境:开发者使用Docker Desktop在本地构建和运行容器
  2. CI/CD流水线:在流水线中使用Docker构建镜像并推送到镜像仓库
  3. 镜像构建:使用Dockerfile定义镜像构建过程

而在生产环境的运行时层面,越来越多的平台选择直接使用containerd(从Docker中剥离出来的核心运行时组件),跳过Docker Daemon,以获得更好的性能和更小的攻击面。

# Docker架构 vs containerd直连
#
# Docker架构(传统):
#   应用 → Docker CLI → Docker Daemon → containerd → runc → 容器
#
# K8s + containerd(现代):
#   K8s → CRI接口 → containerd → runc → 容器
#   (去掉了Docker Daemon中间层)

注意:虽然K8s在1.24版本后移除了dockershim(不再原生支持Docker作为容器运行时),但这并不意味着使用Docker构建的镜像不能在K8s上运行。Docker构建的镜像遵循OCI标准,任何符合OCI标准的容器运行时都能运行它。

1.4 容器编排平台的必要性

当容器数量从几个增长到几十个、几百个甚至上千个时,手动管理变得不现实。容器编排平台应运而生,它负责:

容器编排平台的核心职责:

┌─────────────────────────────────────────────────┐
│                 容器编排平台                       │
├─────────────┬───────────────┬───────────────────┤
│  调度与编排  │  服务发现与LB  │  存储与配置管理     │
│  - 节点选择   │  - DNS解析     │  - 卷管理          │
│  - 资源调度   │  - 负载均衡     │  - 配置注入        │
│  - 亲和性     │  - 健康检查     │  - 密钥管理        │
├─────────────┼───────────────┼───────────────────┤
│  自愈与扩缩   │  滚动更新与回滚 │  网络与安全        │
│  - 故障重启   │  - 滚动发布     │  - 网络隔离        │
│  - 故障迁移   │  - 版本回滚     │  - 网络策略        │
│  - 自动扩缩   │  - 金丝雀发布   │  - RBAC           │
└─────────────┴───────────────┴───────────────────┘

没有编排平台的痛点

想象一个没有编排平台的场景:你有10台服务器,需要部署50个容器实例,分属5个不同的微服务。

  • 每个容器部署到哪台服务器?手动分配?
  • 某台服务器宕机了,上面的容器怎么迁移?手动重新部署?
  • 流量如何分发到多个实例?手动配置Nginx?
  • 需要扩容时怎么办?手动启动更多容器?
  • 需要更新版本时怎么办?逐个停掉旧容器再启动新的?

这些问题在没有编排平台时几乎无法有效解决,尤其在容器规模较大时。

1.5 Docker Swarm vs Kubernetes对比

Docker Swarm和Kubernetes是两大主流容器编排平台,它们各有优劣,适用于不同的场景。

对比维度Docker SwarmKubernetes
学习曲线平缓,几小时掌握陡峭,需数周至数月
安装部署Docker内置,一条命令复杂,需kubeadm或托管服务
架构复杂度简单,Manager/Worker两级复杂,多组件多层级
功能丰富度基础功能完备功能极其丰富
生态社区较小,趋于停滞极其庞大,CNCF生态
性能开销较高
高可用支持多Manager原生多Master高可用
网络模型Overlay网络CNI插件(灵活)
存储管理基础VolumePV/PVC/StorageClass
自动扩缩不支持原生HPA/VPA/Cluster Autoscaler
服务发现内置DNSCoreDNS
负载均衡内置VIP路由kube-proxy + Service
配置管理Docker Config/SecretConfigMap/Secret
滚动更新支持支持,更灵活
批处理任务不支持Job/CronJob
适用场景小规模集群,简单应用大规模集群,复杂微服务
维护状态维护模式(不再积极开发)活跃开发中

选择建议

选择Docker Swarm的场景:
├── 团队规模小,运维能力有限
├── 集群节点数 < 50
├── 应用架构简单,无复杂调度需求
├── 已有Docker基础,希望快速上手
└── 对性能开销敏感

选择Kubernetes的场景:
├── 团队有一定运维能力
├── 集群节点数 > 50 或预期快速增长
├── 微服务架构复杂,需要精细调度
├── 需要自动扩缩容、批处理等高级功能
├── 希望使用云原生生态的丰富工具
└── 长期战略选择(行业事实标准)

1.6 生产环境部署架构设计

一个成熟的生产环境容器化部署架构通常包含以下层次:

生产环境容器化部署架构:

                    ┌─────────────────────────────────┐
                    │          用户流量层               │
                    │    (CDN / DNS / Load Balancer)   │
                    └───────────────┬─────────────────┘
                                    │
                    ┌───────────────┴─────────────────┐
                    │          入口层                   │
                    │   (Ingress Controller / Nginx)   │
                    └───────────────┬─────────────────┘
                                    │
                    ┌───────────────┴─────────────────┐
                    │        应用服务层                 │
                    │  ┌──────┐ ┌──────┐ ┌──────┐    │
                    │  │Web   │ │API   │ │Worker│    │
                    │  │Pods  │ │Pods  │ │Pods  │    │
                    │  └──────┘ └──────┘ └──────┘    │
                    └───────────────┬─────────────────┘
                                    │
                    ┌───────────────┴─────────────────┐
                    │        数据存储层                 │
                    │  ┌──────┐ ┌──────┐ ┌──────┐    │
                    │  │MySQL │ │Redis │ │MQ    │    │
                    │  │集群   │ │集群   │ │集群   │    │
                    │  └──────┘ └──────┘ └──────┘    │
                    └─────────────────────────────────┘
                                    │
          ┌─────────────────────────┼──────────────────────┐
          │                         │                      │
┌─────────┴─────────┐  ┌──────────┴──────────┐  ┌────────┴────────┐
│    可观测层        │  │    CI/CD流水线       │  │    安全层        │
│  Prometheus        │  │  GitLab CI/Jenkins  │  │  RBAC           │
│  Grafana           │  │  Harbor Registry    │  │  Network Policy │
│  ELK/Loki          │  │  ArgoCD(GitOps)    │  │  Secret Manager │
│  Jaeger            │  │                     │  │  Image Scan     │
└───────────────────┘  └─────────────────────┘  └─────────────────┘

架构设计原则

  1. 无状态优先:应用层尽量无状态,状态下沉到专门的存储层
  2. 分层隔离:不同层之间通过明确的接口通信,避免直接依赖
  3. 冗余设计:每一层都有冗余,消除单点故障
  4. 渐进式迁移:从简单架构开始,逐步增加复杂度
# 一个典型的生产环境应用部署架构示例(概念性YAML)
# 展示各组件之间的关系

# 入口层: Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress               # Ingress名称
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"  # 强制HTTPS跳转
spec:
  tls:                            # TLS证书配置
  - hosts:
    - api.example.com             # 域名
    secretName: tls-secret        # 证书存储在Secret中
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /                   # 路由路径
        pathType: Prefix
        backend:
          service:
            name: api-service     # 后端Service名称
            port:
              number: 8080        # 后端端口

---
# 服务层: Deployment + Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3                     # 3个副本保证高可用
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: registry.example.com/api:v1.2.3  # 使用私有镜像仓库
        ports:
        - containerPort: 8080
        resources:                # 资源请求与限制
          requests:
            cpu: "250m"           # 请求250millicore
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:            # 存活探针
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:           # 就绪探针
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

本章我们从宏观角度了解了生产环境部署的挑战和要求。接下来,我们将深入Docker Swarm这个Docker自带的轻量级编排工具。


第二章 Docker Swarm集群

2.1 Docker Swarm概述与架构

Docker Swarm是Docker引擎内置的容器编排工具,它将多台Docker主机组成一个虚拟的Docker集群,对外暴露统一的API。用户可以像操作单机Docker一样操作整个集群。

Swarm的核心特点

  • 内置集成:无需额外安装,Docker Engine自带Swarm模式
  • 去中心化设计:Manager节点使用Raft共识算法管理集群状态
  • 声明式服务模型:描述期望状态,Swarm负责使其一致
  • 弹性伸缩:一条命令即可扩缩容服务副本数
  • 滚动更新:支持零停机的服务版本更新与回滚
  • 内置TLS:节点间通信自动加密,证书自动轮换

Swarm架构图

Docker Swarm 集群架构:

         ┌─────────────────────────────────────────────┐
         │              Swarm 集群                      │
         │                                             │
         │  ┌──────────────┐  ┌──────────────┐        │
         │  │  Manager 1   │←→│  Manager 2   │        │
         │  │  (Leader)    │  │  (Follower)  │        │
         │  │  Raft共识    │  │  Raft共识    │        │
         │  └──────┬───────┘  └──────┬───────┘        │
         │         │   ↕ Raft协议     │                 │
         │  ┌──────┴───────┐  ┌──────┴───────┐        │
         │  │  Manager 3   │  │              │        │
         │  │  (Follower)  │  │              │        │
         │  └──────┬───────┘  └──────────────┘        │
         │         │                                   │
         │    ┌────┴────────────────────┐              │
         │    │ Gossip协议(服务发现)    │              │
         │    └────┬─────────┬──────────┘              │
         │         │         │                          │
         │  ┌──────┴──┐ ┌───┴─────┐ ┌──────────┐      │
         │  │Worker 1 │ │Worker 2 │ │Worker 3  │      │
         │  │ Task    │ │ Task    │ │ Task     │      │
         │  │ Task    │ │ Task    │ │ Task     │      │
         │  └─────────┘ └─────────┘ └──────────┘      │
         └─────────────────────────────────────────────┘

2.2 Swarm核心概念

Node(节点)

节点是Swarm集群中的一台Docker主机,分为两种角色:

  • Manager Node(管理节点):负责集群管理、任务调度、服务编排。Manager之间通过Raft协议维护集群状态的一致性。生产环境建议至少3个Manager节点以保证高可用。
  • Worker Node(工作节点):负责执行Manager分配的任务(运行容器)。Worker节点不参与集群管理决策。

Service(服务)

Service是Swarm中的核心概念,是对微服务的抽象。一个Service定义了:

  • 使用什么镜像
  • 运行多少个副本
  • 暴露什么端口
  • 网络与存储配置
  • 更新与回滚策略

Task(任务)

Task是Swarm调度的最小单位,一个Task对应一个容器。Manager将Service按照副本数拆分成多个Task,然后调度到各个节点上运行。

Service → Task → Container 的关系:

  Service (my-web, replicas=3)
       │
       ├── Task 1 → Container (node-1)
       ├── Task 2 → Container (node-2)
       └── Task 3 → Container (node-3)

2.3 创建Swarm集群

初始化Manager节点

# 在第一台服务器上初始化Swarm集群
# --advertise-addr 指定其他节点用于加入集群的地址
docker swarm init --advertise-addr 192.168.1.100

# 输出示例:
# Swarm initialized: current node (xn7t...mz9) is now a manager.
#
# To add a worker to this swarm, run the following command:
#
#     docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377
#
# To add a manager to this swarm, run 'docker swarm join-token manager'
# and follow the instructions.

加入Worker节点

# 在其他服务器上执行join命令加入集群
# token从swarm init的输出中获取
docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377

# 输出示例:
# This node joined a swarm as a worker.

加入Manager节点(高可用)

# 在Manager节点上获取Manager加入token
docker swarm join-token manager

# 输出:
# To add a manager to this swarm, run the following command:
#     docker swarm join --token SWMTKN-1-yyy 192.168.1.100:2377

# 在新节点上执行
docker swarm join --token SWMTKN-1-yyy 192.168.1.100:2377

查看集群状态

# 查看集群所有节点(在Manager上执行)
docker node ls

# 输出示例:
# ID                            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS
# xn7t9...mz9 *                 node-1      Ready     Active         Leader
# k8b3x...p2q                   node-2      Ready     Active         Reachable
# m9c4y...r7s                   node-3      Ready     Active         Reachable
# p2d5z...t8u                   node-4      Ready     Active
# q3e6a...v9w                   node-5      Ready     Active

2.4 节点管理

# 查看节点详细信息
docker node inspect node-2

# 查看节点上运行的任务
docker node ps node-2

# 将Worker提升为Manager(提升集群管理能力)
docker node promote node-4

# 将Manager降级为Worker
docker node demote node-3

# 更改节点可用性状态
# Active:正常接收新任务
# Pause:不再接收新任务,但已有任务继续运行
# Drain:不再接收新任务,且已有任务被迁移到其他节点
docker node update --availability drain node-4

# 删除节点(必须先drain)
docker node rm node-4

# 查看Manager join token(用于添加新Manager)
docker swarm join-token manager

# 查看Worker join token(用于添加新Worker)
docker swarm join-token worker

# 轮换join token(安全操作,旧token失效)
docker swarm join-token --rotate worker

节点Drain操作详解

当需要维护某个节点时,Drain操作可以将该节点上的所有任务安全迁移到其他节点:

# 1. 将节点设为drain状态
docker node update --availability drain node-4

# 2. Swarm自动将node-4上的任务迁移到其他Active节点
# 3. 维护完成后,将节点恢复为Active
docker node update --availability active node-4

2.5 服务部署

# 基本服务创建
docker service create --name web nginx:1.25

# 完整参数的服务创建(生产环境示例)
docker service create \
  --name my-web \                          # 服务名称
  --replicas 3 \                           # 运行3个副本
  --publish 80:80 \                        # 发布端口80到集群所有节点
  --network my-overlay \                   # 使用overlay网络
  --mount type=volume,source=data-vol,target=/data \  # 挂载存储卷
  --env DB_HOST=mysql.internal \           # 环境变量
  --env DB_PORT=3306 \                     # 环境变量
  --env REDIS_HOST=redis.internal \        # 环境变量
  --constraint node.role==worker \         # 调度约束:只在worker节点运行
  --placement-pref 'spread=node.labels.rack' \  # 传播策略:按机架标签分散
  --update-parallelism 1 \                 # 滚动更新时每次更新1个
  --update-delay 10s \                     # 每次更新间隔10秒
  --update-failure-action rollback \       # 更新失败时自动回滚
  --rollback-parallelism 1 \              # 回滚时每次1个
  --rollback-delay 5s \                   # 回滚间隔5秒
  --restart-condition on-failure \        # 失败时自动重启
  --restart-delay 5s \                    # 重启延迟5秒
  --restart-max-attempts 3 \             # 最多重启3次
  --restart-window 120s \                # 重启窗口120秒
  --health-cmd "curl -f http://localhost/health || exit 1" \  # 健康检查命令
  --health-interval 10s \                # 健康检查间隔
  --health-retries 3 \                   # 健康检查重试次数
  --health-timeout 5s \                  # 健康检查超时
  --limit-cpu 0.5 \                      # CPU限制(0.5核)
  --limit-memory 512m \                  # 内存限制
  --reserve-cpu 0.25 \                   # CPU预留
  --reserve-memory 256m \                # 内存预留
  --label com.example.service=web \       # 服务标签
  --label com.example.env=production \    # 环境标签
  --with-registry-auth \                 # 传递私有仓库认证信息
  myregistry.com/myapp:1.0.0             # 镜像地址

2.6 服务管理

# 列出所有服务
docker service ls

# 查看服务详情
docker service inspect my-web

# 查看服务运行的任务(容器)
docker service ps my-web

# 查看服务日志
docker service logs my-web
docker service logs -f my-web          # 实时跟踪日志
docker service logs --tail 100 my-web  # 查看最后100行

# 扩缩容服务
docker service scale my-web=5           # 扩展到5个副本
docker service scale my-web=2 my-api=4  # 同时调整多个服务

# 更新服务镜像(滚动更新)
docker service update --image myregistry.com/myapp:1.1.0 my-web

# 更新服务配置
docker service update --env-add NEW_VAR=value my-web
docker service update --publish-rm 80:80 --publish-add 8080:80 my-web

# 删除服务
docker service rm my-web

2.7 服务滚动更新与回滚

Swarm内置了滚动更新和回滚机制,是生产环境零停机部署的关键。

# 滚动更新示例:更新镜像版本
docker service update \
  --image myregistry.com/myapp:2.0.0 \   # 新镜像版本
  --update-parallelism 2 \               # 每次更新2个副本
  --update-delay 30s \                   # 每次间隔30秒
  --update-order start-first \           # 先启动新容器再停旧容器
  --update-failure-action rollback \     # 失败自动回滚
  my-web

# 手动回滚到上一个版本
docker service rollback my-web

# 查看更新历史
docker service history my-web

# 回滚到指定版本(需要先查看history获取版本号)
docker service rollback my-web

滚动更新过程详解

滚动更新过程(update-parallelism=1, replicas=3):

初始状态:
  Task1(v1) Task2(v1) Task3(v1)

Step 1: 停止Task1,启动v2
  Task1(v2) Task2(v1) Task3(v1)

Step 2: (等待delay) 停止Task2,启动v2
  Task1(v2) Task2(v2) Task3(v1)

Step 3: (等待delay) 停止Task3,启动v2
  Task1(v2) Task2(v2) Task3(v2)

更新完成!

2.8 Swarm网络

Swarm使用Overlay网络实现跨主机容器通信,内置DNS服务发现和负载均衡。

# 创建Overlay网络
docker network create \
  --driver overlay \           # 使用overlay驱动
  --subnet 10.0.0.0/24 \       # 指定子网
  --attachable \               # 允许独立容器加入(非service容器)
  my-overlay

# 创建服务并加入网络
docker service create \
  --name my-web \
  --network my-overlay \
  --replicas 3 \
  nginx:1.25

# 服务发现:在同一个overlay网络中
# 容器可以通过服务名互相访问
# Swarm内置DNS将服务名解析为虚拟IP(VIP)
# VIP自动负载均衡到各个副本

Swarm网络通信模型

Swarm 网络通信模型:

  外部用户
     │
     │ http://node-ip:80
     ↓
  ┌──────────────────────────────────────┐
  │           Swarm 集群                   │
  │                                      │
  │  ┌─────────┐  ┌─────────┐  ┌──────┐ │
  │  │ Node 1  │  │ Node 2  │  │Node 3│ │
  │  │         │  │         │  │      │ │
  │  │ :80 ──┐ │  │ :80 ──┐ │  │ :80  │ │
  │  │       │ │  │       │ │  │  │   │ │
  │  │  VIP  │ │  │  VIP  │ │  │ VIP  │ │
  │  │  ↓↓↓  │ │  │  ↓↓↓  │ │  │ ↓↓↓  │ │
  │  │ Web1  │ │  │ Web2  │ │  │ Web3 │ │
  │  └───────┘ │  └───────┘ │  └──────┘ │
  │     │           │           │        │
  │     └───── mesh routing ────┘        │
  │     (任一节点的请求可路由到任一副本)    │
  └──────────────────────────────────────┘

  服务发现:
  - DNS: my-web → 10.0.0.5 (VIP)
  - VIP通过IPVS负载均衡到 Web1/Web2/Web3
  - 端口发布: 集群任一节点的80端口都能访问服务

2.9 Swarm存储

Swarm中的存储分为两类:本地Volume和共享存储。

# 本地Volume(仅在单个节点上有效)
docker service create \
  --name my-app \
  --mount type=volume,source=my-vol,target=/data \
  --replicas 1 \
  myapp:1.0

# 使用NFS共享存储(多节点共享数据)
# 先创建NFS volume
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.200,rw \     # NFS服务器地址和读写权限
  --opt device=:/path/to/share \       # NFS共享路径
  nfs-shared

# 在服务中使用NFS volume
docker service create \
  --name my-app \
  --mount type=volume,source=nfs-shared,target=/data \
  --replicas 3 \
  myapp:1.0

2.10 Swarm安全

Swarm内置了完善的安全机制:

# 查看集群TLS证书信息
docker info | grep -i tls

# Swarm安全特性:
# 1. 节点间通信自动TLS加密
# 2. 证书自动轮换(默认90天)
# 3. 旋转CA证书
docker swarm update --cert-expiry 720h    # 设置证书有效期720小时(30天)

# 4. 锁定集群(加密Raft日志)
docker swarm update --autolock=true

# 5. 查看解锁密钥
docker swarm unlock-key

# 6. 重启Manager后需要解锁
docker swarm unlock

# 7. Docker Secret管理敏感信息
echo "my-secret-password" | docker secret create db_password -

# 在服务中使用Secret
docker service create \
  --name my-app \
  --secret db_password \               # 挂载secret
  myapp:1.0
# Secret会以文件形式挂载到 /run/secrets/db_password

2.11 Stack部署

Docker Stack使用Compose v3格式定义多服务应用,是Swarm生产环境部署的推荐方式。

# docker-stack.yml - 生产环境Stack部署文件
version: "3.8"                           # Compose文件版本(支持Swarm)

services:
  # 前端Web服务
  web:
    image: registry.example.com/web:2.1.0  # 镜像地址
    deploy:
      replicas: 3                          # 3个副本
      update_config:                       # 滚动更新配置
        parallelism: 1                     # 每次更新1个
        delay: 10s                         # 间隔10秒
        failure_action: rollback           # 失败回滚
        order: start-first                 # 先启新后停旧
      rollback_config:                     # 回滚配置
        parallelism: 1
        delay: 5s
      restart_policy:                      # 重启策略
        condition: on-failure
        max_attempts: 3
        delay: 5s
        window: 120s
      resources:                           # 资源限制
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
      placement:                           # 调度约束
        constraints:
          - node.role == worker            # 只在worker节点运行
        preferences:
          - spread: node.labels.rack       # 按机架分散
      labels:                              # 服务标签
        com.example.service: web
    ports:
      - "80:80"                            # 端口映射
    networks:
      - frontend                           # 加入前端网络
    environment:                           # 环境变量
      API_URL: "http://api:8080"
      LOG_LEVEL: "info"
    healthcheck:                           # 健康检查
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 10s
      timeout: 5s
      retries: 3
    depends_on:                            # 依赖关系(仅影响启动顺序)
      - api

  # 后端API服务
  api:
    image: registry.example.com/api:2.1.0
    deploy:
      replicas: 4                          # 4个副本
      update_config:
        parallelism: 2
        delay: 15s
        failure_action: rollback
      resources:
        limits:
          cpus: "1.0"
          memory: 1G
        reservations:
          cpus: "0.5"
          memory: 512M
    networks:
      - frontend
      - backend
    environment:
      DB_HOST: "mysql"
      DB_PORT: "3306"
      REDIS_HOST: "redis"
    secrets:                               # 使用Secret
      - db_password
      - api_key
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3

  # MySQL数据库服务
  mysql:
    image: mysql:8.0
    deploy:
      replicas: 1                          # 数据库单副本
      placement:
        constraints:
          - node.labels.db == true         # 只在标记了db的节点运行
    networks:
      - backend
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
      MYSQL_DATABASE: myapp
    volumes:
      - mysql-data:/var/lib/mysql          # 数据持久化
    secrets:
      - db_password

  # Redis缓存服务
  redis:
    image: redis:7-alpine
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.cache == true
    networks:
      - backend
    volumes:
      - redis-data:/data

networks:
  frontend:                                # 前端overlay网络
    driver: overlay
  backend:                                 # 后端overlay网络(内部)
    driver: overlay
    internal: true                         # 内部网络,不对外暴露

volumes:
  mysql-data:                              # MySQL数据卷
    driver: local
  redis-data:                              # Redis数据卷
    driver: local

secrets:
  db_password:                             # 数据库密码
    external: true                         # 引用外部已创建的secret
  api_key:
    external: true
# 部署Stack
docker stack deploy -c docker-stack.yml myapp

# 查看Stack中的服务
docker stack services myapp

# 查看Stack中的任务
docker stack ps myapp

# 删除Stack
docker stack rm myapp

2.12 Swarm高可用方案

Swarm的高可用依赖于Manager节点的Raft共识:

Swarm Manager 高可用:

  奇数个Manager节点(推荐3或5个):

  ┌──────────┐     ┌──────────┐     ┌──────────┐
  │ Manager 1│ ←→  │ Manager 2│ ←→  │ Manager 3│
  │ Leader   │     │ Follower │     │ Follower │
  └──────────┘     └──────────┘     └──────────┘

  - Leader处理所有写请求,同步到Follower
  - Leader故障时,Follower选举新Leader
  - 3个Manager可容忍1个故障
  - 5个Manager可容忍2个故障
  - 不建议超过7个Manager(性能下降)
# 生产环境高可用配置建议:
# 1. 至少3个Manager节点(奇数)
# 2. Manager节点不运行业务任务(只做管理)
docker node update --availability drain manager-1

# 3. Worker节点运行业务任务
# 4. 使用负载均衡器暴露服务
#    LB → Node1:80 / Node2:80 / Node3:80 (Swarm mesh routing)

2.13 Swarm生产环境实战案例

场景:部署一个包含Web前端、API后端、MySQL数据库、Redis缓存的完整应用。

#!/bin/bash
# deploy-swarm.sh - Swarm生产环境部署脚本

set -e

# 1. 创建overlay网络
docker network create --driver overlay app-network 2>/dev/null || true

# 2. 创建Secrets
echo "${DB_PASSWORD}" | docker secret create db_password - 2>/dev/null || true
echo "${API_SECRET_KEY}" | docker secret create api_key - 2>/dev/null || true

# 3. 创建节点标签(在Manager上执行)
docker node update --label-add db=true db-node
docker node update --label-add cache=true cache-node

# 4. 部署Stack
docker stack deploy -c docker-stack.yml myapp

# 5. 等待服务就绪
echo "等待服务启动..."
sleep 10

# 6. 检查服务状态
docker stack services myapp

# 7. 验证健康状态
for service in web api mysql redis; do
  echo "检查 ${service} 服务..."
  docker service ps myapp_${service} --no-trunc | head -5
done

echo "部署完成!"
echo "访问地址: http://$(docker node ls --format '{{.Addr}}' | head -1)"

本章详细介绍了Docker Swarm的完整使用方法。虽然Swarm功能完备且易于使用,但对于更大规模、更复杂的生产环境,Kubernetes是更主流的选择。接下来我们将学习如何将Docker融入CI/CD流水线。


第三章 Docker与CI/CD流水线

3.1 CI/CD概念与容器化的关系

CI/CD是现代软件工程的核心实践,容器化与CI/CD的结合产生了强大的协同效应。

CI/CD的定义

  • CI(Continuous Integration,持续集成):开发者频繁地将代码合并到主干,每次合并自动触发构建和测试。
  • CD(Continuous Delivery/Deployment,持续交付/部署):将通过测试的代码自动部署到生产环境(交付需手动触发,部署全自动)。
CI/CD 流水线全貌:

  开发者 → Git Push → CI阶段 → CD阶段
           │            │          │
           │   ┌────────┘    ┌─────┘
           │   │             │
           ↓   ↓             ↓
  ┌──────────────────────────────────────┐
  │  Code → Build → Test → Push → Deploy │
  │         │       │       │       │    │
  │    Docker     Unit    Harbor  Swarm  │
  │    Build      Test    Registry  K8s  │
  │               Integ             ARM  │
  │               Scan              ESS  │
  └──────────────────────────────────────┘

容器化对CI/CD的价值

  1. 构建一致性:CI环境中构建的镜像与生产环境运行的镜像完全一致
  2. 环境隔离:每个CI任务在独立容器中运行,互不干扰
  3. 可重复性:相同的Dockerfile+代码=相同的镜像
  4. 快速回滚:镜像版本化,出问题时秒级回滚到任意版本
  5. 不可变基础设施:部署的是镜像而非代码,运行环境不会被修改

3.2 GitLab CI/CD + Docker实战

GitLab CI/CD是GitLab内置的CI/CD工具,通过.gitlab-ci.yml文件定义流水线。

# .gitlab-ci.yml - GitLab CI/CD流水线定义
# 定义流水线阶段
stages:
  - lint          # 代码检查
  - build         # 镜像构建
  - test          # 自动化测试
  - scan          # 安全扫描
  - push          # 镜像推送
  - deploy_staging   # 部署到预发布
  - deploy_production # 部署到生产

# 全局变量
variables:
  # 镜像仓库地址
  REGISTRY: registry.example.com
  # 镜像名称(使用项目名)
  IMAGE_NAME: $REGISTRY/$CI_PROJECT_NAME
  # 镜像标签(使用commit短SHA)
  IMAGE_TAG: $CI_COMMIT_SHORT_SHA
  # Docker Host(使用DinD)
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"
  DOCKER_DRIVER: overlay2

# 代码检查阶段
lint:
  stage: lint
  image: node:20-alpine           # 使用Node.js镜像
  script:
    - npm ci                      # 安装依赖
    - npm run lint                # 运行ESLint
    - npm run type-check          # TypeScript类型检查
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"  # MR时触发

# 镜像构建阶段
build:
  stage: build
  image: docker:24.0              # 使用Docker镜像
  services:
    - docker:24.0-dind            # Docker in Docker服务
  before_script:
    # 登录镜像仓库
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $REGISTRY
  script:
    # 多阶段构建镜像
    - docker build -t $IMAGE_NAME:$IMAGE_TAG .
    # 同时打上latest标签(仅主分支)
    - docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
    # 保存镜像到产物(供后续阶段使用)
    - docker save $IMAGE_NAME:$IMAGE_TAG | gzip > image.tar.gz
  artifacts:
    paths:
      - image.tar.gz              # 构建产物
    expire_in: 1 hour             # 1小时后过期
  rules:
    - if: $CI_COMMIT_BRANCH == "main"      # 主分支触发
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# 单元测试阶段
test:unit:
  stage: test
  image: docker:24.0
  services:
    - docker:24.0-dind
  needs: [build]                  # 依赖build阶段
  script:
    # 加载构建好的镜像
    - docker load < image.tar.gz
    # 运行单元测试容器
    - docker run --rm $IMAGE_NAME:$IMAGE_TAG npm test
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 集成测试阶段
test:integration:
  stage: test
  image: docker:24.0
  services:
    - docker:24.0-dind
  needs: [build]
  script:
    - docker load < image.tar.gz
    # 启动测试用数据库
    - docker network create test-net
    - docker run -d --name test-db --network test-net -e POSTGRES_PASSWORD=test postgres:16
    # 等待数据库就绪
    - sleep 10
    # 运行集成测试
    - docker run --rm --network test-net -e DB_HOST=test-db $IMAGE_NAME:$IMAGE_TAG npm run test:integration
    # 清理
    - docker rm -f test-db
    - docker network rm test-net
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 安全扫描阶段
scan:
  stage: scan
  image: docker:24.0
  services:
    - docker:24.0-dind
  needs: [build]
  script:
    - docker load < image.tar.gz
    # 使用Trivy扫描镜像漏洞
    - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_NAME:$IMAGE_TAG
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  allow_failure: false            # 有高危漏洞则失败

# 镜像推送阶段
push:
  stage: push
  image: docker:24.0
  services:
    - docker:24.0-dind
  needs: [test:unit, test:integration, scan]  # 所有测试通过后推送
  before_script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $REGISTRY
  script:
    - docker load < image.tar.gz
    # 推送镜像
    - docker push $IMAGE_NAME:$IMAGE_TAG
    - docker push $IMAGE_NAME:latest
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 部署到预发布环境
deploy:staging:
  stage: deploy_staging
  image: bitnami/kubectl:1.28     # 使用kubectl镜像
  needs: [push]
  environment:
    name: staging                 # 环境名称
    url: https://staging.example.com
  script:
    # 配置kubeconfig
    - echo "$KUBE_CONFIG" | base64 -d > ~/.kube/config
    # 更新Deployment镜像
    - kubectl set image deployment/$CI_PROJECT_NAME app=$IMAGE_NAME:$IMAGE_TAG -n staging
    # 等待滚动更新完成
    - kubectl rollout status deployment/$CI_PROJECT_NAME -n staging --timeout=300s
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 部署到生产环境(需手动触发)
deploy:production:
  stage: deploy_production
  image: bitnami/kubectl:1.28
  needs: [deploy:staging]
  environment:
    name: production
    url: https://api.example.com
  script:
    - echo "$KUBE_CONFIG" | base64 -d > ~/.kube/config
    - kubectl set image deployment/$CI_PROJECT_NAME app=$IMAGE_NAME:$IMAGE_TAG -n production
    - kubectl rollout status deployment/$CI_PROJECT_NAME -n production --timeout=300s
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual                # 手动触发生产部署

3.3 Jenkins + Docker Pipeline实战

Jenkins是最流行的开源CI/CD服务器,通过Jenkinsfile定义流水线。

// Jenkinsfile - Jenkins流水线定义
pipeline {
    agent {
        // 使用Docker作为构建代理
        docker {
            image 'docker:24.0'
            args '--privileged -v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
    
    environment {
        // 环境变量定义
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = "${REGISTRY}/${env.JOB_NAME}"
        IMAGE_TAG = "${env.BUILD_NUMBER}"
        DOCKER_CONFIG = credentials('docker-registry-cred')  // 仓库凭据
    }
    
    stages {
        // 代码检出
        stage('Checkout') {
            steps {
                checkout scm
                echo "构建分支: ${env.BRANCH_NAME}"
                echo "Commit: ${env.GIT_COMMIT}"
            }
        }
        
        // 代码检查
        stage('Lint') {
            steps {
                // 使用Node.js容器运行lint
                docker.image('node:20-alpine').inside() {
                    sh 'npm ci'
                    sh 'npm run lint'
                }
            }
        }
        
        // 镜像构建
        stage('Build') {
            steps {
                sh """
                    docker build \
                      --build-arg BUILD_DATE=\$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
                      --build-arg VCS_REF=${env.GIT_COMMIT} \
                      --build-arg VERSION=${env.IMAGE_TAG} \
                      -t ${IMAGE_NAME}:${IMAGE_TAG} \
                      -t ${IMAGE_NAME}:latest \
                      .
                """
            }
        }
        
        // 测试
        stage('Test') {
            steps {
                sh "docker run --rm ${IMAGE_NAME}:${IMAGE_TAG} npm test"
            }
        }
        
        // 安全扫描
        stage('Security Scan') {
            steps {
                sh "docker run --rm aquasec/trivy image --severity HIGH,CRITICAL ${IMAGE_NAME}:${IMAGE_TAG}"
            }
        }
        
        // 推送镜像
        stage('Push') {
            steps {
                sh "docker push ${IMAGE_NAME}:${IMAGE_TAG}"
                sh "docker push ${IMAGE_NAME}:latest"
            }
        }
        
        // 部署
        stage('Deploy') {
            when {
                branch 'main'  // 仅main分支部署
            }
            steps {
                // 使用kubectl部署
                sh """
                    kubectl set image deployment/myapp \
                      app=${IMAGE_NAME}:${IMAGE_TAG} \
                      -n production
                    kubectl rollout status deployment/myapp -n production
                """
            }
        }
    }
    
    // 后置处理
    post {
        success {
            echo '流水线执行成功!'
            // 发送通知
            slackSend channel: '#deployments',
                      color: 'good',
                      message: "部署成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
        failure {
            echo '流水线执行失败!'
            slackSend channel: '#deployments',
                      color: 'danger',
                      message: "部署失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
        always {
            // 清理Docker镜像
            sh "docker rmi ${IMAGE_NAME}:${IMAGE_TAG} || true"
            // 清理工作空间
            cleanWs()
        }
    }
}

3.4 GitHub Actions + Docker实战

GitHub Actions是GitHub原生的CI/CD平台,通过YAML文件定义工作流。

# .github/workflows/ci-cd.yml - GitHub Actions工作流
name: CI/CD Pipeline                    # 工作流名称

on:
  push:
    branches: [ main ]                  # 主分支push触发
  pull_request:
    branches: [ main ]                  # PR触发

env:
  REGISTRY: ghcr.io                     # GitHub Container Registry
  IMAGE_NAME: ${{ github.repository }}  # 镜像名=仓库名

jobs:
  # 镜像构建任务
  build:
    name: Build Docker Image
    runs-on: ubuntu-latest              # 运行在Ubuntu上
    permissions:
      contents: read
      packages: write                   # 写入GitHub Packages权限
    
    steps:
      # 1. 检出代码
      - name: Checkout code
        uses: actions/checkout@v4
      
      # 2. 设置Docker Buildx(支持多平台构建)
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      # 3. 登录镜像仓库
      - name: Log in to Container Registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      # 4. 提取镜像标签和元数据
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch         # 分支名作为标签
            type=sha,prefix={{branch}}-   # commit SHA作为标签
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
      
      # 5. 构建并推送镜像(使用缓存优化)
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha            # 使用GitHub Actions缓存
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64  # 多平台构建
  
  # 测试任务
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    needs: build                         # 依赖build任务
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Run tests in Docker
        run: |
          docker build -t test-image .
          docker run --rm test-image npm test
  
  # 部署任务
  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: [build, test]                 # 依赖build和test
    if: github.ref == 'refs/heads/main'  # 仅main分支部署
    environment: production              # 需要审批的环境
    
    steps:
      - name: Deploy to K8s
        run: |
          echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
          export KUBECONFIG=kubeconfig
          kubectl set image deployment/myapp app=ghcr.io/${{ env.IMAGE_NAME }}:latest -n production
          kubectl rollout status deployment/myapp -n production

3.5 镜像构建流水线(多阶段构建 + 缓存优化)

# Dockerfile - 生产级多阶段构建示例

# ============================================
# 阶段1: 依赖安装阶段
# ============================================
FROM node:20-alpine AS deps

# 设置工作目录
WORKDIR /app

# 先复制package.json和lock文件(利用Docker缓存层)
COPY package*.json ./

# 安装生产依赖
RUN npm ci --only=production

# ============================================
# 阶段2: 构建阶段
# ============================================
FROM node:20-alpine AS builder

WORKDIR /app

# 复制依赖
COPY --from=deps /app/node_modules ./node_modules

# 复制源代码
COPY . .

# 安装全部依赖(含devDependencies,用于构建)
RUN npm ci

# 构建项目
RUN npm run build

# 运行测试
RUN npm test

# 清理开发依赖,只保留生产依赖
RUN npm prune --production

# ============================================
# 阶段3: 运行阶段(最终镜像)
# ============================================
FROM node:20-alpine AS runtime

# 安装curl用于健康检查
RUN apk add --no-cache curl

# 创建非root用户运行应用
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000

# 从构建阶段复制产物
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

# 切换为非root用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# 启动命令
CMD ["node", "dist/main.js"]

BuildKit缓存优化

# 使用BuildKit进行构建(支持高级缓存)
# 启用BuildKit
export DOCKER_BUILDKIT=1

# 使用内联缓存
docker build \
  --cache-from type=registry,ref=registry.example.com/app:cache \
  --cache-to type=registry,ref=registry.example.com/app:cache,mode=max \
  -t registry.example.com/app:v1.0.0 \
  .

# 多平台构建
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/app:v1.0.0 \
  --push \
  .

3.6 镜像推送与版本管理策略

镜像标签策略

标签类型格式示例说明
时间戳YYYYMMDD-HHMMSS-ShortSHA20240115-143022-a1b2c3d精确到分钟,便于追溯
语义版本vMAJOR.MINOR.PATCHv1.2.3正式发布版本
Git SHAgit-ShortSHAgit-a1b2c3d关联到具体commit
分支名branch-namefeature-auth分支构建
latestlatestlatest最新版本(慎用)
# 推荐的镜像标签策略
IMAGE_TAG="${BUILD_DATE}-${GIT_SHORT_SHA}"  # 如: 20240115-a1b2c3d

# 同时打多个标签
docker tag myapp:latest registry.example.com/myapp:${IMAGE_TAG}
docker tag myapp:latest registry.example.com/myapp:v1.2.3
docker tag myapp:latest registry.example.com/myapp:latest

# 推送所有标签
docker push -a registry.example.com/myapp

3.7 自动化测试集成

# 完整的测试集成示例
version: "3.8"

services:
  # 被测试应用
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DB_HOST: postgres-test
      REDIS_HOST: redis-test
    depends_on:
      postgres-test:
        condition: service_healthy
      redis-test:
        condition: service_started
  
  # 测试用PostgreSQL
  postgres-test:
    image: postgres:16
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: test
      POSTGRES_PASSWORD: testpass
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test"]
      interval: 5s
      timeout: 3s
      retries: 10
  
  # 测试用Redis
  redis-test:
    image: redis:7-alpine
  
  # 测试运行器
  test-runner:
    build:
      context: .
      dockerfile: Dockerfile.test    # 测试专用Dockerfile
    depends_on:
      app:
        condition: service_healthy
    environment:
      API_URL: http://app:3000
    command: ["npm", "run", "test:e2e"]

3.8 蓝绿部署与金丝雀发布

蓝绿部署(Blue-Green Deployment)

蓝绿部署原理:

  初始状态(蓝环境活跃):
  ┌──────────────┐
  │  Router/LB   │──→ 蓝环境 (v1.0)
  └──────────────┘    绿环境 (待命)
  
  部署新版本到绿环境:
  ┌──────────────┐
  │  Router/LB   │──→ 蓝环境 (v1.0)
  └──────────────┘    绿环境 (v2.0) ← 新版本部署
  
  切换流量:
  ┌──────────────┐
  │  Router/LB   │──→ 绿环境 (v2.0) ← 流量切换
  └──────────────┘    蓝环境 (v1.0) ← 保留可回滚
  
  确认无误后删除旧版本:
  ┌──────────────┐
  │  Router/LB   │──→ 绿环境 (v2.0)
  └──────────────┘
# K8s蓝绿部署实现
# 1. 部署新版本(绿环境)
kubectl apply -f deployment-green.yaml

# 2. 等待新版本就绪
kubectl rollout status deployment/app-green

# 3. 切换Service指向新版本
kubectl patch service app-service -p '{"spec":{"selector":{"version":"green"}}}'

# 4. 确认无误后清理旧版本
kubectl delete deployment app-blue

金丝雀发布(Canary Release)

# K8s金丝雀发布(使用两个Deployment)
# canary-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-canary                  # 金丝雀Deployment
spec:
  replicas: 1                       # 先部署1个副本(占总流量的10%)
  selector:
    matchLabels:
      app: myapp
      track: canary                 # 标记为金丝雀
  template:
    metadata:
      labels:
        app: myapp
        track: canary
    spec:
      containers:
      - name: app
        image: registry.example.com/myapp:v2.0.0  # 新版本镜像
        ports:
        - containerPort: 3000
# 逐步增加金丝雀流量
# 稳定版: 9 replicas (90%)
# 金丝雀: 1 replicas (10%)
kubectl scale deployment app-canary --replicas=1
kubectl scale deployment app-stable --replicas=9

# 观察10%流量无异常后,增加到30%
kubectl scale deployment app-canary --replicas=3
kubectl scale deployment app-stable --replicas=7

# 继续增加到50%
kubectl scale deployment app-canary --replicas=5
kubectl scale deployment app-stable --replicas=5

# 完成发布,移除旧版本
kubectl scale deployment app-canary --replicas=10
kubectl delete deployment app-stable

3.9 完整CI/CD流水线示例

以下是代码从提交到部署的完整流水线:

#!/bin/bash
# complete-cicd-pipeline.sh - 完整CI/CD流水线脚本

set -euo pipefail

# ==================== 配置 ====================
REGISTRY="registry.example.com"
APP_NAME="myapp"
GIT_SHA=$(git rev-parse --short HEAD)
BUILD_DATE=$(date -u +'%Y%m%d%H%M%S')
IMAGE_TAG="${BUILD_DATE}-${GIT_SHA}"
KUBE_NAMESPACE="production"

echo "========================================"
echo "CI/CD Pipeline 开始执行"
echo "镜像标签: ${IMAGE_TAG}"
echo "========================================"

# ==================== 1. 代码检查 ====================
echo "[1/8] 代码检查..."
docker run --rm -v $(pwd):/app -w /app node:20-alpine \
  sh -c "npm ci && npm run lint && npm run type-check"
echo "✓ 代码检查通过"

# ==================== 2. 镜像构建 ====================
echo "[2/8] 构建Docker镜像..."
export DOCKER_BUILDKIT=1
docker build \
  --build-arg BUILD_DATE=${BUILD_DATE} \
  --build-arg GIT_SHA=${GIT_SHA} \
  -t ${REGISTRY}/${APP_NAME}:${IMAGE_TAG} \
  -t ${REGISTRY}/${APP_NAME}:latest \
  .
echo "✓ 镜像构建完成"

# ==================== 3. 单元测试 ====================
echo "[3/8] 运行单元测试..."
docker run --rm ${REGISTRY}/${APP_NAME}:${IMAGE_TAG} npm test
echo "✓ 单元测试通过"

# ==================== 4. 集成测试 ====================
echo "[4/8] 运行集成测试..."
docker-compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from test-runner
echo "✓ 集成测试通过"

# ==================== 5. 安全扫描 ====================
echo "[5/8] 镜像安全扫描..."
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image \
  --exit-code 1 \
  --severity HIGH,CRITICAL \
  ${REGISTRY}/${APP_NAME}:${IMAGE_TAG}
echo "✓ 安全扫描通过(无高危漏洞)"

# ==================== 6. 镜像推送 ====================
echo "[6/8] 推送镜像到仓库..."
docker push ${REGISTRY}/${APP_NAME}:${IMAGE_TAG}
docker push ${REGISTRY}/${APP_NAME}:latest
echo "✓ 镜像推送完成"

# ==================== 7. 部署到预发布 ====================
echo "[7/8] 部署到预发布环境..."
kubectl set image deployment/${APP_NAME} \
  app=${REGISTRY}/${APP_NAME}:${IMAGE_TAG} \
  -n staging
kubectl rollout status deployment/${APP_NAME} -n staging --timeout=300s
echo "✓ 预发布环境部署完成"

# ==================== 8. 等待确认后部署生产 ====================
echo "[8/8] 部署到生产环境(需确认)..."
read -p "预发布环境验证通过? 部署生产? (yes/no): " confirm
if [ "$confirm" = "yes" ]; then
    kubectl set image deployment/${APP_NAME} \
      app=${REGISTRY}/${APP_NAME}:${IMAGE_TAG} \
      -n ${KUBE_NAMESPACE}
    kubectl rollout status deployment/${APP_NAME} -n ${KUBE_NAMESPACE} --timeout=300s
    echo "✓ 生产环境部署完成"
    
    # 记录部署信息
    kubectl annotate deployment/${APP_NAME} \
      deployment.kubernetes.io/revision-history=${IMAGE_TAG} \
      -n ${KUBE_NAMESPACE}
else
    echo "部署已取消"
    exit 1
fi

echo "========================================"
echo "CI/CD Pipeline 执行完成!"
echo "镜像: ${REGISTRY}/${APP_NAME}:${IMAGE_TAG}"
echo "========================================"

3.10 CI/CD最佳实践

  1. 构建一次,部署多次:CI阶段构建的镜像在所有环境(测试/预发布/生产)中使用,不在各环境重新构建
  2. 镜像不可变:部署后不修改运行中的容器,需要变更则重新构建镜像
  3. 使用语义化版本:清晰的版本号便于追溯和管理
  4. 流水线即代码:将流水线定义纳入版本控制
  5. 快速失败:在流水线早期发现问题,尽早失败
  6. 并行化:独立的测试任务并行执行,缩短流水线时间
  7. 缓存优化:利用Docker层缓存和构建缓存加速构建
  8. 安全左移:在CI阶段就进行安全扫描,而非部署后才发现问题

本章详细讲解了Docker与三大CI/CD平台的集成方法。接下来,我们将正式进入Kubernetes的世界。


第四章 Kubernetes入门

4.1 Kubernetes概述与设计理念

Kubernetes(简称K8s)是Google基于其内部容器管理系统Borg的开源经验,于2014年捐赠给CNCF基金会后发展起来的容器编排平台。如今,K8s已成为云原生领域的事实标准,被全球绝大多数企业采用。

K8s的设计理念

  1. 声明式而非命令式:用户描述"期望状态"(如3个副本),K8s负责使实际状态趋近期望状态,而非逐条下达命令
  2. 最终一致性:系统会持续 reconciliation(协调),直到实际状态与期望状态一致
  3. 控制循环(Reconciliation Loop):控制器不断监控集群状态,发现偏差时自动纠正
  4. 松耦合与可插拔:各组件通过API通信,网络、存储、运行时等均可替换
  5. 自愈能力:容器崩溃自动重启,节点故障自动迁移
  6. 水平扩展:以Pod为最小调度单位,通过增减Pod数量实现弹性
声明式 vs 命令式:

命令式(Docker):
  "启动3个容器" → 手动管理 → 容器挂了手动重启

声明式(K8s):
  "我需要3个副本在运行" → K8s自动管理 → 容器挂了自动重启
  用户只需描述期望状态,K8s负责维持

4.2 K8s架构详解

K8s集群由控制平面(Control Plane,原称Master)和工作节点(Worker Node)组成。

Kubernetes 集群架构:

┌─────────────────────────────────────────────────────────────┐
│                    控制平面 (Control Plane)                   │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ kube-       │  │ kube-       │  │    kube-            │ │
│  │ apiserver   │  │ scheduler   │  │    controller-      │ │
│  │             │  │             │  │    manager          │ │
│  │ API入口     │  │ Pod调度     │  │    控制器管理       │ │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘ │
│         │                │                    │            │
│  ┌──────┴────────────────┴────────────────────┴──────────┐ │
│  │                      etcd                              │ │
│  │               (集群状态存储, KV数据库)                  │ │
│  └───────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
                          │ API通信
    ┌─────────────────────┼─────────────────────┐
    │                     │                     │
┌───┴──────────┐  ┌──────┴───────────┐  ┌──────┴──────────┐
│  Worker Node 1│  │  Worker Node 2   │  │  Worker Node 3  │
│               │  │                  │  │                 │
│ ┌───────────┐ │  │ ┌───────────┐   │  │ ┌───────────┐  │
│ │ kubelet   │ │  │ │ kubelet   │   │  │ │ kubelet   │  │
│ │ (节点代理) │ │  │ │ (节点代理) │   │  │ │ (节点代理) │  │
│ └─────┬─────┘ │  │ └─────┬─────┘   │  │ └─────┬─────┘  │
│       │       │  │       │         │  │       │        │
│ ┌─────┴─────┐ │  │ ┌─────┴─────┐   │  │ ┌─────┴─────┐  │
│ │kube-proxy │ │  │ │kube-proxy │   │  │ │kube-proxy │  │
│ │(网络代理) │ │  │ │(网络代理) │   │  │ │(网络代理) │  │
│ └─────┬─────┘ │  │ └─────┬─────┘   │  │ └─────┬─────┘  │
│       │       │  │       │         │  │       │        │
│ ┌─────┴─────┐ │  │ ┌─────┴─────┐   │  │ ┌─────┴─────┐  │
│ │containerd │ │  │ │containerd │   │  │ │containerd │  │
│ │  Pod Pod  │ │  │ │  Pod Pod  │   │  │ │  Pod Pod  │  │
│ └───────────┘ │  │ └───────────┘   │  │ └───────────┘  │
└───────────────┘  └─────────────────┘  └────────────────┘

控制平面组件

组件功能说明
kube-apiserverK8s API入口,所有组件通过它通信。是唯一直接操作etcd的组件
etcd分布式键值存储,保存集群所有状态数据。生产环境建议3或5节点集群
kube-scheduler负责将未调度的Pod分配到合适的节点(基于资源、亲和性、污点等)
kube-controller-manager运行各种控制器(副本控制器、节点控制器、端点控制器等)
cloud-controller-manager与云提供商交互(如创建LoadBalancer、管理路由等)

工作节点组件

组件功能说明
kubelet节点代理,负责管理本节点上的Pod生命周期,向API Server汇报节点状态
kube-proxy维护节点上的网络规则,实现Service的负载均衡和流量转发
容器运行时负责运行容器(containerd、CRI-O等,不再使用Docker)

4.3 K8s核心概念

Pod(豆荚)

Pod是K8s中最小的可部署计算单元,一个Pod可以包含一个或多个容器。同一Pod内的容器共享网络和存储命名空间。

# 一个简单的Pod定义
apiVersion: v1                    # API版本
kind: Pod                         # 资源类型
metadata:                         # 元数据
  name: my-pod                    # Pod名称
  labels:                         # 标签(用于选择和过滤)
    app: myapp
spec:                             # 期望状态规格
  containers:                     # 容器列表
  - name: nginx                   # 容器名称
    image: nginx:1.25             # 镜像
    ports:                        # 端口
    - containerPort: 80

Deployment(部署)

Deployment管理Pod的副本集,提供滚动更新和回滚能力。它是最常用的工作负载类型。

# Deployment定义
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3                     # 期望3个Pod副本
  selector:                       # 选择器:管理带有app=myapp标签的Pod
    matchLabels:
      app: myapp
  template:                       # Pod模板
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0
        ports:
        - containerPort: 8080

Service(服务)

Service为一组Pod提供稳定的网络访问入口和负载均衡。Pod的IP会变化,但Service的IP是固定的。

# Service定义
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:                       # 选择后端Pod
    app: myapp
  ports:
  - port: 80                      # Service端口
    targetPort: 8080              # Pod端口
    protocol: TCP
  type: ClusterIP                 # Service类型

ConfigMap(配置映射)

ConfigMap用于存储非敏感的配置数据,将配置与镜像解耦。

# ConfigMap定义
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 简单键值对
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  # 配置文件内容
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://backend:8080;
      }
    }

Secret(密钥)

Secret用于存储敏感数据(密码、证书、API Key),数据以Base64编码存储。

# Secret定义
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque                      # 通用类型
data:
  # Base64编码的值
  username: YWRtaW4=              # echo -n "admin" | base64 → YWRtaW4=
  password: cGFzczEyMw==          # echo -n "pass123" | base64 → cGFzczEyMw==

Namespace(命名空间)

Namespace用于在集群内隔离资源,适用于多团队、多环境场景。

# 查看所有命名空间
kubectl get namespaces

# 创建命名空间
kubectl create namespace production

# 在指定命名空间中操作
kubectl get pods -n production

4.4 Pod详解

Pod生命周期

Pod从创建到销毁经历多个阶段(Phase):

Pod 生命周期阶段:

  Pending → Running → Succeeded
      │        │
      │        ├── Failed
      │        │
      │        └── CrashLoopBackOff (反复崩溃)
      │
      └── ImagePullFailure (镜像拉取失败)

Pod 状态:
  - Pending:    已创建,等待调度或拉取镜像
  - Running:    已调度到节点,至少一个容器在运行
  - Succeeded:  所有容器成功退出(不会重启)
  - Failed:     所有容器退出,至少一个失败
  - Unknown:    无法获取状态(通常因节点失联)

Pod容器重启策略

spec:
  restartPolicy: Always    # Always: 容器退出后总是重启(默认,适用于Deployment)
                           # OnFailure: 仅在非0退出码时重启
                           # Never: 永不重启(适用于Job)

多容器Pod(Sidecar模式)

一个Pod中可以运行多个容器,它们共享网络和存储:

# 多容器Pod: 应用容器 + Sidecar容器
apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
spec:
  containers:
  # 主应用容器
  - name: app
    image: myapp:1.0.0
    ports:
    - containerPort: 8080
    
  # Sidecar容器: 日志收集代理
  - name: log-agent
    image: fluent-bit:2.2
    # 与app容器共享日志目录
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/app
  
  volumes:
  - name: shared-logs            # 两个容器共享的emptyDir卷
    emptyDir: {}

Init容器(初始化容器)

Init容器在主容器启动前运行,用于执行初始化任务:

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  initContainers:                # Init容器(按顺序执行)
  - name: init-db                 # 第一个Init容器:等待数据库就绪
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z mysql 3306; do echo "waiting for mysql"; sleep 2; done;']
  
  - name: init-config             # 第二个Init容器:下载配置文件
    image: busybox:1.36
    command: ['sh', '-c', 'wget -O /config/app.conf http://config-server/app.conf']
    volumeMounts:
    - name: config-vol
      mountPath: /config
  
  containers:                    # 主容器(所有Init容器成功后启动)
  - name: app
    image: myapp:1.0.0
    volumeMounts:
    - name: config-vol
      mountPath: /etc/app/conf.d
  
  volumes:
  - name: config-vol
    emptyDir: {}

静态Pod(Static Pod)

静态Pod由kubelet直接管理,不经过API Server。配置文件放在节点的/etc/kubernetes/manifests/目录下:

# /etc/kubernetes/manifests/static-pod.yaml
# 放在此目录的YAML文件会被kubelet自动创建为Pod
apiVersion: v1
kind: Pod
metadata:
  name: static-web
spec:
  containers:
  - name: web
    image: nginx:1.25

4.5 本地K8s环境搭建

方案对比

工具特点适用场景
Minikube功能全面,支持插件,单节点学习和开发
Kind用Docker容器模拟节点,启动快CI/CD测试
k3s轻量级,二进制不到100MB边缘计算,资源受限环境
Docker Desktop内置K8s,一键启用macOS/Windows开发
kubeadm官方集群引导工具生产环境集群搭建

Minikube安装与使用

# 安装Minikube (Linux)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# 启动集群
minikube start --driver=docker --kubernetes-version=v1.28.0

# 查看集群状态
minikube status

# 打开Dashboard
minikube dashboard

# 停止集群
minikube stop

# 删除集群
minikube delete

Kind安装与使用

# 安装Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# 创建多节点集群
cat > kind-config.yaml <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane     # 控制平面节点
- role: worker            # 工作节点1
- role: worker            # 工作节点2
EOF

kind create cluster --config kind-config.yaml

k3s安装(轻量级)

# 安装k3s server(主节点)
curl -sfL https://get.k3s.io | sh -

# 查看节点
sudo k3s kubectl get nodes

# 获取join token
sudo cat /var/lib/rancher/k3s/server/node-token

# 在worker节点加入集群
curl -sfL https://get.k3s.io | K3S_URL=https://server-ip:6443 K3S_TOKEN=node-token sh -

4.6 kubectl命令大全

kubectl是与K8s API Server交互的命令行工具,是日常操作K8s的核心。

# ==================== 查看资源 ====================
kubectl get pods                      # 查看所有Pod
kubectl get pods -o wide              # 查看详细信息(含节点IP)
kubectl get pods -n kube-system       # 查看指定命名空间的Pod
kubectl get pods --all-namespaces     # 查看所有命名空间的Pod
kubectl get pods -l app=myapp         # 按标签过滤
kubectl get pods -w                   # 持续监视变化(watch)
kubectl get deployments               # 查看Deployment
kubectl get services                  # 查看Service
kubectl get nodes                     # 查看节点
kubectl get all                       # 查看所有资源

# ==================== 查看详情 ====================
kubectl describe pod my-pod           # 查看Pod详细信息(事件、调度、容器状态)
kubectl describe node node-1          # 查看节点详细信息(资源使用、污点)
kubectl describe deployment myapp     # 查看Deployment详细信息

# ==================== 创建资源 ====================
kubectl create deployment myapp --image=nginx:1.25 --replicas=3  # 命令式创建
kubectl apply -f deployment.yaml      # 声明式创建/更新(推荐)
kubectl apply -f ./manifests/         # 应用目录下所有YAML文件

# ==================== 删除资源 ====================
kubectl delete pod my-pod             # 删除Pod
kubectl delete -f deployment.yaml     # 按文件删除
kubectl delete deployment myapp       # 按名称删除Deployment
kubectl delete pods --all             # 删除所有Pod(慎用)

# ==================== 日志 ====================
kubectl logs my-pod                   # 查看Pod日志
kubectl logs -f my-pod                # 实时跟踪日志
kubectl logs --tail=100 my-pod        # 查看最后100行
kubectl logs --previous my-pod        # 查看上一个容器的日志(崩溃后排查)

# ==================== 执行命令 ====================
kubectl exec -it my-pod -- /bin/sh    # 进入Pod的容器
kubectl exec my-pod -- env            # 查看容器环境变量
kubectl exec my-pod -- ls /app        # 在容器中执行命令

# ==================== 端口转发 ====================
kubectl port-forward svc/myapp 8080:80  # 将本地8080转发到Service的80端口
kubectl port-forward pod/my-pod 8080:80 # 转发到Pod

# ==================== 扩缩容 ====================
kubectl scale deployment myapp --replicas=5   # 扩容到5个副本
kubectl autoscale deployment myapp --min=2 --max=10 --cpu-percent=80  # 自动扩缩容

# ==================== 滚动更新与回滚 ====================
kubectl rollout status deployment/myapp     # 查看更新状态
kubectl rollout history deployment/myapp    # 查看更新历史
kubectl rollout undo deployment/myapp       # 回滚到上一版本
kubectl rollout undo deployment/myapp --to-revision=2  # 回滚到指定版本
kubectl rollout restart deployment/myapp    # 重启Deployment

# ==================== 标签与注解 ====================
kubectl label pod my-pod env=prod          # 添加标签
kubectl label pod my-pod env-              # 删除标签
kubectl annotate pod my-pod owner=team-a   # 添加注解

# ==================== 集群管理 ====================
kubectl cluster-info                        # 查看集群信息
kubectl top nodes                           # 查看节点资源使用
kubectl top pods                            # 查看Pod资源使用
kubectl cordon node-1                       # 标记节点不可调度
kubectl drain node-1                        # 驱逐节点上的Pod
kubectl uncordon node-1                     # 恢复节点可调度

# ==================== 配置管理 ====================
kubectl config view                         # 查看kubeconfig
kubectl config use-context prod-cluster     # 切换集群上下文
kubectl config current-context              # 查看当前上下文

4.7 声明式配置与YAML清单文件

K8s使用YAML格式的清单文件(Manifest)来声明资源。理解YAML结构是使用K8s的基础。

YAML文件结构

# K8s YAML文件的通用结构
apiVersion: apps/v1          # 第一部分: API版本
kind: Deployment              # 第二部分: 资源类型
metadata:                     # 第三部分: 元数据
  name: myapp                 #   - 名称(在同一资源类型和命名空间内唯一)
  namespace: production       #   - 命名空间(可选,默认default)
  labels:                     #   - 标签(用于标识和选择)
    app: myapp
    tier: backend
  annotations:                #   - 注解(用于附加信息,不可用于选择)
    description: "My application deployment"
spec:                         # 第四部分: 期望状态规格(不同资源类型不同)
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0
        resources:
          requests:
            cpu: 250m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi

apply vs create

# create: 命令式创建,如果资源已存在会报错
kubectl create -f deployment.yaml

# apply: 声明式创建/更新,资源存在则更新,不存在则创建(推荐)
kubectl apply -f deployment.yaml

# 声明式的优势:
# 1. 幂等性:多次执行结果一致
# 2. 可追踪:K8s记录上次apply的配置,用于计算差异
# 3. 三方合并:支持本地修改与服务端变更的合并

使用kustomize管理配置

# kustomization.yaml - Kustomize配置
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# 基础资源
resources:
  - deployment.yaml
  - service.yaml
  - configmap.yaml

# 修改镜像版本
images:
  - name: myapp
    newTag: 1.2.0

# 添加公共标签
commonLabels:
  env: production
  team: backend

# 命名空间
namespace: production

# 配置生成
configMapGenerator:
  - name: app-config
    literals:
      - LOG_LEVEL=info
      - MAX_CONN=100

4.8 从Docker到K8s的思维转变

从Docker迁移到K8s,需要经历思维方式的根本转变:

维度Docker思维Kubernetes思维
操作方式命令式(run/exec/stop)声明式(apply YAML)
最小单位ContainerPod
管理对象单个容器Deployment/StatefulSet
网络端口映射Service + Ingress
存储VolumePV/PVC/StorageClass
配置环境变量/文件ConfigMap/Secret
扩缩容docker run 多个修改replicas
更新停旧启新滚动更新(自动)
故障恢复手动重启自动重启/迁移
发现手动配置IPDNS自动发现
概念映射关系:

Docker Compose            →  Kubernetes
─────────────────────────────────────────
service                   →  Deployment + Service
container                 →  Pod (container in Pod)
docker-compose.yml        →  kustomization.yaml / Helm Chart
volumes                   →  PersistentVolume + PVC
networks                  →  Network Namespace + CNI
environment               →  ConfigMap + Secret
depends_on                →  initContainers + readinessProbe
healthcheck               →  livenessProbe + readinessProbe
restart: always           →  restartPolicy: Always (默认)
replicas                  →  spec.replicas

本章建立了对Kubernetes的整体认知。接下来,我们将深入学习K8s的工作负载管理。


第五章 Kubernetes工作负载管理

5.1 Deployment(部署无状态应用)

Deployment是K8s中最常用的工作负载控制器,用于管理无状态应用。它通过管理ReplicaSet来控制Pod副本数量,并提供滚动更新和回滚能力。

# deployment.yaml - 生产级Deployment配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app                          # Deployment名称
  namespace: production                  # 命名空间
  labels:
    app: web
    tier: frontend
spec:
  replicas: 3                            # 期望副本数
  strategy:                              # 更新策略
    type: RollingUpdate                  # 滚动更新(默认)
    rollingUpdate:
      maxSurge: 1                        # 更新过程中最多超出期望副本数1个
      maxUnavailable: 0                  # 更新过程中最多不可用0个(零停机)
  # type: Recreate                      # 重建更新(先删全部旧Pod再建新Pod)
  
  minReadySeconds: 10                    # Pod就绪后至少运行10秒才算可用
  
  revisionHistoryLimit: 10               # 保留10个历史版本(用于回滚)
  
  progressDeadlineSeconds: 300           # 更新超时时间300秒
  
  selector:                              # 标签选择器
    matchLabels:
      app: web
  template:                              # Pod模板
    metadata:
      labels:
        app: web
        version: v1                      # 版本标签(用于金丝雀发布)
    spec:
      containers:
      - name: web
        image: registry.example.com/web:v1.0.0
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        
        # 环境变量
        env:
        - name: APP_ENV
          value: "production"
        - name: DB_HOST                  # 从ConfigMap引用
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db_host
        - name: DB_PASSWORD              # 从Secret引用
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
        
        # 资源请求与限制
        resources:
          requests:                      # 调度依据,保证最低资源
            cpu: "250m"                  # 250 millicores = 0.25核
            memory: "256Mi"
            ephemeral-storage: "1Gi"     # 临时存储请求
          limits:                        # 硬限制,超过会被限制或杀死
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "2Gi"
        
        # 存活探针:检查容器是否健康,失败则重启容器
        livenessProbe:
          httpGet:
            path: /health                # 健康检查路径
            port: 8080
            httpHeaders:
            - name: Custom-Header
              value: "health-check"
          initialDelaySeconds: 30        # 容器启动后30秒开始检查
          periodSeconds: 10              # 每10秒检查一次
          timeoutSeconds: 5              # 超时5秒
          failureThreshold: 3            # 连续3次失败才判定不健康
          successThreshold: 1            # 1次成功即恢复健康
        
        # 就绪探针:检查容器是否准备好接收流量,失败则从Service端点移除
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
          failureThreshold: 3
        
        # 启动探针:检查容器是否已启动完成(启动期间禁用liveness/readiness)
        startupProbe:
          httpGet:
            path: /startup
            port: 8080
          failureThreshold: 30           # 最多检查30次(30*10s=5分钟启动时间)
          periodSeconds: 10
        
        # 生命周期钩子
        lifecycle:
          postStart:                     # 容器创建后执行
            exec:
              command: ["/bin/sh", "-c", "echo 'Container started' > /tmp/start.log"]
          preStop:                       # 容器终止前执行(优雅关闭)
            exec:
              command: ["/bin/sh", "-c", "nginx -s quit; sleep 10"]
        
        # 安全上下文(容器级别)
        securityContext:
          runAsUser: 1000                # 以非root用户运行
          runAsGroup: 1000
          runAsNonRoot: true             # 强制非root
          readOnlyRootFilesystem: true   # 只读根文件系统
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]                # 删除所有Linux capabilities
        
        # 卷挂载
        volumeMounts:
        - name: config-volume
          mountPath: /etc/app/conf.d     # 配置文件挂载
          readOnly: true
        - name: tmp-volume
          mountPath: /tmp                # 可写临时目录
        - name: cache-volume
          mountPath: /var/cache
      
      # 卷定义
      volumes:
      - name: config-volume
        configMap:
          name: app-config               # 引用ConfigMap
      - name: tmp-volume
        emptyDir:                        # 临时空目录,Pod删除时消失
          medium: Memory                 # 使用内存(tmpfs)
          sizeLimit: 100Mi
      - name: cache-volume
        emptyDir: {}
      
      # Pod安全上下文(Pod级别)
      securityContext:
        fsGroup: 1000                    # 挂载卷的组ID
        seccompProfile:
          type: RuntimeDefault           # 使用默认seccomp配置
      
      # 节点选择器
      nodeSelector:
        node-role: worker                # 只调度到有此标签的节点
      
      # 亲和性与反亲和性
      affinity:
        # Pod反亲和性:避免同一应用的所有Pod调度到同一节点
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:  # 软约束(尽量满足)
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: web
              topologyKey: kubernetes.io/hostname  # 按节点分散
      
      # 容忍度:允许调度到有污点的节点
      tolerations:
      - key: "dedicated"
        operator: "Equal"
        value: "web"
        effect: "NoSchedule"
      
      # 终止宽限期
      terminationGracePeriodSeconds: 60  # 优雅终止等待60秒
      
      # 镜像拉取策略
      # Always: 总是拉取最新镜像
      # IfNotPresent: 本地不存在才拉取(默认)
      # Never: 从不拉取,只用本地镜像
      # (在container级别设置)

滚动更新与回滚

# 更新镜像版本(触发滚动更新)
kubectl set image deployment/web-app web=registry.example.com/web:v2.0.0

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

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

# 回滚到上一版本
kubectl rollout undo deployment/web-app

# 回滚到指定版本
kubectl rollout undo deployment/web-app --to-revision=2

# 暂停滚动更新(可用于金丝雀发布)
kubectl rollout pause deployment/web-app

# 恢复滚动更新
kubectl rollout resume deployment/web-app

# 重启Deployment(不改变镜像,重新创建Pod)
kubectl rollout restart deployment/web-app

5.2 ReplicaSet(副本控制)

ReplicaSet是Deployment的底层组件,负责维持指定数量的Pod副本。通常不直接使用ReplicaSet,而是通过Deployment间接管理。

# ReplicaSet定义(通常不需要手动创建,Deployment会自动管理)
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
spec:
  replicas: 3                            # 期望3个副本
  selector:
    matchLabels:
      tier: frontend                     # 管理带有tier=frontend标签的Pod
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: nginx
        image: nginx:1.25

5.3 StatefulSet(有状态应用部署)

StatefulSet用于管理有状态应用(如数据库、消息队列),提供稳定的网络标识、稳定的持久化存储和有序的部署/扩缩容。

# statefulset.yaml - MySQL StatefulSet部署
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql                           # StatefulSet名称
  namespace: production
spec:
  serviceName: mysql                   # 关联的Headless Service名称(必须)
  replicas: 3                          # 3个MySQL实例
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - name: mysql
          containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        # 每个Pod有唯一的hostname: mysql-0, mysql-1, mysql-2
        # 可以通过Pod序号进行差异化配置
        - name: MYSQL_ORDINAL
          value: "0"                    # 实际通过Downward API获取
        - name: POD_NAME                # 通过Downward API获取Pod名
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        volumeMounts:
        - name: data                   # 每个Pod有独立的PVC
          mountPath: /var/lib/mysql
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping", "-h", "localhost"]
          initialDelaySeconds: 30
          periodSeconds: 10
  # 持久化存储模板:每个Pod自动创建一个PVC
  volumeClaimTemplates:
  - metadata:
      name: data                        # PVC模板名称
    spec:
      accessModes: ["ReadWriteOnce"]    # 只能被一个节点以读写模式挂载
      storageClassName: fast-ssd        # 使用快速SSD存储类
      resources:
        requests:
          storage: 50Gi                 # 每个实例50GB存储
StatefulSet 的有序特性:

  部署顺序:   mysql-0 → mysql-1 → mysql-2 (顺序创建)
  删除顺序:   mysql-2 → mysql-1 → mysql-0 (逆序删除)
  
  稳定标识:
    - Pod名:     mysql-0, mysql-1, mysql-2 (固定不变)
    - DNS名:     mysql-0.mysql, mysql-1.mysql, mysql-2.mysql
    - 存储:      data-mysql-0, data-mysql-1, data-mysql-2 (各自独立PVC)
  
  vs Deployment:
    - Pod名随机: web-app-abc123, web-app-def456 (每次重建都变)
    - 共享或无存储
    - 并行创建/删除

5.4 DaemonSet(每个节点运行一个Pod)

DaemonSet确保在每个(或部分)节点上运行一个Pod副本。常用于日志收集、监控代理、网络插件等。

# daemonset.yaml - 日志收集DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit                     # DaemonSet名称
  namespace: kube-system
  labels:
    k8s-app: fluent-bit
spec:
  selector:
    matchLabels:
      k8s-app: fluent-bit
  template:
    metadata:
      labels:
        k8s-app: fluent-bit
    spec:
      # 容忍所有污点,确保在所有节点运行(包括Master)
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:2.2
        volumeMounts:
        - name: varlog                 # 挂载节点日志目录
          mountPath: /var/log
        - name: varlibdockercontainers # 挂载Docker容器日志
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: config
          mountPath: /fluent-bit/etc/
      volumes:
      - name: varlog
        hostPath:                      # 使用节点路径
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: config
        configMap:
          name: fluent-bit-config

5.5 Job与CronJob(批处理任务)

Job:运行一次性任务,确保Pod成功完成。

# job.yaml - 数据库迁移Job
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration                   # Job名称
spec:
  completions: 1                       # 需要1个Pod成功完成
  parallelism: 1                       # 并行运行1个Pod
  backoffLimit: 4                      # 失败重试次数(默认6)
  activeDeadlineSeconds: 300           # 最大运行时间300秒
  ttlSecondsAfterFinished: 3600        # 完成后3600秒自动清理
  template:
    spec:
      restartPolicy: OnFailure         # 失败时重启(OnFailure/Never)
      containers:
      - name: migration
        image: registry.example.com/migrator:1.0.0
        command: ["./migrate", "up"]   # 执行迁移命令
        env:
        - name: DB_HOST
          value: "mysql.production.svc.cluster.local"

CronJob:定时执行任务。

# cronjob.yaml - 定时备份CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup                      # CronJob名称
  namespace: production
spec:
  schedule: "0 2 * * *"               # Cron表达式:每天凌晨2点
  # ┌───────────── 分钟 (0 - 59)
  # │ ┌───────────── 小时 (0 - 23)
  # │ │ ┌───────────── 日 (1 - 31)
  # │ │ │ ┌───────────── 月 (1 - 12)
  # │ │ │ │ ┌───────────── 星期 (0 - 6) (0或7是周日)
  # │ │ │ │ │
  # * * * * *
  timeZone: Asia/Shanghai              # 时区
  startingDeadlineSeconds: 200         # 如果错过执行时间,200秒内仍可启动
  concurrencyPolicy: Forbid            # 禁止并发执行(Allow/Forbid/Replace)
  successfulJobsHistoryLimit: 3        # 保留3个成功的Job记录
  failedJobsHistoryLimit: 1            # 保留1个失败的Job记录
  jobTemplate:                         # Job模板
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: backup
            image: registry.example.com/backup-tool:1.0.0
            command:
            - /bin/sh
            - -c
            - |
              # 备份脚本
              DATE=$(date +%Y%m%d_%H%M%S)
              mysqldump -h mysql -u root -p${DB_PASSWORD} myapp > /backup/myapp_${DATE}.sql
              # 上传到对象存储
              aws s3 cp /backup/myapp_${DATE}.sql s3://my-backups/db/
              # 清理本地文件
              rm /backup/myapp_${DATE}.sql
              echo "Backup completed: myapp_${DATE}.sql"
            env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: root-password

5.6 Horizontal Pod Autoscaler(HPA自动扩缩容)

HPA根据CPU使用率、内存使用率或自定义指标自动调整Deployment的副本数。

# hpa.yaml - HPA自动扩缩容配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
  namespace: production
spec:
  scaleTargetRef:                      # 目标资源
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3                       # 最小副本数
  maxReplicas: 20                      # 最大副本数
  metrics:                             # 扩缩容指标
  # CPU使用率指标
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization              # 利用率类型
        averageUtilization: 70         # 平均CPU使用率超过70%则扩容
  # 内存使用率指标
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80         # 平均内存使用率超过80%则扩容
  # 自定义指标(需要Prometheus Adapter)
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second # 自定义指标:每秒请求数
      target:
        type: AverageValue
        averageValue: "1000"           # 每Pod平均1000 req/s
  behavior:                            # 扩缩容行为配置
    scaleUp:                           # 扩容行为
      stabilizationWindowSeconds: 0    # 不需要稳定窗口(立即扩容)
      policies:
      - type: Percent
        value: 100                     # 每次最多扩容100%
        periodSeconds: 15
      - type: Pods
        value: 4                       # 或每次最多扩容4个Pod
        periodSeconds: 15
      selectPolicy: Max                # 选择更激进的策略
    scaleDown:                         # 缩容行为
      stabilizationWindowSeconds: 300  # 5分钟稳定窗口后才缩容
      policies:
      - type: Percent
        value: 10                      # 每次最多缩容10%
        periodSeconds: 60
# 查看HPA状态
kubectl get hpa

# 输出示例:
# NAME          REFERENCE              TARGETS   MINPODS   MAXPODS   REPLICAS
# web-app-hpa   Deployment/web-app    45%/70%   3         20        3
# (当前CPU 45%,目标70%,当前3个副本)

5.7 Pod安全策略(Pod Security Standards)

K8s从1.25开始使用Pod Security Standards替代Pod Security Policy,通过命名空间标签来强制安全策略。

# 通过命名空间标签设置Pod安全标准
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # Pod Security Standards 模式: privileged | baseline | restricted
    # privileged: 不限制(最宽松,不推荐)
    # baseline: 防止已知特权提升(中等,推荐)
    # restricted: 严格限制(最安全,推荐用于生产)
    
    pod-security.kubernetes.io/enforce: restricted     # 强制执行restricted
    pod-security.kubernetes.io/enforce-version: latest
    
    pod-security.kubernetes.io/audit: restricted       # 审计违规
    pod-security.kubernetes.io/audit-version: latest
    
    pod-security.kubernetes.io/warn: restricted        # 警告违规
    pod-security.kubernetes.io/warn-version: latest

restricted模式的限制

restricted模式要求所有Pod必须:

  • 设置 runAsNonRoot: true
  • 不使用特权容器 (privileged: true 禁止)
  • 不使用hostNetwork、hostPID、hostIPC
  • 删除所有Linux capabilities
  • 使用seccomp profile
  • 容器以非root用户和非root组运行

5.8 资源请求与限制(requests/limits)

资源管理是K8s调度的核心。正确设置资源请求和限制对集群稳定性至关重要。

# 资源配置示例
spec:
  containers:
  - name: app
    resources:
      # requests: 调度依据,K8s根据requests决定Pod调度到哪个节点
      # requests值表示该Pod需要的最低资源保证
      requests:
        cpu: "500m"           # 500 millicores = 0.5个CPU核心
        memory: "512Mi"       # 512 Mebibytes
        ephemeral-storage: "1Gi"  # 1 Gibibyte 临时存储
      
      # limits: 硬上限,容器使用的资源不会超过此值
      # CPU: 超过limit会被限流(throttling),不会被杀死
      # Memory: 超过limit会被OOM Killed
      # ephemeral-storage: 超过limit会被驱逐
      limits:
        cpu: "1000m"          # 1个CPU核心
        memory: "1Gi"         # 1 Gibibyte
        ephemeral-storage: "2Gi"

资源单位说明

资源单位说明
CPUm(millicore)1000m = 1核CPU。500m = 0.5核
CPU整数1 = 1核, 2 = 2核
MemoryKi/Mi/Gi/Ti二进制单位(1024进制)
MemoryK/M/G/T十进制单位(1000进制,不推荐)
ephemeral-storageKi/Mi/Gi同Memory

资源QoS等级

K8s根据资源配置将Pod分为三个QoS等级,在节点资源不足时按等级决定驱逐顺序:

QoS等级(从高到低):

1. Guaranteed (保证级) - 最高优先级,最后被驱逐
   条件: requests == limits (CPU和Memory都设置了且相等)
   示例:
     requests: cpu=500m, memory=512Mi
     limits:   cpu=500m, memory=512Mi

2. Burstable (突发级) - 中等优先级
   条件: 至少设置了一个request但不满足Guaranteed条件
   示例:
     requests: cpu=250m, memory=256Mi
     limits:   cpu=500m, memory=512Mi

3. BestEffort (尽力级) - 最低优先级,最先被驱逐
   条件: 没有设置任何requests和limits

5.9 完整应用部署YAML示例

以下是一个包含Deployment、Service、ConfigMap、Secret、HPA的完整应用部署:

# complete-app-deployment.yaml
# 一个完整的生产级应用部署(单文件多资源)

---
# ConfigMap: 应用配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  DB_HOST: "mysql.production.svc.cluster.local"
  DB_PORT: "3306"
  REDIS_HOST: "redis.production.svc.cluster.local"
  REDIS_PORT: "6379"
  nginx.conf: |
    worker_processes auto;
    events { worker_connections 1024; }
    http {
      upstream backend { server 127.0.0.1:8080; }
      server {
        listen 80;
        location / { proxy_pass http://backend; }
      }
    }

---
# Secret: 敏感信息
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  namespace: production
type: Opaque
stringData:                        # 使用stringData可直接写明文(创建时自动编码)
  DB_PASSWORD: "secure-password-here"
  JWT_SECRET: "jwt-secret-key"
  API_KEY: "api-key-value"

---
# Deployment: 应用部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: registry.example.com/myapp:v1.0.0
        ports:
        - containerPort: 8080
          name: http
        envFrom:
        - configMapRef:
            name: app-config       # 批量注入ConfigMap中的所有键值
        envFrom:
        - secretRef:
            name: app-secrets      # 批量注入Secret中的所有键值
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:
          httpGet:
            path: /health
            port: http
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: http
          initialDelaySeconds: 5
          periodSeconds: 5
        lifecycle:
          preStop:
            exec:
              command: ["sleep", "15"]  # 等待连接排空
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d
          readOnly: true
      volumes:
      - name: nginx-config
        configMap:
          name: app-config
      terminationGracePeriodSeconds: 60

---
# Service: 服务暴露
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: production
spec:
  selector:
    app: myapp
  ports:
  - name: http
    port: 80                       # Service端口
    targetPort: http               # Pod端口(引用名称)
    protocol: TCP
  type: ClusterIP

---
# HPA: 自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 3
  maxReplicas: 15
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

本章详细介绍了K8s的各种工作负载类型及其配置。接下来,我们将学习K8s的服务与网络。


第六章 Kubernetes服务与网络

6.1 Service类型

K8s Service是将运行在一组Pod上的应用暴露为网络服务的抽象。Service有四种类型:

Service 类型对比:

  ┌─────────────────────────────────────────────────────────────┐
  │                     集群外部                                │
  │                                                             │
  │  ┌──────────┐  ┌──────────┐  ┌───────────────────────────┐│
  │  │External- │  │  Load    │  │     NodePort              ││
  │  │Name      │  │Balancer  │  │  (NodeIP:NodePort)        ││
  │  │(CNAME)   │  │(云LB)    │  │                           ││
  │  └────┬─────┘  └────┬─────┘  └────────────┬──────────────┘│
  │       │              │                     │               │
  └───────┼──────────────┼─────────────────────┼───────────────┘
          │              │                     │
  ┌───────┼──────────────┼─────────────────────┼───────────────┐
  │       │     集群内部  │                     │               │
  │       │              │                     │               │
  │  ┌────┴──────────────┴─────────────────────┴────────────┐  │
  │  │                 ClusterIP                            │  │
  │  │              (集群内部虚拟IP)                          │  │
  │  │                                                      │  │
  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐              │  │
  │  │  │  Pod 1  │  │  Pod 2  │  │  Pod 3  │              │  │
  │  │  └─────────┘  └─────────┘  └─────────┘              │  │
  │  └──────────────────────────────────────────────────────┘  │
  └────────────────────────────────────────────────────────────┘

ClusterIP(默认)

仅在集群内部可访问,是最常用的Service类型。

apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP                    # 默认类型
  selector:
    app: api
  ports:
  - port: 80                         # Service端口
    targetPort: 8080                 # Pod容器端口
    protocol: TCP
    name: http

NodePort

在每个节点上开放一个端口,将外部流量转发到Service。

apiVersion: v1
kind: Service
metadata:
  name: api-nodeport
spec:
  type: NodePort                     # NodePort类型
  selector:
    app: api
  ports:
  - port: 80                         # Service端口(集群内访问)
    targetPort: 8080                 # Pod端口
    nodePort: 30080                  # 节点端口(30000-32767),不指定则自动分配
    protocol: TCP

LoadBalancer

在NodePort基础上,通过云提供商创建外部负载均衡器。

apiVersion: v1
kind: Service
metadata:
  name: api-lb
  annotations:
    # 云提供商特定注解
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:..."
spec:
  type: LoadBalancer                 # LoadBalancer类型
  selector:
    app: api
  ports:
  - port: 443                        # 对外HTTPS端口
    targetPort: 8080                 # Pod端口
    protocol: TCP
  loadBalancerIP: 203.0.113.10       # 指定外部IP(可选)

Headless Service(无头服务)

不分配ClusterIP,直接返回Pod IP。用于StatefulSet。

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None                    # Headless Service:不分配ClusterIP
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
# DNS查询会直接返回各Pod的IP:
# mysql-0.mysql-headless → 10.0.1.1
# mysql-1.mysql-headless → 10.0.1.2
# mysql-2.mysql-headless → 10.0.1.3

6.2 Service工作原理(kube-proxy)

kube-proxy是运行在每个节点上的网络代理,负责实现Service的负载均衡和流量转发。

kube-proxy 工作模式:

  1. iptables模式(默认):
     ┌──────────────────────────────────────┐
     │              Pod请求                   │
     │        curl http://api-service:80     │
     │              │                        │
     │     DNS解析: api-service → 10.96.0.10 │
     │              │                        │
     │     iptables规则:                     │
     │     10.96.0.10:80 → DNAT →           │
     │       10.0.1.1:8080 (Pod1, 33%)      │
     │       10.0.1.2:8080 (Pod2, 33%)      │
     │       10.0.1.3:8080 (Pod3, 33%)      │
     │              │                        │
     │     随机选择一个Pod IP转发              │
     └──────────────────────────────────────┘

  2. ipvs模式(高性能,推荐大规模集群):
     使用Linux IPVS(内核级负载均衡)
     支持更多调度算法: rr(轮询)/lc(最少连接)/sh(源地址哈希)等
     性能优于iptables,适合大规模Service
# 查看kube-proxy模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

# 切换为ipvs模式(修改kube-proxy ConfigMap)
kubectl edit configmap kube-proxy -n kube-system
# 修改:
#   config.conf: |
#     mode: "ipvs"            # 改为ipvs
#     ipvs:
#       scheduler: "lc"       # 最少连接调度

# 重启kube-proxy Pod使配置生效
kubectl rollout restart daemonset kube-proxy -n kube-system

6.3 Ingress与Ingress Controller

Ingress是K8s中管理外部HTTP/HTTPS访问的API对象,提供基于域名和路径的路由、TLS终止等功能。

Ingress 工作原理:

  外部用户
     │
     │ https://api.example.com/v1/users
     │ https://api.example.com/v2/users
     │ https://admin.example.com/
     ↓
  ┌──────────────────────────┐
  │   Cloud Load Balancer    │
  │   (TCP 443 → NodePort)   │
  └───────────┬──────────────┘
              │
  ┌───────────┴──────────────┐
  │   Ingress Controller     │
  │      (Nginx/Traefik)     │
  │                          │
  │  路由规则:                │
  │  api.example.com/v1/* →  │ api-v1-service (ClusterIP)
  │  api.example.com/v2/* →  │ api-v2-service (ClusterIP)
  │  admin.example.com/*  →  │ admin-service  (ClusterIP)
  └──────────────────────────┘
# ingress.yaml - Ingress路由配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: production
  annotations:
    # Nginx Ingress Controller注解
    nginx.ingress.kubernetes.io/ssl-redirect: "true"       # 强制HTTPS
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"     # 上传文件大小限制
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10" # 连接超时
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"   # 发送超时
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"   # 读取超时
    # 限流注解
    nginx.ingress.kubernetes.io/limit-rps: "100"            # 每秒100请求
    nginx.ingress.kubernetes.io/limit-connections: "50"     # 最大50连接
    # CORS注解
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
spec:
  ingressClassName: nginx            # 指定Ingress Controller
  tls:                               # TLS配置
  - hosts:
    - api.example.com
    - admin.example.com
    secretName: tls-secret           # TLS证书Secret
  rules:
  # API v1路由
  - host: api.example.com
    http:
      paths:
      - path: /v1                    # 路径前缀匹配
        pathType: Prefix
        backend:
          service:
            name: api-v1-service     # 后端Service
            port:
              number: 80
      # API v2路由
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-v2-service
            port:
              number: 80
      # 默认路由(其他路径)
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-default-service
            port:
              number: 80
  
  # Admin路由
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 80

安装Nginx Ingress Controller

# 安装Nginx Ingress Controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.service.type=LoadBalancer \
  --set controller.replicaCount=2

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

6.4 网络策略(Network Policy)

Network Policy用于控制Pod之间的网络通信,实现命名空间和Pod级别的网络隔离。

# network-policy.yaml - 网络策略配置
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-network-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api                        # 策略应用于api Pod
  policyTypes:
  - Ingress                           # 入站策略
  - Egress                            # 出站策略
  ingress:
  # 允许来自web Pod的8080端口访问
  - from:
    - podSelector:
        matchLabels:
          app: web                    # 只允许web标签的Pod访问
    ports:
    - protocol: TCP
      port: 8080
  
  # 允许来自特定命名空间的访问
  - from:
    - namespaceSelector:
        matchLabels:
          team: backend               # 只允许backend团队的命名空间访问
    ports:
    - protocol: TCP
      port: 8080
  
  # 允许来自指定IP段的访问
  - from:
    - ipBlock:
        cidr: 10.0.0.0/8             # 允许10.0.0.0/8网段访问
        except:
        - 10.0.1.0/24                # 但排除10.0.1.0/24
    ports:
    - protocol: TCP
      port: 8080
  
  egress:
  # 允许访问DNS(UDP 53)
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
  
  # 允许访问数据库
  - to:
    - podSelector:
        matchLabels:
          app: mysql
    ports:
    - protocol: TCP
      port: 3306
  
  # 允许访问外部HTTPS
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 443

---
# 默认拒绝所有入站流量(安全基线)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}                    # 空选择器:应用于命名空间内所有Pod
  policyTypes:
  - Ingress                          # 拒绝所有入站(无ingress规则=拒绝全部)

---
# 默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress                           # 拒绝所有出站

6.5 DNS与服务发现(CoreDNS)

K8s使用CoreDNS为集群提供DNS服务发现。每个Service都会自动获得一个DNS记录。

K8s DNS 命名规则:

  Service DNS名格式:
    <service-name>.<namespace>.svc.cluster.local

  示例:
    api-service.production.svc.cluster.local  → ClusterIP
    mysql.production.svc.cluster.local        → ClusterIP

  Headless Service DNS:
    mysql-0.mysql.production.svc.cluster.local → Pod IP (10.0.1.1)
    mysql-1.mysql.production.svc.cluster.local → Pod IP (10.0.1.2)

  Pod DNS(默认):
    10-0-1-1.default.pod.cluster.local → 10.0.1.1
# 在Pod中测试DNS解析
kubectl exec -it my-pod -- nslookup api-service.production
# Name:    api-service.production.svc.cluster.local
# Address: 10.96.0.10

# 使用全限定域名访问
kubectl exec -it my-pod -- curl http://api-service.production.svc.cluster.local:80

# 同命名空间内可使用短名
kubectl exec -it my-pod -- curl http://api-service:80

6.6 Service Mesh简介(Istio, Linkerd)

Service Mesh(服务网格)为微服务提供流量管理、可观测性和安全能力,而无需修改应用代码。

Service Mesh 架构(Istio):

  ┌──────────────────────────────────────────────────┐
  │                 控制平面 (Istiod)                  │
  │  ┌─────────┐  ┌──────────┐  ┌────────────────┐ │
  │  │ Pilot   │  │ Citadel  │  │ Galley         │ │
  │  │流量管理  │  │安全/TLS  │  │配置验证         │ │
  │  └─────────┘  └──────────┘  └────────────────┘ │
  └───────────────────────┬──────────────────────────┘
                          │ 下发配置
  ┌───────────────────────┴──────────────────────────┐
  │              数据平面 (Envoy Sidecar)              │
  │                                                   │
  │  ┌─────────┐    ┌─────────┐    ┌─────────┐      │
  │  │  Pod A  │    │  Pod B  │    │  Pod C  │      │
  │  │┌───────┐│    │┌───────┐│    │┌───────┐│      │
  │  ││ App   ││    ││ App   ││    ││ App   ││      │
  │  ││ Envoy ││ ←→ ││ Envoy ││ ←→ ││ Envoy ││      │
  │  │└───────┘│    │└───────┘│    │└───────┘│      │
  │  └─────────┘    └─────────┘    └─────────┘      │
  │                                                   │
  │  功能: mTLS / 负载均衡 / 熔断 / 重试 / 追踪       │
  └───────────────────────────────────────────────────┘

Istio核心功能

# Istio流量管理示例: 金丝雀发布
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp-canary
spec:
  hosts:
  - myapp                        # 目标Service
  http:
  # 10%流量路由到v2(金丝雀)
  - match:
    - headers:
        x-canary:
          exact: "true"          # 带特定header的请求路由到v2
    route:
    - destination:
        host: myapp
        subset: v2               # 路由到v2子集
  # 90%流量路由到v1(稳定版)
  - route:
    - destination:
        host: myapp
        subset: v1
      weight: 90                 # 90%到v1
    - destination:
        host: myapp
        subset: v2
      weight: 10                 # 10%到v2(金丝雀)

---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: myapp-dest
spec:
  host: myapp
  subsets:
  - name: v1
    labels:
      version: v1                # 选择version=v1的Pod
  - name: v2
    labels:
      version: v2                # 选择version=v2的Pod
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100      # 最大连接数
      http:
        http1MaxPendingRequests: 50  # 最大等待请求
    outlierDetection:
      consecutive5xxErrors: 5    # 连续5次5xx错误则标记不健康
      interval: 10s
      baseEjectionTime: 30s      # 驱逐30秒

6.7 从Docker网络到K8s网络的迁移

概念DockerKubernetes
容器间通信同一网络内通过容器名同一命名空间内通过Service名
端口暴露-p 8080:80Service(ClusterIP/NodePort/LB)
DNSDocker内嵌DNSCoreDNS
网络隔离--networkNetworkPolicy
负载均衡无内置LBkube-proxy(Service LB)
外部路由Nginx/Traefik手动配置Ingress Controller
迁移示例:

Docker Compose:
  web:
    image: nginx
    ports:
      - "80:80"
    networks:
      - frontend
  api:
    image: api
    networks:
      - frontend
      - backend
    depends_on:
      - mysql

K8s等效配置:
  web → Deployment + Service(type: ClusterIP)
  api → Deployment + Service(type: ClusterIP)
  mysql → StatefulSet + Headless Service
  
  Pod间通过Service名通信:
    web → http://api:8080 (ClusterIP DNS)
    api → mysql-0.mysql:3306 (Headless Service DNS)
  
  外部访问通过Ingress:
    *.example.com → Ingress → web Service → web Pods

本章涵盖了K8s网络的核心知识。接下来,我们将学习K8s的存储与配置管理。


第七章 Kubernetes存储与配置

7.1 Volume类型详解

K8s提供了丰富的Volume类型,满足不同场景的存储需求。

# 各种Volume类型使用示例
apiVersion: v1
kind: Pod
metadata:
  name: volume-demo
spec:
  containers:
  - name: app
    image: nginx:1.25
    volumeMounts:
    - name: emptydir-vol
      mountPath: /cache
    - name: hostpath-vol
      mountPath: /host-data
    - name: configmap-vol
      mountPath: /etc/config
      readOnly: true
    - name: secret-vol
      mountPath: /etc/secrets
      readOnly: true
    - name: nfs-vol
      mountPath: /shared
    - name: pv-vol
      mountPath: /data
  
  volumes:
  # 1. emptyDir: 临时空目录,Pod生命周期内存在,Pod删除时消失
  - name: emptydir-vol
    emptyDir:
      medium: Memory              # 可选:使用内存(tmpfs)加速
      sizeLimit: 500Mi            # 限制大小
  
  # 2. hostPath: 使用节点上的目录(生产环境慎用,有安全风险)
  - name: hostpath-vol
    hostPath:
      path: /data/app             # 节点上的路径
      type: DirectoryOrCreate     # 目录不存在则创建
  
  # 3. configMap: 将ConfigMap数据挂载为文件
  - name: configmap-vol
    configMap:
      name: app-config            # ConfigMap名称
      items:                      # 可选:只挂载部分key
      - key: nginx.conf           # ConfigMap中的key
        path: default.conf        # 挂载后的文件名
      defaultMode: 0644           # 文件权限
  
  # 4. secret: 将Secret数据挂载为文件
  - name: secret-vol
    secret:
      secretName: db-secret       # Secret名称
      defaultMode: 0400           # 文件权限(仅owner可读)
  
  # 5. nfs: NFS共享存储
  - name: nfs-vol
    nfs:
      server: 192.168.1.200       # NFS服务器地址
      path: /export/share         # NFS共享路径
      readOnly: false
  
  # 6. persistentVolumeClaim: 持久化存储卷声明
  - name: pv-vol
    persistentVolumeClaim:
      claimName: app-pvc          # PVC名称

Volume类型对比

Volume类型生命周期是否持久化适用场景
emptyDirPod级临时缓存,多容器共享
hostPathNode级是(节点级)系统级访问(DaemonSet)
configMapPod级配置文件注入
secretPod级敏感信息注入
nfs外部多Pod共享数据
persistentVolumeClaim独立生产环境持久化

7.2 PersistentVolume与PersistentVolumeClaim

PV是集群中的存储资源,PVC是用户对存储的请求。这种分离使得存储的供应和使用解耦。

PV/PVC 工作流程:

  管理员                    用户(Pod开发者)
    │                          │
    │  创建PV                  │  创建PVC
    │  (声明存储资源)           │  (请求存储)
    │         │                │       │
    │         └────────────────┴───────┘
    │                    │
    │           K8s自动绑定PV和PVC
    │           (PVC请求 <= PV容量)
    │                    │
    │           Pod通过PVC使用存储
    │                    │
    │           Pod ←── PVC ←── PV ←── 实际存储

静态供应(手动创建PV)

# pv.yaml - 持久化卷
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-001
  labels:
    type: nfs
spec:
  capacity:
    storage: 50Gi                      # 存储容量
  accessModes:
  - ReadWriteMany                      # 多节点读写(RWX)
  # ReadWriteOnce (RWO): 单节点读写
  # ReadOnlyMany (ROX): 多节点只读
  # ReadWriteMany (RWX): 多节点读写
  # ReadWriteOncePod (RWOP): 单Pod读写(K8s 1.22+)
  persistentVolumeReclaimPolicy: Retain  # 回收策略
  # Retain: PVC删除后PV保留(需手动清理)
  # Delete: PVC删除后PV和底层存储自动删除
  # Recycle: 已废弃,清除数据后可再次绑定
  nfs:
    server: 192.168.1.200
    path: /export/pv-001
  storageClassName: nfs                # 存储类名称(用于匹配PVC)
# pvc.yaml - 持久化卷声明
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-pvc
  namespace: production
spec:
  accessModes:
  - ReadWriteMany                      # 需要的访问模式
  storageClassName: nfs                # 匹配存储类
  resources:
    requests:
      storage: 20Gi                    # 请求20GB(可绑定到50GB的PV)
  selector:                            # 可选:通过标签选择PV
    matchLabels:
      type: nfs

7.3 StorageClass动态存储供应

StorageClass允许动态创建PV,无需管理员手动创建。这是生产环境的推荐方式。

# storageclass.yaml - 存储类定义
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd                       # 存储类名称
provisioner: kubernetes.io/aws-ebs     # 存储供应器(云提供商)
# 常见供应器:
# kubernetes.io/aws-ebs       (AWS EBS)
# kubernetes.io/gce-pd        (GCE Persistent Disk)
# kubernetes.io/azure-disk    (Azure Disk)
# kubernetes.io/no-provisioner (本地存储,不动态供应)
# nfs.csi.k8s.io              (NFS CSI)
# csi.trident.netapp.io       (NetApp Trident)
parameters:
  type: gp3                            # AWS EBS卷类型
  fsType: ext4                         # 文件系统类型
  iops: "3000"                         # IOPS
  throughput: "125"                    # 吞吐量(MB/s)
reclaimPolicy: Delete                  # PVC删除时自动删除PV和存储
volumeBindingMode: WaitForFirstConsumer  # 延迟绑定,直到Pod调度后再创建
allowVolumeExpansion: true             # 允许在线扩容
# 使用StorageClass动态创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-pvc
  namespace: production
spec:
  accessModes:
  - ReadWriteOnce                      # EBS只支持RWO
  storageClassName: fast-ssd           # 指定StorageClass
  resources:
    requests:
      storage: 100Gi                   # 请求100GB存储

7.4 StatefulSet与持久化存储

StatefulSet通过volumeClaimTemplates为每个Pod自动创建独立的PVC:

# StatefulSet with volumeClaimTemplates
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        env:
        - name: PGDATA
          value: /var/lib/postgresql/data
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  # 每个Pod自动创建独立的PVC
  volumeClaimTemplates:
  - metadata:
      name: data
      # 生成的PVC名: data-postgres-0, data-postgres-1, data-postgres-2
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 100Gi
StatefulSet存储特点:

  Pod删除/重建后,PVC保持不变,数据持久化:
  
  postgres-0 ←── data-postgres-0 (PVC) ←── PV ←── 100GB SSD
  postgres-1 ←── data-postgres-1 (PVC) ←── PV ←── 100GB SSD
  postgres-2 ←── data-postgres-2 (PVC) ←── PV ←── 100GB SSD
  
  即使Pod迁移到其他节点,PVC仍然绑定原来的PV,数据不丢失

7.5 ConfigMap管理配置文件

ConfigMap是K8s中管理非敏感配置的核心资源。

# configmap.yaml - ConfigMap多种使用方式
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  # 1. 简单键值对(注入为环境变量)
  LOG_LEVEL: "debug"
  MAX_CONNECTIONS: "200"
  FEATURE_FLAG_NEW_UI: "true"
  
  # 2. 配置文件内容(挂载为文件)
  app.properties: |
    [database]
    host=mysql.production.svc.cluster.local
    port=3306
    pool_size=20
    
    [redis]
    host=redis.production.svc.cluster.local
    port=6379
    ttl=3600
    
    [logging]
    level=info
    format=json
  
  # 3. JSON配置
  config.json: |
    {
      "api": {
        "timeout": 30000,
        "retryCount": 3
      },
      "cache": {
        "enabled": true,
        "ttl": 600
      }
    }

ConfigMap的使用方式

# 在Pod中使用ConfigMap的多种方式
apiVersion: v1
kind: Pod
metadata:
  name: configmap-usage-demo
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    env:
    # 方式1: 单个环境变量引用
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL
    - name: MAX_CONN
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: MAX_CONNECTIONS
    
    # 方式2: 批量注入所有键值对为环境变量
    envFrom:
    - configMapRef:
        name: app-config
        prefix: APP_              # 加前缀: APP_LOG_LEVEL, APP_MAX_CONNECTIONS等
    
    volumeMounts:
    # 方式3: 挂载为文件
    - name: config-volume
      mountPath: /etc/app/configs  # 挂载目录
      readOnly: true
    # 方式4: 挂载为指定文件名(子路径)
    - name: config-volume
      mountPath: /app/config.json  # 挂载为指定文件
      subPath: config.json         # 使用ConfigMap中的config.json键
      readOnly: true
  
  volumes:
  - name: config-volume
    configMap:
      name: app-config
      defaultMode: 0644

ConfigMap热更新

# ConfigMap更新后,挂载为Volume的文件会自动更新(有延迟)
# 但环境变量不会更新(需要重启Pod)

# 更新ConfigMap
kubectl edit configmap app-config

# 手动触发Pod重启以加载新配置(环境变量方式)
kubectl rollout restart deployment myapp

7.6 Secret管理敏感信息

Secret用于存储密码、Token、证书等敏感数据。与ConfigMap不同,Secret的数据以Base64编码存储,且可以被配置为不写入磁盘。

# Secret的多种创建方式

# 方式1: kubectl命令创建
# kubectl create secret generic db-secret \
#   --from-literal=username=admin \
#   --from-literal=password=S3cur3P@ss \
#   --from-file=tls.crt=/path/to/cert.pem

# 方式2: YAML定义(data字段需要Base64编码)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: production
type: Opaque
data:
  username: YWRtaW4=                    # echo -n "admin" | base64
  password: UzNjdXIzUEBzcw==            # echo -n "S3cur3P@ss" | base64

---
# 方式3: YAML定义(stringData字段可直接写明文)
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  namespace: production
type: Opaque
stringData:
  DB_PASSWORD: "production-db-password"
  JWT_SECRET: "my-jwt-secret-key"
  API_KEY: "sk-xxxxxxxxxxxxx"

---
# 方式4: TLS证书Secret
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
  namespace: production
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi...           # Base64编码的证书
  tls.key: LS0tLS1CRUdJTi...           # Base64编码的私钥

Secret使用方式

spec:
  containers:
  - name: app
    env:
    # 方式1: 引用为环境变量
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password
    
    # 方式2: 批量引用
    envFrom:
    - secretRef:
        name: app-secrets
    
    volumeMounts:
    # 方式3: 挂载为文件
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
    
    # 方式4: 挂载为SSH密钥
    - name: ssh-key
      mountPath: /root/.ssh
      readOnly: true
  
  volumes:
  - name: secret-volume
    secret:
      secretName: app-secrets
      defaultMode: 0400                # 严格权限
  
  - name: ssh-key
    secret:
      secretName: ssh-key-secret
      defaultMode: 0600

7.7 从Docker Volume到K8s PVC的迁移

概念DockerKubernetes
存储声明docker volume createPVC
存储资源VolumePV
存储驱动--driverStorageClass + Provisioner
配置文件-v config.json:/app/ConfigMap Volume
密码/密钥-e PASSWORD=xxxSecret
临时存储tmpfsemptyDir (medium: Memory)
节点存储hostPathhostPath Volume
共享存储NFS VolumeNFS PV/PVC
# 迁移示例
# 
# Docker:
#   docker run -v /data/mysql:/var/lib/mysql \
#              -e MYSQL_ROOT_PASSWORD=secret \
#              mysql:8.0
#
# K8s等效配置:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD     # Secret替代环境变量密码
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        volumeMounts:
        - name: data                     # PVC替代hostPath
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 100Gi

7.8 存储方案选择指南

存储方案选择决策树:

  是否需要持久化?
  ├── 否 → emptyDir (临时存储)
  │       └── 需要高性能? → emptyDir + medium: Memory (tmpfs)
  │
  └── 是 → 需要多Pod共享?
          ├── 是 → ReadWriteMany (RWX) 存储
          │       ├── NFS PV
          │       ├── 云文件存储 (AWS EFS / Azure Files)
          │       └── CephFS / GlusterFS
          │
          └── 否 → ReadWriteOnce (RWO) 存储
                  ├── 云块存储 (AWS EBS / Azure Disk / GCE PD)
                  ├── 本地存储 (Local PV, 高性能)
                  └── CSI驱动 (各种第三方存储)

有状态应用选择:
  ┌──────────────┬───────────────────────────────┐
  │ 应用类型      │ 推荐存储方案                    │
  ├──────────────┼───────────────────────────────┤
  │ MySQL/PG     │ RWO块存储 + StatefulSet        │
  │ Redis        │ RWO块存储 + StatefulSet        │
  │ Kafka/ZK     │ RWO本地存储 + StatefulSet      │
  │ ElasticSearch│ RWO块存储 + StatefulSet        │
  │ 共享文件      │ RWX文件存储 + Deployment       │
  │ 静态网站      │ RWX对象存储/CDN                │
  └──────────────┴───────────────────────────────┘

本章详细讲解了K8s的存储与配置管理。接下来,我们将学习如何从Docker Compose迁移到Kubernetes。


第八章 从Docker Compose迁移到Kubernetes

8.1 为什么需要从Compose迁移到K8s

Docker Compose在开发环境中非常方便,但在生产环境中存在明显局限:

限制Docker ComposeKubernetes
单机限制仅限单节点多节点集群
高可用无内置HA多Master HA
自动扩缩不支持HPA/VPA
自愈能力有限(restart策略)完整自愈
滚动更新需手动操作原生支持
服务发现仅容器名CoreDNS + Service
网络隔离基础网络NetworkPolicy
存储管理基础VolumePV/PVC/StorageClass
生态支持有限CNCF庞大生态
迁移时机判断:

  何时应该迁移到K8s?
  ├── 集群规模增长到多台服务器
  ├── 需要高可用和故障自动恢复
  ├── 需要自动扩缩容
  ├── 微服务数量增多,需要精细管理
  ├── 需要蓝绿部署、金丝雀发布等高级部署策略
  ├── 需要统一的可观测性方案
  └── 团队有K8s运维能力或愿意学习
  
  何时应该继续使用Compose?
  ├── 单机部署足够
  ├── 团队规模小,无专职运维
  ├── 应用架构简单
  └── K8s学习成本高于收益

8.2 使用Kompose工具自动转换

Kompose是一个将Docker Compose文件转换为K8s资源的工具。

# 安装Kompose
curl -L https://github.com/kubernetes/kompose/releases/download/v1.31.2/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv kompose /usr/local/bin/

# 基本转换
kompose convert -f docker-compose.yml

# 指定输出目录
kompose convert -f docker-compose.yml -o k8s-manifests/

# 转换为Helm Chart
kompose convert -f docker-compose.yml -c

# 转换并指定镜像仓库
kompose convert -f docker-compose.yml --controller deployment

# 转换并生成Service
kompose convert -f docker-compose.yml --service-type LoadBalancer

# 直接部署到K8s集群
kompose up -f docker-compose.yml

Kompose转换示例

原始docker-compose.yml:

version: "3.8"
services:
  web:
    image: nginx:1.25
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - api
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 10s
      timeout: 5s
  
  api:
    image: myapp/api:1.0.0
    ports:
      - "8080:8080"
    environment:
      DB_HOST: postgres
      REDIS_HOST: redis
    volumes:
      - api-data:/data
  
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_PASSWORD: secret
    volumes:
      - pg-data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

volumes:
  api-data:
  pg-data:
  redis-data:

Kompose自动生成的K8s资源(简化):

# 执行转换
kompose convert -f docker-compose.yml

# 生成的文件:
# web-deployment.yaml
# web-service.yaml
# api-deployment.yaml
# api-service.yaml
# postgres-deployment.yaml
# postgres-service.yaml
# redis-deployment.yaml
# redis-service.yaml

Kompose的局限性

Kompose虽然方便,但存在以下局限:

  • 无法自动转换所有Compose特性(如depends_on的等待逻辑)
  • 生成的YAML需要手动优化(添加探针、资源限制等)
  • 无法自动区分无状态(Deployment)和有状态(StatefulSet)应用
  • 网络配置需要手动调整

因此,Kompose适合作为迁移的起点,但不应该完全依赖自动转换。

8.3 手动迁移步骤详解

手动迁移是更可靠的方式,能够确保每个组件都正确配置。

迁移步骤:

  Step 1: 分析Compose文件,梳理服务清单
  Step 2: 区分无状态/有状态服务
  Step 3: 创建命名空间
  Step 4: 创建ConfigMap和Secret
  Step 5: 创建有状态服务的PV/PVC
  Step 6: 创建Deployment/StatefulSet
  Step 7: 创建Service
  Step 8: 创建Ingress
  Step 9: 添加探针和资源限制
  Step 10: 测试验证

8.4 网络配置迁移

# Docker Compose网络配置:
#   networks:
#     frontend:
#     backend:
#   web:
#     networks: [frontend]
#   api:
#     networks: [frontend, backend]
#   postgres:
#     networks: [backend]

# K8s网络迁移方案:
# 1. 不同命名空间实现网络隔离
# 2. NetworkPolicy控制Pod间通信

---
# 命名空间隔离
apiVersion: v1
kind: Namespace
metadata:
  name: app-frontend

---
apiVersion: v1
kind: Namespace
metadata:
  name: app-backend

---
# NetworkPolicy: frontend命名空间只允许访问backend的API端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-access-policy
  namespace: app-backend
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: app-frontend
    ports:
    - protocol: TCP
      port: 8080

8.5 存储配置迁移

# Docker Compose存储:
#   volumes:
#     - pg-data:/var/lib/postgresql/data
#     - ./config:/app/config:ro
#
# K8s存储迁移:
#   named volume → PVC
#   bind mount → ConfigMap/Secret/hostPath

---
# PostgreSQL持久化存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: app-backend
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 50Gi

8.6 环境变量迁移(ConfigMap/Secret)

# Docker Compose环境变量:
#   environment:
#     DB_HOST: postgres
#     DB_PASSWORD: secret
#     DEBUG: "true"
#
# K8s迁移: 非敏感→ConfigMap, 敏感→Secret

---
# ConfigMap: 非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
  namespace: app-backend
data:
  DB_HOST: "postgres.app-backend.svc.cluster.local"
  DEBUG: "true"
  LOG_LEVEL: "info"

---
# Secret: 敏感配置
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
  namespace: app-backend
type: Opaque
stringData:
  DB_PASSWORD: "production-secret-password"
  JWT_SECRET: "jwt-secret-key"

8.7 健康检查迁移(liveness/readiness probe)

# Docker Compose健康检查:
#   healthcheck:
#     test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
#     interval: 10s
#     timeout: 5s
#     retries: 3
#
# K8s探针迁移:
#   healthcheck → livenessProbe + readinessProbe

spec:
  containers:
  - name: api
    image: myapp/api:1.0.0
    # 存活探针(替代healthcheck)
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30      # 对应start_period
      periodSeconds: 10            # 对应interval
      timeoutSeconds: 5            # 对应timeout
      failureThreshold: 3          # 对应retries
    
    # 就绪探针(Compose没有,K8s新增)
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3

8.8 迁移案例: Web应用从Compose到K8s

以下是一个完整的Web应用从Docker Compose迁移到K8s的实战案例。

原始Docker Compose文件

# docker-compose.yml - 待迁移的原始文件
version: "3.8"

services:
  # Nginx反向代理
  nginx:
    image: nginx:1.25
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - web
    restart: always
  
  # Web前端
  web:
    image: myapp/web:1.0.0
    ports:
      - "3000:3000"
    environment:
      API_URL: "http://api:8080"
    deploy:
      replicas: 3
    restart: always
  
  # API后端
  api:
    image: myapp/api:1.0.0
    ports:
      - "8080:8080"
    environment:
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_NAME: myapp
      DB_USER: myapp
      DB_PASSWORD: secretpass
      REDIS_HOST: redis
      REDIS_PORT: "6379"
    deploy:
      replicas: 2
    restart: always
  
  # PostgreSQL数据库
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: secretpass
    volumes:
      - pg-data:/var/lib/postgresql/data
    restart: always
  
  # Redis缓存
  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    restart: always

volumes:
  pg-data:
  redis-data:

迁移后的K8s YAML文件

# k8s-migrated.yaml - 迁移后的完整K8s配置

# ==================== 命名空间 ====================
apiVersion: v1
kind: Namespace
metadata:
  name: myapp-prod
  labels:
    pod-security.kubernetes.io/enforce: baseline

---
# ==================== ConfigMap ====================
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: myapp-prod
data:
  API_URL: "http://api:8080"
  DB_HOST: "postgres"
  DB_PORT: "5432"
  DB_NAME: "myapp"
  DB_USER: "myapp"
  REDIS_HOST: "redis"
  REDIS_PORT: "6379"
  # Nginx配置
  nginx.conf: |
    worker_processes auto;
    events { worker_connections 1024; }
    http {
      upstream web_backend { server web:3000; }
      upstream api_backend { server api:8080; }
      server {
        listen 80;
        server_name _;
        location /api/ { proxy_pass http://api_backend/; }
        location / { proxy_pass http://web_backend; }
      }
    }

---
# ==================== Secret ====================
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: myapp-prod
type: Opaque
stringData:
  POSTGRES_PASSWORD: "secretpass"
  DB_PASSWORD: "secretpass"

---
# ==================== PostgreSQL StatefulSet ====================
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: myapp-prod
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        env:
        - name: POSTGRES_DB
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: DB_NAME
        - name: POSTGRES_USER
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: DB_USER
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: POSTGRES_PASSWORD
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: pg-data
          mountPath: /var/lib/postgresql/data
        livenessProbe:
          exec:
            command: ["pg_isready", "-U", "myapp"]
          initialDelaySeconds: 30
          periodSeconds: 10
  volumeClaimTemplates:
  - metadata:
      name: pg-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 50Gi

---
# PostgreSQL Headless Service
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: myapp-prod
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
  - port: 5432

---
# ==================== Redis Deployment ====================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: myapp-prod
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: redis-data
          mountPath: /data
        livenessProbe:
          exec:
            command: ["redis-cli", "ping"]
          initialDelaySeconds: 10
          periodSeconds: 10
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: redis-pvc

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
  namespace: myapp-prod
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: myapp-prod
spec:
  selector:
    app: redis
  ports:
  - port: 6379

---
# ==================== API Deployment ====================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: myapp-prod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: myapp/api:1.0.0
        ports:
        - containerPort: 8080
        envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: db-secret
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"

---
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: myapp-prod
spec:
  selector:
    app: api
  ports:
  - port: 8080

---
# ==================== Web Deployment ====================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: myapp-prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: myapp/web:1.0.0
        ports:
        - containerPort: 3000
        envFrom:
        - configMapRef:
            name: app-config
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: myapp-prod
spec:
  selector:
    app: web
  ports:
  - port: 3000

---
# ==================== Ingress (替代Nginx) ====================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: myapp-prod
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.example.com
    secretName: tls-secret
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 3000

8.9 迁移常见问题与解决方案

问题原因解决方案
服务间依赖Compose的depends_on使用init容器等待依赖就绪
数据迁移Compose Volume数据使用工具导出/导入数据
DNS差异Compose用容器名K8s用Service名(格式一致)
端口冲突Compose端口映射K8s用Service端口
配置更新Compose需重启ConfigMap Volume自动更新
日志查看docker-compose logskubectl logs
进入容器docker-compose execkubectl exec
健康检查healthchecklivenessProbe + readinessProbe
# 迁移后的常用操作对比

# Docker Compose:
docker-compose up -d              # 启动
docker-compose ps                 # 查看状态
docker-compose logs -f web        # 查看日志
docker-compose exec web sh        # 进入容器
docker-compose scale web=5        # 扩容
docker-compose down               # 停止

# Kubernetes:
kubectl apply -f k8s/             # 部署
kubectl get pods -n myapp-prod   # 查看状态
kubectl logs -f web-xxx -n myapp-prod  # 查看日志
kubectl exec -it web-xxx -n myapp-prod -- sh  # 进入容器
kubectl scale deployment web --replicas=5 -n myapp-prod  # 扩容
kubectl delete -f k8s/           # 停止删除

本章完整展示了从Docker Compose到Kubernetes的迁移过程。接下来,我们将学习生产环境运维的最佳实践。


第九章 生产环境运维

9.1 生产环境Docker配置最佳实践

Docker Daemon配置

// /etc/docker/daemon.json - 生产环境Docker配置
{
  // 日志配置:限制日志大小防止磁盘满
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",         // 单个日志文件最大50MB
    "max-file": "5"            // 最多保留5个日志文件
  },
  
  // 镜像存储驱动
  "storage-driver": "overlay2",
  
  // 镜像拉取并行数
  "max-concurrent-downloads": 10,
  
  // 容器实时恢复:重启Docker daemon时容器不中断
  "live-restore": true,
  
  // 用户命名空间重映射(安全加固)
  "userns-remap": "default",
  
  // 不允许非TLS注册中心(安全)
  "insecure-registries": [],
  
  // 镜像清理
  "gc": {
    "defaultKeepStorage": "20GB"
  },
  
  // 默认ulimit设置
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65536,
      "Soft": 65536
    }
  },
  
  // 数据根目录(建议使用独立磁盘)
  "data-root": "/data/docker",
  
  // 运行时选项
  "default-runtime": "runc",
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  }
}
# 应用配置后重启Docker
sudo systemctl restart docker

# 验证配置
docker info | grep -E "Storage|Logging|Live"

9.2 容器化应用的12要素方法论

12-Factor App是构建SaaS应用的方法论,与容器化天然契合:

要素说明容器化实践
1. 代码库一份代码库,多份部署Git仓库 + 多环境镜像标签
2. 依赖显式声明依赖Dockerfile中声明所有依赖
3. 配置配置存在环境中ConfigMap/Secret/环境变量
4. 后端服务将后端视为附加资源Service抽象,可替换
5. 构建/发布/运行严格分离三阶段CI/CD流水线分离
6. 进程无状态进程Pod无状态,状态下沉
7. 端口绑定通过端口提供服务Service/Ingress
8. 并发通过进程扩展Pod水平扩展
9. 易处理快速启动/优雅终止探针+preStop钩子
10. 环境等价开发/生产尽可能一致同一镜像跨环境
11. 日志日志作为事件流stdout/stderr → 日志收集
12. 管理进程管理任务与进程一致Job/CronJob

9.3 优雅启动与优雅停止

优雅启动

# 优雅启动配置
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    # 启动探针:给应用足够时间启动
    startupProbe:
      httpGet:
        path: /health
        port: 8080
      failureThreshold: 30      # 最多等30*10s=5分钟
      periodSeconds: 10
    
    # 就绪探针:启动后才检查就绪
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5
      failureThreshold: 3
    
    # 初始化:等待依赖就绪
    # (通过init容器实现)

优雅停止

spec:
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          # 优雅关闭步骤:
          # 1. 从负载均衡中注销(停止接收新请求)
          # 2. 等待处理完现有请求
          # 3. 关闭数据库连接
          # 4. 清理资源
          command:
          - /bin/sh
          - -c
          - |
            # 通知应用进入优雅关闭模式
            curl -X POST http://localhost:8080/shutdown
            # 等待连接排空
            sleep 15
  
  # 终止宽限期:给preStop钩子和SIGTERM足够时间
  terminationGracePeriodSeconds: 60
Pod终止流程:

  1. kubectl delete pod / 滚动更新触发
     ↓
  2. Pod状态变为Terminating
     ↓
  3. 从Service Endpoints中移除(不再接收新流量)
     ↓ (同时)
  4. 执行preStop钩子
     ↓
  5. 发送SIGTERM信号给容器主进程
     ↓
  6. 等待terminationGracePeriodSeconds(默认30s)
     ↓
  7. 如果超时,发送SIGKILL强制终止
     ↓
  8. Pod完全删除

  注意:步骤3和4是同时进行的!
  这意味着preStop执行期间,可能仍有流量到达Pod
  解决方案: preStop中先sleep几秒,等Endpoints更新完成

9.4 配置管理(环境变量, 配置中心)

生产环境的配置管理需要考虑配置的动态更新、版本管理和环境隔离。

# 多环境配置管理方案

# 开发环境ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: dev
data:
  LOG_LEVEL: "debug"
  CACHE_TTL: "60"
  DB_POOL_SIZE: "5"

---
# 生产环境ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  LOG_LEVEL: "info"
  CACHE_TTL: "3600"
  DB_POOL_SIZE: "20"

使用外部配置中心

# 使用外部配置中心(如Apollo/Nacos)的Sidecar方案
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    env:
    - name: CONFIG_CENTER_URL
      value: "http://config-center:8080"
    volumeMounts:
    - name: shared-config
      mountPath: /app/config
  
  # 配置同步Sidecar
  - name: config-sync
    image: config-sync-agent:1.0.0
    env:
    - name: APP_ID
      value: "myapp"
    - name: ENV
      value: "production"
    volumeMounts:
    - name: shared-config
      mountPath: /config
  
  volumes:
  - name: shared-config
    emptyDir: {}

9.5 密钥管理方案对比

方案特点适用场景
K8s Secret内置,Base64编码简单场景,小规模
Sealed Secrets加密后可存GitGitOps场景
External Secrets对接外部密钥管理对接AWS Secrets Manager等
HashiCorp Vault企业级密钥管理大型企业,严格安全要求
SOPS加密YAML文件GitOps + 加密配置
# External Secrets Operator示例:从AWS Secrets Manager获取密钥
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-external-secret
  namespace: production
spec:
  refreshInterval: 1h              # 每小时刷新一次
  secretStoreRef:
    name: aws-secrets-manager       # SecretStore名称
    kind: SecretStore
  target:
    name: app-secret                # 生成的K8s Secret名称
    creationPolicy: Owner
  data:
  - secretKey: DB_PASSWORD          # K8s Secret中的key
    remoteRef:
      key: prod/myapp/db-password   # AWS Secrets Manager中的key
  - secretKey: API_KEY
    remoteRef:
      key: prod/myapp/api-key

9.6 日志收集架构

DaemonSet模式(推荐)

DaemonSet日志收集架构:

  Node 1                    Node 2
  ┌──────────────┐         ┌──────────────┐
  │ Pod A  Pod B │         │ Pod C  Pod D │
  │  │      │    │         │  │      │    │
  │ stdout/stderr│         │ stdout/stderr│
  │  │      │    │         │  │      │    │
  │ /var/log/... │         │ /var/log/... │
  │  │           │         │  │           │
  │ Fluent Bit   │         │ Fluent Bit   │
  │ (DaemonSet)  │         │ (DaemonSet)  │
  └──────┬───────┘         └──────┬───────┘
         │                        │
         └────────┬───────────────┘
                  │
         ┌────────┴────────┐
         │  Log Storage    │
         │  (Elasticsearch)│
         │  or Loki        │
         └────────┬────────┘
                  │
         ┌────────┴────────┐
         │    Grafana /    │
         │    Kibana       │
         │   (可视化)       │
         └─────────────────┘

  优点: 资源开销小,每个节点一个收集器
  缺点: 无法按Pod区分日志格式

Sidecar模式

Sidecar日志收集架构:

  Pod
  ┌──────────────────────┐
  │  ┌────────┐          │
  │  │  App   │ → stdout │
  │  │        │ → /logs  │
  │  └───┬────┘          │
  │      │ shared volume │
  │  ┌───┴────────────┐  │
  │  │  Log Sidecar   │  │
  │  │  (Fluent Bit)  │  │
  │  └───────┬────────┘  │
  └──────────┼───────────┘
             │
      → Log Storage

  优点: 可按Pod定制日志处理
  缺点: 每个Pod多一个容器,资源开销大
# DaemonSet日志收集部署
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:2.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: config
          mountPath: /fluent-bit/etc
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: config
        configMap:
          name: fluent-bit-config

9.7 监控告警体系

# Prometheus监控配置示例
# 使用Prometheus Operator

---
# ServiceMonitor: 告诉Prometheus如何抓取指标
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: app-monitor
  namespace: production
spec:
  selector:
    matchLabels:
      app: myapp               # 选择有此标签的Service
  endpoints:
  - port: metrics              # 抓取名为metrics的端口
    interval: 15s              # 每15秒抓取一次
    path: /metrics             # 指标路径
    scrapeTimeout: 10s         # 抓取超时

---
# PrometheusRule: 告警规则
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: app-alerts
  namespace: production
spec:
  groups:
  - name: app.rules
    rules:
    # Pod宕机告警
    - alert: PodDown
      expr: kube_pod_status_phase{phase!="Running"} == 1
      for: 5m                  # 持续5分钟才告警
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.pod }} is not running"
        description: "Pod {{ $labels.pod }} in namespace {{ $labels.namespace }} has been down for more than 5 minutes."
    
    # CPU使用率过高告警
    - alert: HighCPUUsage
      expr: |
        100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "High CPU usage on {{ $labels.instance }}"
    
    # 内存使用率过高告警
    - alert: HighMemoryUsage
      expr: |
        (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "High memory usage on {{ $labels.instance }}"
    
    # Pod重启次数过多告警
    - alert: PodRestartTooMany
      expr: increase(kube_pod_container_status_restarts_total[1h]) > 3
      labels:
        severity: warning
      annotations:
        summary: "Pod {{ $labels.pod }} restarted more than 3 times in the last hour"

9.8 备份与灾难恢复

#!/bin/bash
# backup-k8s.sh - K8s集群备份脚本

BACKUP_DIR="/backup/k8s/$(date +%Y%m%d_%H%M%S)"
mkdir -p ${BACKUP_DIR}

# 1. 备份etcd(最关键的备份)
echo "备份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_DIR}/etcd-snapshot.db

# 验证备份
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  snapshot status ${BACKUP_DIR}/etcd-snapshot.db

# 2. 备份资源定义
echo "备份资源定义..."
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
  kubectl get all -n ${ns} -o yaml > ${BACKUP_DIR}/${ns}-resources.yaml
  kubectl get configmap,secret,pvc,serviceaccount,role,rolebinding -n ${ns} -o yaml > ${BACKUP_DIR}/${ns}-configs.yaml
done

# 3. 备份集群级资源
kubectl get clusterrole,clusterrolebinding -o yaml > ${BACKUP_DIR}/cluster-roles.yaml
kubectl get storageclass,networkpolicy -o yaml > ${BACKUP_DIR}/cluster-policies.yaml

# 4. 备份PV
kubectl get pv -o yaml > ${BACKUP_DIR}/persistent-volumes.yaml

# 5. 压缩并上传
tar czf ${BACKUP_DIR}.tar.gz ${BACKUP_DIR}
aws s3 cp ${BACKUP_DIR}.tar.gz s3://my-k8s-backups/

# 6. 清理30天前的备份
find /backup/k8s/ -mtime +30 -delete

echo "备份完成: ${BACKUP_DIR}"
# etcd恢复(灾难恢复场景)
# 1. 停止所有控制平面组件
systemctl stop kube-apiserver
systemctl stop kube-controller-manager
systemctl stop kube-scheduler

# 2. 恢复etcd
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  snapshot restore /backup/k8s/20240115/etcd-snapshot.db \
  --data-dir=/var/lib/etcd-restored

# 3. 替换etcd数据目录
mv /var/lib/etcd /var/lib/etcd-old
mv /var/lib/etcd-restored /var/lib/etcd

# 4. 重启etcd和控制平面
systemctl restart etcd
systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler

9.9 安全加固清单

K8s生产环境安全加固清单:

  集群层面:
  [ ] 启用RBAC,最小权限原则
  [ ] etcd数据加密(at-rest encryption)
  [ ] API Server使用TLS
  [ ] 定期轮换证书
  [ ] 审计日志开启
  [ ] 控制平面组件只监听内部网络
  [ ] 禁止匿名访问
  
  Pod层面:
  [ ] 使用restricted Pod Security Standard
  [ ] 容器以非root用户运行(runAsNonRoot: true)
  [ ] 只读根文件系统(readOnlyRootFilesystem: true)
  [ ] 删除所有capabilities(drop: ["ALL"])
  [ ] 禁止特权容器(privileged: false)
  [ ] 禁止特权提升(allowPrivilegeEscalation: false)
  [ ] 使用seccomp profile
  
  网络层面:
  [ ] 默认拒绝所有流量(NetworkPolicy)
  [ ] 按需开放端口和来源
  [ ] 使用mTLS(Service Mesh)
  [ ] Ingress启用TLS
  
  镜像层面:
  [ ] 使用可信基础镜像
  [ ] 定期扫描漏洞(Trivy/Clair)
  [ ] 使用私有镜像仓库
  [ ] 禁止使用latest标签
  [ ] 镜像签名验证(Cosign)
  
  密钥层面:
  [ ] 不在YAML中硬编码密钥
  [ ] 使用Secret或外部密钥管理
  [ ] 定期轮换密钥
  [ ] RBAC限制Secret访问权限
# etcd加密配置
# 加密etcd中的Secret数据
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
  - secrets                  # 加密secrets
  providers:
  - aescbc:                  # AES-CBC加密
      keys:
      - name: key1
        secret: <base64-encoded-32-byte-key>
  - identity: {}             # 回退:不加密(用于读取旧数据)

9.10 生产环境运维SOP

生产环境运维标准操作流程(SOP):

  日常巡检(每日):
  ├── 检查集群节点状态: kubectl get nodes
  ├── 检查Pod状态: kubectl get pods --all-namespaces
  ├── 检查资源使用率: kubectl top nodes/pods
  ├── 检查异常事件: kubectl get events --sort-by='.lastTimestamp'
  ├── 检查PV/PVC状态: kubectl get pv,pvc --all-namespaces
  └── 检查证书有效期

  变更流程:
  ├── 1. 提交变更申请(描述变更内容、影响范围、回滚方案)
  ├── 2. 在测试环境验证
  ├── 3. 预发布环境验证
  ├── 4. 生产环境低峰期执行
  ├── 5. 变更后验证
  └── 6. 保留回滚能力(至少30分钟观察期)

  故障响应流程:
  ├── P0(系统不可用): 5分钟响应,30分钟恢复
  ├── P1(核心功能降级): 15分钟响应,2小时恢复
  ├── P2(非核心功能异常): 1小时响应,4小时恢复
  └── P3(轻微问题): 4小时响应,24小时恢复

  常用故障排查命令:
  # Pod异常排查
  kubectl describe pod <pod-name>     # 查看事件
  kubectl logs <pod-name>             # 查看日志
  kubectl logs <pod-name> --previous  # 上一个容器日志
  kubectl get events --field-selector involvedObject.name=<pod-name>
  
  # 节点异常排查
  kubectl describe node <node-name>   # 查看节点详情
  kubectl get nodes -o wide           # 查看节点状态
  journalctl -u kubelet               # 查看kubelet日志
  
  # 网络排查
  kubectl exec -it <pod> -- nslookup <service>  # DNS解析
  kubectl exec -it <pod> -- curl <service>:<port>  # 连通性测试
  kubectl get endpoints <service>     # 查看Service端点

本章涵盖了生产环境运维的各个方面。最后,让我们展望容器化技术的未来趋势,并对整个Docker专栏进行总结。


第十章 容器化未来趋势与总结

10.1 容器运行时演进(containerd, CRI-O)

容器运行时正在从Docker向更轻量级的方案演进。K8s在1.24版本移除dockershim后,containerd和CRI-O成为主流选择。

容器运行时演进历程:

  2013: Docker (完整方案)
    └── Docker Engine = CLI + Daemon + containerd + runc
  
  2017: OCI标准确立
    └── 镜像格式标准 + 运行时标准(runc)
  
  2018: K8s引入CRI接口
    └── 容器运行时通过CRI接口与K8s通信
  
  2020: Docker拆分
    └── containerd独立,成为CNCF毕业项目
  
  2022: K8s移除dockershim
    └── containerd / CRI-O 成为默认运行时
  
  2024+: 运行时精简化
    └── 更轻量、更安全、更高效的运行时

当前主流运行时对比:

  ┌─────────────┬──────────────┬──────────────┐
  │             │  containerd  │   CRI-O      │
  ├─────────────┼──────────────┼──────────────┤
  │ 来源         │ Docker拆分   │ Red Hat开发  │
  │ K8s集成      │ 原生支持      │ 原生支持     │
  │ 镜像兼容     │ OCI标准      │ OCI标准      │
  │ 资源开销     │ 低           │ 更低         │
  │ 生态         │ 广泛         │ OpenShift为主 │
  │ CLI工具      │ ctr/nerdctl │ crictl       │
  │ 生产成熟度   │ 非常成熟     │ 成熟         │
  └─────────────┴──────────────┴──────────────┘
# containerd常用命令(替代docker命令)
# 使用nerdctl作为containerd的CLI(兼容docker命令语法)

# 拉取镜像
nerdctl pull nginx:1.25

# 运行容器
nerdctl run -d --name web -p 80:80 nginx:1.25

# 查看容器
nerdctl ps

# 构建镜像
nerdctl build -t myapp:1.0 .

# K8s中使用crictl管理容器运行时
crictl ps                      # 查看容器
crictl images                  # 查看镜像
crictl logs <container-id>     # 查看日志
crictl exec -it <container-id> sh  # 进入容器

10.2 Serverless容器(AWS Fargate, Google Cloud Run, Azure Container Apps)

Serverless容器让开发者无需管理服务器和集群,只需提交容器镜像即可运行。

Serverless容器演进:

  传统K8s:                    Serverless容器:
  ┌────────────────┐          ┌────────────────┐
  │ 管理节点        │          │ 提交镜像        │
  │ 管理集群        │    →     │ 自动扩缩        │
  │ 管理调度        │          │ 按量计费        │
  │ 管理升级        │          │ 零运维          │
  └────────────────┘          └────────────────┘
平台特点适用场景
AWS FargateECS/EKS无服务器计算AWS生态,不想管节点
Google Cloud RunKnative基础上构建GCP生态,HTTP服务
Azure Container Apps基于K8s+DaprAzure生态,微服务
阿里云ASKServerless K8s阿里云生态
# Google Cloud Run部署示例
# 部署一个容器到Cloud Run(无需管理K8s集群)

# 使用gcloud命令部署
# gcloud run deploy my-service \
#   --source . \
#   --region asia-east1 \
#   --allow-unauthenticated \
#   --memory 512Mi \
#   --cpu 1 \
#   --min-instances 0 \      # 最小0实例(缩容到0)
#   --max-instances 10 \     # 最大10实例
#   --concurrency 80         # 每实例最大并发80

# Cloud Run的优势:
# 1. 无需管理K8s集群
# 2. 缩容到0(无流量时不计费)
# 3. 自动扩缩容
# 4. 按请求计费
# 5. 内置HTTPS和自定义域名

10.3 WebAssembly与容器(Wasm)

WebAssembly(Wasm)正在成为容器技术的补充方案,提供更快的启动速度和更小的体积。

Wasm vs 容器对比:

  ┌──────────────┬──────────────────┬──────────────────┐
  │              │   Docker容器      │   Wasm模块       │
  ├──────────────┼──────────────────┼──────────────────┤
  │ 启动速度      │ 秒级             │ 毫秒级           │
  │ 镜像大小      │ MB级             │ KB级             │
  │ 安全性        │ 命名空间隔离      │ 沙箱隔离(更强)   │
  │ 跨平台        │ 需要多平台构建    │ 一次构建到处运行  │
  │ 生态成熟度    │ 非常成熟         │ 发展中           │
  │ 适用场景      │ 通用             │ 边缘/Serverless  │
  └──────────────┴──────────────────┴──────────────────┘

Wasm运行时:
  - WasmEdge: 轻量级Wasm运行时,CNCF沙箱项目
  - Wasmer: 通用Wasm运行时
  - WAMR: WebAssembly Micro Runtime(Intel)

K8s + Wasm:
  通过containerd的Wasm shim,K8s可以直接运行Wasm模块
  不需要传统容器镜像,直接调度Wasm模块
# 使用Wasm运行应用示例
# 编译Rust为Wasm
cargo build --target wasm32-wasi --release

# 使用WasmEdge运行
wasmedge target/wasm32-wasi/release/myapp.wasm

# 在K8s中运行Wasm(需要Wasm shim)
# Pod spec中使用Wasm镜像
apiVersion: v1
kind: Pod
metadata:
  name: wasm-app
spec:
  runtimeClassName: wasmedge    # 使用WasmEdge运行时
  containers:
  - name: app
    image: myapp.wasm:1.0.0     # Wasm模块作为"镜像"

10.4 边缘计算与容器(K3s, MicroK8s)

边缘计算场景需要轻量级的K8s发行版,在资源受限的环境中运行容器。

轻量级K8s对比:

  ┌──────────────┬──────────────┬──────────────┬──────────────┐
  │              │    K3s       │  MicroK8s    │  K8s(标准)   │
  ├──────────────┼──────────────┼──────────────┼──────────────┤
  │ 二进制大小    │ ~60MB        │ ~200MB       │ ~500MB+      │
  │ 内存占用      │ ~512MB       │ ~1GB         │ ~2GB+        │
  │ etcd         │ SQLite替代   │ 内置dqlite   │ etcd         │
  │ 适用场景      │ 边缘/IoT     │ 开发/边缘    │ 生产集群     │
  │ 维护方        │ Rancher      │ Canonical    │ CNCF         │
  │ ARM支持      │ 原生支持     │ 支持         │ 支持         │
  └──────────────┴──────────────┴──────────────┴──────────────┘
# K3s边缘部署示例

# 在边缘设备上安装K3s(树莓派等ARM设备)
curl -sfL https://get.k3s.io | sh -

# K3s特点:
# 1. 单二进制文件,包含所有K8s组件
# 2. 内置containerd运行时
# 3. 内置Traefik Ingress Controller
# 4. 内置ServiceLB(替代Cloud LB)
# 5. 使用SQLite替代etcd(单节点模式)
# 6. 支持多节点(使用etcd或外部数据库)

# 边缘场景部署架构:
#
#   云端K8s集群(管理)
#         │
#    ┌────┴────┐
#    │         │
#  边缘节点1  边缘节点2
#  (K3s)     (K3s)
#  ┌─────┐   ┌─────┐
#  │IoT  │   │IoT  │
#  │App  │   │App  │
#  └─────┘   └─────┘

10.5 GitOps与ArgoCD

GitOps是一种将Git作为唯一真实来源的持续交付方法论,ArgoCD是其最流行的实现。

GitOps工作流程:

  开发者 → Git Push → Git仓库 → ArgoCD → K8s集群
                          │          │
                    (真实来源)   (持续同步)
                                     │
                              检测Git变更
                                     │
                              自动应用到集群
                                     │
                              集群状态=Git状态

  核心原则:
  1. 声明式: 系统状态用声明式描述
  2. 版本化: 所有变更通过Git提交
  3. 自动拉取: 部署自动从Git拉取
  4. 持续协调: 自动检测并纠正漂移
# ArgoCD Application示例
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-prod
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp-k8s.git  # Git仓库
    targetRevision: HEAD                             # 跟踪分支
    path: manifests/production                       # K8s清单路径
  destination:
    server: https://kubernetes.default.svc           # 目标集群
    namespace: production                            # 目标命名空间
  syncPolicy:
    automated:                                       # 自动同步
      prune: true                                    # 删除Git中不存在的资源
      selfHeal: true                                 # 自动修复手动修改
    syncOptions:
    - CreateNamespace=true                           # 自动创建命名空间
  revisionHistoryLimit: 10                           # 保留10个版本历史

10.6 服务网格的普及

服务网格正在从"可选"变为"标配",为微服务提供统一的流量管理、安全和可观测能力。

服务网格发展趋势:

  2018-2020: Istio主导,功能强大但复杂
            ↓
  2021-2023: Linkerd崛起(更轻量),Istio简化
            ↓
  2024+:    Sidecar-less模式(Ambient Mesh, Cilium Service Mesh)
            ↓
  未来:     eBPF原生服务网格,内核级网络处理

  关键变化:
  - 从Sidecar模式到Sidecar-less模式
  - 从用户空间代理到内核空间(eBPF)
  - 从重量级到轻量级
  - 从手动安装到自动注入

10.7 Docker专栏全系列总结

本专栏从Docker基础到生产环境部署与K8s,共十篇文章,系统覆盖了容器技术的完整知识体系。以下是全系列回顾:

Docker专栏全系列回顾:

  第1篇: Docker基础入门
  ├── 容器与虚拟化概念对比
  ├── Docker架构与核心组件
  ├── Docker安装与环境配置
  └── 第一个Docker容器

  第2篇: Docker镜像详解
  ├── 镜像分层结构与UnionFS
  ├── Dockerfile指令详解
  ├── 镜像构建最佳实践
  └── 镜像优化与瘦身

  第3篇: Docker容器管理
  ├── 容器生命周期管理
  ├── 容器资源限制
  ├── 容器日志与监控
  └── 容器数据管理

  第4篇: Docker网络
  ├── 网络驱动类型(bridge/host/overlay)
  ├── 自定义网络配置
  ├── 容器间通信
  └── 网络安全

  第5篇: Docker存储
  ├── Volume管理
  ├── Bind Mounts
  ├── tmpfs挂载
  └── 存储驱动

  第6篇: Docker Compose
  ├── Compose文件语法
  ├── 多服务编排
  ├── 环境变量管理
  └── 开发环境实战

  第7篇: Dockerfile最佳实践
  ├── 多阶段构建
  ├── 构建缓存优化
  ├── 镜像安全
  └── 生产级Dockerfile

  第8篇: Docker私有仓库
  ├── Harbor部署
  ├── 镜像管理
  ├── 安全扫描
  └── 高可用方案

  第9篇: Docker安全实践
  ├── 容器安全基础
  ├── 镜像漏洞扫描
  ├── 运行时安全
  └── 安全合规

  第10篇: Docker生产环境部署与K8s入门(本文)
  ├── 生产环境部署概述
  ├── Docker Swarm集群
  ├── CI/CD流水线
  ├── Kubernetes入门与实战
  ├── 从Compose到K8s迁移
  ├── 生产环境运维
  └── 未来趋势与总结

全系列知识图谱

                    ┌─────────────┐
                    │  Docker基础  │
                    │  (第1-3篇)  │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
        ┌─────┴─────┐ ┌───┴────┐ ┌────┴────┐
        │  网络存储   │ │Compose │ │Dockerfile│
        │ (第4-5篇)  │ │(第6篇) │ │ (第7篇)  │
        └─────┬─────┘ └───┬────┘ └────┬────┘
              │            │            │
              └────────────┼────────────┘
                           │
                    ┌──────┴──────┐
                    │  仓库与安全   │
                    │  (第8-9篇)  │
                    └──────┬──────┘
                           │
                    ┌──────┴──────┐
                    │ 生产部署+K8s │
                    │  (第10篇)   │
                    └─────────────┘

10.8 学习路线与进阶建议

容器技术学习路线图:

  初级阶段(已通过本专栏完成):
  ├── 掌握Docker核心概念与操作
  ├── 能编写Dockerfile构建镜像
  ├── 能使用Compose编排多服务
  └── 理解容器网络与存储

  中级阶段(建议下一步学习):
  ├── 深入学习Kubernetes
  │   ├── Helm包管理
  │   ├── K8s Operator开发
  │   ├── CRD自定义资源
  │   └── K8s调度原理深入
  ├── 掌握CI/CD工具链
  │   ├── ArgoCD/Flux GitOps
  │   ├── Tekton流水线
  │   └── Harbor镜像仓库管理
  └── 可观测性体系
      ├── Prometheus + Grafana
      ├── ELK/Loki日志
      └── Jaeger/Tempo追踪

  高级阶段(长期目标):
  ├── 云原生架构设计
  │   ├── Service Mesh (Istio/Linkerd)
  │   ├── 事件驱动架构
  │   └── Serverless (Knative)
  ├── K8s集群运维
  │   ├── 多集群管理
  │   ├── 安全加固与合规
  │   └── 性能调优
  └── 技术前沿
      ├── eBPF与可编程网络
      ├── Wasm容器
      └── 边缘计算

认证考试建议

认证颁发机构难度说明
CKA (Certified Kubernetes Administrator)CNCF中高K8s管理员认证,实操考试
CKAD (Certified Kubernetes Application Developer)CNCFK8s应用开发者认证
CKS (Certified Kubernetes Security Specialist)CNCFK8s安全专家认证(需先有CKA)
DCA (Docker Certified Associate)DockerDocker认证(已停考)

10.9 推荐资源与社区

官方文档

  • Docker官方文档: https://docs.docker.com
  • Kubernetes官方文档: https://kubernetes.io/docs
  • CNCF Landscape: https://landscape.cncf.io

推荐书籍

书名作者适合阶段
《Docker技术入门与实战》杨保华初级
《Kubernetes in Action》Marko Lukša中级
《Cloud Native Patterns》Cornelia Davis中级
《Site Reliability Engineering》Google高级
《Designing Data-Intensive Applications》Martin Kleppmann高级

在线学习平台

  • Kubernetes官方互动教程: https://kubernetes.io/docs/tutorials/
  • Killer.sh (CKA/CKAD模拟考试): https://killer.sh
  • Play with K8s: https://labs.play-with-k8s.com
  • Katacoda (已归档但内容仍有参考价值)

开源社区

  • CNCF Slack: https://cloud-native.slack.com
  • Kubernetes GitHub: https://github.com/kubernetes/kubernetes
  • Docker GitHub: https://github.com/docker
  • 阿里云容器服务: https://www.alibabacloud.com/product/kubernetes

总结: Docker专栏完结篇

至此,我们的Docker技术专栏已经走过了十篇文章的完整旅程。从最初认识容器的基本概念,到掌握Docker镜像构建、容器管理、网络存储配置,再到使用Compose编排多服务应用,最后迈向生产环境部署和Kubernetes容器编排,我们构建了一个完整的容器化知识体系。

回顾核心要点

在本专栏中,我们学习了以下核心知识:

  1. Docker基础:理解容器与虚拟化的本质区别,掌握Docker的客户端-服务端架构,学会使用基本命令管理容器生命周期。

  2. 镜像管理:深入理解镜像的分层存储机制,熟练编写Dockerfile,掌握多阶段构建、构建缓存优化等生产级技巧。

  3. 网络与存储:掌握Docker的各种网络模式(bridge/host/overlay)和存储方案(Volume/Bind Mount/tmpfs),能够为不同场景选择合适的配置。

  4. Docker Compose:使用声明式YAML文件编排多容器应用,管理服务依赖、环境变量、网络和存储,实现一键启动完整应用栈。

  5. 安全实践:从镜像扫描、运行时隔离到密钥管理,建立容器安全的完整防线。

  6. 生产部署:从Docker Swarm轻量级集群到Kubernetes企业级编排,理解容器编排的核心概念和最佳实践。

  7. CI/CD集成:将Docker融入持续集成/持续部署流水线,实现从代码提交到生产部署的全自动化。

  8. Kubernetes:掌握K8s的核心概念(Pod/Deployment/Service/ConfigMap/Secret/PV/PVC),能够部署和管理容器化应用,实现滚动更新、自动扩缩容、服务发现等生产级能力。

容器化的核心价值

通过本专栏的学习,我们深刻理解了容器化的核心价值:

  • 一致性:开发、测试、生产环境完全一致,消除"在我机器上能跑"的问题
  • 便携性:一次构建,到处运行,不绑定特定基础设施
  • 效率:秒级启动,资源利用率高,密度大
  • 可扩展:水平扩展简单,配合编排工具实现弹性伸缩
  • 标准化:OCI标准使整个生态互联互通

从Docker到云原生

Docker只是云原生旅程的起点。掌握了Docker之后,真正的挑战在于:

  • 如何在多台服务器上编排和管理成百上千个容器(Kubernetes)
  • 如何实现应用的自动扩缩容和自愈(HPA/控制器)
  • 如何管理微服务间的通信和安全(Service Mesh)
  • 如何实现基础设施即代码(GitOps/Helm)
  • 如何构建完整的可观测性体系(Prometheus/Grafana/ELK)

这些问题的答案,都在云原生生态中。Docker为我们打开了云原生的大门,而Kubernetes和CNCF生态则是这片广阔天地的核心。

写在最后

容器技术正在重塑软件工程的方式。从开发到运维,从架构到安全,容器化思维正在影响每一个技术决策。作为开发者或运维人员,掌握Docker和Kubernetes不仅是一项技能,更是一种面向未来的投资。

本专栏虽然完结,但学习之路永无止境。云原生技术生态日新月异,新的工具和理念不断涌现。希望本专栏能够为你打下坚实的基础,让你在云原生的道路上走得更远、更稳。

感谢每一位读者的陪伴。愿你在容器化和云原生的世界里,不断探索,持续成长。

更多推荐