10-Docker生产环境部署与K8s入门

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一条命令启动整个应用栈。但在生产环境中,应用需要分布在多台服务器上运行,这就引入了跨主机通信、数据同步、状态一致性等复杂问题。

bash 复制代码
# 开发环境:一条命令搞定
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):

bash 复制代码
# 垂直扩展:增加单机资源配置(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,以获得更好的性能和更小的攻击面。

bash 复制代码
# 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 Swarm Kubernetes
学习曲线 平缓,几小时掌握 陡峭,需数周至数月
安装部署 Docker内置,一条命令 复杂,需kubeadm或托管服务
架构复杂度 简单,Manager/Worker两级 复杂,多组件多层级
功能丰富度 基础功能完备 功能极其丰富
生态社区 较小,趋于停滞 极其庞大,CNCF生态
性能开销 较高
高可用 支持多Manager 原生多Master高可用
网络模型 Overlay网络 CNI插件(灵活)
存储管理 基础Volume PV/PVC/StorageClass
自动扩缩 不支持原生 HPA/VPA/Cluster Autoscaler
服务发现 内置DNS CoreDNS
负载均衡 内置VIP路由 kube-proxy + Service
配置管理 Docker Config/Secret ConfigMap/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 复制代码
# 一个典型的生产环境应用部署架构示例(概念性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节点

bash 复制代码
# 在第一台服务器上初始化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节点

bash 复制代码
# 在其他服务器上执行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节点(高可用)

bash 复制代码
# 在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

查看集群状态

bash 复制代码
# 查看集群所有节点(在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 节点管理

bash 复制代码
# 查看节点详细信息
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操作可以将该节点上的所有任务安全迁移到其他节点:

bash 复制代码
# 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 服务部署

bash 复制代码
# 基本服务创建
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 服务管理

bash 复制代码
# 列出所有服务
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内置了滚动更新和回滚机制,是生产环境零停机部署的关键。

bash 复制代码
# 滚动更新示例:更新镜像版本
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服务发现和负载均衡。

bash 复制代码
# 创建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和共享存储。

bash 复制代码
# 本地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内置了完善的安全机制:

bash 复制代码
# 查看集群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生产环境部署的推荐方式。

yaml 复制代码
# 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
bash 复制代码
# 部署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(性能下降)
bash 复制代码
# 生产环境高可用配置建议:
# 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缓存的完整应用。

bash 复制代码
#!/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文件定义流水线。

yaml 复制代码
# .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定义流水线。

groovy 复制代码
// 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文件定义工作流。

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 复制代码
# 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缓存优化

bash 复制代码
# 使用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-ShortSHA 20240115-143022-a1b2c3d 精确到分钟,便于追溯
语义版本 vMAJOR.MINOR.PATCH v1.2.3 正式发布版本
Git SHA git-ShortSHA git-a1b2c3d 关联到具体commit
分支名 branch-name feature-auth 分支构建
latest latest latest 最新版本(慎用)
bash 复制代码
# 推荐的镜像标签策略
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 自动化测试集成

yaml 复制代码
# 完整的测试集成示例
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)
  └──────────────┘
bash 复制代码
# 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)

yaml 复制代码
# 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
bash 复制代码
# 逐步增加金丝雀流量
# 稳定版: 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流水线示例

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

bash 复制代码
#!/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-apiserver K8s 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内的容器共享网络和存储命名空间。

yaml 复制代码
# 一个简单的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的副本集,提供滚动更新和回滚能力。它是最常用的工作负载类型。

yaml 复制代码
# 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是固定的。

yaml 复制代码
# 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用于存储非敏感的配置数据,将配置与镜像解耦。

yaml 复制代码
# 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编码存储。

yaml 复制代码
# 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用于在集群内隔离资源,适用于多团队、多环境场景。

bash 复制代码
# 查看所有命名空间
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容器重启策略

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

多容器Pod(Sidecar模式)

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

yaml 复制代码
# 多容器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容器在主容器启动前运行,用于执行初始化任务:

yaml 复制代码
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/目录下:

yaml 复制代码
# /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安装与使用

bash 复制代码
# 安装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安装与使用

bash 复制代码
# 安装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安装(轻量级)

bash 复制代码
# 安装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的核心。

bash 复制代码
# ==================== 查看资源 ====================
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文件结构

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

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

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

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

使用kustomize管理配置

yaml 复制代码
# 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)
最小单位 Container Pod
管理对象 单个容器 Deployment/StatefulSet
网络 端口映射 Service + Ingress
存储 Volume PV/PVC/StorageClass
配置 环境变量/文件 ConfigMap/Secret
扩缩容 docker run 多个 修改replicas
更新 停旧启新 滚动更新(自动)
故障恢复 手动重启 自动重启/迁移
发现 手动配置IP DNS自动发现
复制代码
概念映射关系:

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副本数量,并提供滚动更新和回滚能力。

yaml 复制代码
# 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级别设置)

滚动更新与回滚

bash 复制代码
# 更新镜像版本(触发滚动更新)
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间接管理。

yaml 复制代码
# 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用于管理有状态应用(如数据库、消息队列),提供稳定的网络标识、稳定的持久化存储和有序的部署/扩缩容。

yaml 复制代码
# 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副本。常用于日志收集、监控代理、网络插件等。

yaml 复制代码
# 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成功完成。

yaml 复制代码
# 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:定时执行任务。

yaml 复制代码
# 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的副本数。

yaml 复制代码
# 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
bash 复制代码
# 查看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,通过命名空间标签来强制安全策略。

yaml 复制代码
# 通过命名空间标签设置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调度的核心。正确设置资源请求和限制对集群稳定性至关重要。

yaml 复制代码
# 资源配置示例
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"

资源单位说明

资源 单位 说明
CPU m(millicore) 1000m = 1核CPU。500m = 0.5核
CPU 整数 1 = 1核, 2 = 2核
Memory Ki/Mi/Gi/Ti 二进制单位(1024进制)
Memory K/M/G/T 十进制单位(1000进制,不推荐)
ephemeral-storage Ki/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的完整应用部署:

yaml 复制代码
# 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类型。

yaml 复制代码
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。

yaml 复制代码
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基础上,通过云提供商创建外部负载均衡器。

yaml 复制代码
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。

yaml 复制代码
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
bash 复制代码
# 查看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)
  └──────────────────────────┘
yaml 复制代码
# 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

bash 复制代码
# 安装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级别的网络隔离。

yaml 复制代码
# 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
bash 复制代码
# 在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核心功能

yaml 复制代码
# 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网络的迁移

概念 Docker Kubernetes
容器间通信 同一网络内通过容器名 同一命名空间内通过Service名
端口暴露 -p 8080:80 Service(ClusterIP/NodePort/LB)
DNS Docker内嵌DNS CoreDNS
网络隔离 --network NetworkPolicy
负载均衡 无内置LB kube-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类型,满足不同场景的存储需求。

yaml 复制代码
# 各种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类型 生命周期 是否持久化 适用场景
emptyDir Pod级 临时缓存,多容器共享
hostPath Node级 是(节点级) 系统级访问(DaemonSet)
configMap Pod级 配置文件注入
secret Pod级 敏感信息注入
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)

yaml 复制代码
# 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)
yaml 复制代码
# 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,无需管理员手动创建。这是生产环境的推荐方式。

yaml 复制代码
# 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             # 允许在线扩容
yaml 复制代码
# 使用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:

yaml 复制代码
# 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中管理非敏感配置的核心资源。

yaml 复制代码
# 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的使用方式

yaml 复制代码
# 在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热更新

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

# 更新ConfigMap
kubectl edit configmap app-config

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

7.6 Secret管理敏感信息

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

yaml 复制代码
# 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使用方式

yaml 复制代码
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的迁移

概念 Docker Kubernetes
存储声明 docker volume create PVC
存储资源 Volume PV
存储驱动 --driver StorageClass + Provisioner
配置文件 -v config.json:/app/ ConfigMap Volume
密码/密钥 -e PASSWORD=xxx Secret
临时存储 tmpfs emptyDir (medium: Memory)
节点存储 hostPath hostPath Volume
共享存储 NFS Volume NFS PV/PVC
yaml 复制代码
# 迁移示例
# 
# 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 Compose Kubernetes
单机限制 仅限单节点 多节点集群
高可用 无内置HA 多Master HA
自动扩缩 不支持 HPA/VPA
自愈能力 有限(restart策略) 完整自愈
滚动更新 需手动操作 原生支持
服务发现 仅容器名 CoreDNS + Service
网络隔离 基础网络 NetworkPolicy
存储管理 基础Volume PV/PVC/StorageClass
生态支持 有限 CNCF庞大生态
复制代码
迁移时机判断:

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

8.2 使用Kompose工具自动转换

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

bash 复制代码
# 安装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:

yaml 复制代码
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资源(简化):

bash 复制代码
# 执行转换
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 网络配置迁移

yaml 复制代码
# 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 存储配置迁移

yaml 复制代码
# 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)

yaml 复制代码
# 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)

yaml 复制代码
# 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文件

yaml 复制代码
# 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文件

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 logs kubectl logs
进入容器 docker-compose exec kubectl exec
健康检查 healthcheck livenessProbe + readinessProbe
bash 复制代码
# 迁移后的常用操作对比

# 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配置

json 复制代码
// /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": []
    }
  }
}
bash 复制代码
# 应用配置后重启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 优雅启动与优雅停止

优雅启动

yaml 复制代码
# 优雅启动配置
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容器实现)

优雅停止

yaml 复制代码
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 配置管理(环境变量, 配置中心)

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

yaml 复制代码
# 多环境配置管理方案

# 开发环境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"

使用外部配置中心

yaml 复制代码
# 使用外部配置中心(如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 加密后可存Git GitOps场景
External Secrets 对接外部密钥管理 对接AWS Secrets Manager等
HashiCorp Vault 企业级密钥管理 大型企业,严格安全要求
SOPS 加密YAML文件 GitOps + 加密配置
yaml 复制代码
# 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多一个容器,资源开销大
yaml 复制代码
# 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 监控告警体系

yaml 复制代码
# 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 备份与灾难恢复

bash 复制代码
#!/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}"
bash 复制代码
# 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访问权限
yaml 复制代码
# 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       │
  │ 生产成熟度   │ 非常成熟     │ 成熟         │
  └─────────────┴──────────────┴──────────────┘
bash 复制代码
# 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 Fargate ECS/EKS无服务器计算 AWS生态,不想管节点
Google Cloud Run Knative基础上构建 GCP生态,HTTP服务
Azure Container Apps 基于K8s+Dapr Azure生态,微服务
阿里云ASK Serverless K8s 阿里云生态
yaml 复制代码
# 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模块
bash 复制代码
# 使用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支持      │ 原生支持     │ 支持         │ 支持         │
  └──────────────┴──────────────┴──────────────┴──────────────┘
bash 复制代码
# 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. 持续协调: 自动检测并纠正漂移
yaml 复制代码
# 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) CNCF K8s应用开发者认证
CKS (Certified Kubernetes Security Specialist) CNCF K8s安全专家认证(需先有CKA)
DCA (Docker Certified Associate) Docker Docker认证(已停考)

10.9 推荐资源与社区

官方文档

推荐书籍

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

在线学习平台

开源社区


总结: 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不仅是一项技能,更是一种面向未来的投资。

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

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

相关推荐
茯苓gao1 小时前
无感FOC核心原理:没有编码器,电机如何获得转子电角度?
笔记·嵌入式硬件·学习
蜀道山老天师2 小时前
Zabbix监控Docker容器应用实践完整指南
linux·运维·docker·容器·zabbix
xiaoxiangsiyan2 小时前
机房交换机交换技术与服务器链路聚合深度指南
运维·服务器·网络·学习·自动化·bond
天天爱吃肉82182 小时前
【工程师笔记|新能源整车电控一次过CISPR25/BCI,汽车EMC/EMI落地十大核心设计技巧】
大数据·人工智能·笔记·python·汽车
星空2 小时前
WordPress在docker中部署
运维·docker·容器
代码地平线2 小时前
C++类与对象(中):默认成员函数与日期类实战
c++·笔记·算法
chemddd2 小时前
快递 选择
学习·交友
单词记忆方法研究2 小时前
探索上海背单词小程序品牌:高效学习工具的优化之路
大数据·学习·小程序
wsad05323 小时前
CentOS Stream 10 Docker 容器无法访问外网:xt_addrtype 内核模块缺失解决方法
linux·网络·docker·云原生·eureka·centos