Skip to content

필사 모드: DevOps/SRE 完全精通:从 CI/CD 到 Kubernetes、MLOps

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言

在现代软件开发中,DevOpsSRE(Site Reliability Engineering)已不再是可选项,而是必需品。Netflix 每天部署数千次,Google 为数十亿用户保证 99.99% 的可用性。这背后,是彻底自动化的流水线与数据驱动的运维哲学。

本指南将从 DevOps/SRE 的核心概念出发,到 Kubernetes 实战运维、AI/ML 工作流自动化,结合实战代码为你完全讲透。


1. DevOps 基础:CI/CD 流水线

什么是 CI/CD

CI(Continuous Integration,持续集成)是开发者频繁集成代码、自动构建/测试的实践。CD(Continuous Delivery/Deployment,持续交付/部署)则是将验证通过的代码自动部署到生产环境。

分类目的自动化范围
CI代码集成验证构建、测试、检查
CD (Delivery)部署准备自动化到预发布
CD (Deployment)自动部署自动化到生产环境

用 GitHub Actions 构建 CI/CD

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

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

jobs:
  test:
    name: Test & Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov flake8

      - name: Lint with flake8
        run: flake8 src/ --max-line-length=88

      - name: Run tests
        run: pytest tests/ --cov=src --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml

  build:
    name: Build & Push Docker Image
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

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

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-
            type=ref,event=branch
            type=semver,pattern={{version}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    name: Deploy to Kubernetes
    runs-on: ubuntu-latest
    needs: build
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Configure kubectl
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.KUBECONFIG }}

      - name: Deploy with Helm
        run: |
          helm upgrade --install my-app ./helm/my-app \
            --namespace production \
            --set image.tag=${{ github.sha }} \
            --wait --timeout=5m

部署策略对比

Blue-Green 部署:同时维持两套完全相同的生产环境(Blue/Green)。将新版本部署到 Green 之后,一次性切换全部流量。回滚是即时的,但需要双倍资源。

Canary 部署:只把一部分流量(例如 5%)路由到新版本,再逐步扩大比例。用真实用户流量做验证,同时把风险降到最低。

# Argo Rollouts Canary 部署示例
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 10m }
        - setWeight: 60
        - pause: { duration: 10m }
        - setWeight: 100
      canaryService: my-app-canary
      stableService: my-app-stable

2. GitOps 与 Infrastructure as Code

GitOps 原则

GitOps 是把 Git 当作 单一事实来源(Single Source of Truth)来使用的运维模型。

  • 声明式(Declarative):把系统状态用代码来声明
  • 版本管理:所有变更都被 Git 历史记录追踪
  • 自动化:Git 变更 → 自动同步
  • 可审计:基于 PR 的变更,记录了谁在什么时候、为什么做了改动

代表性工具是 ArgoCDFlux

用 Terraform 实现 IaC

# main.tf - AWS EKS 集群配置
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/eks/terraform.tfstate"
    region = "ap-northeast-2"
  }
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "prod-cluster"
  cluster_version = "1.29"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  eks_managed_node_groups = {
    general = {
      instance_types = ["m5.xlarge"]
      min_size       = 2
      max_size       = 10
      desired_size   = 3
    }
    gpu = {
      instance_types = ["g4dn.xlarge"]
      min_size       = 0
      max_size       = 5
      desired_size   = 1
      taints = [{
        key    = "nvidia.com/gpu"
        value  = "true"
        effect = "NO_SCHEDULE"
      }]
    }
  }
}

3. Kubernetes 完全精通

理解核心资源

Kubernetes 的核心对象整理如下。

资源角色
Pod执行单元,由一个或多个容器组成
Deployment管理 Pod 副本,支持滚动更新
Service为一组 Pod 提供网络端点
HPA基于 CPU/内存的自动水平扩缩容
ConfigMap分离环境变量/配置文件
Secret管理敏感信息(密码、令牌)
Ingress外部 HTTP 流量路由

实战 Deployment + HPA 清单

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ml-inference-api
  namespace: production
  labels:
    app: ml-inference-api
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ml-inference-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: ml-inference-api
        version: v1
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '8080'
        prometheus.io/path: '/metrics'
    spec:
      containers:
        - name: api
          image: ghcr.io/myorg/ml-inference-api:sha-abc123
          ports:
            - containerPort: 8080
          env:
            - name: MODEL_NAME
              valueFrom:
                configMapKeyRef:
                  name: ml-config
                  key: model_name
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
          resources:
            requests:
              cpu: '500m'
              memory: '512Mi'
            limits:
              cpu: '2000m'
              memory: '2Gi'
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
---
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ml-inference-api-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ml-inference-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: '100'
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300

用 Helm Chart 管理包

Helm 是 Kubernetes 的包管理器。它把复杂应用的部署过程模板化管理。

# helm/my-app/values.yaml
replicaCount: 3

image:
  repository: ghcr.io/myorg/my-app
  pullPolicy: IfNotPresent
  tag: 'latest'

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: api.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: api-tls
      hosts:
        - api.example.com

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 2000m
    memory: 2Gi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20
  targetCPUUtilizationPercentage: 70

postgresql:
  enabled: true
  auth:
    database: myapp
    existingSecret: db-credentials

redis:
  enabled: true
  architecture: replication

4. 监控与可观测性

可观测性的三大支柱

支柱工具用途
指标(Metrics)Prometheus, Grafana数值数据,仪表盘
日志(Logs)Loki, Elasticsearch事件记录,调试
追踪(Traces)Jaeger, Tempo分布式请求追踪

Prometheus 告警规则

# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: ml-service-alerts
  namespace: monitoring
spec:
  groups:
    - name: ml-service.rules
      interval: 30s
      rules:
        - alert: HighErrorRate
          expr: |
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total[5m])) > 0.05
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: 'High error rate detected'
            description: 'Error rate is {{ $value | humanizePercentage }} for the last 5 minutes'

        - alert: SlowResponseTime
          expr: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
            ) > 1.0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'Slow p99 latency'
            description: 'p99 latency is {{ $value }}s for {{ $labels.service }}'

        - alert: PodCrashLooping
          expr: |
            increase(kube_pod_container_status_restarts_total[15m]) > 3
          for: 0m
          labels:
            severity: critical
          annotations:
            summary: 'Pod crash looping'
            description: 'Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping'

        - alert: HighMemoryUsage
          expr: |
            container_memory_usage_bytes
            /
            container_spec_memory_limit_bytes > 0.85
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'High memory usage'

OpenTelemetry 埋点

# instrumentation.py
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

def setup_telemetry(service_name: str, otlp_endpoint: str):
    """OpenTelemetry 配置"""
    # 追踪器设置
    tracer_provider = TracerProvider()
    otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
    tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
    trace.set_tracer_provider(tracer_provider)

    # 指标设置
    meter_provider = MeterProvider()
    metrics.set_meter_provider(meter_provider)

    return trace.get_tracer(service_name)

# 应用到 FastAPI
from fastapi import FastAPI

app = FastAPI()
tracer = setup_telemetry("ml-inference-api", "http://otel-collector:4317")

# 自动埋点
FastAPIInstrumentor.instrument_app(app)
RequestsInstrumentor().instrument()

@app.post("/predict")
async def predict(payload: dict):
    with tracer.start_as_current_span("model-inference") as span:
        span.set_attribute("model.name", "bert-base")
        span.set_attribute("input.length", len(str(payload)))

        # 模型推理逻辑
        result = run_inference(payload)

        span.set_attribute("prediction.confidence", result["confidence"])
        return result

5. SRE 原则

SLI / SLO / SLA 层级结构

  • SLI(Service Level Indicator):实际测量到的服务性能指标(例如请求成功率、延迟)
  • SLO(Service Level Objective):SLI 的目标值(例如 99.9% 可用性)
  • SLA(Service Level Agreement):与客户约定的合同水平(通常比 SLO 更宽松)

Error Budget 计算

如果 SLO 是 99.9%,按一个月(30 天)计算,允许的停机时间如下。

Error Budget = 100% - SLO = 0.1%
月度允许停机时间 = 30天 × 24小时 × 60分钟 × 0.1%43.2分钟

Error Budget 一旦耗尽,就要停止发布新功能,转而集中精力做稳定性工作。这是 SRE 的核心机制。

SLO 水平月度允许停机时间
99%7 小时 18 分钟
99.9%43 分 48 秒
99.99%4 分 22 秒
99.999%26 秒

降低 Toil 的策略

Toil 指的是手动、重复、可自动化的运维工作。Google SRE 建议把 Toil 控制在总工作时间的 50% 以下。

降低 Toil 的方法:

  1. 把重复性工作脚本化/自动化
  2. 把 Runbook(运行手册)转化为自动化代码
  3. 提升告警质量,降低噪声
  4. 构建自愈(Self-Healing)系统

6. AI/ML 工作流自动化

用 MLflow 追踪实验

# train.py
import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score

mlflow.set_tracking_uri("http://mlflow-server:5000")
mlflow.set_experiment("fraud-detection-v2")

with mlflow.start_run(run_name="rf-baseline"):
    # 记录超参数
    params = {"n_estimators": 100, "max_depth": 10, "random_state": 42}
    mlflow.log_params(params)

    # 训练模型
    model = RandomForestClassifier(**params)
    model.fit(X_train, y_train)

    # 记录指标
    y_pred = model.predict(X_test)
    mlflow.log_metric("accuracy", accuracy_score(y_test, y_pred))
    mlflow.log_metric("f1_score", f1_score(y_test, y_pred))

    # 保存模型
    mlflow.sklearn.log_model(model, "model",
        registered_model_name="fraud-detector")

用 Argo Workflows 构建 ML 流水线

# ml-pipeline.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  name: ml-training-pipeline
spec:
  entrypoint: ml-pipeline
  templates:
    - name: ml-pipeline
      dag:
        tasks:
          - name: data-prep
            template: prepare-data
          - name: train
            template: train-model
            dependencies: [data-prep]
          - name: evaluate
            template: evaluate-model
            dependencies: [train]
          - name: deploy
            template: deploy-model
            dependencies: [evaluate]

    - name: prepare-data
      container:
        image: ghcr.io/myorg/data-prep:latest
        command: [python, prepare_data.py]
        resources:
          requests:
            memory: 4Gi
            cpu: '2'

    - name: train-model
      container:
        image: ghcr.io/myorg/ml-trainer:latest
        command: [python, train.py]
        resources:
          requests:
            memory: 16Gi
            cpu: '8'
            nvidia.com/gpu: '1'

    - name: evaluate-model
      container:
        image: ghcr.io/myorg/ml-evaluator:latest
        command: [python, evaluate.py]

    - name: deploy-model
      container:
        image: ghcr.io/myorg/model-deployer:latest
        command: [python, deploy.py]

Python ML 服务的 Dockerfile

# Dockerfile
FROM python:3.11-slim AS builder

WORKDIR /app

# 分离依赖安装层(利用缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.11-slim AS runtime

# 安全:不以 root 权限运行
RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app

# 从构建阶段复制包
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser src/ ./src/
COPY --chown=appuser:appuser models/ ./models/

USER appuser

ENV PATH=/home/appuser/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD python -c "import requests; requests.get('http://localhost:8080/health').raise_for_status()"

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "4"]

7. 安全:RBAC 与 Secrets 管理

Kubernetes RBAC

# rbac.yaml
# 创建 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ml-service-account
  namespace: production
---
# 最小权限 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ml-service-role
  namespace: production
rules:
  - apiGroups: ['']
    resources: ['pods', 'services']
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources: ['secrets']
    resourceNames: ['ml-model-secrets']
    verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ml-service-rolebinding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: ml-service-account
    namespace: production
roleRef:
  kind: Role
  apiGroup: rbac.authorization.k8s.io
  name: ml-service-role

用 HashiCorp Vault 管理 Secret

# vault_client.py
import hvac
import os

def get_secret(secret_path: str) -> dict:
    """从 Vault 安全地读取密钥"""
    client = hvac.Client(
        url=os.environ["VAULT_ADDR"],
        token=os.environ["VAULT_TOKEN"]
    )

    if not client.is_authenticated():
        raise RuntimeError("Vault 认证失败")

    secret = client.secrets.kv.v2.read_secret_version(
        path=secret_path,
        mount_point="secret"
    )
    return secret["data"]["data"]

# 在 Kubernetes 中使用 Vault Agent Injector
# 通过 Pod 注解自动注入密钥

用 Network Policy 控制流量

# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ml-service-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: ml-inference-api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: database
      ports:
        - protocol: TCP
          port: 5432
    - to:
        - namespaceSelector:
            matchLabels:
              name: monitoring
      ports:
        - protocol: TCP
          port: 4317 # OTLP gRPC

8. 事件管理

事件响应流程

  1. 检测(Detection):Prometheus 告警或用户上报
  2. 分类(Triage):判断严重程度(P1/P2/P3)
  3. 沟通(Communication):更新状态页面,通知相关方
  4. 缓解(Mitigation):流量切换、回滚、扩容
  5. 解决(Resolution):解决根本原因
  6. 事后复盘(Post-Mortem):撰写 Blameless post-mortem(无责复盘)

写出有效的 Post-Mortem

好的 Post-Mortem 不是聚焦个人责任,而是聚焦 系统改进

  • 详细记录时间线
  • 区分根本原因(Root Cause)与触发因素(Trigger)
  • 使用 5 Whys 分析法
  • 给出具体的行动项(负责人 + 截止日期)

结语

DevOps/SRE 不只是一套工具集合,而是一种 文化与哲学。用自动化减少人为失误,用数据做决策,并通过持续改进来提升系统可靠性。

核心原则总结如下:

  • 自动化优先:所有重复性工作都用代码完成
  • 可衡量性:用 SLI/SLO 让目标变得清晰
  • 快速失败:用 Canary 部署把风险降到最低
  • Blameless 文化:改进系统,而不是指责个人

测验

Q1. 在 CI/CD 流水线中,Blue-Green 部署与 Canary 部署有什么区别?

答案:Blue-Green 是同时维持两套完全相同的生产环境、一次性切换全部流量的方式;Canary 是只把一部分流量逐步切换到新版本的方式。

说明:Blue-Green 部署的回滚是即时的(只需切换流量)且没有停机时间,但需要双倍资源。Canary 部署可以用真实用户流量验证新版本、把风险降到最低,但监控更复杂。像 Argo Rollouts 这样的工具两种方式都支持。

Q2. Kubernetes HPA 用来扩缩容的基准指标是什么?

答案:默认基于 CPU 使用率和内存使用率,通过 Custom Metrics API 还可以使用 RPS(每秒请求数)、队列深度等自定义指标。

说明:HPA(HorizontalPodAutoscaler)在 v2 API 中支持 resourcepodsobjectexternal 四种指标类型。设定 CPU 70% 目标后,当前平均 CPU 一旦超过该值就会增加 Pod 数量。安装 Prometheus Adapter 之后,就能把 Prometheus 指标接入 HPA。

Q3. Error Budget 在 SRE 中扮演什么角色?

答案:Error Budget 是 SLO 中定义的可容忍失败量,是在稳定性与功能开发速度之间取得平衡的机制。

说明:如果 SLO 是 99.9%,Error Budget 就是 0.1%。这份预算充足时,可以发布新功能、做实验性变更。预算耗尽后,就要冻结新的发布,集中精力做稳定性改进。借此,开发团队和运维团队能够基于数据协商发布节奏。

Q4. Prometheus 采用 pull 方式采集指标有什么优点?

答案:pull 方式让 Prometheus 能够集中管理抓取目标,配置更简单;目标服务宕机时可以立即察觉;在安全层面也不需要开放防火墙入站规则。

说明:push 方式(如 StatsD、InfluxDB 等)要求每个服务都知道指标服务器的地址,网络出问题时数据可能会丢失。pull 方式下,Prometheus 通过配置文件或服务发现来管理目标,服务的增删因此更灵活。不过,对于生命周期极短的批处理任务,需要借助 Pushgateway。

Q5. GitOps 与传统 CI/CD 有什么区别?

答案:GitOps 把 Git 当作单一事实来源,持续同步声明式状态;而传统 CI/CD 是由流水线直接以命令式方式执行部署。

说明:传统 CI/CD(Jenkins、GitHub Actions)由流水线直接执行 kubectl applyhelm upgrade。GitOps(ArgoCD、Flux)持续比较 Git 中的声明式状态与集群的实际状态,并自动同步。GitOps 能够做到漂移检测、自动回滚、完整的审计追踪,而且不需要把集群访问权限交给 CI/CD 系统,从而增强了安全性。

현재 단락 (1/602)

在现代软件开发中,**DevOps** 与 **SRE**(Site Reliability Engineering)已不再是可选项,而是必需品。Netflix 每天部署数千次,Google 为数十亿...

작성 글자: 0원문 글자: 14,601작성 단락: 0/602