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)
可观测性是指能够通过外部输出理解系统内部状态的能力,包含三大支柱:
-
日志(Logging):记录系统中发生的事件,用于故障排查和审计
-
指标(Metrics):量化系统的运行状态,如CPU使用率、请求延迟等
-
追踪(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的典型使用场景是:
- 开发环境:开发者使用Docker Desktop在本地构建和运行容器
- CI/CD流水线:在流水线中使用Docker构建镜像并推送到镜像仓库
- 镜像构建:使用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 │
└───────────────────┘ └─────────────────────┘ └─────────────────┘
架构设计原则
- 无状态优先:应用层尽量无状态,状态下沉到专门的存储层
- 分层隔离:不同层之间通过明确的接口通信,避免直接依赖
- 冗余设计:每一层都有冗余,消除单点故障
- 渐进式迁移:从简单架构开始,逐步增加复杂度
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的价值
- 构建一致性:CI环境中构建的镜像与生产环境运行的镜像完全一致
- 环境隔离:每个CI任务在独立容器中运行,互不干扰
- 可重复性:相同的Dockerfile+代码=相同的镜像
- 快速回滚:镜像版本化,出问题时秒级回滚到任意版本
- 不可变基础设施:部署的是镜像而非代码,运行环境不会被修改
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最佳实践
- 构建一次,部署多次:CI阶段构建的镜像在所有环境(测试/预发布/生产)中使用,不在各环境重新构建
- 镜像不可变:部署后不修改运行中的容器,需要变更则重新构建镜像
- 使用语义化版本:清晰的版本号便于追溯和管理
- 流水线即代码:将流水线定义纳入版本控制
- 快速失败:在流水线早期发现问题,尽早失败
- 并行化:独立的测试任务并行执行,缩短流水线时间
- 缓存优化:利用Docker层缓存和构建缓存加速构建
- 安全左移:在CI阶段就进行安全扫描,而非部署后才发现问题
本章详细讲解了Docker与三大CI/CD平台的集成方法。接下来,我们将正式进入Kubernetes的世界。
第四章 Kubernetes入门
4.1 Kubernetes概述与设计理念
Kubernetes(简称K8s)是Google基于其内部容器管理系统Borg的开源经验,于2014年捐赠给CNCF基金会后发展起来的容器编排平台。如今,K8s已成为云原生领域的事实标准,被全球绝大多数企业采用。
K8s的设计理念
-
声明式而非命令式:用户描述"期望状态"(如3个副本),K8s负责使实际状态趋近期望状态,而非逐条下达命令
-
最终一致性:系统会持续 reconciliation(协调),直到实际状态与期望状态一致
-
控制循环(Reconciliation Loop):控制器不断监控集群状态,发现偏差时自动纠正
-
松耦合与可插拔:各组件通过API通信,网络、存储、运行时等均可替换
-
自愈能力:容器崩溃自动重启,节点故障自动迁移
-
水平扩展:以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官方文档: https://docs.docker.com
- Kubernetes官方文档: https://kubernetes.io/docs
- CNCF Landscape: https://landscape.cncf.io
推荐书籍
| 书名 | 作者 | 适合阶段 |
|---|---|---|
| 《Docker技术入门与实战》 | 杨保华 | 初级 |
| 《Kubernetes in Action》 | Marko Lukša | 中级 |
| 《Cloud Native Patterns》 | Cornelia Davis | 中级 |
| 《Site Reliability Engineering》 | 高级 | |
| 《Designing Data-Intensive Applications》 | Martin Kleppmann | 高级 |
在线学习平台
- Kubernetes官方互动教程: https://kubernetes.io/docs/tutorials/
- Killer.sh (CKA/CKAD模拟考试): https://killer.sh
- Play with K8s: https://labs.play-with-k8s.com
- Katacoda (已归档但内容仍有参考价值)
开源社区
- CNCF Slack: https://cloud-native.slack.com
- Kubernetes GitHub: https://github.com/kubernetes/kubernetes
- Docker GitHub: https://github.com/docker
- 阿里云容器服务: https://www.alibabacloud.com/product/kubernetes
总结: Docker专栏完结篇
至此,我们的Docker技术专栏已经走过了十篇文章的完整旅程。从最初认识容器的基本概念,到掌握Docker镜像构建、容器管理、网络存储配置,再到使用Compose编排多服务应用,最后迈向生产环境部署和Kubernetes容器编排,我们构建了一个完整的容器化知识体系。
回顾核心要点
在本专栏中,我们学习了以下核心知识:
-
Docker基础:理解容器与虚拟化的本质区别,掌握Docker的客户端-服务端架构,学会使用基本命令管理容器生命周期。
-
镜像管理:深入理解镜像的分层存储机制,熟练编写Dockerfile,掌握多阶段构建、构建缓存优化等生产级技巧。
-
网络与存储:掌握Docker的各种网络模式(bridge/host/overlay)和存储方案(Volume/Bind Mount/tmpfs),能够为不同场景选择合适的配置。
-
Docker Compose:使用声明式YAML文件编排多容器应用,管理服务依赖、环境变量、网络和存储,实现一键启动完整应用栈。
-
安全实践:从镜像扫描、运行时隔离到密钥管理,建立容器安全的完整防线。
-
生产部署:从Docker Swarm轻量级集群到Kubernetes企业级编排,理解容器编排的核心概念和最佳实践。
-
CI/CD集成:将Docker融入持续集成/持续部署流水线,实现从代码提交到生产部署的全自动化。
-
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不仅是一项技能,更是一种面向未来的投资。
本专栏虽然完结,但学习之路永无止境。云原生技术生态日新月异,新的工具和理念不断涌现。希望本专栏能够为你打下坚实的基础,让你在云原生的道路上走得更远、更稳。
感谢每一位读者的陪伴。愿你在容器化和云原生的世界里,不断探索,持续成长。