Python的CI/CD:如何使用GitLab CI和GitHub Actions实现自动化测试和部署。

好的,我们开始。

Python CI/CD:GitLab CI 和 GitHub Actions 实战

大家好,今天我们来聊聊 Python 项目的持续集成和持续部署 (CI/CD)。CI/CD 旨在自动化软件开发流程,从而实现更频繁、更可靠的发布。我们将重点介绍如何使用 GitLab CI 和 GitHub Actions 这两个流行的平台来实现 Python 项目的自动化测试和部署。

CI/CD 核心概念

在深入实践之前,我们需要理解一些关键概念:

  • 持续集成 (Continuous Integration, CI):频繁地将代码集成到共享存储库中。每次集成都会触发自动化的构建和测试,以便尽早发现问题。
  • 持续交付 (Continuous Delivery, CD):确保代码可以随时发布到生产环境。除了自动化构建和测试之外,还包括自动化部署到 staging 或预生产环境。
  • 持续部署 (Continuous Deployment, CD):自动将代码部署到生产环境。这是持续交付的延伸,完全自动化了发布过程。
  • 流水线 (Pipeline):CI/CD 的核心,定义了自动化流程的各个阶段 (stages) 和步骤 (jobs)。
  • 阶段 (Stage):流水线中的一个逻辑分组,通常包含多个并行执行的作业。
  • 作业 (Job):流水线中的一个最小执行单元,执行特定的任务,例如运行测试或部署代码。
  • 构建 (Build):将源代码编译、打包成可执行文件或软件包的过程。
  • 测试 (Test):验证代码功能和质量的过程,包括单元测试、集成测试、端到端测试等。
  • 部署 (Deploy):将构建好的应用程序或服务部署到目标环境的过程。

使用 GitLab CI 实现 Python 项目 CI/CD

GitLab CI 是 GitLab 内置的 CI/CD 工具,通过 .gitlab-ci.yml 文件定义流水线。

1. 创建 .gitlab-ci.yml 文件

在项目根目录下创建 .gitlab-ci.yml 文件,这是 GitLab CI 的配置文件。

stages:
  - test
  - build
  - deploy

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  PYTHON_VERSION: "3.9"

cache:
  paths:
    - .cache/pip
    - venv/

before_script:
  - python -V
  - python -m venv venv
  - source venv/bin/activate
  - pip install --upgrade pip
  - pip install -r requirements.txt

test:
  stage: test
  script:
    - pytest --cov=./ --cov-report term-missing

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  after_script:
    - docker logout $CI_REGISTRY

deploy:
  stage: deploy
  image: alpine/kubectl:latest
  variables:
    KUBE_NAMESPACE: your-kubernetes-namespace
  before_script:
    - apk add --no-cache curl
    - curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
    - chmod +x ./kubectl
    - mv ./kubectl /usr/local/bin/kubectl
    - kubectl config use-context your-kubernetes-cluster-context
  script:
    - kubectl set image deployment/your-deployment-name your-container-name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n $KUBE_NAMESPACE
  only:
    - main

2. 文件内容详解

  • stages: 定义流水线的阶段,这里有 testbuilddeploy 三个阶段。
  • variables: 定义全局变量,例如 PIP_CACHE_DIR 用于缓存 pip 包,PYTHON_VERSION 指定 Python 版本。
  • cache: 定义缓存路径,用于加速构建过程。
  • before_script: 在每个作业执行之前运行的脚本,用于设置环境,例如创建虚拟环境、安装依赖。
  • test: 测试阶段,使用 pytest 运行测试,并生成代码覆盖率报告。
  • build: 构建阶段,使用 Docker 构建镜像,并推送到 GitLab Registry。 需要配置 CI_REGISTRY_USER, CI_REGISTRY_PASSWORDCI_REGISTRY_IMAGE 变量。
  • deploy: 部署阶段,使用 kubectl 将镜像部署到 Kubernetes 集群。 需要配置 KUBE_NAMESPACEyour-kubernetes-cluster-contextyour-deployment-nameyour-container-name
  • only: 限制作业只在 main 分支上运行。

3. 代码解释

  • PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip": 这行代码定义了一个名为 PIP_CACHE_DIR 的变量,其值为 $CI_PROJECT_DIR/.cache/pip$CI_PROJECT_DIR 是 GitLab CI 提供的一个预定义变量,表示当前项目的根目录。 因此,这行代码的目的是将 pip 的缓存目录设置为项目根目录下的 .cache/pip 文件夹。 这样做的好处是,可以利用 GitLab CI 的缓存机制,避免每次构建都重新下载 pip 包,从而加速构建过程。

  • python -m venv venv: 这行代码使用 Python 的 venv 模块创建一个名为 venv 的虚拟环境。虚拟环境是一个独立的 Python 运行环境,可以隔离不同项目的依赖,避免冲突。

  • source venv/bin/activate: 这行代码激活刚刚创建的虚拟环境。激活虚拟环境后,所有后续的 pip 命令和 Python 脚本都会在虚拟环境中执行。

  • pytest --cov=./ --cov-report term-missing: 这行代码使用 pytest 运行测试,并生成代码覆盖率报告。--cov=./ 指定代码覆盖率的根目录为当前目录。--cov-report term-missing 指定代码覆盖率报告的格式为终端输出,并只显示缺失覆盖的代码行。

  • docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY: 这行代码使用 Docker 客户端登录到 GitLab Registry。$CI_REGISTRY_USER$CI_REGISTRY_PASSWORD$CI_REGISTRY 都是 GitLab CI 提供的预定义变量,分别表示 GitLab Registry 的用户名、密码和地址。需要配置这些变量才能成功登录。

  • docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .: 这行代码使用 Docker 客户端构建镜像。-t 选项指定镜像的名称和标签。$CI_REGISTRY_IMAGE 是 GitLab CI 提供的预定义变量,表示 GitLab Registry 中镜像的名称。$CI_COMMIT_SHA 也是 GitLab CI 提供的预定义变量,表示当前提交的 SHA 值。使用提交 SHA 值作为镜像标签可以确保每次提交都构建一个唯一的镜像。. 表示 Dockerfile 位于当前目录。

  • docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA: 这行代码使用 Docker 客户端将构建好的镜像推送到 GitLab Registry。

  • kubectl set image deployment/your-deployment-name your-container-name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n $KUBE_NAMESPACE: 这行代码使用 kubectl 命令更新 Kubernetes Deployment 中的镜像。deployment/your-deployment-name 指定要更新的 Deployment 的名称。your-container-name 指定要更新的容器的名称。$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA 指定新的镜像名称和标签。-n $KUBE_NAMESPACE 指定 Deployment 所在的命名空间。 需要替换 your-deployment-nameyour-container-name 为你的实际值。

4. 配置 GitLab CI 变量

在 GitLab 项目的 "Settings" -> "CI/CD" -> "Variables" 中添加以下变量:

  • CI_REGISTRY_USER: GitLab 用户名
  • CI_REGISTRY_PASSWORD: GitLab 密码或访问令牌
  • CI_REGISTRY_IMAGE: GitLab Registry 中镜像的名称 (例如:registry.gitlab.com/your-group/your-project/your-image)
  • KUBE_NAMESPACE: Kubernetes 命名空间
  • your-kubernetes-cluster-context: kubectl context

5. 推送代码并触发流水线

.gitlab-ci.yml 文件推送到 GitLab 仓库,GitLab CI 将自动检测到该文件并触发流水线。你可以在 GitLab CI/CD 页面上查看流水线的执行状态。

6. Kubernetes部署准备

确保你已经配置好了 kubectl 并且可以连接到你的 Kubernetes 集群。你需要替换 .gitlab-ci.yml 文件中的 your-kubernetes-cluster-context 为你集群的 context 名称,并替换 your-deployment-nameyour-container-name 为你的 deployment 和 container 的名称。

使用 GitHub Actions 实现 Python 项目 CI/CD

GitHub Actions 是 GitHub 提供的 CI/CD 工具,通过 YAML 文件定义工作流程 (workflow)。

1. 创建 workflow 文件

在项目根目录下创建 .github/workflows/main.yml 文件。

name: Python CI/CD

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.8", "3.9", "3.10"]

    steps:
      - uses: actions/checkout@v3
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v3
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r requirements.txt
      - name: Lint with flake8
        run: |
          # stop the build if there are Python syntax errors or undefined names
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
      - name: Test with pytest
        run: |
          pytest --cov=./ --cov-report term-missing

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python 3.9
        uses: actions/setup-python@v3
        with:
          python-version: "3.9"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r requirements.txt
      - name: Build package
        run: |
          python setup.py sdist bdist_wheel

      - name: Upload package to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_API_TOKEN }}

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to Server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /path/to/your/app
            git pull origin main
            # restart your application
            sudo systemctl restart your-app

2. 文件内容详解

  • name: 工作流程的名称。
  • on: 触发工作流程的事件,例如 pushpull_request
  • jobs: 定义工作流程中的作业。
  • runs-on: 指定作业运行的操作系统。
  • strategy: 定义作业的执行策略,例如使用矩阵 (matrix) 来并行测试不同的 Python 版本。
  • steps: 定义作业中的步骤。
  • uses: 使用 GitHub Actions 社区提供的 actions。
  • run: 运行 shell 命令。
  • needs: 定义作业的依赖关系。
  • secrets: 引用 GitHub Secrets 中存储的敏感信息,例如 API 密钥和 SSH 密钥。

3. 代码解释

  • strategy.matrix.python-version: 这部分定义了一个矩阵,用于并行测试不同的 Python 版本。python-version: ["3.8", "3.9", "3.10"] 表示将分别使用 Python 3.8、3.9 和 3.10 运行测试作业。

  • uses: actions/checkout@v3: 这行代码使用 actions/checkout@v3 action 来检出代码。actions/checkout 是 GitHub 官方提供的 action,用于将仓库的代码克隆到运行工作流程的虚拟机上。@v3 指定使用 action 的版本。

  • uses: actions/setup-python@v3: 这行代码使用 actions/setup-python@v3 action 来设置 Python 环境。actions/setup-python 是 GitHub 官方提供的 action,用于在运行工作流程的虚拟机上安装指定版本的 Python。

  • flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics: 这行代码使用 flake8 进行代码风格检查。flake8 是一个流行的 Python 代码风格检查工具。--count 选项表示显示错误总数。--select=E9,F63,F7,F82 选项表示只检查指定的错误类型。--show-source 选项表示显示错误代码的源代码。--statistics 选项表示显示统计信息。

  • python setup.py sdist bdist_wheel: 这行代码使用 setup.py 构建 Python 包。sdist 命令创建一个源代码分发包。bdist_wheel 命令创建一个 wheel 包。wheel 包是一种二进制分发包,可以更快地安装。

  • uses: pypa/gh-action-pypi-publish@release/v1: 这行代码使用 pypa/gh-action-pypi-publish@release/v1 action 将 Python 包发布到 PyPI。pypa/gh-action-pypi-publish 是一个由 Python Packaging Authority (PyPA) 维护的 action,用于将 Python 包发布到 PyPI。

  • with: password: ${{ secrets.PYPI_API_TOKEN }}: 这行代码将 PyPI API 密钥传递给 pypa/gh-action-pypi-publish action。secrets.PYPI_API_TOKEN 引用 GitHub Secrets 中存储的 PyPI API 密钥。

  • uses: appleboy/ssh-action@master: 这行代码使用 appleboy/ssh-action@master action 通过 SSH 连接到服务器。appleboy/ssh-action 是一个社区维护的 action,用于通过 SSH 连接到远程服务器并执行命令。

  • with: host: ${{ secrets.SSH_HOST }}: 这行代码将 SSH 主机名传递给 appleboy/ssh-action action。secrets.SSH_HOST 引用 GitHub Secrets 中存储的 SSH 主机名。

  • script: | cd /path/to/your/app git pull origin main sudo systemctl restart your-app: 这部分定义了要在服务器上执行的脚本。脚本首先进入应用程序的目录,然后从 GitHub 仓库拉取最新的代码,最后重启应用程序。

4. 配置 GitHub Secrets

在 GitHub 仓库的 "Settings" -> "Secrets and variables" -> "Actions" 中添加以下 secrets:

  • PYPI_API_TOKEN: PyPI API 密钥
  • SSH_HOST: SSH 主机名
  • SSH_USERNAME: SSH 用户名
  • SSH_PRIVATE_KEY: SSH 私钥

5. 推送代码并触发工作流程

.github/workflows/main.yml 文件推送到 GitHub 仓库,GitHub Actions 将自动检测到该文件并触发工作流程。你可以在 GitHub Actions 页面上查看工作流程的执行状态.

6. 服务器部署准备

确保你已经在服务器上配置好了 SSH 并且可以从 GitHub Actions 运行的虚拟机上访问。你需要在服务器上安装 Git,并且配置好应用程序的 systemd 服务。 你需要替换 .github/workflows/main.yml 文件中的 /path/to/your/appyour-app 为你的应用程序的实际路径和 systemd 服务名称。

GitLab CI 和 GitHub Actions 的比较

特性 GitLab CI GitHub Actions
平台 GitLab 内置 GitHub
配置文件 .gitlab-ci.yml .github/workflows/main.yml (或其他 YAML 文件)
语言 YAML YAML
社区 GitLab 社区 GitHub 社区
易用性 紧密集成 GitLab,易于上手 丰富的 actions 市场,灵活但可能需要更多配置
适用场景 GitLab 用户,需要深度集成 CI/CD 与代码托管 GitHub 用户,需要灵活的 CI/CD 流程,可定制性强
价格 GitLab 提供免费和付费版本 GitHub 提供免费和付费版本,按使用时间或存储量收费
Secret管理 在 GitLab 项目设置中配置 在 GitHub 仓库设置中配置
Kubernetes部署 需要配置 kubectl 和 kubeconfig 需要配置 kubectl 和 kubeconfig
SSH部署 可以通过 SSH 连接到服务器部署代码 可以通过 SSH 连接到服务器部署代码
依赖管理 使用 before_script 安装依赖 使用 steps 安装依赖
缓存 使用 cache 定义缓存路径 使用 actions/cache action 定义缓存路径
并行测试 可以通过矩阵 (matrix) 并行测试 可以通过矩阵 (matrix) 并行测试
代码风格检查 可以使用 flake8 等工具进行代码风格检查 可以使用 flake8 等工具进行代码风格检查
包发布 可以使用 twine 等工具发布到 PyPI 可以使用 pypa/gh-action-pypi-publish action 发布到 PyPI

最佳实践

  • 使用虚拟环境: 始终使用虚拟环境隔离项目依赖。
  • 缓存依赖: 利用 CI/CD 平台的缓存机制加速构建过程。
  • 编写单元测试: 编写全面的单元测试来保证代码质量。
  • 使用代码风格检查工具: 使用 flake8 或 pylint 等工具来保持代码风格一致。
  • 配置代码覆盖率: 使用 pytest-cov 等工具来生成代码覆盖率报告,并确保代码覆盖率达到一定水平。
  • 使用 Secrets 管理敏感信息: 不要将 API 密钥和 SSH 密钥等敏感信息直接写在配置文件中,而是使用 CI/CD 平台的 Secrets 管理功能。
  • 进行代码审查: 在合并代码之前进行代码审查,可以发现潜在的问题并提高代码质量。
  • 监控部署: 监控应用程序的运行状态,及时发现和解决问题。
  • 使用基础设施即代码 (Infrastructure as Code, IaC): 使用 Terraform 或 Ansible 等工具来管理基础设施,可以提高部署效率和可靠性。
  • 采用蓝绿部署或金丝雀发布: 使用蓝绿部署或金丝雀发布等策略来降低部署风险。

总结

我们学习了如何使用 GitLab CI 和 GitHub Actions 实现 Python 项目的自动化测试和部署。 GitLab CI 和 GitHub Actions 都是强大的 CI/CD 工具,选择哪个取决于你的具体需求和偏好。关键在于理解 CI/CD 的核心概念,并将其应用于你的项目,以提高开发效率和软件质量。

持续集成让开发更高效

持续集成和持续部署是现代软件开发的关键实践,通过自动化测试和部署流程,可以有效提高开发效率和软件质量,并降低发布风险。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注