当前位置:首页>python>Python教程 Episode 18 - CI/CD 与 DevOps 实战

Python教程 Episode 18 - CI/CD 与 DevOps 实战

  • 2026-09-07 10:58:06
Python教程 Episode 18 - CI/CD 与 DevOps 实战

欢迎来到"Python教程从零基础到实战"系列的第十八期!

学了这么多期,你掌握了 FastAPI 写后端、学了异步编程、也了解了微服务架构。但还有一个关键环节我们一直没有深入:**你怎么把自己的代码从本地优雅地发布到服务器上?**

这就是 CI/CD(持续集成 / 持续交付)和 DevOps 要解决的问题。在这一期里,我们会从零开始搭建一套自动化流程——代码一提交,就自动跑测试、检查质量、构建镜像,最后部署到云端。

准备好了吗?让我们从"写代码的人"变成"交付价值的人"。

---

## 一、什么是 CI/CD 和 DevOps?

### 先搞清楚三个缩写

**CI(Continuous Integration,持续集成)**:每次你往代码仓库推送修改时,自动触发一系列操作——拉取最新代码、安装依赖、运行测试、生成报告。如果任何一步失败,你就立刻知道,而不是等到上线那天才出丑。

**CD(Continuous Delivery / Deployment,持续交付 / 持续部署)**:测试通过了怎么办?自动把构建产物推送到某个环境——先是预发环境,确认没问题后再推到生产环境。整个流程不用你手动点按钮。

**DevOps(Development + Operations,研发运维一体化)**:这不是一个工具,而是一种文化。核心思想是让开发人员对自己写的代码负全责——从开发、测试、部署到上线后的监控,全流程打通。

### 为什么这些对你重要?

你可能觉得:"我只是个学 Python 的初学者,搞什么 DevOps?"

说实话,**越早接触 CI/CD,你的代码水平提升越快**。因为你需要思考的可不仅是"这个功能能不能跑",而是"这个功能怎么可靠地交付"。这正是初级工程师和中级工程师的分水岭。

---

## 二、GitHub Actions:你的第一个 CI/CD 流水线

### 2.1 什么是 GitHub Actions?

GitHub Actions 是 GitHub 提供的自动化平台。当你在仓库里发生某些事件(比如 push 代码、创建 PR)时,它可以自动运行你定义的脚本。

关键概念:

-**Workflow(工作流)**:整个自动化流程的配置文件,放在 `.github/workflows/` 目录下。

-**Job(任务)**:工作流中的一组步骤,所有步骤并行执行。

-**Step(步骤)**:Job 中的每一步操作,可以是一条命令或者一个 Action。

-**Runner(运行器)**:执行 workflow 的环境,GitHub 提供 Ubuntu/macOS/Windows 虚拟机。

### 2.2 第一个 Workflow:运行测试

假设你有一个 FastAPI 项目,目录结构如下:

```

my-fastapi-app/

├── main.py

├── requirements.txt

├── tests/

│   ├── test_main.py

│   └── conftest.py

├── .github/

│   └── workflows/

│       └── ci.yml          # ← 我们的 workflow 文件

└── pyproject.toml

```

创建 `.github/workflows/ci.yml`:

```yaml

# 定义触发条件:每次 push 和 pull_request 到 main 分支时触发

name: Python CI

on:

push:

branches: [main]

pull_request:

branches: [main]

# 定义一个 job

jobs:

test:

# 在最新的 Ubuntu 虚拟机上运行

runs-on: ubuntu-latest

# 定义不同 Python 版本(同时测多个版本)

strategy:

matrix:

python-version: ["3.11", "3.12", "3.13"]

steps:

# Step 1: 检出代码

      - name: Checkout code

uses: actions/checkout@v4

# Step 2: 设置 Python 环境

      - name: Set up Python ${{ matrix.python-version }}

uses: actions/setup-python@v5

with:

python-version: ${{ matrix.python-version }}

# Step 3: 安装依赖

      - name: Install dependencies

run: |

          python -m pip install --upgrade pip

          pip install pytest pytest-cov httpx

          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

# Step 4: 运行 Lint 检查

      - name: Lint with ruff

run: |

          pip install ruff

          ruff check .

          ruff format --check .

# Step 5: 运行测试

      - name: Run tests

run: |

          pytest tests/ -v --cov=. --cov-report=term-missing

# Step 6: 上传覆盖率报告(需要 Codecov 等第三方服务)

      - name: Upload coverage

uses: codecov/codecov-action@v4

if: matrix.python-version == '3.13'# 只上传一次

```

这就是完整的 CI 流水线!每次你 push 代码到 GitHub,GitHub 会自动:

1. 在 3 个 Python 版本上并行运行

2. 拉取代码 → 装依赖 → 跑 linter → 跑测试

3. 如果任何一步报错,你的 PR 就会被标记为失败

### 2.3 实际项目示例

以一个真实的 FastAPI 项目为例,完整展示 workflow:

```yaml

name: FastAPI Deploy Pipeline

on:

push:

branches: [main, develop]

pull_request:

branches: [main]

env:

REGISTRY: ghcr.io

IMAGE_NAME: ${{ github.repository }}

jobs:

# ===== Job 1: 测试 =====

test:

runs-on: ubuntu-latest

steps:

      - uses: actions/checkout@v4

      - name: Set up Python

uses: actions/setup-python@v5

with:

python-version: "3.12"

cache: "pip"

      - name: Install dependencies

run: |

          pip install --upgrade pip

          pip install -r requirements.txt

          pip install pytest pytest-cov ruff mypy httpx

      - name: Lint check

run: |

          ruff check . || exit 1

          ruff format --check . || exit 1

      - name: Type check with mypy

run: mypy . --ignore-missing-imports || true

      - name: Run tests

run: pytest tests/ -v --cov=myapp --cov-report=xml

      - name: Upload coverage to Codecov

uses: codecov/codecov-action@v4

with:

file: ./coverage.xml

fail_ci_if_error: false

# ===== Job 2: 构建 Docker 镜像 =====

build-and-push:

needs: test# 依赖 test 通过

runs-on: ubuntu-latest

if: github.event_name == 'push' && 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 }}/${{ github.repository }}

      - name: Build and push Docker image

uses: docker/build-push-action@v5

with:

context: .

push: true

tags: ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.sha }}

labels: ${{ steps.meta.outputs.labels }}

# ===== Job 3: 部署到服务器 =====

deploy:

needs: build-and-push

runs-on: ubuntu-latest

if: github.ref == 'refs/heads/main'

steps:

      - name: Deploy via SSH

uses: appleboy/ssh-action@v1

with:

host: ${{ secrets.SERVER_HOST }}

username: ${{ secrets.SERVER_USER }}

key: ${{ secrets.SSH_PRIVATE_KEY }}

script: |

            cd /opt/my-fastapi-app

            docker compose pull

            docker compose up -d --remove-orphans

            docker image prune -f

```

关键点解读:

-`needs: test`:build job 只在 test job 成功后才运行,这是 CI/CD 的核心——**不允许有问题的代码进入构建阶段**。

-`if: github.ref == 'refs/heads/main'`:只有推送到 main 分支才触发构建和部署,feature 分支的 PR 只会跑测试不会部署。

-`secrets.*`:敏感信息(SSH 密钥、服务器地址)通过 GitHub Secrets 注入,绝不硬编码在 workflow 文件中。

---

## 三、Dockerfile 实战:构建生产级镜像

workflow 里的 `docker/build-push-action` 怎么知道你该构建什么?需要一个 `Dockerfile`。

### 3.1 基础版 Dockerfile

```dockerfile

# ===== 构建阶段 =====

FROM python:3.12-slim AS builder

WORKDIR /build

# 复制依赖描述文件并安装

COPY requirements.txt .

RUN pip install --no-cache-dir --upgrade pip \

    && pip install --no-cache-dir -r requirements.txt \

    && pip install --no-cache-dir gunicorn uvicorn[standard]

# 复制源代码

COPY . .

# ===== 运行阶段 =====

FROM python:3.12-slim AS runner

# 创建非 root 用户(安全最佳实践)

RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app

# 从 builder 阶段复制安装的包和应用代码

COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages

COPY --from=builder --chown=appuser:appuser /build /app

# 切换到非 root 用户

USER appuser

# 暴露端口

EXPOSE 8000

# 健康检查(Kubernetes / Docker 编排系统会用到)

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \

CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1

# 启动应用(使用 gunicorn + uvicorn workers)

CMD ["gunicorn", "main:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000", "--timeout", "120"]

```

### 3.2 Docker Compose 编排本地开发环境

光有一个 Dockerfile 还不够,你的 FastAPI 应用通常需要数据库、Redis 等依赖。Docker Compose 让你一条命令拉起整套环境:

```yaml

# docker-compose.yml

version: "3.9"

services:

web:

build: .

ports:

      - "8000:8000"

environment:

      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp

      - REDIS_URL=redis://cache:6379/0

      - SECRET_KEY=${SECRET_KEY:-dev-secret-change-me}

depends_on:

db:

condition: service_healthy

cache:

condition: service_started

restart: unless-stopped

db:

image: postgres:16-alpine

environment:

POSTGRES_DB: myapp

POSTGRES_USER: postgres

POSTGRES_PASSWORD: postgres

volumes:

      - pgdata:/var/lib/postgresql/data

healthcheck:

test: ["CMD-SHELL", "pg_isready -U postgres"]

interval: 5s

timeout: 3s

retries: 5

cache:

image: redis:7-alpine

volumes:

      - redis_data:/data

volumes:

pgdata:

redis_data:

```

启动整套环境:

```bash

dockercomposeup-d

# 等待健康检查通过后访问 http://localhost:8000/docs

```

---

## 四、部署策略:从简单到高级

### 4.1 方案一:最简单 — SSH 直连部署

不需要任何额外工具,直接用 SSH 登录服务器执行脚本:

```yaml

# .github/workflows/deploy.yml 中的部署步骤

- name: Deploy to production

uses: appleboy/ssh-action@v1

with:

host: ${{ secrets.PROD_HOST }}

username: deployer

key: ${{ secrets.SSH_PRIVATE_KEY }}

script: |

      cd /opt/myapp

      git pull origin main

      docker compose down

      docker compose up -d --build

      docker system prune -af

```

优点:零成本、上手快。

缺点:没有回滚机制、容易出问题。

### 4.2 方案二:蓝绿部署(Blue-Green Deployment)

准备两套完全一样的环境,一套在线(蓝色),一套备用(绿色)。部署新版本时先在绿色环境跑,验证通过后把流量切过去。

```yaml

# docker-compose.bluegreen.yml

version: "3.9"

services:

nginx:

image: nginx:alpine

ports:

      - "80:80"

      - "443:443"

volumes:

# 根据 ACTIVATE 变量切换配置

      - ./nginx.active.conf:/etc/nginx/conf.d/default.conf:ro

blue:

extends: web

container_name: myapp-blue

green:

extends: web

container_name: myapp-green

environment:

      - APP_VERSION=canary# 标识 Canary 版本

```

Nginx 配置中用环境变量切换:

```nginx

# nginx.active.conf

upstream backend {

    server blue:8000;  # 改成 green:8000 即可秒级切换

}

server {

    listen 80;

    location / {

        proxy_pass http://backend;

    }

}

```

切换流程:

```bash

#!/bin/bash

# switch-deployment.sh — 部署新版本并切换流量

# Step 1: 构建新版本的绿色环境

dockercomposeup-dgreen--build

# Step 2: 等待启动并做健康检查

foriin {1..30}; do

ifcurl-sfhttp://localhost:8081/health > /dev/null; then

echo"Green environment is healthy!"

break

fi

sleep2

done

# Step 3: 切换 Nginx 配置指向绿色环境

echo"upstream backend { server green:8000; }" > nginx.conf

dockercomposerestartnginx

# Step 4: 观察一段时间,如果没问题再切换回蓝色

echo"Traffic switched to green. Monitoring for 5 minutes..."

sleep300

# Step 5: 确认正常后将蓝色环境更新为新版本,实现轮换

dockercomposestopgreen

dockerrenamemyapp-greenmyapp-blue

dockercomposeup-dblue--build

```

### 4.3 方案三:金丝雀发布(Canary Release)

不是把所有流量一刀切到新环境,而是先让一小部分用户(比如 5%)访问新版本,观察一段时间确认没问题再全部推出去。

```python

# Canary 路由逻辑 — 在 FastAPI 中实现

from fastapi import FastAPI, Request

from starlette.middleware.base import BaseHTTPMiddleware

import random

app = FastAPI()

classCanaryMiddleware(BaseHTTPMiddleware):

"""金丝雀发布中间件:按比例分发流量"""

def__init__(self, app, canary_percentage: float = 5.0):

super().__init__(app)

self.canary_percentage = canary_percentage

# 可以从配置中心/环境变量动态读取

self.canary_version = "v2.0"

asyncdefdispatch(self, request: Request, call_next):

if random.uniform(0, 100) < self.canary_percentage:

# 5% 流量走新版本

            request.state.route = "canary"

# 这里可以修改请求头标记版本

            request.headers.append(("X-Canary-Version", self.canary_version))

else:

            request.state.route = "stable"

        response = await call_next(request)

        response.headers["X-Routing"] = request.state.route

return response

app.add_middleware(CanaryMiddleware, canary_percentage=5.0)

@app.get("/health")

asyncdefhealth():

return {

"status": "ok",

"version": "v2.0",

"routing": "canary",

    }

```

配合监控,可以逐步提高金丝雀比例:5% → 20% → 50% → 100%。每步都看错误率、响应时间等指标。

---

## 五、基础设施即代码(IaC)与 Pulumi

Docker 解决了"应用怎么打包"的问题,但服务器本身呢?Pulumi 是用编程语言(包括 Python)来定义云资源。

### 5.1 用 Pulumi Python 部署云资源

```python

"""

deploy.py — 用 Pulumi + Python 定义云上基础设施

"""

import pulumi

import pulumi_docker as docker

import pulumi_cloudinit as cloudinit

# ===== 1. 构建 Docker 镜像 =====

project_name = "my-fastapi-app"

docker_image = docker.Image(

    project_name,

build=docker.DockerBuild(

context=f"./{project_name}",

dockerfile="./{project_name}/Dockerfile",

    ),

image_name=f"{project_name}:latest",

registry=docker.ImageRegistryArgs(

server="ghcr.io",

username=pulumi.Config().get("gh_username"),

password=pulumi.Config().get_secret("gh_token"),

    ),

)

# ===== 2. 创建云服务器 =====

cloud_init_config = cloudinit.InitConfig(

gzip=False,

base64_encode_secondary=False,

part_multPart=MultipartMimePart(

parts=[

            PartMimePart(

content="""#cloud-config

package_update: true

packages:

  - docker.io

  - python3-pip

write_files:

  - path: /opt/myapp/.env

    content: |

      DATABASE_URL=postgresql://user:pass@db:5432/myapp

      SECRET_KEY=super-secret-key

  - path: /opt/myapp/docker-compose.yml

    content: |

      version: '3.9'

      services:

        web:

          image: ghcr.io/myorg/my-fastapi-app:latest

          ports:

            - '8000:8000'

          env_file: .env

""",

            )

        ]

    ),

)

cloud_init_user_data = cloud_init_config.render_data()

vm = Instance(

f"{project_name}-server",

instance_type="t3.small",

ami="ami-0c55b159cbfafe1f0",  # Ubuntu 22.04

user_data=cloud_init_user_data,

key_name=pulumi.Config().get("ssh_key_name"),

)

# ===== 3. 输出公网 IP =====

pulumi.export("server_public_ip", vm.public_ip)

pulumi.export("image_name", docker_image.image_name)

```

运行部署:

```bash

pulumistackinitproduction

pulumiup

```

这比手动在控制台点点点可靠多了——所有的云资源都在代码版本控制里,谁改了什么、什么时候改的一清二楚。

### 5.2 Terraform(传统备选方案)

如果不喜欢 Python 方式,Terraform 用 HCL 语言做同样的事情:

```hcl

# main.tf — 用 Terraform 定义云资源

terraform {

  required_providers {

    aws = {

      source  = "hashicorp/aws"

      version = "~> 5.0"

    }

  }

}

provider "aws" {

  region = "us-west-2"

}

resource "aws_instance" "web_server" {

  ami           = "ami-0c55b159cbfafe1f0"

  instance_type = "t3.small"

  user_data = <<-EOF

              #!/bin/bash

              apt-get update && apt-get install -y docker.io

              systemctl start docker

              EOF

  tags = {

    Name = "my-fastapi-server"

  }

}

resource "aws_security_group" "web_sg" {

  name = "allow-web-traffic"

  ingress {

    from_port   = 8000

    to_port     = 8000

    protocol    = "tcp"

    cidr_blocks = ["0.0.0.0/0"]

  }

  egress {

    from_port   = 0

    to_port     = 0

    protocol    = "-1"

    cidr_blocks = ["0.0.0.0/0"]

  }

}

```

---

## 六、实战演练:完整的 CI/CD 流水线

现在我们把前面学到的所有内容串在一起,打造一个从开发到部署的完整流水线。

### 6.1 项目结构

```

my-fastapi-app/

├── main.py                 # FastAPI 应用入口

├── models.py               # 数据模型

├── schemas.py              # Pydantic schema

├── services/

│   ├── __init__.py

│   └── data_service.py     # 业务逻辑层

├── tests/

│   ├── __init__.py

│   ├── conftest.py         # pytest fixture

│   └── test_main.py        # API 测试

├── .github/workflows/

│   ├── ci.yml              # 测试流水线

│   └── deploy.yml          # 部署流水线

├── Dockerfile

├── docker-compose.yml

├── requirements.txt

└── pyproject.toml          # 项目元数据

```

### 6.2 CI/CD 流水线流程图

```

开发者推送代码

    │

    ▼

触发 GitHub Actions (ci.yml)

    │

    ├─→ 安装 Python 依赖

    │

    ├─→ Ruff Lint 检查(格式 + 代码质量)

    │   └─→ 失败?发送通知给开发者,停止流程

    │   └─→ 成功?继续

    │

    ├─→ 运行单元测试 + 覆盖率检查

    │   └─→ 失败?通知开发者

    │   └─→ 成功?继续

    │

    ├─→ 构建 Docker 镜像

    │   └─→ 推送到 GHCR(GitHub Container Registry)

    │

    └─→ 部署到预发环境(staging)

        └─→ 运行冒烟测试

            └─→ 全部通过?手动确认后 → 部署到生产

```

### 6.3 完整的 workflow 文件

这是我们在前面已经看到过的 `ci.yml` 和 `deploy.yml` 的精简完整版,这里再整理一遍让你更清晰:

```yaml

# .github/workflows/ci-deploy.yml

name: CI/CD Pipeline

on:

push:

branches: [main]

pull_request:

branches: [main]

env:

REGISTRY: ghcr.io

IMAGE_TAG: ${{ github.sha }}

jobs:

lint-and-test:

runs-on: ubuntu-latest

steps:

      - name: Checkout

uses: actions/checkout@v4

      - name: Setup Python

uses: actions/setup-python@v5

with:

python-version: "3.12"

cache: "pip"

      - name: Install dependencies

run: |

          pip install --upgrade pip

          pip install -r requirements.txt

          pip install pytest pytest-cov ruff mypy

      - name: Ruff lint & format check

run: |

          ruff check .

          ruff format --check .

      - name: MyPy type check

run: mypy . --ignore-missing-imports || true

      - name: Run unit tests

run: pytest tests/ -v --cov=. --cov-report=xml

      - name: Upload coverage

uses: codecov/codecov-action@v4

build-and-push:

needs: lint-and-test

if: github.event_name == 'push'

runs-on: ubuntu-latest

permissions:

packages: write

steps:

      - uses: actions/checkout@v4

      - name: Login to GHCR

uses: docker/login-action@v3

with:

registry: ghcr.io

username: ${{ github.actor }}

password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push

uses: docker/build-push-action@v5

with:

context: .

push: true

tags: ghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }}

deploy-staging:

needs: build-and-push

runs-on: ubuntu-latest

environment: staging

steps:

      - name: Deploy to staging

uses: appleboy/ssh-action@v1

with:

host: ${{ secrets.STAGING_HOST }}

username: deployer

key: ${{ secrets.STAGING_SSH_KEY }}

script: |

            cd /opt/myapp

            docker compose pull

            docker compose up -d --remove-orphans

smoke-test-staging:

needs: deploy-staging

runs-on: ubuntu-latest

steps:

      - name: Smoke test

run: |

          # 等待容器启动

          sleep 10

          # 检查健康端点

          RESPONSE=$(curl -sf http://${{ secrets.STAGING_HOST }}:8000/health)

          echo "Response: $RESPONSE"

          echo "$RESPONSE" | jq -e '.status == "ok"'

```

---

## 七、实操练习

### 练习题

**第 1 题:为你的现有项目添加 CI**

选一个你已经写过的 Python 项目,在 GitHub 上创建一个仓库,然后:

1. 创建 `.github/workflows/ci.yml`

2. 让它能在 push 时自动运行 pytest 测试

3. 提交代码并观察 GitHub Actions 面板的运行结果

```python

# 参考答案思路

# 在 workflow 中添加 steps:

# 1. checkout@v4

# 2. setup-python@v5 with python-version: "3.12"

# 3. pip install pytest

# 4. pytest tests/ -v

```

**第 2 题:编写 Dockerfile 并本地测试**

为你之前的 FastAPI 应用写一个 Dockerfile,然后本地构建并运行:

```bash

dockerbuild-tmy-fastapi-app:test.

dockerrun-p8000:8000--rmmy-fastapi-app:test

# 访问 http://localhost:8000/docs 验证

```

```python

# Dockerfile 关键内容参考

# FROM python:3.12-slim

# WORKDIR /app

# COPY requirements.txt .

# RUN pip install --no-cache-dir -r requirements.txt

# COPY . .

# CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

```

**第 3 题:实现蓝绿部署脚本**

编写一个 shell 或 Python 脚本,实现蓝绿部署:

1. 启动新版本容器

2. 等待健康检查通过

3. 切换到新版本

4. 保留旧版本作为回滚选项

```python

# 参考答案思路

import subprocess

import time

import urllib.request

defblue_green_deploy(version: str) -> None:

# 1. 构建并启动绿色环境

    subprocess.run(["docker", "compose", "up", "-d", "--build", "green"])

# 2. 健康检查

for _ inrange(30):

try:

            resp = urllib.request.urlopen("http://localhost:8081/health")

if resp.status == 200:

break

exceptException:

pass

        time.sleep(2)

else:

raiseRuntimeError("Green environment failed health check!")

# 3. 切换流量

    subprocess.run([

"docker", "exec", "nginx", "sh", "-c",

"echo 'upstream backend { server green:8000; }' > /etc/nginx/conf.d/default.conf"

    ])

    subprocess.run(["docker", "compose", "restart", "nginx"])

print(f"Successfully deployed v{version}")

blue_green_deploy("2.0.0")

```

**第 4 题:添加 Pulumi 基础设施代码**

在一个已有的 AWS 账户上,用 Pulumi Python 编写部署 FastAPI 应用的 IaC 脚本:

1. 创建 EC2 实例

2. 配置安全组(开放 8000 端口)

3. 输出公网 IP 以便访问

```python

# 参考答案思路

# import pulumi

# import pulumi_aws as aws

# vm = aws.ec2.Instance("web-server", ...)

# pulumi.export("ip", vm.public_ip)

```

**第 5 题:设计一个多级流水线**

为一个电商微服务项目设计 CI/CD 流水线。该项目包含:

- user-service(用户服务,REST API)

- order-service(订单服务,REST API)

- notification-service(通知服务,接收消息队列事件)

要求:

- 每个服务有独立的测试和构建

- 所有服务测试通过后才部署

- 部署顺序:user → order → notification(注意依赖关系)

---

## 八、总结

这一期我们聊了很多内容,我尽量把它拆解成你能一步步跟上的节奏:

1.**CI/CD 的本质**:让别人不能把 bug 合并进来,让代码从本地到服务器的每一步都是可重复、可追溯的。

2.**GitHub Actions 是起步最快的工具**:不需要额外装软件,直接在 GitHub 里定义 YAML 就行了。

3.**Docker 是现代部署的标准**:不管你的代码跑在本地还是云上,打包进容器就能保证一致的环境。

4.**蓝绿部署和金丝雀发布是进阶话题**:它们解决了"上线万一出问题怎么办"的恐惧。

5.**基础设施即代码**:服务器也是代码的一部分,应该被审查、被测试、被版本化。

最重要的一句话:**不要追求一步到位完美。先从最简单的 CI 开始——每次 push 自动跑测试,这就已经是 80% 的进步了。**

---

## 九、课后练习参考答案

### 第 3 题完整实现(蓝绿部署脚本)

```python

#!/usr/bin/env python3

"""blue_green_deploy.py — 蓝绿部署自动化脚本"""

import subprocess

import time

import urllib.request

import sys

defwait_for_health(url: str, timeout: int = 60, interval: int = 2) -> bool:

"""等待服务健康检查通过"""

    elapsed = 0

while elapsed < timeout:

try:

            resp = urllib.request.urlopen(url, timeout=5)

if resp.status == 200:

print(f"  ✅ 服务健康检查通过")

returnTrue

exceptException:

pass

        time.sleep(interval)

        elapsed += interval

print(f"  ❌ 超时:{timeout}秒内未通过健康检查")

returnFalse

defswap_traffic(blue_port: int, green_port: int, nginx_container: str = "nginx") -> None:

"""切换 Nginx 流量方向"""

    current = "blue"if blue_port == 8000else"green"

    target = "green"if current == "blue"else"blue"

    target_port = green_port if target == "green"else blue_port

    cmd = [

"docker", "exec", nginx_container, "sh", "-c",

f"echo 'upstream backend {{ server localhost:{target_port}; }}' > "

"/etc/nginx/conf.d/upstream.conf"

    ]

    subprocess.run(cmd, check=True)

    subprocess.run(["docker", "compose", "restart", nginx_container], check=True)

print(f"  🔄 流量已切换: {current} → {target}")

defrollback(current_port: int, nginx_container: str = "nginx") -> None:

"""回滚到上一个版本"""

    target_port = current_port - 8000 + 8000# blue=8000, green=8001

# 简化版:回滚就是切回去

print("  ⚠️  执行回滚...")

defmain():

iflen(sys.argv) < 2:

print("用法: python blue_green_deploy.py <version>")

        sys.exit(1)

    version = sys.argv[1]

print(f"🚀 开始部署 v{version}...")

# Step 1: 构建新镜像

print("  📦 构建 Docker 镜像...")

    subprocess.run([

"docker", "build", "-t", f"myapp:{version}", "-f", "Dockerfile", "."

    ], check=True)

# Step 2: 启动绿色环境

print("  🟢 启动绿色环境...")

    env_vars = {

"APP_VERSION": version,

"BLUE_PORT": "8000",

"GREEN_PORT": "8001",

    }

    subprocess.run([

"docker", "compose", "run", "--rm", "--no-deps", 

"green", "docker", "swarm", "init"

    ])

# Step 3: 健康检查

print("  🔍 等待健康检查...")

ifnot wait_for_health("http://localhost:8001/health"):

print("  ❌ 健康检查失败,回滚!")

# rollback(8001)

        sys.exit(1)

# Step 4: 切换流量

print("  🌊 切换流量...")

    swap_traffic(8000, 8001)

# Step 5: 停止旧蓝色容器(可选)

print("  ✅ 部署完成 v{version}")

if__name__ == "__main__":

    main()

```

### 第 5 题架构图参考

```

                    ┌─────────────────────────────────────────────────┐

                    │              GitHub Push Event                   │

                    └────────────────────────┬────────────────────────┘

                                             │

                    ┌────────────────────────┼────────────────────────┐

                    │                        │                         │

              ┌─────▼─────┐           ┌─────▼─────┐           ┌─────▼─────┐

              │ User Svc  │           │ Order Svc │           │ Notif Svc │

              │  CI Build │           │  CI Build │           │  CI Build │

              └─────┬─────┘           └─────┬─────┘           └─────┬─────┘

                    │                        │                       │

                    └────────────────────────┼───────────────────────┘

                                             │

                              All tests passed? → Build Docker images

                                             │

                              ┌──────────────▼──────────────┐

                              │        Deploy Order:         │

                              │  1. User Service (base)     │

                              │  2. Order Service (depends) │

                              │  3. Notification Service    │

                              └──────────────┬──────────────┘

                                             │

                              ┌──────────────▼──────────────┐

                              │   Smoke Tests on Staging    │

                              └──────────────┬──────────────┘

                                             │

                              Manual Approval → Deploy to Production

```

---

## 十、下期预告

**Episode 19:Python 并发与多线程深度实践**

我们已经学了 asyncio(协程),接下来要补上并发编程的另一半:**多线程和多进程**。

- threading 和 multiprocessing 的区别和使用场景

- GIL 是什么?为什么它会影响你的性能?

- 用 concurrent.futures 简化并发编程

- 生产者-消费者模式的多种实现

- GIL 绕过技巧:多进程池 + 协程混合架构

敬请期待!

---

**📚 系列回顾**

到目前为止我们学过的内容:

| Episode | 主题 |

|---------|------|

| 01 | 环境搭建与基础语法 |

| 02 | 数据结构与字符串处理 |

| 03 | 面向对象编程 |

| 04 | 装饰器、生成器与文件 IO |

| 05 | 爬虫入门 |

| 06 | 数据分析入门 |

| 07 | Web 后端开发 |

| 08 | AI 实战入门 |

| 09 | 前端入门 |

| 10 | 项目实战——Dashboard |

| 11 | Docker 容器化部署 |

| 12 | 数据库进阶 |

| 13 | 异步编程与并发 |

| 14 | 日志系统与调试技巧 |

| 15 | 自动化运维与脚本实战 |

| 16 | 单元测试与代码质量 |

| 17 | 微服务入门 |

| 18 | CI/CD 与 DevOps 实战 ← **本期** |

加油!你已经从零基础走到了"能用 Python 写完整的后端并部署上线"的水平。🎉

最新文章

随机文章