Docker容器化与Kubernetes部署实战

72 阅读29分钟

一、引言

在传统的应用部署模式下,我们是否经常遇到这样的痛点:"在我机器上明明可以运行,为什么到服务器上就不行了?" 环境不一致、依赖冲突、资源利用率低、扩缩容困难等问题一直困扰着开发和运维团队。

随着微服务架构的普及,这些问题变得更加突出。想象一下,一个电商平台可能有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镜像是一个只读模板,包含了应用程序及其所有依赖。镜像采用分层存储结构,每个指令(如RUNCOPY)都会创建一层,底层使用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架构

91-docker-architecture.svg

Docker采用经典的C/S(客户端-服务器)架构:

Docker Client: 用户通过docker命令与Docker Daemon交互,如docker builddocker rundocker push。Client通过REST API或Unix Socket与Daemon通信。

Docker Daemon(dockerd): Docker的后台守护进程,负责镜像管理、容器管理、网络管理、存储管理等核心功能。Daemon调用Container Runtime(containerd/runc)来实际创建和运行容器。

Docker Registry: 镜像仓库,存储和分发镜像。Docker Hub是官方公共仓库,Harbor是企业级私有仓库,支持镜像复制、漏洞扫描、RBAC权限控制等高级功能。

工作流程:

  1. 用户通过Docker Client发送命令(如docker run nginx)
  2. Docker Daemon接收请求,检查本地是否有nginx镜像
  3. 如果没有,从Registry拉取镜像(pull)
  4. Daemon调用containerd创建容器
  5. containerd调用runc启动隔离的容器进程

3.1.2 Docker底层技术

Docker的核心是Linux内核的三大技术: Namespace、Cgroups、UnionFS。

91-namespace-cgroups.svg

(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_uscpu.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(联合文件系统) - 镜像分层

91-docker-image-layers.svg

UnionFS(如OverlayFS、AUFS)将多个目录(层)合并成一个统一的文件系统视图:

  • 镜像层(只读层): Dockerfile的每个指令(FROM、RUN、COPY等)创建一层,这些层是只读的,可以被多个容器共享。
  • 容器层(可写层): 容器运行时创建一个可写层,所有文件修改都写入该层。容器删除后,该层数据丢失。
  • 写时复制(Copy-On-Write): 修改镜像层的文件时,先将文件复制到容器层,再修改,原镜像层文件不变。

优势:

  1. 层复用: 多个镜像共享基础层(如openjdk基础镜像),节省存储空间。
  2. 增量构建: 只重新构建变更的层,加速镜像构建。
  3. 快速部署: 拉取镜像时,已存在的层不需要重复下载。

示例:

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架构总览

91-k8s-architecture.svg

Kubernetes采用Master/Worker架构,分为控制平面和数据平面:

Master节点(控制平面) - 集群大脑,管理整个集群:

  1. API Server: 集群的统一入口,所有组件都通过API Server通信。提供RESTful API,处理认证、授权、准入控制。所有集群状态变更都经过API Server。

  2. etcd: 分布式KV存储,保存集群所有状态数据(Pod、Service、ConfigMap等)。采用Raft协议保证一致性,通常部署3或5个实例保证高可用。

  3. Controller Manager: 控制器管理器,运行各种Controller:

    • Deployment Controller: 管理Deployment,创建ReplicaSet
    • ReplicaSet Controller: 管理ReplicaSet,创建Pod
    • Service Controller: 管理Service,分配ClusterIP
    • Node Controller: 监控Node状态,处理Node故障
    • Namespace Controller: 管理Namespace生命周期
  4. Scheduler: 调度器,为新创建的Pod选择合适的Node:

    • 预选(Predicate): 过滤不符合条件的Node
    • 优选(Priority): 给剩余Node打分,选择最优Node
    • 考虑资源、亲和性、污点/容忍等因素

Worker节点(数据平面) - 实际运行容器:

  1. Kubelet: 节点代理,管理Pod生命周期:

    • 监听API Server分配给自己的Pod
    • 调用Container Runtime创建容器
    • 定期上报Node和Pod状态
    • 执行健康检查(liveness/readiness probe)
  2. Kube-proxy: 网络代理,实现Service负载均衡:

    • 监听Service和Endpoints变化
    • 通过iptables或ipvs规则实现负载均衡
    • 将Service ClusterIP的流量转发到后端Pod
  3. Container Runtime: 容器运行时,实际创建和运行容器:

    • Docker(已弃用,K8s 1.24+移除)
    • containerd(推荐,轻量级)
    • CRI-O(专为K8s设计)

3.2.2 K8s核心工作流程

91-k8s-pod-creation-flow.svg

Pod创建完整流程:

  1. 用户提交Deployment YAML: 通过kubectl apply -f deployment.yaml提交到API Server
  2. API Server验证: 认证(你是谁)、授权(你能做什么)、准入控制(请求是否合法)
  3. 存储到etcd: API Server将Deployment对象持久化到etcd
  4. Controller Manager监听: Deployment Controller检测到新Deployment,创建ReplicaSet对象
  5. ReplicaSet创建Pod: ReplicaSet Controller根据副本数创建Pod对象
  6. Pod存储到etcd: Pod对象存储到etcd,状态为Pending,nodeName为空
  7. Scheduler监听: Scheduler检测到nodeName为空的Pod,开始调度
  8. 调度决策:
    • 预选: 过滤资源不足、有污点、不满足亲和性的Node
    • 优选: 给剩余Node打分(资源均衡、镜像已存在、亲和性得分)
    • 选择得分最高的Node
  9. 更新nodeName: Scheduler将Pod的nodeName更新到etcd,绑定到目标Node
  10. Kubelet监听: 目标Node的Kubelet检测到分配给自己的Pod
  11. 创建容器: Kubelet调用Container Runtime拉取镜像,创建并启动容器
  12. 更新状态: 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周
  2. 第二批:迁移准核心服务(商品服务、库存服务),观察1周
  3. 第三批:迁移核心服务(订单服务、支付服务),观察1周

迁移流程:

  1. 在K8s部署新版本服务(副本数1)
  2. 配置双写双读:流量同时发往虚拟机和K8s
  3. 对比两边数据一致性
  4. 逐步切换流量:10% → 50% → 100%
  5. 观察监控指标(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台VM30台物理机(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) 长期方案

  1. 增加Worker节点: 从10台扩容到15台,提供更多资源
  2. 配置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
  1. 使用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 CPULimit CPURequest MemoryLimit Memory
Java应用500m2000m512Mi2Gi
Go应用100m1000m128Mi512Mi
Nginx100m500m64Mi256Mi
MySQL1000m2000m2Gi4Gi
Redis500m1000m512Mi2Gi

特殊场景:

  • 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应用部署与运维,敬请期待!

🔗 相关资源: