Docker+K8s生产级部署实战:从0到1打造高可用微服务集群

前言

"这个服务在我本地明明跑得好好的!"

这句话大概是 DevOps 工程师听到最多的抱怨。本地能跑,生产必挂------这是没有容器化的项目逃不掉的宿命。

2025年,Docker + Kubernetes(K8s) 已经是企业级部署的事实标准。无论你是:

  • 🐳 想从零学会 Docker 的后端工程师
  • ☸️ 想进阶 K8s 的 DevOps 从业者
  • 🏗️ 想搭建高可用微服务架构的架构师

这篇文章,手把手带你从镜像构建集群部署 ,从本地开发生产高可用,完成一个完整的 DevOps 实战流程。


目录

  1. 前置知识:Docker和K8s到底是什么?
  2. 环境准备:安装Docker和Kubernetes
  3. 第一章:Docker化改造:把应用装进容器
  4. [第二章:Docker Compose:本地多容器编排](#第二章:Docker Compose:本地多容器编排)
  5. 第三章:Kubernetes入门:核心概念详解
  6. 第四章:从零部署应用到K8s集群
  7. 第五章:服务治理:Ingress、Service与网络
  8. 第六章:数据持久化:ConfigMap、Secret与PV/PVC
  9. 第七章:高可用与弹性扩缩容
  10. [第八章:CI/CD实战:GitHub Actions自动部署](#第八章:CI/CD实战:GitHub Actions自动部署)
  11. 实战完整项目代码
  12. 常见问题与排错指南

1. 前置知识:Docker 和 K8s 到底是什么?

1.1 Docker:应用的"集装箱"

Docker 的核心思想是 Container(容器),类似于集装箱:

复制代码
传统部署:              Docker 部署:
┌─────────────┐        ┌─────────────────────┐
│ 服务器A      │        │ 服务器A(Docker宿主机)│
│  app.exe    │        │  ┌──────┐ ┌──────┐ │
│  config.ini │   →    │  │容器1 │ │容器2 │ │
│  python.exe │        │  │app   │ │db    │ │
│  mysql.exe  │        │  │config│ │mysql │ │
│  nginx.exe │        │  │依赖  │ │依赖  │ │
└─────────────┘        │  └──────┘ └──────┘ │
                       │      ↑ Docker Engine  │
                       └─────────────────────┘

容器 vs 虚拟机

对比项 虚拟机 (VM) 容器 (Container)
启动时间 几分钟 几秒钟
资源占用 完整操作系统,很重 共享宿主机内核,很轻
隔离性 完全隔离 进程级隔离
性能 有损耗 原生性能
镜像大小 GB级别 MB级别

1.2 Kubernetes:容器的"指挥官"

Docker 解决了单容器的问题,但当应用扩展到几十个、几百个容器时:

复制代码
谁管理这些容器的部署?
如何实现负载均衡?
如何做滚动更新?
如何处理容器故障?
如何扩缩容?

Kubernetes(K8s) 就是来解决这些问题的------它是容器编排平台

复制代码
Kubernetes 集群架构:

┌──────────────────────────────────────────┐
│              Control Plane(控制平面)     │
│  ┌─────────┐ ┌─────────┐ ┌───────────┐  │
│  │ API     │ │ Scheduler│ │ Controller│  │
│  │ Server  │ │         │ │ Manager   │  │
│  └────┬────┘ └─────────┘ └───────────┘  │
│       │                                     │
└───────┼─────────────────────────────────────┘
        │ 管理指令
┌───────┴─────────────────────────────────────┐
│              Data Plane(数据平面)           │
│  ┌─────────────┐    ┌─────────────┐         │
│  │   Node 1    │    │   Node 2    │         │
│  │ ┌────┐┌───┐ │    │ ┌────┐┌───┐ │         │
│  │ │Pod1││Pod2│ │    │ │Pod3││Pod4│ │         │
│  │ └────┘└───┘ │    │ └────┘└───┘ │         │
│  └─────────────┘    └─────────────┘         │
│              Worker Nodes                     │
└─────────────────────────────────────────────┘

2. 环境准备:安装 Docker 和 Kubernetes

2.1 安装 Docker(Windows)

方法一:Docker Desktop(推荐新手)

  1. 下载:https://www.docker.com/products/docker-desktop/
  2. 安装 Docker Desktop(约 1GB)
  3. 启动后,右下角托盘出现 Docker 图标即成功
powershell 复制代码
# 验证安装
docker --version
# Docker version 27.3.1, build 0f6c8d5

docker-compose --version
# docker-compose version v2.29.2

docker ps
# CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

方法二:WSL2 后端(Windows 专业版/教育版推荐)

powershell 复制代码
# 1. 启用 WSL2
wsl --install

# 2. 安装 Docker(Linux 方式,WSL2 内)
curl -fsSL https://get.docker.com | sh

# 3. 启动 Docker 服务
sudo service docker start

# 4. 验证
docker run hello-world

⚠️ Windows Home 版:只能使用 WSL2 后端,无法使用 Hyper-V。

2.2 安装 Docker(macOS)

bash 复制代码
# 方法一:Homebrew
brew install --cask docker

# 方法二:官网下载 dmg 文件
# https://docs.docker.com/desktop/install/mac-install/

2.3 安装 Minikube(本地 K8s 学习环境)

Minikube 是在本地启动单节点 K8s 集群的工具,非常适合学习和开发。

powershell 复制代码
# Windows(需要先安装 Docker Desktop)
choco install minikube -y

# macOS
brew install minikube

# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
powershell 复制代码
# 启动 Minikube(使用 Docker 驱动)
minikube start --driver=docker --cpus=4 --memory=8g

# 验证
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   2m    v1.28.0

2.4 安装 kubectl(K8s 命令行工具)

powershell 复制代码
# Windows(Chocolatey)
choco install kubernetes-cli -y

# macOS
brew install kubectl

# 验证
kubectl version --client

2.5 Kubernetes Dashboard(可视化界面)

bash 复制代码
# 部署 Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

# 启动代理
kubectl proxy

# 访问
# http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

3. 第一章:Docker化改造:把应用装进容器

3.1 目标应用:一 RESTful API 服务

我们用 Python Flask 写一个待办事项 API 作为演示应用:

python 复制代码
# app.py
from flask import Flask, jsonify, request
import json
import os

app = Flask(__name__)

# 简单的内存存储(生产用数据库)
todos = [
    {"id": "1", "title": "学习Docker", "completed": False},
    {"id": "2", "title": "部署K8s", "completed": False},
]

DATA_FILE = "/data/todos.json"

def load_todos():
    """从文件加载待办"""
    if os.path.exists(DATA_FILE):
        with open(DATA_FILE, "r") as f:
            return json.load(f)
    return todos

def save_todos(data):
    """保存待办到文件"""
    with open(DATA_FILE, "w") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

@app.route("/api/todos", methods=["GET"])
def get_todos():
    """获取所有待办"""
    return jsonify(load_todos())

@app.route("/api/todos", methods=["POST"])
def create_todo():
    """创建待办"""
    data = request.json
    todos = load_todos()
    new_todo = {
        "id": str(len(todos) + 1),
        "title": data.get("title", ""),
        "completed": False
    }
    todos.append(new_todo)
    save_todos(todos)
    return jsonify(new_todo), 201

@app.route("/api/todos/<id>", methods=["PUT"])
def update_todo(id):
    """更新待办"""
    data = request.json
    todos = load_todos()
    for todo in todos:
        if todo["id"] == id:
            todo["title"] = data.get("title", todo["title"])
            todo["completed"] = data.get("completed", todo["completed"])
            save_todos(todos)
            return jsonify(todo)
    return jsonify({"error": "Not found"}), 404

@app.route("/api/todos/<id>", methods=["DELETE"])
def delete_todo(id):
    """删除待办"""
    todos = load_todos()
    todos = [t for t in todos if t["id"] != id]
    save_todos(todos)
    return jsonify({"message": "Deleted"})

@app.route("/health", methods=["GET"])
def health():
    """健康检查接口"""
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
txt 复制代码
# requirements.txt
flask==3.0.0
gunicorn==21.2.0

3.2 编写 Dockerfile

Dockerfile 是 Docker 镜像的"配方",告诉 Docker 如何构建你的应用镜像。

dockerfile 复制代码
# ====== Dockerfile ======
# 基础镜像(Python 运行环境)
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# 安装依赖(先复制依赖文件,利用 Docker 缓存层)
COPY requirements.txt .

# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY app.py .

# 创建数据目录(用于持久化)
RUN mkdir -p /data

# 暴露端口
EXPOSE 5000

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

# 启动命令(生产用 Gunicorn)
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "--threads", "2", "app:app"]

3.3 构建镜像

bash 复制代码
# 构建镜像(-t 指定镜像名和标签)
docker build -t todo-api:latest .

# 查看镜像
docker images | grep todo
# REPOSITORY   TAG       IMAGE ID       SIZE
# todo-api     latest    abc123def456   150MB

# 给镜像打标签(推送到远程仓库前必须打标签)
docker tag todo-api:latest your-dockerhub-username/todo-api:v1.0.0

3.4 运行容器

bash 复制代码
# 后台运行容器
docker run -d \
  --name todo-api \
  -p 5000:5000 \
  -v $(pwd)/data:/data \
  --restart unless-stopped \
  todo-api:latest

# 查看运行状态
docker ps
# CONTAINER ID   IMAGE          STATUS          PORTS                  NAMES
# xxxxxxxx       todo-api       Up 2 minutes    0.0.0.0:5000->5000/tcp  todo-api

# 测试接口
curl http://localhost:5000/api/todos
# [{"id":"1","title":"学习Docker","completed":false},...]

curl http://localhost:5000/health
# {"status":"healthy"}

# 查看日志
docker logs -f todo-api

# 进入容器内部
docker exec -it todo-api /bin/bash

# 停止/删除
docker stop todo-api
docker rm todo-api

3.5 多阶段构建(生产优化)

dockerfile 复制代码
# ====== 多阶段构建(优化镜像大小)======

# Stage 1: 构建阶段
FROM python:3.11-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt

# Stage 2: 运行阶段(只复制需要的内容)
FROM python:3.11-slim

WORKDIR /app

# 只复制依赖包
COPY --from=builder /install /usr/local

# 只复制应用代码
COPY app.py .

RUN mkdir -p /data

EXPOSE 5000

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]

效果对比

复制代码
普通构建:  ~500MB
多阶段构建:~150MB  (减少 70%)

3.6 .dockerignore 文件

.gitignore 类似,忽略不需要的文件:

dockerignore 复制代码
# 忽略以下文件,不打包进镜像
__pycache__/
*.pyc
*.pyo
*.log
.git/
.gitignore
data/
node_modules/
.env
.env.*
*.md
tests/
.pytest_cache/

4. 第二章:Docker Compose:本地多容器编排

4.1 场景:API + MySQL + Redis

生产环境的应用通常由多个服务组成,用 Docker Compose 可以一键启动。

yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  # ===== API 服务 =====
  api:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: todo-api
    ports:
      - "5000:5000"
    volumes:
      - ./data:/data
      - ./app.py:/app/app.py  # 热更新:修改代码后容器内自动生效
    environment:
      - FLASK_ENV=development
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_USER=root
      - DB_PASSWORD=secret123
      - REDIS_HOST=redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - todo-network

  # ===== MySQL 数据库 =====
  mysql:
    image: mysql:8.0
    container_name: todo-mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: secret123
      MYSQL_DATABASE: todoapp
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - todo-network

  # ===== Redis 缓存 =====
  redis:
    image: redis:7-alpine
    container_name: todo-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    restart: unless-stopped
    networks:
      - todo-network

  # ===== Nginx 反向代理(可选)=====
  nginx:
    image: nginx:alpine
    container_name: todo-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - api
    restart: unless-stopped
    networks:
      - todo-network

volumes:
  mysql_data:
  redis_data:

networks:
  todo-network:
    driver: bridge

4.2 Docker Compose 常用命令

bash 复制代码
# 启动所有服务(-d 后台运行)
docker-compose up -d

# 启动并重新构建镜像
docker-compose up -d --build

# 查看运行状态
docker-compose ps

# 查看日志(-f 实时跟踪)
docker-compose logs -f api
docker-compose logs -f  # 所有服务日志

# 停止并删除容器
docker-compose down

# 停止并删除容器 + 数据卷(慎用,会删除数据)
docker-compose down -v

# 进入容器
docker-compose exec api /bin/bash

# 查看资源使用
docker-compose top

# 扩缩容(启动 3 个 API 实例)
docker-compose up -d --scale api=3

4.3 热更新开发技巧

yaml 复制代码
# 开发环境使用 volumes 挂载源代码
services:
  api:
    volumes:
      - ./app.py:/app/app.py:ro  # 挂载但只读
      - ./requirements.txt:/app/requirements.txt:ro
    environment:
      - FLASK_ENV=development
    command: flask run --host=0.0.0.0 --reload

5. 第三章:Kubernetes入门:核心概念详解

5.1 K8s 核心对象一览

复制代码
Kubernetes 核心对象关系图:

┌─────────────────────────────────────────────────────┐
│                    Cluster(集群)                   │
│  ┌───────────────────────────────────────────────┐ │
│  │                  Namespace(命名空间)            │ │
│  │  ┌─────────────────────────────────────────┐  │ │
│  │  │        Deployment(部署 + 副本管理)       │  │ │
│  │  │  ┌──────────┐ ┌──────────┐ ┌────────┐ │  │ │
│  │  │  │   Pod    │ │   Pod    │ │  Pod   │ │  │ │
│  │  │  │ (nginx)  │ │ (nginx)  │ │(nginx) │ │  │ │
│  │  │  └────┬─────┘ └────┬─────┘ └───┬────┘ │  │ │
│  │  │       │           │           │       │  │ │
│  │  │       └───────────┼───────────┘       │  │ │
│  │  │                   │                   │  │ │
│  │  │            Service(服务发现)           │  │ │
│  │  │                   │                   │  │ │
│  │  │            Ingress(外网入口)           │  │ │
│  │  └─────────────────────────────────────────┘  │ │
│  └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘

5.2 核心对象解释

📦 Pod(豆荚)------ K8s 最小调度单位
yaml 复制代码
# 一个 Pod 包含一个或多个容器
apiVersion: v1
kind: Pod
metadata:
  name: todo-api-pod
  labels:
    app: todo-api
spec:
  containers:
    - name: api
      image: todo-api:latest
      ports:
        - containerPort: 5000
      resources:
        limits:
          memory: "256Mi"
          cpu: "500m"
        requests:
          memory: "128Mi"
          cpu: "250m"
      livenessProbe:
        httpGet:
          path: /health
          port: 5000
        initialDelaySeconds: 10
        periodSeconds: 15
      readinessProbe:
        httpGet:
          path: /health
          port: 5000
        initialDelaySeconds: 5
        periodSeconds: 10
🚀 Deployment(部署)------ 管理 Pod 的生命周期
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-api-deployment
spec:
  replicas: 3  # 3个副本,保证高可用
  selector:
    matchLabels:
      app: todo-api
  template:
    metadata:
      labels:
        app: todo-api
    spec:
      containers:
        - name: api
          image: todo-api:latest
          ports:
            - containerPort: 5000
          resources:
            limits:
              memory: "256Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 5000
          readinessProbe:
            httpGet:
              path: /health
              port: 5000
🔗 Service(服务)------ 负载均衡与服务发现
yaml 复制代码
# ClusterIP:集群内部访问(默认)
apiVersion: v1
kind: Service
metadata:
  name: todo-api-svc
spec:
  type: ClusterIP
  selector:
    app: todo-api
  ports:
    - port: 80        # Service 端口
      targetPort: 5000 # Pod 端口
yaml 复制代码
# NodePort:通过节点IP:端口访问(测试用)
apiVersion: v1
kind: Service
metadata:
  name: todo-api-svc-nodeport
spec:
  type: NodePort
  selector:
    app: todo-api
  ports:
    - port: 80
      targetPort: 5000
      nodePort: 30007  # 节点端口 30000-32767
yaml 复制代码
# LoadBalancer:云厂商负载均衡(生产推荐)
apiVersion: v1
kind: Service
metadata:
  name: todo-api-svc-lb
spec:
  type: LoadBalancer
  selector:
    app: todo-api
  ports:
    - port: 80
      targetPort: 5000

6. 第四章:从零部署应用到 K8s 集群

6.1 将镜像推送到远程仓库

bash 复制代码
# 1. 登录 Docker Hub
docker login

# 2. 打标签
docker tag todo-api:latest your-username/todo-api:v1.0.0

# 3. 推送
docker push your-username/todo-api:v1.0.0

# 或者推送到阿里云(国内更快)
docker login --username=your-username registry.cn-hangzhou.aliyuncs.com
docker tag todo-api:latest registry.cn-hangzhou.aliyuncs.com/your-namespace/todo-api:v1.0.0
docker push registry.cn-hangzhou.aliyuncs.com/your-namespace/todo-api:v1.0.0

6.2 编写完整的 K8s 部署清单

yaml 复制代码
# k8s-deployment.yaml
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-api
  labels:
    app: todo-api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: todo-api
  template:
    metadata:
      labels:
        app: todo-api
    spec:
      containers:
        - name: api
          # ⬇️ 替换为你实际的镜像地址
          image: your-username/todo-api:v1.0.0
          imagePullPolicy: Always
          ports:
            - containerPort: 5000
              name: http
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 5000
            initialDelaySeconds: 15
            periodSeconds: 20
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health
              port: 5000
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          env:
            - name: FLASK_ENV
              value: "production"
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          volumeMounts:
            - name: app-config
              mountPath: /app/config
              readOnly: true
      volumes:
        - name: app-config
          configMap:
            name: todo-api-config
      restartPolicy: Always

---
# Service
apiVersion: v1
kind: Service
metadata:
  name: todo-api-svc
  labels:
    app: todo-api
spec:
  type: ClusterIP
  selector:
    app: todo-api
  ports:
    - name: http
      port: 80
      targetPort: 5000
  sessionAffinity: None

---
# HorizontalPodAutoscaler(自动扩缩容)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: todo-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: todo-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

6.3 部署到集群

bash 复制代码
# 方式一:单文件部署
kubectl apply -f k8s-deployment.yaml

# 方式二:分目录部署(推荐)
# k8s/
#   ├── namespace.yaml
#   ├── configmap.yaml
#   ├── deployment.yaml
#   ├── service.yaml
#   └── ingress.yaml
kubectl apply -f k8s/ -n todo-app

# 查看部署状态
kubectl get deployment -n todo-app
kubectl get pods -n todo-app
kubectl get svc -n todo-app

# 查看 Pod 日志
kubectl logs -f deployment/todo-api -n todo-app

# 查看 Pod 详细信息
kubectl describe pod todo-api-7d9f8b6c5-x2m4n -n todo-app

6.4 验证部署

bash 复制代码
# 通过 Service 访问(创建临时测试 Pod)
kubectl run -it --rm test-client --image=busybox --restart=Never -- sh

# 在测试 Pod 内访问
wget -qO- http://todo-api-svc/api/todos
# [{"id":"1","title":"学习Docker","completed":false},...]

# 或者用 Port Forward 端口转发(本地调试用)
kubectl port-forward svc/todo-api-svc 8080:80 -n todo-app
# 然后浏览器访问 http://localhost:8080

7. 第五章:服务治理:Ingress、Service 与网络

7.1 Ingress:HTTP/HTTPS 入口

Ingress 是 K8s 的 HTTP/HTTPS 路由控制器:

yaml 复制代码
# k8s-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todo-api-ingress
  annotations:
    # Nginx Ingress Controller 注解
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "30"
spec:
  ingressClassName: nginx
  rules:
    - host: todo-api.example.com  # 生产环境替换为真实域名
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: todo-api-svc
                port:
                  number: 80
  # TLS 配置(生产环境必须启用)
  # tls:
  #   - hosts:
  #       - todo-api.example.com
  #     secretName: todo-api-tls

7.2 完整的 K8s 命名空间隔离

yaml 复制代码
# k8s-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: todo-app
  labels:
    name: todo-app
    env: production

7.3 网络策略(安全隔离)

yaml 复制代码
# k8s-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-network-policy
  namespace: todo-app
spec:
  podSelector:
    matchLabels:
      app: todo-api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 5000
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: mysql
      ports:
        - protocol: TCP
          port: 3306

8. 第六章:数据持久化:ConfigMap、Secret 与 PV/PVC

8.1 ConfigMap:应用配置

yaml 复制代码
# k8s-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: todo-api-config
  namespace: todo-app
data:
  FLASK_ENV: "production"
  LOG_LEVEL: "info"
  MAX_TODOS: "1000"
  # 配置文件也可以整体挂载
  nginx.conf: |
    server {
      listen 80;
      server_name _;
      location / {
        proxy_pass http://todo-api-svc;
      }
    }

8.2 Secret:敏感信息

yaml 复制代码
# k8s-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: todo-api-secret
  namespace: todo-app
type: Opaque
data:
  # echo -n "your-password" | base64
  DB_PASSWORD: eW91ci1wYXNzd29yZA==
  API_KEY: eW91ci1hcGkta2V5LWJhc2U2NA==
  # TLS 证书
  tls.crt: LS0tLS1...
  tls.key: LS0tLS1...
yaml 复制代码
# Deployment 中引用 Secret
spec:
  containers:
    - name: api
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: todo-api-secret
              key: DB_PASSWORD

8.3 PersistentVolumeClaim:持久化存储

yaml 复制代码
# k8s-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: todo-data-pvc
  namespace: todo-app
spec:
  accessModes:
    - ReadWriteOnce  # 单节点读写
    # - ReadOnlyMany   # 多节点只读
    # - ReadWriteMany  # 多节点读写(需要 NFS/GlusterFS)
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard  # AWS: gp3 / Azure: managed-premium
yaml 复制代码
# Deployment 中使用 PVC
spec:
  containers:
    - name: api
      volumeMounts:
        - name: todo-data
          mountPath: /data
  volumes:
    - name: todo-data
      persistentVolumeClaim:
        claimName: todo-data-pvc

9. 第七章:高可用与弹性扩缩容

9.1 HPA 详细配置

yaml 复制代码
# 基于 CPU 和内存的自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: todo-api-hpa
  namespace: todo-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: todo-api
  minReplicas: 2
  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% 时扩容
  # 扩容/缩容速率控制
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # 扩容稳定 5 分钟再缩容
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60  # 每分钟最多缩容 50%
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 15  # 每 15 秒最多扩容 100%
bash 复制代码
# 查看 HPA 状态
kubectl get hpa -n todo-app
# NAME          REFERENCE            TARGETS     MINPODS   MAXPODS   REPLICAS
# todo-api-hpa  Deployment/todo-api  45%/70%     2         20        8

# 手动触发扩缩容
kubectl scale deployment todo-api --replicas=10 -n todo-app

9.2 PodDisruptionBudget:滚动更新时保证可用

yaml 复制代码
# 确保滚动更新时至少有 2 个 Pod 可用
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: todo-api-pdb
  namespace: todo-app
spec:
  minAvailable: 2
  # 或者用百分比
  # maxUnavailable: 25%
  selector:
    matchLabels:
      app: todo-api

9.3 滚动更新与回滚

bash 复制代码
# 滚动更新(修改镜像版本后自动触发)
kubectl set image deployment/todo-api api=your-username/todo-api:v1.1.0 -n todo-app

# 查看更新状态
kubectl rollout status deployment/todo-api -n todo-app

# 查看历史版本
kubectl rollout history deployment/todo-api -n todo-app

# 回滚到上一个版本
kubectl rollout undo deployment/todo-api -n todo-app

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

10. 第八章:CI/CD实战:GitHub Actions 自动部署

10.1 整体流程

复制代码
代码提交 → GitHub Actions 自动构建 → 构建镜像 → 推送到仓库
    ↓
K8s 集群拉取新镜像 → 滚动更新 → 验证 → 完成

10.2 GitHub Actions 工作流

yaml 复制代码
# .github/workflows/deploy.yml
name: Build and Deploy to Kubernetes

on:
  push:
    branches:
      - main  # main 分支 push 时触发
  pull_request:
    branches:
      - main

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # ===== Job 1: 构建和推送镜像 =====
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.build.outputs.image }}
      tag: ${{ steps.build.outputs.tag }}
    
    steps:
      - name: Checkout 代码
        uses: actions/checkout@v4

      - name: 设置 QEMU(多平台构建)
        uses: docker/setup-qemu-action@v3

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录 Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 提取元数据(构建标签)
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=
            type=raw,value=latest

      - name: 构建镜像
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: 输出镜像信息
        id: build
        run: |
          echo "image=${{ env.REGISTRY }}/${{ github.repository }}" >> $GITHUB_OUTPUT
          echo "tag=$(echo ${{ steps.meta.outputs.tags }} | awk '{print $1}')" >> $GITHUB_OUTPUT

  # ===== Job 2: 部署到 Kubernetes =====
  deploy:
    needs: build  # 等待 build job 完成
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'  # 只在 main 分支部署
    
    steps:
      - name: Checkout 代码
        uses: actions/checkout@v4

      - name: 设置 kubectl
        uses: azure/setup-kubectl@v4
        with:
          version: 'v1.28.0'

      - name: 配置 Kubeconfig
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.KUBE_CONFIG }}
          context: ${{ secrets.K8S_CLUSTER_NAME }}

      - name: 更新镜像版本
        run: |
          # 替换 Deployment 中的镜像版本
          sed -i 's|image: .*todo-api:.*|image: ${{ needs.build.outputs.image }}:${{ needs.build.outputs.tag }}|g' k8s/deployment.yaml
          cat k8s/deployment.yaml | grep image

      - name: 部署到 Kubernetes
        run: |
          kubectl apply -f k8s/namespace.yaml
          kubectl apply -f k8s/configmap.yaml
          kubectl apply -f k8s/secret.yaml
          kubectl apply -f k8s/deployment.yaml
          kubectl apply -f k8s/service.yaml
          kubectl apply -f k8s/ingress.yaml
          kubectl apply -f k8s/hpa.yaml

      - name: 等待滚动更新完成
        run: |
          kubectl rollout status deployment/todo-api -n todo-app --timeout=300s

      - name: 验证部署
        run: |
          kubectl get pods -n todo-app
          kubectl get svc -n todo-app
          kubectl get hpa -n todo-app

10.3 配置 GitHub Secrets

在 GitHub 仓库 Settings → Secrets and variables → Actions 中配置:

复制代码
KUBE_CONFIG    → kubeconfig 文件内容(base64 编码)
K8S_CLUSTER_NAME → 集群名称,如 production
REGISTRY_PASSWORD  → 镜像仓库密码(如需)

11. 实战完整项目代码

复制代码
todo-microservice/
├── app.py                 # Flask 应用主文件
├── requirements.txt       # Python 依赖
├── Dockerfile             # Docker 构建文件
├── Dockerfile.multi       # 多阶段构建
├── .dockerignore          # Docker 忽略文件
├── docker-compose.yml     # Docker Compose 本地编排
├── docker-compose.prod.yml # 生产环境编排
├── nginx.conf             # Nginx 反向代理配置
├── k8s/
│   ├── namespace.yaml     # 命名空间
│   ├── configmap.yaml     # 配置
│   ├── secret.yaml        # 密钥
│   ├── deployment.yaml    # 部署
│   ├── service.yaml       # 服务
│   ├── ingress.yaml       # 入口
│   ├── hpa.yaml           # 自动扩缩容
│   ├── pdb.yaml           # Pod 中断预算
│   └── pvc.yaml           # 持久化卷
├── tests/
│   ├── test_api.py        # API 测试
│   └── test_docker.py     # Docker 测试
└── .github/
    └── workflows/
        └── deploy.yml     # CI/CD 流水线

12. 常见问题与排错指南

❌ Pod 一直处于 Pending 状态

bash 复制代码
kubectl describe pod <pod-name>
# 常见原因:
# 1. 资源不足(CPU/内存不够)→ 降低 requests
# 2. PVC 未绑定 → 检查 storageclass 是否存在
# 3. 节点选择器不匹配 → 检查 nodeSelector/affinity

❌ ImagePullBackOff(拉取镜像失败)

bash 复制代码
# 原因1:镜像地址错误
# 解决:检查镜像地址是否正确

# 原因2:未登录私有仓库
# 解决:创建 imagePullSecret
kubectl create secret docker-registry regcred \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=your-username \
  --docker-password=your-password \
  --docker-email=your-email

# Deployment 中引用
spec:
  imagePullSecrets:
    - name: regcred

❌ CrashLoopBackOff(容器启动后崩溃)

bash 复制代码
# 查看日志
kubectl logs <pod-name> --previous
# 常见原因:
# 1. 应用启动慢(livenessProbe 太激进)→ 调大 initialDelaySeconds
# 2. 配置错误(环境变量不对)→ 检查 ConfigMap/Secret
# 3. 依赖服务未就绪 → 添加 readinessProbe + depends_on

❌ Service 无法访问 Pod

bash 复制代码
# 检查标签匹配
kubectl get pods --show-labels
kubectl describe svc <svc-name>
# Endpoints 应该不为空

# 检查网络策略
kubectl get networkpolicy -n todo-app

❌ Ingress 不生效

bash 复制代码
# 检查 Ingress Controller 是否安装
kubectl get pods -n ingress-nginx
kubectl get ingressclass

# 检查 Ingress 配置
kubectl describe ingress <ingress-name>
# 查看 Events 找错误信息

❌ HPA 不工作

bash 复制代码
# 检查 metrics-server 是否安装(必需)
kubectl get pods -n kube-system | grep metrics
# 如果没有:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 查看 HPA 详细信息
kubectl describe hpa <hpa-name>

总结:Docker + K8s 部署全流程速查

复制代码
1. 写 Dockerfile      → 构建镜像
2. docker build       → 本地测试镜像
3. docker-compose     → 本地多容器联调
4. 推送镜像           → 远程仓库
5. 写 K8s YAML        → Deployment + Service + HPA
6. kubectl apply      → 部署到集群
7. GitHub Actions     → 自动化 CI/CD

从本地到生产,完整链路打通!

相关推荐
ejinxian1 小时前
微虚拟机 smolvm 与Docker 容器比较
运维·docker·容器·smolvm
爱码少年2 小时前
Docker如何一次查看多个容器日志
运维·docker·容器
蜀道山老天师2 小时前
K8s 数据存储全解析:从 EmptyDir 到 PV/PVC
云原生·容器·kubernetes
创世宇图3 小时前
【Python工程化实战】Kubernetes 中 Python 应用的优雅启停与健康检查:零停机滚动更新实战
python·云原生·kubernetes·优雅停机
江畔柳前堤5 小时前
第16章:docker企业级实战综合项目
运维·git·安全·docker·容器·eureka
zh73146 小时前
docker日志监控dozzle,高性能,性能消耗小
运维·docker·容器
weixin_471383037 小时前
Docker - 05 - Railway 部署
运维·docker·容器
江畔柳前堤7 小时前
第15章:docker故障排查与面试题
大数据·运维·git·elasticsearch·docker·容器·eureka
江畔柳前堤7 小时前
第07章:Docker 网络模型
运维·网络·git·elasticsearch·docker·容器·架构