- 新增 Go、Node.js、Rust 服务的 Dockerfile 模板 - 新增 Rust 快速参考指南 - 新增 Rust 后端工作流模板 - 优化 create-runner.md,明确 host 网络模式为缓存必需条件 - 更新 gitea skill 主文档
35 KiB
35 KiB
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
解决:
- 检查服务端口是否正确映射
- 确认服务启动时间,可能需要增加
sleep时间 - 验证端点路径是否正确
- 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
解决:
- 确认
REGISTRY_TOKEN已正确配置 - 检查 Token 权限(需要
package:write) - 验证 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
参考项目
模板更新日志
2026-01-30
- 优化 Workflow 结构:分离 test、build-docker、health-check、release、notify 阶段
- 新增健康检查阶段:自动验证 Docker 镜像的健康端点
- 新增并发控制:避免重复构建浪费资源
- 新增单服务/多服务双模板:适应不同项目结构
- 优化 Dockerfile:基于实际项目改进多阶段构建
- 完善文档:添加详细的配置说明和常见问题