Skip to content

DevOps 商业场景训练营

这页把 DevOps 放到真实商业系统交付里学习。目标不是背 Docker、Nginx、Jenkins、Kubernetes 命令,而是能解释代码如何变成线上服务,发布失败怎么定位,服务访问不了从哪一层查,配置和密钥为什么不能乱放,灰度和回滚为什么依赖镜像、配置、数据库变更一起设计。

训练目标

学完这页,你要能做到:

  1. 画出从 Git 提交到用户访问服务的完整交付链路。
  2. 解释源码、构建产物、镜像、容器、Pod、Service、Ingress 的区别。
  3. 写出 Spring Boot 服务的 Dockerfile、Compose、Jenkinsfile、K8s Deployment/Service。
  4. 解释 Nginx 反向代理、负载均衡、限流、真实 IP 透传。
  5. 解释 Kubernetes 滚动发布、健康检查、ConfigMap、Secret、资源限制。
  6. 排查构建失败、镜像失败、Pod 起不来、服务访问不了、发布后接口变慢。
  7. 设计订单、采集、搜索这类商业系统的发布、灰度、回滚和可观测性。

商业交付总链路

mermaid
flowchart TD
    A["开发提交代码"] --> B["Git 仓库"]
    B --> C["Jenkins Pipeline"]
    C --> D["编译、测试、质量检查"]
    D --> E["生成 jar 或 dist"]
    E --> F["构建 Docker 镜像"]
    F --> G["推送镜像仓库"]
    G --> H["更新 K8s Deployment"]
    H --> I["Pod 拉镜像并启动"]
    I --> J["readinessProbe 通过"]
    J --> K["Service 接入流量"]
    K --> L["Ingress/Nginx 暴露入口"]
    L --> M["用户访问"]
    M --> N["日志、指标、链路追踪、告警"]

这条链路里每一步都要可追踪:

环节要记录什么为什么
Gitcommit、分支、提交人知道代码来源
构建构建号、测试结果知道产物是否可信
镜像image tag、digest知道线上跑的是什么
部署环境、命名空间、发布时间出问题能回滚
运行日志、指标、traceId出问题能定位

Demo 一:Spring Boot Dockerfile

JDK8 仍然是很多企业项目的主力,所以示例不只基于最新 JDK。

dockerfile
FROM eclipse-temurin:8-jre

WORKDIR /app

COPY target/order-api.jar /app/order-api.jar

ENV JAVA_OPTS="-Xms512m -Xmx512m"
ENV SPRING_PROFILES_ACTIVE="prod"

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/order-api.jar"]

为什么这样写:

  1. FROM 固定运行时,避免服务器 Java 版本漂移。
  2. WORKDIR 统一应用目录。
  3. JAVA_OPTS 让堆大小、GC 参数可配置。
  4. SPRING_PROFILES_ACTIVE 区分环境。
  5. 不把数据库密码写进镜像,密钥要由环境变量、Secret 或配置中心注入。

常见错误:

错误后果
使用 latest 基础镜像基础镜像变化导致不可复现
把配置文件和密码打进镜像镜像泄露就等于密钥泄露
没设置 JVM 内存容器内存限制下容易 OOM
容器内写大量本地日志磁盘被打满

Demo 二:本地 Compose 联调

yaml
version: "3.8"
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: order_db
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  order-api:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/order_db
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root
      SPRING_REDIS_HOST: redis
    depends_on:
      - mysql
      - redis

volumes:
  mysql-data:

重点:

  1. 容器间访问 MySQL 用服务名 mysql,不是 127.0.0.1
  2. depends_on 只保证启动顺序,不保证 MySQL 已经可连接。
  3. 应用要配置连接超时和重试。
  4. 数据要挂 volume,否则容器删除后数据丢失。

Demo 三:Jenkins Pipeline

groovy
pipeline {
  agent any

  environment {
    APP_NAME = 'order-api'
    REGISTRY = 'registry.example.com/backend'
    IMAGE_TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(8)}"
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Test') {
      steps {
        sh 'mvn clean test'
      }
    }

    stage('Package') {
      steps {
        sh 'mvn package -DskipTests'
        archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
      }
    }

    stage('Build Image') {
      steps {
        sh 'docker build -t $REGISTRY/$APP_NAME:$IMAGE_TAG .'
      }
    }

    stage('Push Image') {
      steps {
        sh 'docker push $REGISTRY/$APP_NAME:$IMAGE_TAG'
      }
    }

    stage('Deploy') {
      steps {
        sh 'kubectl set image deployment/order-api order-api=$REGISTRY/$APP_NAME:$IMAGE_TAG -n prod'
        sh 'kubectl rollout status deployment/order-api -n prod --timeout=180s'
      }
    }
  }
}

生产原则:

  1. 镜像 tag 要可追踪,不能只用 latest
  2. 凭据放 Jenkins Credentials。
  3. 测试失败不能继续发布。
  4. 部署后必须等 rollout 结果。
  5. 流水线日志不能打印密码、token、私钥。

Demo 四:Kubernetes 部署

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
  namespace: prod
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: order-api
  template:
    metadata:
      labels:
        app: order-api
    spec:
      containers:
        - name: order-api
          image: registry.example.com/backend/order-api:1.0.0
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: order-api-config
            - secretRef:
                name: order-api-secret
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: order-api
  namespace: prod
spec:
  selector:
    app: order-api
  ports:
    - port: 80
      targetPort: 8080

为什么 maxUnavailable: 0?为了滚动发布时尽量保证旧实例不先下线,避免可用实例不足。具体配置要看服务容量,不能所有系统照抄。

为什么要 readinessProbe?因为应用进程启动了不代表能接流量。数据库连接、缓存预热、配置加载还没完成时,Service 不应该把流量转发给它。

Nginx 入口配置

nginx
upstream order_api {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
}

server {
    listen 80;
    server_name api.example.com;

    location /api/ {
        proxy_pass http://order_api/;
        proxy_connect_timeout 3s;
        proxy_read_timeout 30s;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Request-Id $request_id;
    }
}

关键点:

  1. proxy_connect_timeout 控制连接后端超时。
  2. proxy_read_timeout 控制等待后端响应超时。
  3. X-Forwarded-For 用于真实 IP 追踪。
  4. X-Request-Id 可以和后端 traceId 打通。
  5. Nginx 超时不能无限大,否则会占用连接资源。

灰度发布和回滚

mermaid
flowchart TD
    A["新版本构建完成"] --> B["部署少量实例"]
    B --> C["只接少量流量"]
    C --> D["观察错误率、P99、业务指标"]
    D --> E{"指标是否正常"}
    E -- "正常" --> F["扩大流量"]
    E -- "异常" --> G["停止发布并回滚"]

回滚不仅是 kubectl rollout undo。要检查:

  1. 旧镜像是否还在镜像仓库。
  2. 配置是否兼容旧代码。
  3. 数据库变更是否向前兼容。
  4. MQ 消息格式是否兼容旧消费者。
  5. 前端静态资源是否和后端接口兼容。

数据库变更推荐:

阶段做法
第一次发布先加字段、加表,不删除旧字段
代码灰度新旧代码都能跑
稳定后再清理旧字段和旧逻辑

商业场景一:订单系统发布

订单系统发布风险高,因为涉及支付、库存、MQ 和状态机。

发布前检查:

  1. 数据库迁移是否兼容旧版本。
  2. MQ 消息字段是否兼容旧消费者。
  3. 支付回调接口是否保持可用。
  4. 限流和熔断配置是否合理。
  5. 监控是否覆盖下单成功率、支付回调失败率、订单卡状态数量。

发布后观察:

mermaid
flowchart TD
    A["订单系统发布"] --> B["接口错误率"]
    A --> C["P95/P99 延迟"]
    A --> D["下单成功率"]
    A --> E["支付回调成功率"]
    A --> F["MQ 堆积"]
    A --> G["数据库慢 SQL"]

商业场景二:医疗采集平台发布

采集平台发布要考虑长任务和外部医院接口。

发布策略:

  1. 调度任务先暂停或降频。
  2. 正在执行的采集批次要能完成或可恢复。
  3. 采集网关滚动发布,避免所有连接同时断开。
  4. 采集配置变更要审计。
  5. 发布后观察采集成功率、失败批次、MQ 堆积、ES 同步延迟。

生产排查决策树

服务访问不了

mermaid
flowchart TD
    A["服务访问不了"] --> B["DNS 是否解析"]
    B --> C["Nginx/Ingress 是否收到请求"]
    C --> D["Service 是否有 endpoints"]
    D --> E["Pod 是否 Ready"]
    E --> F["容器是否启动成功"]
    F --> G["应用端口是否监听"]
    G --> H["依赖 DB/Redis/MQ 是否可用"]

Pod 起不来

mermaid
flowchart TD
    A["Pod 起不来"] --> B["kubectl describe pod"]
    B --> C["ImagePullBackOff 看镜像和凭据"]
    B --> D["CrashLoopBackOff 看应用日志"]
    B --> E["Pending 看资源和调度"]
    B --> F["探针失败看 health endpoint"]

发布后接口变慢

mermaid
flowchart TD
    A["发布后变慢"] --> B["看新旧版本流量占比"]
    B --> C["看 P99 和错误率"]
    C --> D["看 Pod CPU/内存/GC"]
    D --> E["看 DB/Redis/MQ 依赖"]
    E --> F["看配置和线程池变化"]
    F --> G["必要时回滚"]

磁盘打满

mermaid
flowchart TD
    A["磁盘打满"] --> B["容器日志是否过大"]
    B --> C["镜像和无用层是否堆积"]
    C --> D["应用文件上传或临时文件"]
    D --> E["数据库或中间件数据目录"]
    E --> F["清理并补日志轮转和告警"]

常用排查命令

bash
kubectl get pod -n prod
kubectl describe pod order-api-xxx -n prod
kubectl logs order-api-xxx -n prod --tail=200
kubectl get events -n prod --sort-by=.metadata.creationTimestamp
kubectl get endpoints order-api -n prod
kubectl rollout history deployment/order-api -n prod
kubectl rollout undo deployment/order-api -n prod
kubectl top pod -n prod

Docker:

bash
docker ps -a
docker logs order-api --tail=200
docker inspect order-api
docker exec -it order-api sh
docker system df

Nginx:

bash
nginx -t
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
curl -H "Host: api.example.com" http://127.0.0.1/api/health

面试标准回答

代码到上线全过程怎么说

text
开发提交代码到 Git 后,CI 流水线拉取代码,执行编译、测试和打包,生成 jar 或前端 dist。然后构建 Docker 镜像并推送到镜像仓库。部署阶段更新 Kubernetes Deployment,Pod 拉取镜像启动,通过 readinessProbe 后加入 Service,最终由 Ingress 或 Nginx 对外暴露。上线后通过日志、指标和链路追踪观察错误率、延迟和业务指标,异常时停止发布或回滚。

服务访问不了怎么排查

text
我会先确认请求到达哪一层:DNS 是否解析,Nginx 或 Ingress 是否收到请求,Service 是否有 endpoints,Pod 是否 Ready,容器是否启动成功,应用端口是否监听,最后看依赖的数据库、Redis、MQ 是否可用。不要一开始只看应用日志,因为访问不了可能发生在入口、网络、服务发现、探针或容器启动任意一层。

为什么发布要有健康检查和回滚

text
进程启动不代表服务能接流量,数据库连接、配置加载、缓存预热失败都可能导致应用不可用。readinessProbe 可以控制是否接入流量,livenessProbe 可以发现进程假死。回滚是为了在新版本错误率、延迟或业务指标异常时快速恢复,但回滚必须依赖镜像、配置、数据库变更和消息格式兼容。

关联知识点