Files
opencode/skill/gitea/workflow-templates/rust-backend.md
voson 425ca5b5fd feat(gitea): 添加 Dockerfile 模板和 Rust 支持,优化 runner 网络配置说明
- 新增 Go、Node.js、Rust 服务的 Dockerfile 模板
- 新增 Rust 快速参考指南
- 新增 Rust 后端工作流模板
- 优化 create-runner.md,明确 host 网络模式为缓存必需条件
- 更新 gitea skill 主文档
2026-01-30 10:12:09 +08:00

35 KiB
Raw Blame History

Rust 后端服务 Workflow 模板

适用于 Rust 后端 API 服务、微服务、CLI 工具的 CI/CD workflow。

适用场景

  • Rust HTTP API 服务Axum、Actix-web、Rocket 等)
  • 异步后端服务Tokio、async-std
  • MQTT/IoT 设备服务
  • CLI 工具
  • 需要构建 Docker 镜像的 Rust 项目

环境要求

依赖 Runner 要求
Rust 1.75+ 通过脚本自动安装
Docker Runner 主机已安装
Zig可选 用于交叉编译,脚本自动安装

特性说明

交叉编译支持

  • ARM64 主机 → x86_64 目标:使用 Zig + cargo-zigbuild
  • musl 静态链接:生成独立可执行文件,适配 Alpine 容器
  • 缓存优化:支持 Rust 依赖缓存,加速构建

镜像加速

使用 rsproxy.cn 镜像加速:

  • rustup 安装源
  • crates.io 索引
  • cargo 依赖下载

构建优化

env:
  CARGO_INCREMENTAL: 0        # 禁用增量编译CI 环境)
  CARGO_NET_RETRY: 10         # 网络重试 10 次
  CARGO_TERM_COLOR: always    # 保持彩色输出
  RUST_BACKTRACE: short       # 简短的错误栈

Workflow 骨架模板

完整版本(推荐用于生产项目)

参考实际项目 api-hub 的最佳实践,包含完整的测试、构建、健康检查和发布流程。

name: Rust Backend CI/CD

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

# 并发控制:同一分支/标签的重复构建会取消旧的运行
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  # Rust CI 构建优化
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: short
  # 使用中国 Rust 镜像加速
  RUSTUP_DIST_SERVER: https://rsproxy.cn
  RUSTUP_UPDATE_ROOT: https://rsproxy.cn/rustup

jobs:
  test:
    name: 测试
    runs-on: ubuntu-latest                  # 修改为你的 Runner 标签
    env:
      RUNNER_TOOL_CACHE: /toolcache
    steps:
      - uses: actions/checkout@v4

      - name: 配置 Cargo 镜像
        run: |
          mkdir -p ~/.cargo
          cat >> ~/.cargo/config.toml << 'EOF'
          [source.crates-io]
          replace-with = 'rsproxy-sparse'
          [source.rsproxy-sparse]
          registry = "sparse+https://rsproxy.cn/index/"
          [registries.rsproxy]
          index = "https://rsproxy.cn/crates.io-index"
          [net]
          git-fetch-with-cli = true
          EOF

      - name: 安装 Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y --default-toolchain stable --component rustfmt,clippy
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      # rust-cache 需要 act_runner 正确配置缓存服务器
      # 如果出现 "getCacheEntry failed: connect ETIMEDOUT" 错误,
      # 需要在服务器端修改 act_runner 的 config.yaml 中的 cache 配置
      - name: 缓存 Rust 依赖
        uses: https://github.com/Swatinem/rust-cache@v2
        continue-on-error: true
        with:
          cache-targets: true
          cache-on-failure: true
          cache-all-crates: true

      - name: 检查代码格式
        run: cargo fmt -- --check

      - name: 运行 Clippy 检查
        run: cargo clippy -- -D warnings

      - name: 运行测试
        run: cargo test --verbose

      - name: 构建
        run: cargo build --release --verbose

  build-docker:
    name: 构建 Docker 镜像
    needs: test
    runs-on: ubuntu-latest
    outputs:
      registry: ${{ steps.vars.outputs.registry }}
      image_repo: ${{ steps.vars.outputs.image_repo }}
    steps:
      - uses: actions/checkout@v4

      - name: 设置变量
        id: vars
        run: |
          registry=$(echo "${{ github.server_url }}" | cut -d '/' -f 3)
          echo "registry=${registry}" >> $GITHUB_OUTPUT
          echo "REGISTRY=${registry}" >> $GITHUB_ENV
          echo "image_repo=${{ github.event.repository.name }}" >> $GITHUB_OUTPUT

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录 Gitea 容器仓库
        uses: docker/login-action@v3
        with:
          registry: ${{ steps.vars.outputs.registry }}
          username: ${{ github.actor }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: 提取元数据
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=,suffix=,format=short

      - name: 构建并推送镜像
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache,mode=max

  health-check:
    name: 健康检查
    needs: build-docker
    if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
    runs-on: ubuntu-latest
    steps:
      - name: 设置变量
        id: vars
        run: |
          registry=$(echo "${{ github.server_url }}" | cut -d '/' -f 3)
          echo "REGISTRY=${registry}" >> $GITHUB_ENV

      - name: 登录 Gitea 容器仓库
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: 运行健康检查测试
        run: |
          # 启动服务
          docker run -d --name ${{ github.event.repository.name }}-test -p 8080:8080 ${{ env.REGISTRY }}/${{ github.repository }}:main
          
          # 等待服务启动
          sleep 5
          
          # 健康检查 - 根据实际端点修改
          echo "检查健康端点..."
          curl -f http://localhost:8080/health || exit 1
          
          # 就绪检查 - 根据实际端点修改
          echo "检查就绪端点..."
          curl -f http://localhost:8080/ready || exit 1
          
          # 存活检查 - 根据实际端点修改
          echo "检查存活端点..."
          curl -f http://localhost:8080/live || exit 1
          
          echo "所有健康检查通过!"
          
          # 清理
          docker stop ${{ github.event.repository.name }}-test
          docker rm ${{ github.event.repository.name }}-test

  release:
    name: 创建发布
    needs: test
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    env:
      RUNNER_TOOL_CACHE: /toolcache
    steps:
      - uses: actions/checkout@v4

      - name: 配置 Cargo 镜像
        run: |
          mkdir -p ~/.cargo
          cat >> ~/.cargo/config.toml << 'EOF'
          [source.crates-io]
          replace-with = 'rsproxy-sparse'
          [source.rsproxy-sparse]
          registry = "sparse+https://rsproxy.cn/index/"
          [registries.rsproxy]
          index = "https://rsproxy.cn/crates.io-index"
          [net]
          git-fetch-with-cli = true
          EOF

      - name: 安装 Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y --default-toolchain stable --target x86_64-unknown-linux-gnu,x86_64-unknown-linux-musl
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      - name: 缓存 Rust 依赖
        uses: https://github.com/Swatinem/rust-cache@v2
        continue-on-error: true
        with:
          cache-targets: true
          cache-on-failure: true
          cache-all-crates: true

      - name: 构建 Release 二进制文件
        run: |
          SERVICE_NAME="${{ github.event.repository.name }}"
          
          # Linux GNU 版本
          cargo build --release --target x86_64-unknown-linux-gnu
          strip target/x86_64-unknown-linux-gnu/release/${SERVICE_NAME}
          tar czf ${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz \
            -C target/x86_64-unknown-linux-gnu/release ${SERVICE_NAME}
          
          # Linux MUSL 版本 (静态链接)
          cargo build --release --target x86_64-unknown-linux-musl
          strip target/x86_64-unknown-linux-musl/release/${SERVICE_NAME}
          tar czf ${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz \
            -C target/x86_64-unknown-linux-musl/release ${SERVICE_NAME}

      - name: 生成 Changelog
        id: changelog
        run: |
          SERVICE_NAME="${{ github.event.repository.name }}"
          echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
          if [ -f CHANGELOG.md ]; then
            awk '/^## \[${{ github.ref_name }}\]/{flag=1;next} /^## \[/{flag=0} flag' CHANGELOG.md || echo "查看完整变更日志CHANGELOG.md"
          else
            echo "## ${{ github.ref_name }}"
            echo ""
            echo "### 变更内容"
            echo "- 发布版本 ${{ github.ref_name }}"
            echo ""
            echo "### 下载"
            echo "- \`${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz\`: 标准 Linux 版本"
            echo "- \`${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz\`: 静态链接版本(推荐用于容器)"
          fi
          EOF

      - name: 创建 Release
        uses: softprops/action-gh-release@v1
        with:
          name: ${{ github.event.repository.name }} ${{ github.ref_name }}
          body: ${{ steps.changelog.outputs.CHANGELOG }}
          files: |
            ${{ github.event.repository.name }}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz
            ${{ github.event.repository.name }}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz
          draft: false
          prerelease: ${{ contains(github.ref_name, '-') }}

  notify:
    name: 发送通知
    needs: [test, build-docker, release]
    if: always()
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - name: 发送构建通知
        run: |
          TEST_STATUS="${{ needs.test.result }}"
          BUILD_STATUS="${{ needs.build-docker.result }}"
          RELEASE_STATUS="${{ needs.release.result }}"
          
          if [[ "$TEST_STATUS" == "success" && ("$BUILD_STATUS" == "success" || "$BUILD_STATUS" == "skipped") && ("$RELEASE_STATUS" == "success" || "$RELEASE_STATUS" == "skipped") ]]; then
            STATUS="成功"
          else
            STATUS="失败"
          fi
          
          curl -X POST ${{ vars.WEBHOOK_URL }} \
            -H "Content-Type: application/json" \
            -d '{
              "repository": "${{ github.repository }}",
              "ref": "${{ github.ref }}",
              "commit": "${{ github.sha }}",
              "status": "'"$STATUS"'",
              "workflow": "${{ github.workflow }}",
              "actor": "${{ github.actor }}",
              "test_result": "'"$TEST_STATUS"'",
              "build_result": "'"$BUILD_STATUS"'",
              "release_result": "'"$RELEASE_STATUS"'"
            }' || true

多服务版本(单仓库多 Rust 服务)

如果你的单仓库包含多个 Rust 服务,使用以下模板:

name: Rust Multi-Service CI/CD

on:
  push:
    paths:
      - 'your-service/**'                   # 修改为实际目录
      - '.gitea/workflows/your-service.yml'
    tags:
      - 'your-service-*'                    # 修改为实际 tag 前缀

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  SERVICE_PREFIX: your-service              # 修改为实际服务名
  SERVICE_DIR: your-service                 # 修改为实际目录名
  # Rust CI 构建优化
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: short
  # 使用中国 Rust 镜像加速
  RUSTUP_DIST_SERVER: https://rsproxy.cn
  RUSTUP_UPDATE_ROOT: https://rsproxy.cn/rustup

jobs:
  test:
    name: 测试
    runs-on: ubuntu-latest
    env:
      RUNNER_TOOL_CACHE: /toolcache
    steps:
      - uses: actions/checkout@v4

      - name: 配置 Cargo 镜像
        run: |
          mkdir -p ~/.cargo
          cat >> ~/.cargo/config.toml << 'EOF'
          [source.crates-io]
          replace-with = 'rsproxy-sparse'
          [source.rsproxy-sparse]
          registry = "sparse+https://rsproxy.cn/index/"
          [registries.rsproxy]
          index = "https://rsproxy.cn/crates.io-index"
          [net]
          git-fetch-with-cli = true
          EOF

      - name: 安装 Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y --default-toolchain stable --component rustfmt,clippy
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      - name: 缓存 Rust 依赖
        uses: https://github.com/Swatinem/rust-cache@v2
        continue-on-error: true
        with:
          workspaces: ${{ env.SERVICE_DIR }} -> target
          cache-targets: true
          cache-on-failure: true
          cache-all-crates: true

      - name: 检查代码格式
        working-directory: ${{ env.SERVICE_DIR }}
        run: cargo fmt -- --check

      - name: 运行 Clippy 检查
        working-directory: ${{ env.SERVICE_DIR }}
        run: cargo clippy -- -D warnings

      - name: 运行测试
        working-directory: ${{ env.SERVICE_DIR }}
        run: cargo test --verbose

      - name: 构建
        working-directory: ${{ env.SERVICE_DIR }}
        run: cargo build --release --verbose

  build-docker:
    name: 构建 Docker 镜像
    needs: test
    runs-on: ubuntu-latest
    outputs:
      registry: ${{ steps.vars.outputs.registry }}
      image_repo: ${{ steps.vars.outputs.image_repo }}
      git_tag: ${{ steps.vars.outputs.git_tag }}
    steps:
      - uses: actions/checkout@v4

      - name: 设置变量
        id: vars
        run: |
          registry=$(echo "${{ github.server_url }}" | cut -d '/' -f 3)
          git_tag=$(git describe --tags --abbrev=0 --always)
          echo "registry=${registry}" >> $GITHUB_OUTPUT
          echo "REGISTRY=${registry}" >> $GITHUB_ENV
          echo "image_repo=${{ github.event.repository.name }}" >> $GITHUB_OUTPUT
          echo "git_tag=${git_tag}" >> $GITHUB_OUTPUT

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录 Gitea 容器仓库
        uses: docker/login-action@v3
        with:
          registry: ${{ env.registry }}
          username: ${{ github.actor }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: 构建并推送镜像
        uses: docker/build-push-action@v6
        with:
          context: ./${{ env.SERVICE_DIR }}
          file: ./${{ env.SERVICE_DIR }}/Dockerfile
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.SERVICE_PREFIX }}-latest
            ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.git_tag }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}:buildcache-${{ env.SERVICE_PREFIX }}
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}:buildcache-${{ env.SERVICE_PREFIX }},mode=max

  health-check:
    name: 健康检查
    needs: build-docker
    if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
    runs-on: ubuntu-latest
    steps:
      - name: 设置变量
        run: |
          registry=$(echo "${{ github.server_url }}" | cut -d '/' -f 3)
          echo "REGISTRY=${registry}" >> $GITHUB_ENV

      - name: 登录 Gitea 容器仓库
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: 运行健康检查测试
        run: |
          # 启动服务 - 根据实际端口和端点修改
          docker run -d --name ${{ env.SERVICE_PREFIX }}-test -p 8080:8080 ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.SERVICE_PREFIX }}-latest
          
          sleep 5
          
          curl -f http://localhost:8080/health || exit 1
          curl -f http://localhost:8080/ready || exit 1
          curl -f http://localhost:8080/live || exit 1
          
          echo "健康检查通过!"
          
          docker stop ${{ env.SERVICE_PREFIX }}-test
          docker rm ${{ env.SERVICE_PREFIX }}-test

  release:
    name: 创建发布
    needs: test
    if: startsWith(github.ref, 'refs/tags/${{ env.SERVICE_PREFIX }}-')
    runs-on: ubuntu-latest
    env:
      RUNNER_TOOL_CACHE: /toolcache
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: 配置 Cargo 镜像
        run: |
          mkdir -p ~/.cargo
          cat >> ~/.cargo/config.toml << 'EOF'
          [source.crates-io]
          replace-with = 'rsproxy-sparse'
          [source.rsproxy-sparse]
          registry = "sparse+https://rsproxy.cn/index/"
          [registries.rsproxy]
          index = "https://rsproxy.cn/crates.io-index"
          [net]
          git-fetch-with-cli = true
          EOF

      - name: 安装 Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y --default-toolchain stable --target x86_64-unknown-linux-gnu,x86_64-unknown-linux-musl
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      - name: 缓存 Rust 依赖
        uses: https://github.com/Swatinem/rust-cache@v2
        continue-on-error: true
        with:
          workspaces: ${{ env.SERVICE_DIR }} -> target
          cache-targets: true
          cache-on-failure: true
          cache-all-crates: true

      - name: 构建 Release 二进制文件
        run: |
          SERVICE_NAME="${{ github.event.repository.name }}"
          
          # Linux GNU 版本
          cargo build --release --target x86_64-unknown-linux-gnu --manifest-path ${{ env.SERVICE_DIR }}/Cargo.toml
          strip target/x86_64-unknown-linux-gnu/release/${SERVICE_NAME}
          tar czf ${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz \
            -C target/x86_64-unknown-linux-gnu/release ${SERVICE_NAME}
          
          # Linux MUSL 版本
          cargo build --release --target x86_64-unknown-linux-musl --manifest-path ${{ env.SERVICE_DIR }}/Cargo.toml
          strip target/x86_64-unknown-linux-musl/release/${SERVICE_NAME}
          tar czf ${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz \
            -C target/x86_64-unknown-linux-musl/release ${SERVICE_NAME}

      - name: 生成 Changelog
        id: changelog
        run: |
          SERVICE_NAME="${{ github.event.repository.name }}"
          
          # 从 tag 中提取版本号
          version="${{ github.ref_name }}"
          version="${version#${{ env.SERVICE_PREFIX }}-}"
          
          echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
          if [ -f ${{ env.SERVICE_DIR }}/CHANGELOG.md ]; then
            awk "/^## \\[${version}\\]/{flag=1;next} /^## \\[/{flag=0} flag" ${{ env.SERVICE_DIR }}/CHANGELOG.md || echo "查看完整变更日志"
          else
            echo "## ${{ github.ref_name }}"
            echo ""
            echo "### 下载"
            echo "- \`${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz\`: 标准 Linux 版本"
            echo "- \`${SERVICE_NAME}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz\`: 静态链接版本"
          fi
          EOF

      - name: 创建 Release
        uses: softprops/action-gh-release@v1
        with:
          name: ${{ github.event.repository.name }} ${{ github.ref_name }}
          body: ${{ steps.changelog.outputs.CHANGELOG }}
          files: |
            ${{ github.event.repository.name }}-${{ github.ref_name }}-x86_64-linux-gnu.tar.gz
            ${{ github.event.repository.name }}-${{ github.ref_name }}-x86_64-linux-musl.tar.gz
          draft: false
          prerelease: ${{ contains(github.ref_name, '-') }}

  notify:
    name: 发送通知
    needs: [test, build-docker, release]
    if: always()
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - name: 发送构建通知
        run: |
          TEST_STATUS="${{ needs.test.result }}"
          BUILD_STATUS="${{ needs.build-docker.result }}"
          RELEASE_STATUS="${{ needs.release.result }}"
          
          if [[ "$TEST_STATUS" == "success" && ("$BUILD_STATUS" == "success" || "$BUILD_STATUS" == "skipped") && ("$RELEASE_STATUS" == "success" || "$RELEASE_STATUS" == "skipped") ]]; then
            STATUS="成功"
          else
            STATUS="失败"
          fi
          
          curl -X POST ${{ vars.WEBHOOK_URL }} \
            -H "Content-Type: application/json" \
            -d '{
              "repository": "${{ github.repository }}",
              "ref": "${{ github.ref }}",
              "commit": "${{ github.sha }}",
              "status": "'"$STATUS"'",
              "workflow": "${{ github.workflow }}",
              "actor": "${{ github.actor }}",
              "test_result": "'"$TEST_STATUS"'",
              "build_result": "'"$BUILD_STATUS"'",
              "release_result": "'"$RELEASE_STATUS"'"
            }' || true

Dockerfile 模板

Dockerfile推荐用于 CI/CD

使用多阶段构建,优化镜像大小和构建缓存:

# 构建阶段
FROM rust:1.84-slim AS builder

WORKDIR /app

# 安装构建依赖
RUN apt-get update && apt-get install -y \
    pkg-config \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# 先复制 Cargo 文件以缓存依赖
COPY Cargo.toml Cargo.lock ./

# 创建虚拟 main.rs 来缓存依赖编译
RUN mkdir src && echo 'fn main() {}' > src/main.rs
RUN cargo build --release && rm -rf src

# 复制源代码
COPY src ./src

# 构建应用(使用 touch 确保重新编译)
RUN touch src/main.rs && cargo build --release

# 运行阶段 - 使用最小镜像
FROM debian:bookworm-slim

WORKDIR /app

# 安装运行依赖
RUN apt-get update && apt-get install -y \
    ca-certificates \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 复制二进制文件(根据实际项目名称修改)
COPY --from=builder /app/target/release/your-app-name /app/your-app-name

# 创建日志目录(如果有日志输出)
RUN mkdir -p /app/logs

# 暴露端口(根据实际服务修改)
EXPOSE 8080

# 健康检查(根据实际端点修改)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# 运行应用
CMD ["/app/your-app-name"]

Dockerfile.musl静态链接版本

使用 musl 进行静态链接,生成独立可执行文件:

# 构建阶段
FROM rust:1.84-alpine AS builder

# 安装构建依赖
RUN apk add --no-cache musl-dev openssl-dev pkgconfig

# 配置 Cargo 镜像加速(可选)
RUN mkdir -p /root/.cargo && \
    echo '[source.crates-io]' > /root/.cargo/config.toml && \
    echo 'replace-with = "rsproxy-sparse"' >> /root/.cargo/config.toml && \
    echo '[source.rsproxy-sparse]' >> /root/.cargo/config.toml && \
    echo 'registry = "sparse+https://rsproxy.cn/index/"' >> /root/.cargo/config.toml

WORKDIR /build

# 先复制 Cargo 文件以缓存依赖
COPY Cargo.toml Cargo.lock ./

# 创建虚拟 main.rs 来缓存依赖编译
RUN mkdir src && echo 'fn main() {}' > src/main.rs
RUN cargo build --release --target x86_64-unknown-linux-musl && rm -rf src

# 复制源代码并构建
COPY src ./src
RUN touch src/main.rs && cargo build --release --target x86_64-unknown-linux-musl

# 运行阶段 - 使用 scratch 或 alpine
FROM alpine:3.21

RUN apk add --no-cache ca-certificates tzdata curl

WORKDIR /app

# 从构建阶段复制二进制文件
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-app-name .

ENV TZ=Asia/Shanghai
ENV RUST_LOG=info

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

CMD ["./your-app-name"]

Dockerfile.ci使用 CI 预编译二进制)

适用于 CI 中已经构建好二进制文件的场景:

FROM debian:bookworm-slim

# 安装运行时依赖
RUN apt-get update && apt-get install -y \
    ca-certificates \
    curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# 复制 CI 构建好的二进制文件
COPY your-app-name .

# 创建日志目录
RUN mkdir -p /app/logs

ENV RUST_LOG=info

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

CMD ["./your-app-name"]

模板选择指南

单服务仓库 vs 多服务仓库

场景 推荐模板 说明
单 Rust 项目 完整版本 简单直接,基于分支触发
单仓库多服务 多服务版本 按路径和 tag 前缀触发,避免无关构建
微服务架构 多服务版本 每个服务独立 workflow

Workflow 阶段说明

┌─────────┐     ┌──────────────┐     ┌─────────────┐
│  Test   │────▶│ Build Docker │────▶│ Health Check│
│(fmt,    │     │              │     │             │
│ clippy, │     └──────────────┘     └─────────────┘
│ test)   │
└─────────┘
     │
     ▼
┌─────────┐     ┌─────────┐
│ Release │────▶│ Notify  │
│(tag v*) │     │(always) │
└─────────┘     └─────────┘
阶段 触发条件 说明
Test 每次 push/PR 代码格式、Clippy 检查、单元测试、编译
Build Docker Test 成功后 构建并推送 Docker 镜像到仓库
Health Check main 分支 push 启动容器并验证健康端点
Release Tag 以 v 开头 构建双平台二进制并创建 Release
Notify 始终执行 发送构建结果通知

自定义配置说明

必须修改的变量

完整版本(单服务)

变量 位置 说明 示例
runs-on jobs.*.runs-on Runner 标签 ubuntu-latest
健康检查端点 health-check job 根据实际服务修改 /health, /ready, /live
服务端口号 health-check job Docker 端口映射 8080:8080

多服务版本

变量 位置 说明 示例
SERVICE_PREFIX env 服务前缀,用于 tag 匹配 device-rs
SERVICE_DIR env 项目目录名 device-rs
runs-on jobs.*.runs-on Runner 标签 ubuntu-latest
健康检查端点 health-check job 根据实际服务修改 /health, /ready, /live

Secrets 配置

在 Gitea 仓库的 Settings → Secrets 中添加:

Secret 说明 必需
REGISTRY_TOKEN 容器仓库推送 Token
RELEASE_TOKEN Release 创建 Token 可选(默认使用 GITHUB_TOKEN

Variables 配置

在 Gitea 仓库的 Settings → Variables 中添加:

Variable 说明 必需
WEBHOOK_URL 构建通知 Webhook URL 可选

可选修改的变量

变量 默认值 说明
CARGO_INCREMENTAL 0 CI 环境禁用增量编译
CARGO_NET_RETRY 10 网络重试次数
RUST_BACKTRACE short 错误栈长度

健康检查端点配置

模板默认检查以下端点,请根据你的服务实际端点修改:

# 在 health-check job 中修改
- name: 运行健康检查测试
  run: |
    # 修改端口映射
    docker run -d --name app-test -p 你的端口:容器端口 ...
    
    # 修改检查端点
    curl -f http://localhost:你的端口/health || exit 1
    curl -f http://localhost:你的端口/ready || exit 1
    curl -f http://localhost:你的端口/live || exit 1

推荐的端点实现Axum 示例):

pub fn health_routes() -> Router {
    Router::new()
        .route("/health", get(health))    // 健康检查
        .route("/ready", get(ready))      // 就绪检查
        .route("/live", get(live))        // 存活检查
}

async fn health() -> Json<serde_json::Value> {
    Json(json!({"status": "healthy"}))
}

async fn ready() -> Json<serde_json::Value> {
    // 检查依赖服务(数据库、缓存等)
    Json(json!({"status": "ready"}))
}

async fn live() -> &'static str {
    "OK"  // 简单存活检查
}

build.rs 示例

如果需要在编译时注入版本号,创建 build.rs

/// 构建脚本 - 将环境变量传递给编译过程
///
/// 在 CI 构建时GIT_TAG 环境变量会被设置为版本号(如 "0.6.3"
/// 该脚本将其传递给 rustc使 `option_env!("GIT_TAG")` 能在编译时获取版本。
fn main() {
    // 传递 GIT_TAG 环境变量到编译时
    if let Ok(git_tag) = std::env::var("GIT_TAG") {
        println!("cargo:rustc-env=GIT_TAG={}", git_tag);
    }

    // 重新运行条件GIT_TAG 环境变量变化时重新编译
    println!("cargo:rerun-if-env-changed=GIT_TAG");
}

在代码中使用:

fn main() {
    let version = option_env!("GIT_TAG").unwrap_or("dev");
    println!("Version: {}", version);
}

性能优化建议

1. CI 环境优化

env:
  # 禁用增量编译CI 环境不需要)
  CARGO_INCREMENTAL: 0
  # 增加网络重试次数
  CARGO_NET_RETRY: 10
  # 保持彩色输出
  CARGO_TERM_COLOR: always
  # 简短错误栈
  RUST_BACKTRACE: short

2. 缓存策略

# 使用 rust-cache action推荐
- name: Cache Rust dependencies
  uses: https://github.com/Swatinem/rust-cache@v2
  continue-on-error: true  # 缓存失败不中断构建
  with:
    cache-targets: true
    cache-on-failure: true
    cache-all-crates: true

3. 并行构建

- name: Build
  run: |
    export CARGO_BUILD_JOBS="$(nproc)"
    cargo build --release

4. Docker 构建缓存

- name: 构建并推送镜像
  uses: docker/build-push-action@v6
  with:
    cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache
    cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache,mode=max

5. 依赖优化

# Cargo.toml - 优化 Release 构建
[profile.release]
opt-level = 3           # 最高优化级别
lto = true              # 链接时优化
codegen-units = 1       # 单个代码生成单元(更好的优化)
strip = true            # 去除符号表(减小体积)
panic = 'abort'         # 减小二进制大小

健康检查端点实现

使用 Axum 实现健康检查(参考 device-rs

use axum::{Router, routing::get, Json};
use serde_json::json;

pub fn health_routes() -> Router {
    Router::new()
        .route("/healthz", get(healthz))    // 存活探针
        .route("/readyz", get(readyz))      // 就绪探针
        .route("/health", get(health))      // 详细状态
        .route("/metrics", get(metrics))    // Prometheus metrics
}

// 简单存活检查
async fn healthz() -> &'static str {
    "OK"
}

// 就绪检查(检查依赖服务)
async fn readyz() -> Json<serde_json::Value> {
    // 检查数据库、Redis、MQTT 等
    Json(json!({"status": "ready"}))
}

// 详细健康状态
async fn health() -> Json<serde_json::Value> {
    Json(json!({
        "status": "healthy",
        "version": env!("CARGO_PKG_VERSION"),
        "uptime": "1h 23m"
    }))
}

// Prometheus metrics
async fn metrics() -> String {
    // 导出 metrics
    "# HELP requests_total Total requests\n".to_string()
}

常见问题

Q1: rust-cache 报错 "getCacheEntry failed"

症状: 缓存步骤报错 getCacheEntry failed: connect ETIMEDOUT

解决: 在 act_runner 的 config.yaml 中配置缓存服务器:

cache:
  enabled: true
  dir: "/cache"
  host: "127.0.0.1"  # 替换为你的缓存服务器地址
  port: 9000

或在 workflow 中设置 continue-on-error: true 跳过缓存失败。

Q2: 健康检查失败

症状: Health Check job 失败,提示 curl: (7) Failed to connect

解决:

  1. 检查服务端口是否正确映射
  2. 确认服务启动时间,可能需要增加 sleep 时间
  3. 验证端点路径是否正确
- name: 运行健康检查测试
  run: |
    docker run -d --name app-test -p 8080:8080 ${{ env.IMAGE }}
    sleep 10  # 增加等待时间
    curl -f http://localhost:8080/health || exit 1

Q3: Docker 推送失败401 Unauthorized

症状: denied: unauthorized to access repository

解决:

  1. 确认 REGISTRY_TOKEN 已正确配置
  2. 检查 Token 权限(需要 package:write
  3. 验证 registry URL 是否正确

Q4: 并发构建取消

症状: 新推送取消了正在运行的构建

解决: 这是预期行为。concurrency 配置会自动取消同一分支的旧构建:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

如需保留所有构建,可以移除或修改此配置。

Q5: 多服务仓库触发混乱

症状: 修改服务 A 但服务 B 也触发了构建

解决: 确保 paths 配置正确:

on:
  push:
    paths:
      - 'service-a/**'              # 仅监听服务 A 的变更
      - '.gitea/workflows/service-a.yml'
    tags:
      - 'service-a-*'               # 仅匹配服务 A 的 tag

Q6: musl 编译链接错误

# 安装 musl 工具链
rustup target add x86_64-unknown-linux-musl

# 或在 CI 中自动安装
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | \
  sh -s -- -y --default-toolchain stable --target x86_64-unknown-linux-musl

参考项目

  • api-hub - Rust API 网关服务MQTT5 + HTTP 健康检查
  • device-rs - Rust IoT 设备服务

模板更新日志

2026-01-30

  • 优化 Workflow 结构:分离 test、build-docker、health-check、release、notify 阶段
  • 新增健康检查阶段:自动验证 Docker 镜像的健康端点
  • 新增并发控制:避免重复构建浪费资源
  • 新增单服务/多服务双模板:适应不同项目结构
  • 优化 Dockerfile:基于实际项目改进多阶段构建
  • 完善文档:添加详细的配置说明和常见问题

相关文档