一、引言
在传统的应用部署模式下,我们是否经常遇到这样的痛点:"在我机器上明明可以运行,为什么到服务器上就不行了?" 环境不一致、依赖冲突、资源利用率低、扩缩容困难等问题一直困扰着开发和运维团队。
随着微服务架构的普及,这些问题变得更加突出。想象一下,一个电商平台可能有30+个微服务,每个服务都有不同的运行环境、依赖库和配置文件。在大促期间,订单服务需要从3个实例快速扩容到30个实例,而手动部署需要大量时间和人力,且容易出错。
容器化技术的出现彻底改变了这一现状。Docker提供了轻量级、可移植的运行环境,保证了"一次构建,到处运行"。Kubernetes作为容器编排平台,实现了自动化部署、弹性伸缩和故障自愈,让我们可以像管理宠物一样管理服务器,变成管理牲畜一样管理容器集群。
本文将带你深入理解Docker和Kubernetes的核心原理,通过实战案例学习微服务容器化改造,并分享生产环境部署的最佳实践。无论你是刚接触容器技术的初学者,还是希望优化现有容器化方案的工程师,都能从本文中获得实用的知识和经验。
文章大纲:
- Docker核心原理: Namespace、Cgroups、UnionFS镜像分层机制
- Kubernetes架构: Master/Worker节点、核心组件、Pod创建流程
- 微服务容器化实战: Dockerfile编写、K8s部署、多环境管理
- 生产案例分享: 电商大促迁移、资源调度问题、Pod重启排查
- 最佳实践总结: 镜像优化、资源配置、高可用部署、Service Mesh简介
二、核心概念
2.1 Docker核心概念
镜像(Image): Docker镜像是一个只读模板,包含了应用程序及其所有依赖。镜像采用分层存储结构,每个指令(如RUN、COPY)都会创建一层,底层使用UnionFS联合文件系统将多层合并为单一视图。
容器(Container): 容器是镜像的运行实例,是一个隔离的进程。与虚拟机不同,容器共享宿主机内核,通过Namespace实现进程隔离,通过Cgroups实现资源限制,因此容器启动速度极快(秒级),资源占用极小(MB级)。
仓库(Registry): 镜像仓库用于存储和分发镜像。公共仓库如Docker Hub,企业内部通常搭建私有仓库如Harbor,支持镜像扫描、访问控制和镜像签名。
Dockerfile: 构建镜像的脚本文件,包含一系列指令(FROM、RUN、COPY、CMD等),Docker引擎按顺序执行这些指令,最终生成镜像。
2.2 Kubernetes核心概念
Pod: K8s的最小部署单元,包含一个或多个容器。同一个Pod内的容器共享网络命名空间(通过localhost通信)和存储卷,整体调度到同一个Node上。
Node: 工作节点,可以是物理机或虚拟机,运行Kubelet代理管理Pod生命周期,运行Kube-proxy配置Service网络规则,运行Container Runtime(如containerd)启动容器。
Master: 控制平面,管理整个集群。包含API Server(统一入口)、etcd(集群状态存储)、Controller Manager(控制器管理)、Scheduler(Pod调度器)。
Service: 为一组Pod提供稳定的网络访问入口。Pod的IP会随着重启而变化,Service通过Label Selector选择后端Pod,提供固定的ClusterIP或域名,实现服务发现和负载均衡。
Deployment: 声明式部署管理,定义Pod的期望状态(副本数、镜像版本等)。Controller持续监控实际状态,自动调谐使其与期望状态一致,支持滚动更新和版本回滚。
2.3 容器化 vs 虚拟化对比
| 特性 | 虚拟化(VM) | 容器化(Container) |
|---|---|---|
| 启动速度 | 分钟级(需启动完整OS) | 秒级(只启动应用进程) |
| 资源占用 | GB级(每个VM完整OS) | MB级(共享宿主机内核) |
| 隔离性 | 强(完全隔离,独立内核) | 中(进程隔离,共享内核) |
| 性能 | 有损耗(Hypervisor开销) | 接近原生(无虚拟化开销) |
| 镜像大小 | 几GB到几十GB | 几十MB到几百MB |
| 适用场景 | 需要完全隔离环境、运行不同OS | 微服务、快速部署、弹性伸缩 |
| 密度 | 单机10-20个VM | 单机100-1000个容器 |
容器的优势: 轻量快速、环境一致、弹性伸缩、资源利用率高 虚拟机的优势: 隔离性强、安全性高、可运行不同操作系统
实际生产中,两者常常结合使用:虚拟机提供基础资源隔离,容器提供应用层隔离和快速部署。
三、原理剖析
3.1 Docker核心原理与架构
3.1.1 Docker架构
Docker采用经典的C/S(客户端-服务器)架构:
Docker Client: 用户通过docker命令与Docker Daemon交互,如docker build、docker run、docker push。Client通过REST API或Unix Socket与Daemon通信。
Docker Daemon(dockerd): Docker的后台守护进程,负责镜像管理、容器管理、网络管理、存储管理等核心功能。Daemon调用Container Runtime(containerd/runc)来实际创建和运行容器。
Docker Registry: 镜像仓库,存储和分发镜像。Docker Hub是官方公共仓库,Harbor是企业级私有仓库,支持镜像复制、漏洞扫描、RBAC权限控制等高级功能。
工作流程:
- 用户通过Docker Client发送命令(如
docker run nginx) - Docker Daemon接收请求,检查本地是否有nginx镜像
- 如果没有,从Registry拉取镜像(pull)
- Daemon调用containerd创建容器
- containerd调用runc启动隔离的容器进程
3.1.2 Docker底层技术
Docker的核心是Linux内核的三大技术: Namespace、Cgroups、UnionFS。
(1) Namespace(命名空间) - 进程隔离
Namespace为进程提供独立的系统视图,让容器内的进程认为自己拥有独立的系统资源:
- PID namespace: 进程隔离。容器内的进程看到的PID从1开始(init进程),看不到宿主机和其他容器的进程。
- NET namespace: 网络隔离。每个容器有独立的网卡、IP地址、路由表、iptables规则。
- MNT namespace: 文件系统隔离。容器有独立的根目录(
/),看不到宿主机的完整文件系统。 - UTS namespace: 主机名隔离。容器可以有独立的hostname。
- IPC namespace: 进程间通信隔离。消息队列、信号量、共享内存等独立。
- USER namespace: 用户隔离。容器内的root用户可以映射到宿主机的普通用户,提升安全性。
示例:在容器内运行ps aux,只能看到容器内的进程;在宿主机运行ps aux,可以看到所有容器进程。
(2) Cgroups(控制组) - 资源限制
Cgroups限制容器可以使用的系统资源,防止某个容器耗尽宿主机资源:
- CPU限制:
cpu.cfs_quota_us和cpu.cfs_period_us限制CPU使用率。例如设置0.5核,容器最多使用50%的单核CPU。 - 内存限制:
memory.limit_in_bytes限制内存使用。超过限制触发OOM Killer,容器被杀死重启。 - 磁盘IO限制:
blkio.throttle.read_bps_device限制磁盘读写速度。 - 网络带宽限制: 配合
tc(traffic control)工具限制网络带宽。
示例:
docker run -it --cpus=0.5 --memory=512m nginx
该容器最多使用0.5核CPU和512MB内存。
(3) UnionFS(联合文件系统) - 镜像分层
UnionFS(如OverlayFS、AUFS)将多个目录(层)合并成一个统一的文件系统视图:
- 镜像层(只读层): Dockerfile的每个指令(FROM、RUN、COPY等)创建一层,这些层是只读的,可以被多个容器共享。
- 容器层(可写层): 容器运行时创建一个可写层,所有文件修改都写入该层。容器删除后,该层数据丢失。
- 写时复制(Copy-On-Write): 修改镜像层的文件时,先将文件复制到容器层,再修改,原镜像层文件不变。
优势:
- 层复用: 多个镜像共享基础层(如openjdk基础镜像),节省存储空间。
- 增量构建: 只重新构建变更的层,加速镜像构建。
- 快速部署: 拉取镜像时,已存在的层不需要重复下载。
示例:
FROM openjdk:17-jdk-alpine # Layer 0: 100MB
WORKDIR /app # Layer 1: 0KB
COPY target/*.jar app.jar # Layer 2: 50MB
EXPOSE 8080 # Layer 3: 0KB
镜像总大小150MB,但Layer 0可与其他Java应用共享,实际存储可能只增加50MB。
3.2 Kubernetes架构深度剖析
3.2.1 K8s架构总览
Kubernetes采用Master/Worker架构,分为控制平面和数据平面:
Master节点(控制平面) - 集群大脑,管理整个集群:
-
API Server: 集群的统一入口,所有组件都通过API Server通信。提供RESTful API,处理认证、授权、准入控制。所有集群状态变更都经过API Server。
-
etcd: 分布式KV存储,保存集群所有状态数据(Pod、Service、ConfigMap等)。采用Raft协议保证一致性,通常部署3或5个实例保证高可用。
-
Controller Manager: 控制器管理器,运行各种Controller:
- Deployment Controller: 管理Deployment,创建ReplicaSet
- ReplicaSet Controller: 管理ReplicaSet,创建Pod
- Service Controller: 管理Service,分配ClusterIP
- Node Controller: 监控Node状态,处理Node故障
- Namespace Controller: 管理Namespace生命周期
-
Scheduler: 调度器,为新创建的Pod选择合适的Node:
- 预选(Predicate): 过滤不符合条件的Node
- 优选(Priority): 给剩余Node打分,选择最优Node
- 考虑资源、亲和性、污点/容忍等因素
Worker节点(数据平面) - 实际运行容器:
-
Kubelet: 节点代理,管理Pod生命周期:
- 监听API Server分配给自己的Pod
- 调用Container Runtime创建容器
- 定期上报Node和Pod状态
- 执行健康检查(liveness/readiness probe)
-
Kube-proxy: 网络代理,实现Service负载均衡:
- 监听Service和Endpoints变化
- 通过iptables或ipvs规则实现负载均衡
- 将Service ClusterIP的流量转发到后端Pod
-
Container Runtime: 容器运行时,实际创建和运行容器:
- Docker(已弃用,K8s 1.24+移除)
- containerd(推荐,轻量级)
- CRI-O(专为K8s设计)
3.2.2 K8s核心工作流程
Pod创建完整流程:
- 用户提交Deployment YAML: 通过
kubectl apply -f deployment.yaml提交到API Server - API Server验证: 认证(你是谁)、授权(你能做什么)、准入控制(请求是否合法)
- 存储到etcd: API Server将Deployment对象持久化到etcd
- Controller Manager监听: Deployment Controller检测到新Deployment,创建ReplicaSet对象
- ReplicaSet创建Pod: ReplicaSet Controller根据副本数创建Pod对象
- Pod存储到etcd: Pod对象存储到etcd,状态为Pending,nodeName为空
- Scheduler监听: Scheduler检测到nodeName为空的Pod,开始调度
- 调度决策:
- 预选: 过滤资源不足、有污点、不满足亲和性的Node
- 优选: 给剩余Node打分(资源均衡、镜像已存在、亲和性得分)
- 选择得分最高的Node
- 更新nodeName: Scheduler将Pod的nodeName更新到etcd,绑定到目标Node
- Kubelet监听: 目标Node的Kubelet检测到分配给自己的Pod
- 创建容器: Kubelet调用Container Runtime拉取镜像,创建并启动容器
- 更新状态: Kubelet持续更新Pod状态到API Server(Pending → Running)
整个流程耗时: 通常2-10秒,主要取决于镜像拉取速度。
3.2.3 K8s资源对象详解
Workload资源(工作负载):
- Pod: 最小部署单元,包含一个或多个容器。通常不直接创建Pod,而是通过Deployment等控制器管理。
- Deployment: 无状态应用部署,支持滚动更新、版本回滚、弹性伸缩。适用场景:Web应用、API服务。
- StatefulSet: 有状态应用部署,Pod有稳定的网络标识(Pod名+序号)和持久化存储。适用场景:数据库、缓存、消息队列。
- DaemonSet: 每个Node运行一个Pod,常用于日志采集(Filebeat)、监控(Node Exporter)、网络插件(Calico)。
- Job/CronJob: 批处理任务。Job运行一次,CronJob定时运行。适用场景:数据备份、报表生成。
服务发现资源:
- Service: 为一组Pod提供稳定访问入口,支持ClusterIP(集群内访问)、NodePort(节点端口访问)、LoadBalancer(云厂商负载均衡)。
- Ingress: HTTP/HTTPS流量路由,基于域名和路径将流量转发到不同Service。需要Ingress Controller(如Nginx Ingress)才能生效。
配置资源:
- ConfigMap: 非敏感配置,如数据库地址、日志级别。可通过环境变量或文件挂载注入Pod。
- Secret: 敏感信息,如密码、密钥、证书。Base64编码存储,可加密(Encryption at Rest)。
3.3 K8s调度原理
调度流程分两步:
(1) 预选(Predicate) - 过滤不符合条件的Node:
- 资源限制: CPU/内存Request是否满足?节点剩余资源是否充足?
- NodeSelector: Pod指定的标签是否匹配Node?
- NodeAffinity: 节点亲和性规则是否满足?(硬性要求或软性偏好)
- PodAffinity: Pod间亲和性,如将同一应用的Pod部署在一起。
- PodAntiAffinity: Pod间反亲和性,如将同一应用的Pod分散到不同Node,避免单点故障。
- Taints/Tolerations: Node有污点(Taint),Pod需要容忍(Toleration)才能调度。常用于专用节点(如GPU节点)。
- Volume: 节点是否支持所需的存储卷?
(2) 优选(Priority) - 给剩余Node打分,选择最优:
- 资源均衡性: 优先选择资源使用率低的Node,避免某些Node过载。
- 镜像已存在: 优先选择已有镜像的Node,减少镜像拉取时间。
- 亲和性得分: 根据NodeAffinity/PodAffinity/PodAntiAffinity的权重打分。
- 自定义调度策略: 可通过Scheduler Extender扩展调度逻辑。
资源管理: Request vs Limit
resources:
requests:
memory: "512Mi"
cpu: "500m" # 0.5核
limits:
memory: "2Gi"
cpu: "2000m" # 2核
- Request: 调度时的最低资源保证。Scheduler根据Request判断Node是否有足够资源。Pod运行时保证至少有Request的资源。
- Limit: 运行时的最大资源限制。Pod使用超过Limit,CPU会被节流(throttle),内存会被OOM Killer杀死。
QoS等级(Quality of Service):
- Guaranteed: Request = Limit,资源有保证,最不容易被驱逐。
- Burstable: Request < Limit,可突发使用资源,中等优先级。
- BestEffort: 未设置Request/Limit,优先级最低,资源不足时最先被驱逐。
四、实战应用
4.1 Spring Boot应用Docker化
4.1.1 编写Dockerfile
多阶段构建,减小镜像体积:
# ============ 构建阶段 ============
FROM maven:3.8-openjdk-17-slim AS builder
WORKDIR /build
# 复制pom.xml,缓存依赖层
COPY pom.xml .
RUN mvn dependency:go-offline
# 复制源码,编译打包
COPY src ./src
RUN mvn clean package -DskipTests
# ============ 运行阶段 ============
FROM openjdk:17-jdk-alpine
LABEL maintainer="dev@example.com"
LABEL version="1.0.0"
# 安装curl(用于健康检查)
RUN apk add --no-cache curl
WORKDIR /app
# 从构建阶段复制jar包
COPY --from=builder /build/target/*.jar app.jar
# 创建非root用户运行应用(安全最佳实践)
RUN addgroup -S appuser && adduser -S appuser -G appuser
RUN chown -R appuser:appuser /app
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["java", \
"-Djava.security.egd=file:/dev/./urandom", \
"-Xms512m", "-Xmx1024m", \
"-XX:+UseG1GC", \
"-jar", "app.jar"]
关键点解析:
- 多阶段构建: 构建阶段(maven镜像)只用于编译,运行阶段(jdk-alpine)只包含必要运行时,镜像体积从800MB降到200MB。
- 依赖缓存: 先复制
pom.xml并下载依赖,再复制源码。pom.xml不变时,依赖层会被缓存,加速构建。 - 非root用户: 创建普通用户运行应用,避免容器逃逸风险。
- 健康检查: Docker会定期检查容器健康状态,不健康的容器会被标记。
4.1.2 构建与推送镜像
# 1. 构建镜像
docker build -t myapp:1.0.0 .
# 2. 本地测试
docker run -d -p 8080:8080 --name myapp-test myapp:1.0.0
curl http://localhost:8080/actuator/health
# 3. 标记镜像(推送到私有仓库)
docker tag myapp:1.0.0 harbor.example.com/project/myapp:1.0.0
# 4. 登录私有仓库
docker login harbor.example.com
# 5. 推送镜像
docker push harbor.example.com/project/myapp:1.0.0
# 6. 扫描镜像漏洞(可选)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image harbor.example.com/project/myapp:1.0.0
4.1.3 Docker Compose本地编排
开发环境可使用Docker Compose快速启动多个服务:
version: '3.8'
services:
# 应用服务
app:
build: .
image: myapp:1.0.0
container_name: myapp
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_REDIS_HOST=redis
depends_on:
- mysql
- redis
networks:
- app-network
restart: unless-stopped
# MySQL数据库
mysql:
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: mydb
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
networks:
- app-network
# Redis缓存
redis:
image: redis:7-alpine
container_name: redis
command: redis-server --appendonly yes
volumes:
- redis-data:/data
ports:
- "6379:6379"
networks:
- app-network
volumes:
mysql-data:
redis-data:
networks:
app-network:
driver: bridge
启动:
docker-compose up -d
docker-compose ps
docker-compose logs -f app
4.2 K8s部署Spring Boot应用
4.2.1 完整Deployment配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
labels:
app: myapp
version: v1.0.0
component: backend
annotations:
description: "Spring Boot应用"
maintainer: "dev@example.com"
spec:
replicas: 3
selector:
matchLabels:
app: myapp
# 滚动更新策略
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多超出期望副本数1个
maxUnavailable: 0 # 更新时保证所有Pod可用
# Pod最少就绪时间(就绪探针成功后等待时间)
minReadySeconds: 10
# 保留历史版本数(用于回滚)
revisionHistoryLimit: 10
template:
metadata:
labels:
app: myapp
version: v1.0.0
annotations:
# Prometheus监控
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
spec:
# Pod反亲和性,避免部署在同一节点
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: myapp
topologyKey: kubernetes.io/hostname
containers:
- name: myapp
image: harbor.example.com/project/myapp:1.0.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
# 资源限制(重要!)
resources:
requests:
memory: "512Mi"
cpu: "500m" # 0.5核
limits:
memory: "2Gi"
cpu: "2000m" # 2核
# 环境变量
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: JAVA_OPTS
value: "-Xms512m -Xmx1536m -XX:+UseG1GC"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# 从ConfigMap和Secret加载配置
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secret
# 存活探针(失败重启容器)
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
scheme: HTTP
initialDelaySeconds: 60 # 应用启动需要时间
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
# 就绪探针(失败从Service移除)
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
# 启动探针(适用于启动慢的应用)
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 30 # 最多150秒启动时间
# 生命周期钩子
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Container started at $(date)' >> /var/log/lifecycle.log"]
preStop:
exec:
# 优雅停机:等待15秒,让Nginx停止转发新请求
command: ["/bin/sh", "-c", "sleep 15"]
# 挂载卷
volumeMounts:
- name: log-volume
mountPath: /var/log
- name: config-volume
mountPath: /app/config
readOnly: true
# 存储卷定义
volumes:
- name: log-volume
emptyDir: {}
- name: config-volume
configMap:
name: myapp-config-files
# 镜像拉取密钥
imagePullSecrets:
- name: harbor-secret
# DNS配置
dnsPolicy: ClusterFirst
# 重启策略
restartPolicy: Always
# 优雅终止时间(Pod删除时等待时间)
terminationGracePeriodSeconds: 30
---
# Service
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: production
labels:
app: myapp
spec:
type: ClusterIP
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
sessionAffinity: ClientIP # 会话保持
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3小时
---
# Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: tls-secret
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
4.2.2 ConfigMap与Secret管理
# ConfigMap: 非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: production
data:
APP_ENV: "production"
LOG_LEVEL: "INFO"
REDIS_HOST: "redis-service.production.svc.cluster.local"
MYSQL_HOST: "mysql-service.production.svc.cluster.local"
MYSQL_PORT: "3306"
CACHE_EXPIRE_SECONDS: "3600"
---
# ConfigMap: 配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config-files
namespace: production
data:
application.yml: |
server:
port: 8080
tomcat:
max-threads: 200
spring:
datasource:
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/mydb
hikari:
maximum-pool-size: 20
redis:
host: ${REDIS_HOST}
port: 6379
timeout: 3000ms
---
# Secret: 敏感信息(base64编码)
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
namespace: production
type: Opaque
data:
# echo -n 'password123' | base64
MYSQL_PASSWORD: cGFzc3dvcmQxMjM=
REDIS_PASSWORD: cmVkaXMxMjM=
# echo -n 'your-jwt-secret-key-at-least-256-bits' | base64
JWT_SECRET: eW91ci1qd3Qtc2VjcmV0LWtleS1hdC1sZWFzdC0yNTYtYml0cw==
创建Secret(推荐方式):
kubectl create secret generic myapp-secret \
--from-literal=MYSQL_PASSWORD=password123 \
--from-literal=REDIS_PASSWORD=redis123 \
--from-literal=JWT_SECRET=your-jwt-secret-key \
-n production
4.3 K8s部署完整微服务应用
4.3.1 部署脚本
#!/bin/bash
set -e
NAMESPACE="production"
echo "开始部署微服务到K8s集群..."
# 1. 创建命名空间
echo "[1/8] 创建命名空间 $NAMESPACE"
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
# 2. 创建镜像拉取密钥
echo "[2/8] 配置Harbor镜像仓库密钥"
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.example.com \
--docker-username=admin \
--docker-password=Harbor12345 \
--namespace=$NAMESPACE \
--dry-run=client -o yaml | kubectl apply -f -
# 3. 部署基础设施: MySQL
echo "[3/8] 部署MySQL数据库"
kubectl apply -f k8s/mysql-statefulset.yaml -n $NAMESPACE
kubectl wait --for=condition=ready pod -l app=mysql -n $NAMESPACE --timeout=300s
# 4. 部署基础设施: Redis
echo "[4/8] 部署Redis缓存"
kubectl apply -f k8s/redis-deployment.yaml -n $NAMESPACE
kubectl wait --for=condition=ready pod -l app=redis -n $NAMESPACE --timeout=120s
# 5. 部署网关服务
echo "[5/8] 部署API Gateway"
kubectl apply -f k8s/gateway-deployment.yaml -n $NAMESPACE
kubectl wait --for=condition=ready pod -l app=gateway -n $NAMESPACE --timeout=180s
# 6. 部署业务服务
echo "[6/8] 部署业务微服务"
kubectl apply -f k8s/user-service-deployment.yaml -n $NAMESPACE
kubectl apply -f k8s/product-service-deployment.yaml -n $NAMESPACE
kubectl apply -f k8s/order-service-deployment.yaml -n $NAMESPACE
kubectl apply -f k8s/payment-service-deployment.yaml -n $NAMESPACE
# 等待所有业务服务就绪
kubectl wait --for=condition=ready pod -l tier=backend -n $NAMESPACE --timeout=300s
# 7. 部署Ingress
echo "[7/8] 配置Ingress流量入口"
kubectl apply -f k8s/ingress.yaml -n $NAMESPACE
# 8. 查看部署状态
echo "[8/8] 查看部署状态"
echo "================== Pods =================="
kubectl get pods -n $NAMESPACE -o wide
echo ""
echo "================== Services =================="
kubectl get svc -n $NAMESPACE
echo ""
echo "================== Ingress =================="
kubectl get ingress -n $NAMESPACE
echo "✅ 部署完成!"
echo "访问地址: https://api.example.com"
4.3.2 多环境管理
方式1: 使用Namespace隔离环境
# 开发环境
kubectl apply -f deployment.yaml -n dev
# 测试环境
kubectl apply -f deployment.yaml -n test
# 生产环境
kubectl apply -f deployment.yaml -n production
方式2: 使用Kustomize管理多环境
目录结构:
k8s/
├── base/ # 基础配置
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
├── overlays/
│ ├── dev/ # 开发环境覆盖
│ │ ├── kustomization.yaml
│ │ └── replicas.yaml
│ ├── test/ # 测试环境覆盖
│ │ └── kustomization.yaml
│ └── prod/ # 生产环境覆盖
│ ├── kustomization.yaml
│ └── resources.yaml
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
replicas:
- name: myapp
count: 5
patchesStrategicMerge:
- resources.yaml
部署:
kubectl apply -k k8s/overlays/prod
五、生产案例
5.1 电商大促容器化迁移全流程
背景
某电商平台原有30+微服务部署在虚拟机上,面临以下痛点:
- 部署慢: 手动部署一个服务需要30分钟,大促前扩容需要提前2天申请资源
- 资源利用率低: 虚拟机平均CPU使用率仅20%,内存使用率30%,造成资源浪费
- 环境不一致: 开发、测试、生产环境配置差异,经常出现"在我机器上能跑"问题
- 故障恢复慢: 服务宕机需要人工介入重启,平均恢复时间15分钟
为应对双11大促,决定将所有服务迁移到K8s,实现自动化部署和弹性伸缩。
迁移步骤
(1) 应用容器化改造 (第1-2周)
- 编写Dockerfile,采用多阶段构建减小镜像体积
- 配置外部化:数据库连接、Redis地址等通过环境变量注入
- 无状态化改造:Session从内存迁移到Redis,文件上传从本地磁盘改为OSS
- 添加健康检查端点:
/actuator/health
改造示例:
// 改造前:配置写死在代码中
String dbUrl = "jdbc:mysql://192.168.1.100:3306/mydb";
// 改造后:从环境变量读取
@Value("${spring.datasource.url}")
private String dbUrl;
(2) 搭建K8s集群 (第3周)
- Master节点: 3台(2核4G),高可用部署
- Worker节点: 10台(8核16G),可根据流量动态扩容
- 网络插件: Calico(支持NetworkPolicy)
- 存储插件: NFS CSI Driver(共享存储)
- 监控: Prometheus + Grafana
- 日志: ELK Stack
使用kubeadm快速部署:
# Master节点初始化
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.1.10
# Worker节点加入
kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx
# 安装Calico网络插件
kubectl apply -f calico.yaml
(3) 迁移基础设施 (第4周)
- MySQL: 使用StatefulSet部署主从架构,主库1个,从库2个,数据持久化到NFS
- Redis: 使用Deployment部署哨兵模式,3个哨兵节点,6个Redis节点(3主3从)
- RocketMQ: 使用Operator部署,2个NameServer,4个Broker(2主2从)
(4) 微服务逐步迁移 (第5-7周)
采用灰度迁移策略,降低风险:
- 第一批:迁移非核心服务(消息中心、文件服务),观察1周
- 第二批:迁移准核心服务(商品服务、库存服务),观察1周
- 第三批:迁移核心服务(订单服务、支付服务),观察1周
迁移流程:
- 在K8s部署新版本服务(副本数1)
- 配置双写双读:流量同时发往虚拟机和K8s
- 对比两边数据一致性
- 逐步切换流量:10% → 50% → 100%
- 观察监控指标(QPS、RT、错误率)无异常后,下线虚拟机服务
(5) 压测与验证 (第8周)
- 全链路压测:模拟10倍流量(峰值QPS 50万)
- 故障演练:随机Kill Pod,验证自动恢复
- 性能对比:K8s部署的服务响应时间降低20%
迁移效果
| 指标 | 迁移前(虚拟机) | 迁移后(K8s) | 提升 |
|---|---|---|---|
| 部署时间 | 30分钟/服务 | 5分钟/服务 | 提升6倍 |
| 资源利用率 | CPU 20%, 内存 30% | CPU 60%, 内存 70% | 提升3倍 |
| 扩容速度 | 小时级(申请+部署) | 分钟级(HPA自动) | 提升60倍 |
| 故障恢复 | 15分钟(人工) | 30秒(自动) | 提升30倍 |
| 成本 | 100台VM | 30台物理机(K8s节点) | 节省70% |
大促表现:
- 订单服务从5个实例自动扩容到50个实例,全程无人工干预
- 峰值QPS 80万,P99延迟保持在100ms以下
- 0故障,0投诉,平稳度过双11大促
5.2 K8s资源不足导致Pod无法调度
故障现象
某天下午,运维同学反馈新部署的订单服务Pod一直处于Pending状态,无法启动。
$ kubectl get pods -n production
NAME READY STATUS RESTARTS AGE
order-service-xxx 0/1 Pending 0 5m
排查过程
(1) 查看Pod详情
$ kubectl describe pod order-service-xxx -n production
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m default-scheduler 0/10 nodes are available: 10 Insufficient cpu.
错误信息:"0/10 nodes are available: 10 Insufficient cpu" 原因:所有10个Node的CPU资源都不足。
(2) 查看Node资源使用情况
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 7800m 97% 14Gi 87%
worker-2 7600m 95% 13Gi 81%
worker-3 7900m 98% 15Gi 93%
...
所有Node的CPU使用率都在95%以上,资源几乎耗尽。
(3) 查看Pod的资源请求
$ kubectl get pods -n production -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources.requests}{"\n"}{end}'
order-service-xxx map[cpu:2000m memory:2Gi]
product-service-yyy map[cpu:2000m memory:1Gi]
user-service-zzz map[cpu:2000m memory:512Mi]
...
发现订单服务的CPU Request设置为2000m(2核),而每个Node只有8核,已被其他Pod占用7.8核,无法满足新Pod的Request。
问题原因
开发同学在配置资源限制时,参考了虚拟机配置(4核8G),直接将CPU Request设置为2核。但实际运行时,订单服务平均CPU使用率只有500m(0.5核),造成资源浪费和调度失败。
解决方案
(1) 短期方案:调整CPU Request
resources:
requests:
cpu: "500m" # 从2000m降到500m
memory: "1Gi" # 从2Gi降到1Gi
limits:
cpu: "2000m" # Limit保持2核,允许突发
memory: "2Gi"
重新部署后,Pod成功调度到Node上。
(2) 长期方案
- 增加Worker节点: 从10台扩容到15台,提供更多资源
- 配置HPA自动扩缩容:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- 使用Cluster Autoscaler: 当Node资源不足时,自动向云厂商申请新Node
经验教训
- Request设置要合理: Request应该设置为应用正常运行所需资源,而非峰值资源
- 监控资源使用: 定期查看Pod实际CPU/内存使用率,调整Request/Limit
- 预留资源: Node不要跑满,预留10-20%资源用于突发流量
- 配置HPA: 自动扩缩容,避免手动调整副本数
5.3 Pod频繁重启问题排查
故障现象
告警系统显示,某商品服务Pod频繁重启,RESTARTS次数不断增加。
$ kubectl get pods -n production
NAME READY STATUS RESTARTS AGE
product-service-abc 0/1 CrashLoopBackOff 5 10m
CrashLoopBackOff:容器启动后立即退出,K8s不断尝试重启,重启间隔逐渐增加(10s、20s、40s、80s...最多5分钟)。
排查过程
(1) 查看Pod日志
$ kubectl logs product-service-abc -n production --previous
...
2024-11-20 10:30:15 INFO Application starting...
2024-11-20 10:30:20 INFO Loading product data from database...
2024-11-20 10:30:25 ERROR java.lang.OutOfMemoryError: Java heap space
at java.util.HashMap.resize(HashMap.java:704)
at com.example.ProductService.loadData(ProductService.java:45)
日志显示:java.lang.OutOfMemoryError: Java heap space (JVM堆内存溢出)
(2) 查看Pod资源限制
$ kubectl describe pod product-service-abc -n production
Containers:
product-service:
Limits:
memory: 512Mi
cpu: 1
Requests:
memory: 256Mi
cpu: 500m
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
关键信息:
- 内存Limit: 512Mi
- Last State Reason: OOMKilled (被OOM Killer杀死)
- Exit Code: 137 (128 + 9 = SIGKILL)
(3) 分析原因
商品服务启动时会从数据库加载全部商品数据(约200万条)到内存中构建缓存,内存占用约800MB,超过了512Mi的Limit,触发OOM Killer,容器被杀死。
解决方案
(1) 增加内存Limit
resources:
requests:
memory: "512Mi"
limits:
memory: "2Gi" # 从512Mi增加到2Gi
(2) 应用层优化
- 延迟加载: 启动时不加载全部数据,按需从数据库或缓存查询
- 分批加载: 分批加载数据,降低内存峰值
// 改造前:启动时加载全部商品
public void init() {
List<Product> products = productMapper.selectAll(); // 200万条
productCache.putAll(products);
}
// 改造后:延迟加载,只缓存热门商品
public void init() {
List<Product> hotProducts = productMapper.selectHotProducts(1000);
productCache.putAll(hotProducts);
}
(3) 调整JVM参数
env:
- name: JAVA_OPTS
value: "-Xms512m -Xmx1536m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
-Xms512m: 初始堆512MB-Xmx1536m: 最大堆1536MB(留出空间给堆外内存、线程栈等)-XX:+UseG1GC: 使用G1垃圾收集器-XX:MaxGCPauseMillis=200: 最大GC停顿时间200ms
(4) 调整健康检查
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 90 # 从60秒增加到90秒
periodSeconds: 10
failureThreshold: 3
增加initialDelaySeconds,避免应用启动时(加载数据)被误判为不健康而被杀死。
效果
调整后,Pod成功启动并稳定运行,内存使用稳定在1.2GB,未再出现OOM。
经验教训
- 内存Limit要合理: 考虑应用启动时的内存峰值,而非稳定运行时的内存
- JVM堆内存:
-Xmx应小于内存Limit,留出空间给堆外内存(通常留出20-30%) - 应用优化优先: 优先优化应用逻辑,减少内存占用,而非一味增加资源
- 监控内存使用: 定期查看Pod内存使用趋势,提前发现问题
5.4 镜像拉取失败导致部署慢
问题描述
部署新版本订单服务时,Pod长时间处于ImagePullBackOff状态,30分钟后才成功启动,严重影响发布速度。
$ kubectl get pods -n production
NAME READY STATUS RESTARTS AGE
order-service-new-xxx 0/1 ImagePullBackOff 0 25m
原因分析
查看Pod Events:
$ kubectl describe pod order-service-new-xxx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulling 10m (x4 over 25m) kubelet Pulling image "harbor.example.com/project/order-service:2.0.0"
Warning Failed 9m (x4 over 24m) kubelet Failed to pull image: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Warning Failed 9m (x4 over 24m) kubelet Error: ErrImagePull
原因:镜像仓库Harbor部署在海外,网络不稳定,拉取镜像超时。
解决方案
(1) 搭建国内Harbor私有镜像仓库
在国内阿里云搭建Harbor,镜像拉取速度从10MB/s提升到100MB/s。
(2) 配置镜像加速器
修改containerd配置,添加镜像加速器:
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://your-mirror.aliyuncs.com"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.example.com"]
endpoint = ["https://harbor-cn.example.com"]
(3) 使用imagePullPolicy: IfNotPresent
containers:
- name: order-service
image: harbor.example.com/project/order-service:2.0.0
imagePullPolicy: IfNotPresent # 优先使用本地镜像
避免每次都从远程拉取镜像。
(4) 预拉取镜像
大版本升级前,在所有Node上预拉取镜像:
# 在所有Node上执行
for node in $(kubectl get nodes -o name); do
kubectl debug $node -it --image=harbor.example.com/project/order-service:2.0.0 -- sh -c "exit"
done
效果
- 镜像拉取时间从30分钟降到30秒
- 部署成功率从70%提升到99%
- 发布效率大幅提升
六、最佳实践
6.1 Dockerfile最佳实践
6.1.1 镜像体积优化
(1) 使用Alpine或Distroless基础镜像
# 普通镜像:800MB
FROM openjdk:17-jdk
# Alpine镜像:200MB (减少75%)
FROM openjdk:17-jdk-alpine
# Distroless镜像:180MB (最小化,仅包含运行时)
FROM gcr.io/distroless/java17-debian11
(2) 多阶段构建,分离构建环境和运行环境
# ❌ 单阶段构建:镜像包含Maven、源码等,体积800MB
FROM maven:3.8-openjdk-17
WORKDIR /app
COPY . .
RUN mvn clean package
CMD ["java", "-jar", "target/app.jar"]
# ✅ 多阶段构建:运行镜像仅包含JDK和jar,体积200MB
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=builder /build/target/app.jar .
CMD ["java", "-jar", "app.jar"]
(3) 清理临时文件和缓存
RUN apk add --no-cache curl \
&& rm -rf /var/cache/apk/* # 删除apk缓存
(4) 合并RUN指令,减少层数
# ❌ 多个RUN指令,每个指令创建一层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*
# ✅ 合并RUN指令,仅创建一层
RUN apt-get update && \
apt-get install -y curl vim && \
rm -rf /var/lib/apt/lists/*
6.1.2 安全最佳实践
(1) 不使用root用户运行应用
# 创建普通用户
RUN addgroup -S appuser && adduser -S appuser -G appuser
# 切换到普通用户
USER appuser
(2) 使用.dockerignore排除敏感文件
.git
.env
*.log
node_modules
target
.idea
(3) 定期更新基础镜像,修复安全漏洞
# 使用Trivy扫描镜像漏洞
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:1.0.0
# 输出:
# HIGH: CVE-2023-xxxx (修复版本: 1.2.3)
(4) 镜像签名与验证
使用Docker Content Trust或Cosign签名镜像,防止镜像被篡改:
# 启用Docker Content Trust
export DOCKER_CONTENT_TRUST=1
docker push harbor.example.com/project/myapp:1.0.0
6.2 K8s资源配置最佳实践
6.2.1 资源限制设置
原则:
- 始终设置Request和Limit: 防止Pod无限占用资源,影响其他Pod
- Request = 正常运行所需: 保证Pod至少有足够资源运行
- Limit = Request的1.5-2倍: 允许突发,但防止无限增长
不同应用类型的配置:
| 应用类型 | Request CPU | Limit CPU | Request Memory | Limit Memory |
|---|---|---|---|---|
| Java应用 | 500m | 2000m | 512Mi | 2Gi |
| Go应用 | 100m | 1000m | 128Mi | 512Mi |
| Nginx | 100m | 500m | 64Mi | 256Mi |
| MySQL | 1000m | 2000m | 2Gi | 4Gi |
| Redis | 500m | 1000m | 512Mi | 2Gi |
特殊场景:
- CPU密集型应用: Limit = Request,避免CPU节流影响性能
- 内存密集型应用: Limit可以放宽到Request的2-3倍
# CPU密集型应用(如数据处理)
resources:
requests:
cpu: "2000m"
limits:
cpu: "2000m" # Limit = Request,避免节流
# 内存密集型应用(如大数据)
resources:
requests:
memory: "4Gi"
limits:
memory: "12Gi" # Limit = Request的3倍,允许突发
6.2.2 健康检查配置
LivenessProbe(存活探针) - 检测容器是否运行,失败重启:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
scheme: HTTP
initialDelaySeconds: 60 # 应用启动需要时间
periodSeconds: 10 # 每10秒检查一次
timeoutSeconds: 5 # 超时时间5秒
successThreshold: 1 # 成功1次即认为健康
failureThreshold: 3 # 连续失败3次才重启
配置建议:
initialDelaySeconds: 设置为应用启动时间+10秒failureThreshold: 至少3次,避免偶尔网络抖动导致重启
ReadinessProbe(就绪探针) - 检测容器是否准备好接收流量,失败从Service移除:
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30 # 稍短于LivenessProbe
periodSeconds: 5 # 检查更频繁,快速发现不可用
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
StartupProbe(启动探针) - 适用于启动慢的应用:
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 30 # 最多等待150秒启动
启动探针成功后,才开始执行Liveness和Readiness探针。
6.3 生产环境部署Checklist
镜像管理
- 使用多阶段构建,减小镜像体积
- 镜像标签使用语义化版本(v1.2.3),避免使用
latest - 镜像扫描通过,无高危漏洞
- 推送到私有镜像仓库(Harbor)
- 配置镜像签名与验证
资源配置
- 设置合理的CPU和内存Request/Limit
- 配置LivenessProbe和ReadinessProbe
- 配置优雅停机(preStop hook, terminationGracePeriodSeconds)
- 配置PodDisruptionBudget,防止批量驱逐
高可用配置
- 副本数≥3,确保高可用
- 配置Pod反亲和性,分散到不同Node
- 配置HPA自动扩缩容
- 配置资源Quota和LimitRange,防止资源耗尽
安全配置
- 不使用root用户运行容器
- 敏感信息使用Secret存储,不写在代码/镜像中
- 配置NetworkPolicy网络隔离
- 配置RBAC权限控制,最小权限原则
监控与日志
- 配置Prometheus监控,采集QPS/RT/错误率
- 配置日志采集(Filebeat/Fluentd → ELK)
- 配置告警规则(CPU/内存/Pod重启/错误率)
- 配置链路追踪(SkyWalking/Jaeger)
灰度发布
- 使用滚动更新策略(maxSurge/maxUnavailable)
- 配置金丝雀发布(Istio或Nginx Ingress)
- 观察监控指标,逐步放量(10% → 50% → 100%)
- 准备回滚方案(kubectl rollout undo)
6.4 Service Mesh (Istio) 简介
什么是Service Mesh
Service Mesh(服务网格)是处理服务间通信的专用基础设施层,提供流量管理、安全、可观测性功能,解耦业务逻辑与通信逻辑。
核心价值:
- 开发人员专注业务逻辑,不用关心重试、超时、熔断等通信细节
- 统一配置流量策略,无需修改应用代码
- 细粒度流量控制,支持灰度发布、A/B测试、流量镜像
Istio核心功能
1. 流量管理
- 灰度发布: 10%流量到新版本,90%流量到旧版本
- 金丝雀发布: 先发布到部分用户,逐步扩大范围
- A/B测试: 不同用户路由到不同版本
- 流量镜像: 复制生产流量到测试环境
示例:将10%流量路由到v2版本
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- route:
- destination:
host: order-service
subset: v1
weight: 90
- destination:
host: order-service
subset: v2
weight: 10
2. 安全
- mTLS加密: 服务间通信自动加密,防止中间人攻击
- 授权策略: 细粒度访问控制,如"订单服务只能被网关调用"
- 证书管理: 自动颁发、轮换证书
3. 可观测性
- 分布式追踪: 集成Jaeger/Zipkin,追踪请求链路
- 指标采集: 自动采集请求量、延迟、错误率
- 服务拓扑: 可视化服务依赖关系
Istio架构
- 数据平面: Envoy Sidecar代理,每个Pod注入一个Envoy容器,拦截所有进出流量
- 控制平面: Istiod,统一管理配置、证书、策略
何时使用Service Mesh
适用场景:
- ✅ 服务数量多(>20个微服务)
- ✅ 需要细粒度流量控制(灰度发布、A/B测试)
- ✅ 需要零信任安全(mTLS、授权策略)
- ✅ 需要深度可观测性(分布式追踪、服务拓扑)
不适用场景:
- ❌ 服务数量少(<10个微服务),复杂度不值得
- ❌ 团队规模小,运维能力不足
- ❌ 性能要求极高(Sidecar有5-10ms延迟)
七、总结回顾
本文深入讲解了Docker容器化与Kubernetes部署的核心原理和实战经验,主要内容包括:
知识点梳理
✅ Docker核心原理:
- Namespace实现进程隔离(PID、NET、MNT、UTS、IPC、USER)
- Cgroups实现资源限制(CPU、内存、磁盘IO、网络带宽)
- UnionFS实现镜像分层(层复用、写时复制、增量构建)
✅ Kubernetes架构:
- Master节点:API Server、etcd、Controller Manager、Scheduler
- Worker节点:Kubelet、Kube-proxy、Container Runtime
- Pod创建流程:从用户提交到容器运行的完整链路
✅ K8s资源对象:
- Workload:Pod、Deployment、StatefulSet、DaemonSet、Job/CronJob
- Service:ClusterIP、NodePort、LoadBalancer
- 配置:ConfigMap、Secret
- Ingress:HTTP/HTTPS流量路由
✅ 容器化实战:
- Dockerfile编写(多阶段构建、非root用户、健康检查)
- K8s部署(Deployment、Service、Ingress配置)
- ConfigMap/Secret管理
- 多环境管理(Namespace、Kustomize)
✅ 生产案例:
- 电商大促容器化迁移:部署时间从30分钟降到5分钟,资源利用率提升3倍
- 资源不足导致调度失败:合理设置Request/Limit,配置HPA
- Pod频繁重启:OOM问题排查,内存优化,JVM参数调整
- 镜像拉取失败:搭建私有仓库,配置镜像加速,预拉取镜像
本文从Docker容器化基础到Kubernetes生产级部署,涵盖核心原理、实战应用、生产案例和最佳实践。希望能帮助你系统掌握容器技术,在实际项目中游刃有余。下一篇我们将深入Kubernetes应用部署与运维,敬请期待!
🔗 相关资源:
- 本文配套代码: GitHub仓库
- Kubernetes官方文档: kubernetes.io/docs/
- Docker官方文档: docs.docker.com/
- Istio官方文档: istio.io/docs/