好的,我们开始。
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
: 定义流水线的阶段,这里有test
、build
和deploy
三个阶段。variables
: 定义全局变量,例如PIP_CACHE_DIR
用于缓存 pip 包,PYTHON_VERSION
指定 Python 版本。cache
: 定义缓存路径,用于加速构建过程。before_script
: 在每个作业执行之前运行的脚本,用于设置环境,例如创建虚拟环境、安装依赖。test
: 测试阶段,使用pytest
运行测试,并生成代码覆盖率报告。build
: 构建阶段,使用 Docker 构建镜像,并推送到 GitLab Registry。 需要配置CI_REGISTRY_USER
,CI_REGISTRY_PASSWORD
和CI_REGISTRY_IMAGE
变量。deploy
: 部署阶段,使用kubectl
将镜像部署到 Kubernetes 集群。 需要配置KUBE_NAMESPACE
,your-kubernetes-cluster-context
,your-deployment-name
和your-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-name
和your-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-name
和 your-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
: 触发工作流程的事件,例如push
和pull_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/app
和 your-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 的核心概念,并将其应用于你的项目,以提高开发效率和软件质量。
持续集成让开发更高效
持续集成和持续部署是现代软件开发的关键实践,通过自动化测试和部署流程,可以有效提高开发效率和软件质量,并降低发布风险。