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

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

  • 2026-08-18 23:10:46
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 分支时触发

namePython CI

on:

push:

branches: [main]

pull_request:

branches: [main]

# 定义一个 job

jobs:

test:

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

runs-onubuntu-latest

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

strategy:

matrix:

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

steps:

# Step 1: 检出代码

      - nameCheckout code

usesactions/checkout@v4

# Step 2: 设置 Python 环境

      - nameSet up Python ${{ matrix.python-version }}

usesactions/setup-python@v5

with:

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

# Step 3: 安装依赖

      - nameInstall 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 检查

      - nameLint with ruff

run|

          pip install ruff

          ruff check .

          ruff format --check .

# Step 5: 运行测试

      - nameRun tests

run|

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

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

      - nameUpload coverage

usescodecov/codecov-action@v4

ifmatrix.python-version == '3.13'# 只上传一次

```

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

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

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

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

### 2.3 实际项目示例

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

```yaml

nameFastAPI Deploy Pipeline

on:

push:

branches: [maindevelop]

pull_request:

branches: [main]

env:

REGISTRYghcr.io

IMAGE_NAME${{ github.repository }}

jobs:

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

test:

runs-onubuntu-latest

steps:

      - usesactions/checkout@v4

      - nameSet up Python

usesactions/setup-python@v5

with:

python-version"3.12"

cache"pip"

      - nameInstall dependencies

run|

          pip install --upgrade pip

          pip install -r requirements.txt

          pip install pytest pytest-cov ruff mypy httpx

      - nameLint check

run|

          ruff check . || exit 1

          ruff format --check . || exit 1

      - nameType check with mypy

runmypy . --ignore-missing-imports || true

      - nameRun tests

runpytest tests/ -v --cov=myapp --cov-report=xml

      - nameUpload coverage to Codecov

usescodecov/codecov-action@v4

with:

file./coverage.xml

fail_ci_if_errorfalse

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

build-and-push:

needstest# 依赖 test 通过

runs-onubuntu-latest

ifgithub.event_name == 'push' && github.ref == 'refs/heads/main'

permissions:

contentsread

packageswrite

steps:

      - usesactions/checkout@v4

      - nameLog in to Container Registry

usesdocker/login-action@v3

with:

registry${{ env.REGISTRY }}

username${{ github.actor }}

password${{ secrets.GITHUB_TOKEN }}

      - nameExtract metadata

idmeta

usesdocker/metadata-action@v5

with:

images${{ env.REGISTRY }}/${{ github.repository }}

      - nameBuild and push Docker image

usesdocker/build-push-action@v5

with:

context.

pushtrue

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

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

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

deploy:

needsbuild-and-push

runs-onubuntu-latest

ifgithub.ref == 'refs/heads/main'

steps:

      - nameDeploy via SSH

usesappleboy/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:

conditionservice_healthy

cache:

conditionservice_started

restartunless-stopped

db:

imagepostgres:16-alpine

environment:

POSTGRES_DBmyapp

POSTGRES_USERpostgres

POSTGRES_PASSWORDpostgres

volumes:

      - pgdata:/var/lib/postgresql/data

healthcheck:

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

interval5s

timeout3s

retries5

cache:

imageredis: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 中的部署步骤

nameDeploy to production

usesappleboy/ssh-action@v1

with:

host${{ secrets.PROD_HOST }}

usernamedeployer

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:

imagenginx:alpine

ports:

      - "80:80"

      - "443:443"

volumes:

# 根据 ACTIVATE 变量切换配置

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

blue:

extendsweb

container_namemyapp-blue

green:

extendsweb

container_namemyapp-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/nullthen

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__(selfappcanary_percentagefloat = 5.0):

super().__init__(app)

self.canary_percentage = canary_percentage

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

self.canary_version = "v2.0"

asyncdefdispatch(selfrequest: Request, call_next):

if random.uniform(0100) < 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

nameCI/CD Pipeline

on:

push:

branches: [main]

pull_request:

branches: [main]

env:

REGISTRYghcr.io

IMAGE_TAG${{ github.sha }}

jobs:

lint-and-test:

runs-onubuntu-latest

steps:

      - nameCheckout

usesactions/checkout@v4

      - nameSetup Python

usesactions/setup-python@v5

with:

python-version"3.12"

cache"pip"

      - nameInstall dependencies

run|

          pip install --upgrade pip

          pip install -r requirements.txt

          pip install pytest pytest-cov ruff mypy

      - nameRuff lint & format check

run|

          ruff check .

          ruff format --check .

      - nameMyPy type check

runmypy . --ignore-missing-imports || true

      - nameRun unit tests

runpytest tests/ -v --cov=. --cov-report=xml

      - nameUpload coverage

usescodecov/codecov-action@v4

build-and-push:

needslint-and-test

ifgithub.event_name == 'push'

runs-onubuntu-latest

permissions:

packageswrite

steps:

      - usesactions/checkout@v4

      - nameLogin to GHCR

usesdocker/login-action@v3

with:

registryghcr.io

username${{ github.actor }}

password${{ secrets.GITHUB_TOKEN }}

      - nameBuild and push

usesdocker/build-push-action@v5

with:

context.

pushtrue

tagsghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }}

deploy-staging:

needsbuild-and-push

runs-onubuntu-latest

environmentstaging

steps:

      - nameDeploy to staging

usesappleboy/ssh-action@v1

with:

host${{ secrets.STAGING_HOST }}

usernamedeployer

key${{ secrets.STAGING_SSH_KEY }}

script|

            cd /opt/myapp

            docker compose pull

            docker compose up -d --remove-orphans

smoke-test-staging:

needsdeploy-staging

runs-onubuntu-latest

steps:

      - nameSmoke 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(versionstr) -> 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(urlstrtimeoutint = 60intervalint = 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_portintgreen_portintnginx_containerstr = "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_portintnginx_containerstr = "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(80008001)

# 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 写完整的后端并部署上线"的水平。🎉

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 15:51:33 HTTP/2.0 GET : https://f.mffb.com.cn/a/505904.html
  2. 运行时间 : 0.206014s [ 吞吐率:4.85req/s ] 内存消耗:4,567.34kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=54a0e96c5e0e3c085f0bfc03fcbab7b0
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001094s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001376s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.004317s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000655s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001410s ]
  6. SELECT * FROM `set` [ RunTime:0.005530s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001533s ]
  8. SELECT * FROM `article` WHERE `id` = 505904 LIMIT 1 [ RunTime:0.003152s ]
  9. UPDATE `article` SET `lasttime` = 1787298693 WHERE `id` = 505904 [ RunTime:0.013877s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000672s ]
  11. SELECT * FROM `article` WHERE `id` < 505904 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001248s ]
  12. SELECT * FROM `article` WHERE `id` > 505904 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001367s ]
  13. SELECT * FROM `article` WHERE `id` < 505904 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001498s ]
  14. SELECT * FROM `article` WHERE `id` < 505904 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001951s ]
  15. SELECT * FROM `article` WHERE `id` < 505904 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001959s ]
0.209512s