Skip to main content

2 posts tagged with "CI/CD"

View All Tags

在开发名为 Textpod(用 Rust 编写的笔记应用)时,我需要自动化地在 GitHub 上完成构建和发布。 以下内容(以及相应的 YAML 配置文件)演示了整套自动化流程的配置步骤,包括:

  1. 为 Windows、Linux、macOS (Intel + ARM) 构建二进制
  2. 将这些构建产物及校验文件上传到最新的 GitHub Release
  3. 发布到 crates.io
  4. 构建面向 amd64 和 arm64 的精简 Docker 镜像,并推送到 Docker Hub

一、触发条件:GitHub Release

首先,我们以 release 事件作为执行触发器。YAML 配置大致如下:

on:
release:
types:
- created

当我们在 GitHub 中创建一个新的 Release 时,这些后续的构建及发布任务就会自动执行。


二、在 Linux 环境中构建 Linux 与 Windows 二进制

可以在同一个 Linux 环境中通过 rustup 对不同的目标进行交叉编译(例如 Windows 和 Linux)。

jobs:
linux_windows:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2

- name: Install Linux and Windows Cross Compilers
run: sudo apt-get install --yes --no-install-recommends musl-tools gcc-mingw-w64-x86-64-win32

- name: Install rustup targets
run: rustup target add x86_64-unknown-linux-musl x86_64-pc-windows-gnu

- name: Build the executable
run: cargo build --release --target x86_64-unknown-linux-musl --target x86_64-pc-windows-gnu

- name: Tar x86_64 binary
run: tar -czvf textpod-gnu-linux-x86_64.tar.gz -C target/x86_64-unknown-linux-musl/release textpod

- name: Zip windows binary
run: zip -j textpod-windows.zip target/x86_64-pc-windows-gnu/release/textpod.exe

- name: Generate SHA256 checksums
run: |
shasum -a 256 textpod-gnu-linux-x86_64.tar.gz > textpod-gnu-linux-x86_64.tar.gz.sha256
shasum -a 256 textpod-windows.zip > textpod-windows.zip.sha256

- name: Upload release binaries
uses: alexellis/upload-assets@0.4.0
env:
GITHUB_TOKEN: ${{ github.token }}
with:
asset_paths: '["textpod-gnu-linux-x86_64.tar.gz", "textpod-windows.zip", "textpod-gnu-linux-x86_64.tar.gz.sha256", "textpod-windows.zip.sha256"]'

构建说明

  1. Step 4 (cargo build) 一次性构建两个目标:
    • x86_64-unknown-linux-musl
    • x86_64-pc-windows-gnu
  2. 后续分别将 Linux 产物打包为 tar.gz 文件、Windows 产物打包为 zip 文件。
  3. 生成各自的 SHA256 校验文件:textpod-gnu-linux-x86_64.tar.gz.sha256textpod-windows.zip.sha256
  4. 使用 alexellis/upload-assets@0.4.0 Action 将这四个文件一并上传到 GitHub Release。

注意${{ github.token }} 是 GitHub 自动提供的内置 Token,不需要你额外创建或配置。


三、在 macOS 环境中构建 x86 和 ARM 二进制

对 macOS 的构建与上述类似,但需要指定在 macOS 平台下(runs-on: macos-latest)进行,并添加两个目标:Intel 和 ARM。

rustup target add x86_64-apple-darwin aarch64-apple-darwin
cargo build --release --target=x86_64-apple-darwin --target=aarch64-apple-darwin

同理,你可以将打包、校验、上传至 Release 等步骤与 Linux/Windows 的做法相结合,为 macOS 平台生成并上传相应产物。


四、发布到 crates.io

在完成了所有平台的构建(Linux、Windows、macOS)后,可以通过以下方式将包发布到 crates.io:

  crates:
runs-on: ubuntu-latest
needs: [linux_windows, macos]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: katyo/publish-crates@v2
with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
  • needs: [linux_windows, macos] 表示这个 job 必须等前面的两个构建都成功后才会执行。
  • 你需要在 crates.io 创建一个 API Token,并将其作为机密变量(CARGO_REGISTRY_TOKEN)添加到 GitHub 的 secrets 中。

五、构建并推送 Docker 镜像至 Docker Hub

最后,可以构建面向 amd64 和 arm64 平台的 Docker 镜像,然后推送到 Docker Hub:

  docker:
runs-on: ubuntu-latest
needs: crates
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: freetonik/textpod:latest

说明

  • docker/build-push-action@v6 可以自动构建多架构镜像。
  • 在构建之前,先使用 setup-qemu-actionsetup-buildx-action 来启用对多平台(amd64、arm64)的支持。
  • 将 Docker Hub 的认证信息 (DOCKERHUB_USERNAME, DOCKERHUB_TOKEN) 配置为 GitHub secrets。
  • 构建完成后,镜像会被推送到 freetonik/textpod:latest,你可以换成自己的 Docker Hub 命名空间和仓库名称。

需要注意,多平台构建常常比较耗时,可能需要 20-40 分钟左右。


六、最终效果

  • GitHub Release:创建新的 Release 后,一开始只会显示源代码链接;几分钟后,构建完成并将二进制文件(以及校验文件)上传到 Release 页。
  • Docker 镜像:我们使用了 rust:alpine 作为基础镜像(参见项目中的 Dockerfile),通常可获得体积只有约 10 MB 的容器镜像,已发布在 Docker Hub。

以下是项目目录示例,可在对应仓库的 .github/workflows/ 中查看 YAML 文件的历史版本。
整个流程可以保证在一次 Release 后,自动完成所有平台的编译、打包、上传与发布。

小结:这样就轻松搞定了多平台二进制 + Docker 镜像 + crates.io 同步发布的完整 CI/CD。


参考链接

鱼雪

本文详细介绍如何使用 Axum 框架在 Rust 中构建一个通用化的 Web 应用模板,包括:

  • 构建 RESTful API
  • 使用 SqlxPostgres 数据库实现数据库交互
  • 采用类似 Nest.js 的项目组织结构,以提升代码可维护性
  • 包含丰富的单元测试和集成测试
  • 使用 Github Actions 实现 CI/CD 流程

基础开发环境搭建

为了快速开始,可以参考我的 Rust 项目模板,点击这里获取项目代码。该模板包含基础项目结构和一些配置,帮助你迅速搭建开发环境。

类似 Nest.js 的项目组织方式

为了提升项目的可维护性和扩展性,本文中采用了类似 Nest.js 的项目组织结构:

├── docs                # 文档文件
├── fixtures # 必要文件,比如公私钥,测试 SQL 脚本
├── migrations # 数据库迁移文件,适用于 Sqlx
├── rest_client # VS Code REST Client 测试 API 文件
├── src # 源代码
│   ├── common # 公共模块,如加解密、错误处理、配置等
│   └── modules # 业务模块
│   ├── auth # 认证模块,包括: `handlers`, `services`, `dto`, `tests`, `middleware` 等
│   └── users # 用户管理模块,包括: `handlers`, `services`, `dto`, `tests`, `entity` 等

业务模块文件说明

  • mod.rs:模块的导入导出,创建当前模块的路由器
  • handlers.rs:处理用户请求的逻辑。
  • services.rs:处理业务逻辑以及与数据库的交互。
  • entity.rs:定义与数据库表对应的结构体,便于结构操作。
  • dto.rs:定义数据转换对象,用于请求和响应的序列化和反序列化。
  • tests.rs:包含单元测试和集成测试。
  • middleware.rs:定义模块的中间件,用于认证、权限控制等。
  • src/lib.rs:定义项目的路由器,加载配置文件、初始化全局状态、错误处理等。
  • src/main.rs:应用入口。

开发经验分享

在项目开发过程中,我积累了一些经验,分享如下,帮助大家更好地构建 Axum Web 应用:

1. 测试的排序和并发控制

  • 测试数量多或者测试较为复杂时,可能会遇到并发导致的错误,特别是在集成测试中。可以使用 serial_test 来对测试进行排序,防止这些问题。

2. 测试数据库的创建与销毁

  • 在测试时需要创建临时数据库,并在测试结束后销毁它。可以使用 sqlx-db-tester,并为测试初始化一个连接到测试数据库的全局状态。

3. 全局配置与数据库连接

  • 全局状态中应包含全局配置和数据库连接,这样可以统一管理数据库操作,并将所有 services.rs 中的数据库操作通过全局状态来执行,方便管理。

4. Github Actions CI 配置

  • 集成测试中,reqwest 默认依赖 OpenSSL,这在 CI 环境下可能会导致问题。因此,我们需要在 OpenSSLBoringSSL 之间做出选择。由于 jwt-simple 依赖于 BoringSSL,所以应禁用掉 reqwest 的默认 OpenSSL 依赖。

5. 公私钥生成

  • 将公私钥生成的逻辑放在 build.rs 中,这样每次执行 cargo buildcargo run 等构建操作时,都会自动生成证书文件到 fixtures 目录下,方便开发和部署。

6. 测试模块管理

  • 在 Rust 中,可以将单独文件当作一个模块(mod)。但需要注意的是,Rust 不会自动识别 integration_tests.rs,但会识别 tests.rs。因此,我将单元测试和集成测试都写在 tests.rs 中,并使用 util_testsintegration_tests 模块分别包裹,这样可以更清晰地查看测试日志。

7. 代码质量检查

  • 使用 pre-commit 执行各类代码检查工具,如 cargo-deny,确保代码规范和安全性,避免潜在的漏洞和错误。

8. Token 签名与安全性

  • 选择使用 Ed25519 作为 Token 的签名算法。公钥用于验证签名,私钥用于生成签名。这样可以将公钥公开,用于独立的服务来验证 Token,保证安全性。

9. 参数验证

  • 使用 validator crate 来验证请求参数,减少错误输入和代码量,简化开发过程。

CI/CD 集成(Github Actions)

在本项目中,使用 Github Actions 进行持续集成(CI)和持续部署(CD)。你可以通过配置 .github/workflows/ci.yml 文件来实现每次代码推送后的自动化测试和构建,确保代码的稳定性和质量。

示例配置文件

name: Rust CI

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run tests
run: cargo test

Rust Axum Web 应用的最佳实践

为了提升代码的可维护性和扩展性,建议:

  1. 模块化设计:保持代码模块化,采用类似 Nest.js 的项目结构,有利于团队合作和项目扩展。
  2. 自动化测试:编写单元测试和集成测试,确保各个模块的功能和整体系统的稳定性。
  3. 持续集成:通过 Github Actions 等工具实现持续集成,保证代码的高质量。

链接与资源

项目展示效果

以下是单元测试和集成测试的部分运行效果展示:

Axum单元测试与集成测试

鱼雪