7 Commits
v1.5.0 ... main

Author SHA1 Message Date
voson
95d131eb51 feat: add claude-opus-4-5 model configuration with thinking mode 2026-02-02 10:42:21 +08:00
voson
8fa9df03a0 feat(gitea): 为 runner 创建添加前置条件检查
添加网络和硬件配置的前置条件检查:
- 网络连接检查:确保能访问外网(google.com)
- 硬件资源检查:确保满足最低配置 2C4G
- 新增命令行选项用于跳过检查
- 更新文档说明前置条件要求
2026-01-30 13:59:33 +08:00
voson
330aea4d16 refactor(gitea): 优化 runner 标签系统
**标签简化**:
- 移除所有标签的 :host 后缀重复,统一添加单个 host 标签
- 删除私网 IP 标签(不适用于实际场景)

**发行版检测增强**:
- 自动检测 Linux 发行版(debian, ubuntu, centos 等)
- 添加发行版版本标签(如 debian-12, ubuntu-22)
- 保留 ubuntu-latest 作为兼容标签,适用于所有 Linux 系统

**新的标签结构**:
- 基础标签:host, ubuntu-latest, self-hosted, x64, linux-x86_64
- 发行版标签:debian, debian-12, linux(实际系统信息)
- 配置标签:4c3g(保留,用于资源调度)

**标签示例(Debian 12)**:
  host, ubuntu-latest, self-hosted, x64, linux-x86_64,
  debian, debian-12, linux, 4c3g

**优势**:
- 更简洁的标签结构
- 精确的系统信息(实际发行版)
- 保持 GitHub Actions 兼容性(ubuntu-latest)
- 支持多种匹配策略(兼容模式、精确模式、配置筛选)
2026-01-30 11:02:08 +08:00
voson
bb93a6bc6c feat(gitea): 增强 runner 标签,添加设备配置和 IP 信息
- Host 模式默认添加 ubuntu-latest 标签,兼容 GitHub Actions workflow
- 自动检测并添加设备配置标签(如 4c3g 表示 4核3G内存)
- 自动检测并添加 IP 地址标签(如 ip-172-20-0-81)
- 支持 Linux 和 macOS 的配置检测
- 添加标签说明文档,帮助用户在 workflow 中使用配置标签
- 完整标签示例:ubuntu-latest, ubuntu, self-hosted, x64, linux-x86_64, 4c3g, ip-172-20-0-81
2026-01-30 10:56:15 +08:00
voson
3cb55db864 refactor(gitea): 简化 runner 命名,恢复默认 Gitea URL
- 修改 runner 默认命名:从 'runner-hostname-mode' 简化为 'hostname'
- 批量模式命名:从 'runner-hostname-docker/host' 改为 'hostname-docker/host'
- 恢复默认 Gitea URL 配置(https://git.shigongcao.com)
- 优化参数验证逻辑,使 GITEA_URL 参数可选
- 保持用户可通过 --name 参数自定义 runner 名称
2026-01-30 10:46:31 +08:00
voson
f62146b655 refactor(gitea): 移除默认 Gitea URL,要求用户明确指定 2026-01-30 10:43:19 +08:00
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
8 changed files with 3384 additions and 319 deletions

View File

@@ -15,8 +15,17 @@
"budgetTokens": 16000 "budgetTokens": 16000
} }
} }
},
"claude-opus-4-5": {
"options": {
"thinking": {
"type": "enabled",
"budgetTokens": 16000
}
}
} }
} }
} }
} },
"mcp": {}
} }

View File

@@ -395,6 +395,7 @@ Runner 信息
|------|---------|---------| |------|---------|---------|
| **Rust 后端** | [rust-backend.md](./workflow-templates/rust-backend.md) | **Rust API 服务、微服务、CLI 工具** | | **Rust 后端** | [rust-backend.md](./workflow-templates/rust-backend.md) | **Rust API 服务、微服务、CLI 工具** |
| Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 | | Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 |
| Rust 后端 | [rust-backend.md](./workflow-templates/rust-backend.md) | Rust API 服务、异步服务、IoT 设备服务 |
| Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js | | Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js |
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose | | Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
| 微信小程序 | [wechat-miniprogram.md](./workflow-templates/wechat-miniprogram.md) | 微信小程序 CI/CD | | 微信小程序 | [wechat-miniprogram.md](./workflow-templates/wechat-miniprogram.md) | 微信小程序 CI/CD |
@@ -407,6 +408,31 @@ AI 会自动:
详见:[Workflow 生成器](./workflow-generator.md) 详见:[Workflow 生成器](./workflow-generator.md)
## Dockerfile 模板
提供多种 Dockerfile 模板,适配不同的构建场景:
| 类型 | 模板文档 | 适用场景 |
|------|---------|---------|
| Go 服务 | [go-service.md](./dockerfile-templates/go-service.md) | Go 后端服务容器化CI 构建、完整构建、代码生成) |
| Rust 服务 | [rust-service.md](./dockerfile-templates/rust-service.md) | Rust 后端服务容器化CI 构建、完整构建、多平台) |
| Node.js 前端 | [nodejs-frontend.md](./dockerfile-templates/nodejs-frontend.md) | 前端静态文件服务Nginx + SPA 路由) |
**模板特性对比**
| 特性 | Go 服务 | Rust 服务 | Node.js 前端 |
|------|---------|-----------|-------------|
| CI 构建版 | ✅ | ✅ | ✅ |
| 完整构建版 | ✅ | ✅ | ✅ |
| 健康检查脚本 | ✅ | ✅ | ✅Nginx |
| 多阶段构建 | ✅ | ✅ | ✅ |
| 镜像大小 | ~15MB | ~20MB | ~20MB |
| 代码生成支持 | ✅ | ❌ | ❌ |
| 多平台构建 | ❌ | ✅ | ❌ |
| API 代理配置 | ❌ | ❌ | ✅Nginx |
详见各模板文档了解具体使用方法。
## API 调用 ## API 调用
所有与 Gitea 服务器的交互都通过 API 完成,使用配置文件中的: 所有与 Gitea 服务器的交互都通过 API 完成,使用配置文件中的:
@@ -466,9 +492,9 @@ Gitea Actions 提供的内置变量(推荐优先使用):
## 版本 ## 版本
- **Skill Version**: 1.3 - **Skill Version**: 1.4
- **Last Updated**: 2026-01-29 - **Last Updated**: 2026-01-29
- **整合内容**: gitea-runner + gitea-workflow + 增强仓库管理 + SSH 密钥管理 + Rust 模板 - **整合内容**: gitea-runner + gitea-workflow + 增强仓库管理 + SSH 密钥管理 + 完整 Dockerfile 模板
- **主要改进**: - **主要改进**:
- 仓库创建智能解析(优先使用指定组织) - 仓库创建智能解析(优先使用指定组织)
- **简化验证**默认假设组织存在API失败时提示创建组织 - **简化验证**默认假设组织存在API失败时提示创建组织
@@ -477,6 +503,12 @@ Gitea Actions 提供的内置变量(推荐优先使用):
- **简洁高效**减少预先验证API失败时给出具体解决方案 - **简洁高效**减少预先验证API失败时给出具体解决方案
- 工作目录概念澄清(配置 vs 仓库操作) - 工作目录概念澄清(配置 vs 仓库操作)
- **SSH 密钥管理**:完整的密钥创建、部署和跨设备使用指南 - **SSH 密钥管理**:完整的密钥创建、部署和跨设备使用指南
- **Rust 项目支持**:新增 Rust workflow 和 Dockerfile 模板支持交叉编译、musl 静态链接、多平台构建
- **完整 Dockerfile 模板库**:新增 Go 和 Node.js 前端 Dockerfile 模板,涵盖所有后端和前端容器化需求
## 快速参考
- [Rust 项目快速配置](./rust-quick-reference.md) - 3 步设置 Rust 项目 CI/CD
## 相关资源 ## 相关资源

View File

@@ -7,8 +7,112 @@ agent: general
本文档提供了多种创建 Gitea Actions Runner 的方式,支持 Host 模式和 Docker 模式。 本文档提供了多种创建 Gitea Actions Runner 的方式,支持 Host 模式和 Docker 模式。
## ⚠️ 重要:前置条件
**创建 Runner 前请确保满足以下条件:**
### 1. 网络访问要求
- **必须能够访问外网**(如 google.com
- Runner 需要从 Docker Hub、GitHub 等拉取镜像和依赖
- 如果无法访问外网workflow 执行会失败
### 2. 硬件配置要求
- **最低配置2C4G**2 核 CPU + 4GB 内存)
- **推荐配置4C8G** 或更高
- 配置过低会导致 workflow 执行缓慢或失败
### 3. 网络模式要求
**两种模式都必须使用 host 网络模式,否则缓存功能无法正常工作!**
- **Docker 模式**Runner 容器和 Job 容器都使用 host 网络
- **Self-host 模式**Job 进程直接在宿主机运行,使用 127.0.0.1
原因Gitea Actions 的缓存服务运行在 runner 本地job 容器需要通过网络访问 runner 的缓存端口。只有在 host 网络模式下job 容器才能访问到 runner 的缓存服务。
## 📋 Runner 标签说明
### Host 模式标签
Runner 会自动检测系统配置并生成以下标签:
**基础标签**
- `host` - 运行模式标识
- `self-hosted` - 自托管标识
- `ubuntu-latest` - GitHub Actions 兼容标签(所有 Linux 系统)
- `x64` / `ARM64` - CPU 架构
- `linux-x86_64` / `darwin-arm64` - 系统-架构组合
**发行版标签**(自动检测):
- `debian` - Debian 系统
- `debian-12` - Debian 12 版本
- `ubuntu` - Ubuntu 系统
- `ubuntu-22` - Ubuntu 22.04 版本
- `centos` - CentOS 系统
- `macOS` - macOS 系统
- `linux` - 通用 Linux 标签
**设备配置标签**(自动检测):
- `4c8g` - CPU 核心数 + 内存大小(如 4核8G内存
**标签示例Debian 12 系统)**
```
host, ubuntu-latest, self-hosted, x64, linux-x86_64,
debian, debian-12, linux, 4c3g
```
**标签示例Ubuntu 22.04 系统)**
```
host, ubuntu-latest, self-hosted, x64, linux-x86_64,
ubuntu, ubuntu-22, linux, 8c16g
```
**标签示例macOS ARM64 系统)**
```
host, ubuntu-latest, self-hosted, ARM64, darwin-arm64,
macOS, 8c16g
```
**在 Workflow 中使用**
```yaml
# 兼容模式 - 匹配所有 Linux Host Runner
runs-on: ubuntu-latest
# 精确匹配 - 只在 Debian 系统上运行
runs-on: [self-hosted, debian]
# 更精确 - 指定 Debian 版本
runs-on: [self-hosted, debian-12]
# 根据配置筛选 - 需要 8核16G 的 runner
runs-on: [self-hosted, 8c16g]
# 组合条件 - Debian 系统且 8核16G
runs-on: [debian, 8c16g]
# macOS 环境
runs-on: [self-hosted, macOS]
```
### Docker 模式标签
Docker 模式使用预定义的镜像映射:
- `ubuntu-latest:docker://catthehacker/ubuntu:act-latest`
- `ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04`
- `ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04`
- `linux:docker://catthehacker/ubuntu:act-latest`
**标签设计说明**
1. **`ubuntu-latest` 是兼容标签**:所有 Linux Host Runner 都包含此标签,方便兼容 GitHub Actions workflow
2. **实际发行版标签提供精确控制**:如 `debian``ubuntu` 等,用于需要特定系统的场景
3. **`host` 标签**:用于区分 Host 模式和 Docker 模式的 runner
4. **配置标签**:如 `4c8g`,方便根据资源需求调度任务
## 📦 快速使用 ## 📦 快速使用
**使用前请确认:**
1. ✅ 主机能访问外网(如 google.com
2. ✅ 硬件配置满足 2C4G推荐 4C8G
### 方法一:直接执行(推荐) ### 方法一:直接执行(推荐)
复制以下命令到终端执行,可指定参数: 复制以下命令到终端执行,可指定参数:
@@ -38,7 +142,7 @@ gitea-create-runner() {
local mode="${1:-docker}" local mode="${1:-docker}"
local token="${2:-$GITEA_TOKEN}" local token="${2:-$GITEA_TOKEN}"
local gitea_url="${3:-https://git.shigongcao.com}" local gitea_url="${3:-https://git.shigongcao.com}"
local name="${4:-runner-$(hostname -s)-$mode}" local name="${4:-$(hostname -s)}"
echo "创建 $mode runner: $name" echo "创建 $mode runner: $name"
@@ -54,7 +158,7 @@ set -e
MODE="docker" MODE="docker"
TOKEN="" TOKEN=""
GITEA_URL="https://git.shigongcao.com" GITEA_URL="https://git.shigongcao.com"
RUNNER_NAME="runner-$(hostname -s)-docker" RUNNER_NAME="$(hostname -s)"
ALL_MODE=false ALL_MODE=false
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@@ -77,7 +181,7 @@ fi
create_runner() { create_runner() {
local mode=$1 local mode=$1
local name=$2 local name="${2:-$(hostname -s)}"
echo "创建 $mode runner: $name" echo "创建 $mode runner: $name"
if [ "$mode" = "docker" ]; then if [ "$mode" = "docker" ]; then
@@ -108,6 +212,7 @@ cache:
host: "host.docker.internal" host: "host.docker.internal"
port: 9040 port: 9040
container: container:
# 【关键】必须使用 host 网络!否则 job 容器无法访问 runner 的缓存服务
network: "host" network: "host"
privileged: false privileged: false
options: options:
@@ -123,6 +228,7 @@ YAML
# 注意:不要使用 node:16-bullseye 等纯运行时镜像,它们不包含 docker 命令 # 注意:不要使用 node:16-bullseye 等纯运行时镜像,它们不包含 docker 命令
local labels="ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04,linux:docker://catthehacker/ubuntu:act-latest" local labels="ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04,linux:docker://catthehacker/ubuntu:act-latest"
# 必须使用 --network host这样 job 容器才能访问 runner 的缓存服务
docker run -d \ docker run -d \
--name "$name" \ --name "$name" \
--restart always \ --restart always \
@@ -178,6 +284,7 @@ runner:
cache: cache:
enabled: true enabled: true
dir: "$runner_dir/cache" dir: "$runner_dir/cache"
# Self-host 模式job 进程直接在宿主机运行,使用 127.0.0.1 访问缓存
host: "127.0.0.1" host: "127.0.0.1"
port: 0 port: 0
host: host:
@@ -208,8 +315,8 @@ YAML
} }
if [ "$ALL_MODE" = true ]; then if [ "$ALL_MODE" = true ]; then
create_runner "docker" "runner-$(hostname -s)-docker" create_runner "docker" "$(hostname -s)-docker"
create_runner "host" "runner-$(hostname -s)-host" create_runner "host" "$(hostname -s)-host"
else else
create_runner "$MODE" "$RUNNER_NAME" create_runner "$MODE" "$RUNNER_NAME"
fi fi
@@ -292,6 +399,8 @@ RUNNER_NAME=""
ALL_MODE=false ALL_MODE=false
INTERACTIVE=false INTERACTIVE=false
SKIP_CHECKS=false SKIP_CHECKS=false
SKIP_NETWORK_CHECK=false
SKIP_RESOURCE_CHECK=false
# 显示帮助 # 显示帮助
show_help() { show_help() {
@@ -306,8 +415,10 @@ Gitea Actions Runner 创建脚本
--all 同时创建 Docker 和 Host runner --all 同时创建 Docker 和 Host runner
--token TOKEN Gitea 注册令牌 (必需) --token TOKEN Gitea 注册令牌 (必需)
--gitea-url URL Gitea 实例地址 (默认: https://git.shigongcao.com) --gitea-url URL Gitea 实例地址 (默认: https://git.shigongcao.com)
--name NAME Runner 名称 (默认: runner-\$(hostname)-模式) --name NAME Runner 名称 (默认: hostname)
--skip-checks 跳过依赖检查 --skip-checks 跳过所有检查
--skip-network-check 跳过网络连接检查
--skip-resource-check 跳过硬件资源检查
--interactive 交互模式 (传统方式) --interactive 交互模式 (传统方式)
--help 显示此帮助信息 --help 显示此帮助信息
@@ -333,7 +444,9 @@ while [[ $# -gt 0 ]]; do
--token) GITEA_TOKEN="$2"; shift ;; --token) GITEA_TOKEN="$2"; shift ;;
--gitea-url) GITEA_URL="$2"; shift ;; --gitea-url) GITEA_URL="$2"; shift ;;
--name) RUNNER_NAME="$2"; shift ;; --name) RUNNER_NAME="$2"; shift ;;
--skip-checks) SKIP_CHECKS=true ;; --skip-checks) SKIP_CHECKS=true; SKIP_NETWORK_CHECK=true; SKIP_RESOURCE_CHECK=true ;;
--skip-network-check) SKIP_NETWORK_CHECK=true ;;
--skip-resource-check) SKIP_RESOURCE_CHECK=true ;;
--interactive) INTERACTIVE=true ;; --interactive) INTERACTIVE=true ;;
--help) show_help ;; --help) show_help ;;
*) echo "❌ 未知参数: $1"; echo "使用 --help 查看帮助"; exit 1 ;; *) echo "❌ 未知参数: $1"; echo "使用 --help 查看帮助"; exit 1 ;;
@@ -349,7 +462,95 @@ if [ -z "$GITEA_TOKEN" ] && [ "$INTERACTIVE" = false ]; then
fi fi
# ========================================== # ==========================================
# 2. Interactive Mode (向后兼容) # 2. Pre-flight Checks (前置条件检查)
# ==========================================
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "前置条件检查"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 2.1 网络连接检查
if [ "$SKIP_NETWORK_CHECK" = false ]; then
echo "检查外网连接..."
# 尝试连接 Google使用 curl 超时 5 秒)
if curl -s --connect-timeout 5 --max-time 10 https://www.google.com > /dev/null 2>&1; then
echo "✓ 外网连接正常 (google.com 可访问)"
else
echo "⚠️ 警告:无法访问 google.com"
echo " Runner 需要访问外网来拉取镜像和依赖"
echo " 如果你确认网络正常,可以使用 --skip-network-check 跳过检查"
read -p " 是否继续?[y/N]: " continue_choice
if [ "$continue_choice" != "y" ] && [ "$continue_choice" != "Y" ]; then
echo "❌ 已取消"
exit 1
fi
fi
else
echo "⊘ 已跳过网络连接检查"
fi
echo ""
# 2.2 硬件资源检查
if [ "$SKIP_RESOURCE_CHECK" = false ]; then
echo "检查硬件资源..."
# 检测 CPU 核心数
if command -v nproc > /dev/null 2>&1; then
cpu_cores=$(nproc)
elif command -v sysctl > /dev/null 2>&1; then
cpu_cores=$(sysctl -n hw.ncpu 2>/dev/null || echo "0")
else
cpu_cores=0
fi
# 检测内存大小GB
if command -v free > /dev/null 2>&1; then
mem_gb=$(free -g | awk '/^Mem:/{print $2}')
elif command -v sysctl > /dev/null 2>&1; then
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo "0")
mem_gb=$((mem_bytes / 1024 / 1024 / 1024))
else
mem_gb=0
fi
echo " CPU: ${cpu_cores}"
echo " 内存: ${mem_gb}GB"
# 检查是否满足最低配置
config_ok=true
if [ "$cpu_cores" -lt 2 ]; then
echo "⚠️ 警告CPU 核心数不足 (当前 ${cpu_cores} 核,建议至少 2 核)"
config_ok=false
fi
if [ "$mem_gb" -lt 4 ]; then
echo "⚠️ 警告:内存不足 (当前 ${mem_gb}GB建议至少 4GB)"
config_ok=false
fi
if [ "$config_ok" = false ]; then
echo ""
echo " 最低配置要求2C4G (2 核 CPU + 4GB 内存)"
echo " 推荐配置4C8G 或更高"
echo " 配置过低可能导致 workflow 执行失败或缓慢"
read -p " 是否继续?[y/N]: " continue_choice
if [ "$continue_choice" != "y" ] && [ "$continue_choice" != "Y" ]; then
echo "❌ 已取消"
exit 1
fi
else
echo "✓ 硬件资源满足要求"
fi
else
echo "⊘ 已跳过硬件资源检查"
fi
echo ""
# ==========================================
# 3. Interactive Mode (向后兼容)
# ========================================== # ==========================================
if [ "$INTERACTIVE" = true ]; then if [ "$INTERACTIVE" = true ]; then
@@ -370,7 +571,7 @@ if [ "$INTERACTIVE" = true ]; then
fi fi
# ========================================== # ==========================================
# 3. Load Gitea Configuration # 4. Load Gitea Configuration
# ========================================== # ==========================================
# 如果未通过命令行提供,尝试从配置文件加载 # 如果未通过命令行提供,尝试从配置文件加载
@@ -413,7 +614,7 @@ if [ -z "$RUNNER_NAME" ]; then
# 批量模式会创建两个runner # 批量模式会创建两个runner
echo "批量模式:将创建 docker 和 host runner" echo "批量模式:将创建 docker 和 host runner"
else else
RUNNER_NAME="runner-$hostname-$RUNNER_MODE" RUNNER_NAME="$hostname"
echo "生成 Runner 名称: $RUNNER_NAME" echo "生成 Runner 名称: $RUNNER_NAME"
fi fi
else else
@@ -481,7 +682,7 @@ fi
echo "" echo ""
# ========================================== # ==========================================
# 3. Load Gitea Configuration # 4. Load Gitea Configuration
# ========================================== # ==========================================
# 如果未通过命令行提供,尝试从配置文件加载 # 如果未通过命令行提供,尝试从配置文件加载
@@ -514,7 +715,7 @@ echo " Token: ${GITEA_TOKEN:0:8}..."
echo "" echo ""
# ========================================== # ==========================================
# 4. Generate Runner Name # 5. Generate Runner Name
# ========================================== # ==========================================
# 如果未指定名称,生成默认名称 # 如果未指定名称,生成默认名称
@@ -524,7 +725,7 @@ if [ -z "$RUNNER_NAME" ]; then
# 批量模式会创建两个runner # 批量模式会创建两个runner
echo "批量模式:将创建 docker 和 host runner" echo "批量模式:将创建 docker 和 host runner"
else else
RUNNER_NAME="runner-$hostname-$RUNNER_MODE" RUNNER_NAME="$hostname"
echo "生成 Runner 名称: $RUNNER_NAME" echo "生成 Runner 名称: $RUNNER_NAME"
fi fi
else else
@@ -539,7 +740,7 @@ fi
echo "" echo ""
# ========================================== # ==========================================
# 5. Main Creation Function # 6. Main Creation Function
# ========================================== # ==========================================
create_runner() { create_runner() {
@@ -574,7 +775,7 @@ create_runner() {
fi fi
# ========================================== # ==========================================
# 5.1 Check Dependencies # 6.1 Check Dependencies
# ========================================== # ==========================================
if [ "$SKIP_CHECKS" = false ]; then if [ "$SKIP_CHECKS" = false ]; then
@@ -603,28 +804,73 @@ create_runner() {
fi fi
# ========================================== # ==========================================
# 5.2 Detect System Environment & Labels # 6.2 Detect System Environment & Labels
# ========================================== # ==========================================
echo "生成 Labels..." echo "生成 Labels..."
if [ "$mode" = "host" ]; then if [ "$mode" = "host" ]; then
OS=$(uname -s) OS=$(uname -s)
case "$OS" in
Darwin) os_label="macOS" ;;
Linux) os_label="ubuntu" ;;
*) os_label="unknown" ;;
esac
ARCH=$(uname -m) ARCH=$(uname -m)
# 检测架构
case "$ARCH" in case "$ARCH" in
arm64|aarch64) arch_label="ARM64" ;; arm64|aarch64) arch_label="ARM64" ;;
x86_64) arch_label="x64" ;; x86_64) arch_label="x64" ;;
*) arch_label="unknown" ;; *) arch_label="unknown" ;;
esac esac
# 检测操作系统和发行版
if [ "$OS" = "Darwin" ]; then
os_label="macOS"
distro_label=""
distro_version_label=""
elif [ "$OS" = "Linux" ]; then
# 检测 Linux 发行版
if [ -f /etc/os-release ]; then
. /etc/os-release
distro_label="$ID" # debian, ubuntu, centos, etc.
distro_version_label="${ID}-${VERSION_ID%%.*}" # debian-12, ubuntu-22, etc.
else
distro_label="linux"
distro_version_label=""
fi
os_label="$distro_label"
else
os_label="unknown"
distro_label=""
distro_version_label=""
fi
# 检测设备配置
cpu_cores=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "unknown")
mem_gb=$(free -g 2>/dev/null | awk '/^Mem:/{print $2}' || sysctl -n hw.memsize 2>/dev/null | awk '{print int($1/1024/1024/1024)}' || echo "unknown")
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]') combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
# 构建标签(移除 :host 后缀,统一添加 host 模式标签)
# 添加 ubuntu-latest 以兼容 GitHub Actions workflow
labels="host,ubuntu-latest,self-hosted,${arch_label},${combined}"
# 添加实际发行版标签
if [ -n "$distro_label" ] && [ "$distro_label" != "linux" ]; then
labels="${labels},${distro_label}"
if [ -n "$distro_version_label" ]; then
labels="${labels},${distro_version_label}"
fi
elif [ "$os_label" != "unknown" ]; then
labels="${labels},${os_label}"
fi
# 添加通用 linux 标签(用于区分 macOS
if [ "$OS" = "Linux" ]; then
labels="${labels},linux"
fi
# 添加设备配置标签
if [ "$cpu_cores" != "unknown" ] && [ "$mem_gb" != "unknown" ]; then
labels="${labels},${cpu_cores}c${mem_gb}g"
fi
else else
# Docker mode uses standard labels mapping to images # Docker mode uses standard labels mapping to images
@@ -643,7 +889,7 @@ create_runner() {
echo "" echo ""
# ========================================== # ==========================================
# 5.3 Create Runner Directory # 6.3 Create Runner Directory
# ========================================== # ==========================================
echo "创建 Runner 目录..." echo "创建 Runner 目录..."
@@ -656,7 +902,7 @@ create_runner() {
echo "" echo ""
# ========================================== # ==========================================
# 5.4 Create Configuration File # 6.4 Create Configuration File
# ========================================== # ==========================================
echo "创建配置文件..." echo "创建配置文件..."
@@ -747,6 +993,7 @@ EOF
echo "启动 Docker 容器 (自动注册)..." echo "启动 Docker 容器 (自动注册)..."
# 必须使用 --network host这样 job 容器才能访问 runner 的缓存服务
docker run -d \ docker run -d \
--name "$name" \ --name "$name" \
--restart always \ --restart always \
@@ -773,7 +1020,7 @@ EOF
fi fi
# ========================================== # ==========================================
# 5.5 Display Runner Info # 6.5 Display Runner Info
# ========================================== # ==========================================
echo "" echo ""
@@ -794,7 +1041,7 @@ EOF
} }
# ========================================== # ==========================================
# 6. Main Execution Logic # 7. Main Execution Logic
# ========================================== # ==========================================
echo "" echo ""
@@ -813,8 +1060,8 @@ if [ "$ALL_MODE" = true ]; then
host_name="${RUNNER_NAME}-host" host_name="${RUNNER_NAME}-host"
else else
# 使用默认名称 # 使用默认名称
docker_name="runner-${hostname}-docker" docker_name="${hostname}-docker"
host_name="runner-${hostname}-host" host_name="${hostname}-host"
fi fi
echo "批量创建模式:" echo "批量创建模式:"
@@ -910,7 +1157,7 @@ echo " $labels"
echo "" echo ""
# ========================================== # ==========================================
# 6. Create Runner Directory # 7. Create Runner Directory
# ========================================== # ==========================================
echo "创建 Runner 目录..." echo "创建 Runner 目录..."
@@ -1074,7 +1321,7 @@ cache:
host: "host.docker.internal" host: "host.docker.internal"
port: 9040 port: 9040
container: container:
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问 # 【关键】必须使用 host 网络!否则 job 容器无法访问 runner 的缓存服务
network: "host" network: "host"
privileged: false privileged: false
options: options:
@@ -1088,7 +1335,7 @@ EOF
echo "启动 Docker 容器 (自动注册)..." echo "启动 Docker 容器 (自动注册)..."
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问 # 【关键】必须使用 --network host否则 job 容器无法访问 runner 的缓存服务
docker run -d \ docker run -d \
--name "$runner_name" \ --name "$runner_name" \
--restart always \ --restart always \

View File

@@ -0,0 +1,550 @@
# Go 服务 Dockerfile 模板
Go 后端服务的 Docker 容器化模板,支持多种构建场景。
## 模板类型
### 1. CI 构建镜像Dockerfile.ci
**适用场景**:在 CI/CD 中使用,二进制文件已在外部构建完成。
**优点**
- 镜像体积最小(~15MB
- 构建速度快
- 适合生产环境
```dockerfile
# CI 构建专用 Dockerfile
# 将不常变化的层放在前面,最大化利用缓存
# 使用固定版本避免 latest 导致的缓存失效和不确定性
FROM alpine:3.21
# 基础设置层 - 很少变化,可以长期缓存
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata wget netcat-openbsd && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 健康检查脚本 - 偶尔变化
COPY docker-healthcheck.sh .
RUN chmod +x docker-healthcheck.sh
# 应用二进制 - 每次构建都变化,放在最后
COPY your-binary-name .
# 健康检查(根据实际服务修改端口和端点)
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD ./docker-healthcheck.sh
CMD ["./your-binary-name"]
```
**使用说明**
1.`your-binary-name` 替换为实际的二进制文件名
2. 创建 `docker-healthcheck.sh` 健康检查脚本
3. 在 CI workflow 中先构建二进制,再构建镜像
### 2. 完整构建镜像Dockerfile
**适用场景**:本地开发、无 CI 环境、独立构建。
**优点**
- 自包含构建流程
- 可在任何环境构建
- 利用 Docker 缓存加速
```dockerfile
# ============================================
# 构建阶段
# ============================================
FROM golang:1.25-alpine AS builder
# 安装构建依赖
RUN apk add --no-cache git make
# 设置工作目录
WORKDIR /build
# 设置 Go 代理(国内加速)
ENV GOPROXY=https://goproxy.cn,direct
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
# 先复制依赖文件,利用 Docker 层缓存
COPY go.mod go.sum ./
RUN go mod download
# 复制源码
COPY . .
# 构建应用(根据项目需求修改)
RUN go build -o app \
-ldflags '-s -w -X main.GitTag=dev' \
.
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
# 安装运行时依赖
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata wget netcat-openbsd && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
# 从构建阶段复制二进制文件
COPY --from=builder /build/app .
COPY --from=builder /build/docker-healthcheck.sh .
RUN chmod +x app docker-healthcheck.sh
# 暴露端口(根据实际服务修改)
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD ./docker-healthcheck.sh
CMD ["./app"]
```
**使用说明**
1. 将构建命令修改为项目实际的构建命令
2. 调整 ldflags 参数(版本号注入、符号表去除等)
3. 修改端口号
### 3. 代码生成支持版Dockerfile.codegen
**适用场景**:需要在容器中生成代码(如 Ent、Wire、oapi-codegen
```dockerfile
# ============================================
# 构建阶段
# ============================================
FROM golang:1.25-alpine AS builder
# 安装构建工具
RUN apk add --no-cache git make
WORKDIR /build
ENV GOPROXY=https://goproxy.cn,direct
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
# 复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
# 安装代码生成工具(根据项目需求选择)
RUN go install entgo.io/ent/cmd/ent@latest && \
go install github.com/google/wire/cmd/wire@latest && \
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest && \
go install golang.org/x/tools/cmd/stringer@latest
# 复制源码
COPY . .
# 代码生成(根据项目需求修改)
RUN go generate ./...
# 或使用 go tool 调用
# RUN go tool ent generate ./ent/schema
# RUN go tool wire ./...
# RUN go tool oapi-codegen -config oapi.yaml api.yaml
# 测试(可选)
RUN go test ./...
RUN go vet ./...
# 构建
RUN go build -o app \
-ldflags '-s -w' \
.
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata wget netcat-openbsd && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
COPY --from=builder /build/app .
COPY --from=builder /build/docker-healthcheck.sh .
RUN chmod +x app docker-healthcheck.sh
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD ./docker-healthcheck.sh
CMD ["./app"]
```
## 健康检查脚本
### docker-healthcheck.sh基础版
```bash
#!/bin/sh
# Docker 健康检查脚本
# 检查应用端口是否在监听
if nc -z localhost 8080 2>/dev/null; then
exit 0
fi
exit 1
```
### docker-healthcheck.shHTTP 端点版)
```bash
#!/bin/sh
# Docker 健康检查脚本
# 检查 HTTP 端点是否可访问
# 使用 /health 或 /metrics 端点
if wget --spider --quiet --timeout=5 http://localhost:8080/health 2>/dev/null; then
exit 0
fi
# 备用:检查端口是否在监听
if nc -z localhost 8080 2>/dev/null; then
exit 0
fi
exit 1
```
### docker-healthcheck.sh多端口版
```bash
#!/bin/sh
# Docker 健康检查脚本
# 检查多个端口(适用于多个 API 服务)
# 检查端口 2020 (Platform API)
if ! nc -z localhost 2020 2>/dev/null; then
echo "Port 2020 is not responding"
exit 1
fi
# 检查端口 2021 (WxMP API)
if ! nc -z localhost 2021 2>/dev/null; then
echo "Port 2021 is not responding"
exit 1
fi
# 或使用 HTTP 端点
# if ! wget --spider --quiet --timeout=5 http://localhost:2020/metrics 2>/dev/null; then
# exit 1
# fi
exit 0
```
## 高级配置
### 环境变量配置
```dockerfile
# 在 Dockerfile 中设置默认环境变量
ENV MODE=production
ENV LOG_LEVEL=info
ENV GIN_MODE=release
# 运行时可覆盖
# docker run -e MODE=test -e LOG_LEVEL=debug your-image
```
### 配置文件挂载
```dockerfile
# 创建配置目录
RUN mkdir -p /app/configs
# 运行时挂载配置文件
# docker run -v ./configs:/app/configs your-image
```
### 非 root 用户运行
```dockerfile
# 创建非特权用户
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
# 复制文件
COPY --from=builder --chown=appuser:appuser /build/app .
# 切换用户
USER appuser
CMD ["./app"]
```
### 多阶段构建优化
```dockerfile
# ============================================
# 依赖下载阶段(缓存 go.mod
# ============================================
FROM golang:1.25-alpine AS deps
WORKDIR /build
ENV GOPROXY=https://goproxy.cn,direct
COPY go.mod go.sum ./
RUN go mod download
# ============================================
# 构建阶段
# ============================================
FROM golang:1.25-alpine AS builder
WORKDIR /build
# 从依赖阶段复制缓存
COPY --from=deps /go/pkg /go/pkg
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
COPY . .
RUN go build -o app -ldflags '-s -w' .
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
# ... 省略后续步骤
```
## 镜像优化技巧
### 1. 减小镜像体积
```dockerfile
# 使用 Alpine 基础镜像
FROM alpine:3.21
# 去除符号表和调试信息
RUN go build -ldflags '-s -w' -o app
# 使用 upx 压缩(可选,启动时间会变长)
RUN apk add --no-cache upx && \
upx --best --lzma app && \
apk del upx
```
### 2. 利用构建缓存
```dockerfile
# 先复制 go.mod 和 go.sum利用依赖缓存
COPY go.mod go.sum ./
RUN go mod download
# 再复制源码(源码变化频繁)
COPY . .
```
### 3. 合并 RUN 命令
```dockerfile
# 合并多个 RUN 命令减少层数
RUN apk add --no-cache tzdata wget netcat-openbsd && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
addgroup -g 1000 appuser
```
### 4. 使用 .dockerignore
```
# .dockerignore
.git/
.gitignore
*.md
docs/
tests/
.github/
.gitea/
Dockerfile*
docker-compose.yml
*.log
tmp/
vendor/
```
## 构建命令示例
### 本地构建
```bash
# 基础构建
docker build -t your-service:latest .
# 指定 Dockerfile
docker build -f Dockerfile.ci -t your-service:latest .
# 传递构建参数
docker build --build-arg GIT_TAG=v1.0.0 -t your-service:1.0.0 .
# 查看构建过程
docker build --progress=plain -t your-service:latest .
```
### CI/CD 构建
```bash
# 使用 BuildKit 缓存
export DOCKER_BUILDKIT=1
# 使用注册表缓存
docker build \
--cache-from=type=registry,ref=your-service:buildcache \
--cache-to=type=registry,ref=your-service:buildcache,mode=max \
-t your-service:latest \
.
```
## 运行容器示例
```bash
# 基础运行
docker run -d -p 8080:8080 your-service:latest
# 挂载配置文件
docker run -d \
-p 8080:8080 \
-v ./configs:/app/configs \
-v ./logs:/app/logs \
your-service:latest
# 设置环境变量
docker run -d \
-p 8080:8080 \
-e MODE=production \
-e DATABASE_URL=postgres://... \
your-service:latest
# 查看日志
docker logs -f <container-id>
# 进入容器调试
docker exec -it <container-id> /bin/sh
```
## 安全建议
### 1. 使用特定版本标签
```dockerfile
# 不推荐:使用 latest
FROM alpine:latest
# 推荐:使用特定版本
FROM alpine:3.21
```
### 2. 定期更新依赖
```bash
# 更新 Go 模块
go get -u ./...
go mod tidy
# 更新基础镜像
docker pull alpine:3.21
```
### 3. 扫描漏洞
```bash
# 使用 Trivy 扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image your-service:latest
```
### 4. 最小权限原则
```dockerfile
# 使用非 root 用户
USER appuser
# 只读文件系统(如果适用)
# docker run --read-only your-service
```
## 常见问题
### Q1: 时区不正确
**原因**: 容器默认使用 UTC 时区
**解决方案**:
```dockerfile
RUN apk add --no-cache tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
```
### Q2: 网络连接超时
**原因**: DNS 解析问题或网络配置
**解决方案**:
```bash
# 运行时指定 DNS
docker run --dns 8.8.8.8 your-service
# 或在 Dockerfile 中配置
RUN echo 'nameserver 8.8.8.8' > /etc/resolv.conf
```
### Q3: CGO 依赖问题
**错误信息**: `binary was compiled with 'CGO_ENABLED=1'`
**解决方案**:
```dockerfile
# 禁用 CGO
ENV CGO_ENABLED=0
# 或安装 gcc
RUN apk add --no-cache gcc musl-dev
```
### Q4: 静态链接失败
**错误信息**: `dynamic executable`
**解决方案**:
```dockerfile
# 确保 CGO_ENABLED=0
ENV CGO_ENABLED=0
# 或使用 musl 交叉编译
RUN apk add --no-cache musl-dev
RUN go build -ldflags '-linkmode external -extldflags "-static"' -o app
```
## 参考资源
- [Go 官方 Docker 指南](https://docs.docker.com/language/golang/)
- [Alpine Linux 包搜索](https://pkgs.alpinelinux.org/packages)
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
- [Go 编译器标志](https://pkg.go.dev/cmd/go)

View File

@@ -0,0 +1,631 @@
# Node.js 前端 Dockerfile 模板
Node.js 前端项目的 Docker 容器化模板,使用 Nginx 提供静态文件服务。
## 模板类型
### 1. CI 构建镜像Dockerfile.ci
**适用场景**:在 CI/CD 中使用,静态文件已在外部构建完成。
**优点**
- 镜像体积最小(~20MB
- 构建速度快
- 适合生产环境
```dockerfile
# CI 构建专用 Dockerfile
# 将不常变化的层放在前面,最大化利用缓存
# 使用固定版本避免 latest 导致的缓存失效和不确定性
FROM nginx:1.28.0-alpine
# 基础设置层 - 很少变化,可以长期缓存
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# Nginx 配置文件 - 配置 SPA 路由处理
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 应用静态文件 - 每次构建都变化,放在最后
COPY dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
**使用说明**
1. 创建 `nginx.conf` 配置文件
2. 在 CI workflow 中先构建前端(生成 dist 目录),再构建镜像
3. 根据实际需求修改 Nginx 配置
### 2. 完整构建镜像Dockerfile
**适用场景**:本地开发、无 CI 环境、独立构建。
**优点**
- 自包含构建流程
- 可在任何环境构建
- 利用 Docker 缓存加速
```dockerfile
# ============================================
# 构建阶段
# ============================================
FROM node:22-alpine AS builder
# 安装构建依赖
RUN apk add --no-cache git
WORKDIR /build
# 设置 npm 镜像源(国内加速)
RUN npm config set registry https://registry.npmmirror.com
# 安装 pnpm
RUN npm install -g pnpm@latest-10
# 先复制依赖文件,利用 Docker 层缓存
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# 复制源码
COPY . .
# 构建应用
# 根据项目使用的构建工具修改命令
RUN pnpm run build
# ============================================
# 运行阶段
# ============================================
FROM nginx:1.28.0-alpine
# 安装运行时依赖
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 从构建阶段复制静态文件
COPY --from=builder /build/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
**使用说明**
1. 根据项目使用 npm、yarn 或 pnpm 修改安装命令
2. 根据构建工具Vite、Webpack、Next.js 等)修改构建命令和输出目录
3. 调整 Nginx 配置
### 3. 多环境构建版Dockerfile.multienv
**适用场景**需要根据不同环境dev/test/prod构建不同配置。
```dockerfile
# ============================================
# 构建阶段
# ============================================
FROM node:22-alpine AS builder
ARG BUILD_ENV=production
WORKDIR /build
RUN npm config set registry https://registry.npmmirror.com && \
npm install -g pnpm@latest-10
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
# 根据 BUILD_ENV 设置不同的环境变量
ENV NODE_ENV=${BUILD_ENV}
ENV VITE_APP_ENV=${BUILD_ENV}
RUN pnpm run build
# ============================================
# 运行阶段
# ============================================
FROM nginx:1.28.0-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /build/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
**构建命令**
```bash
# 生产环境
docker build --build-arg BUILD_ENV=production -t app:prod .
# 测试环境
docker build --build-arg BUILD_ENV=test -t app:test .
# 开发环境
docker build --build-arg BUILD_ENV=development -t app:dev .
```
## Nginx 配置模板
### nginx.conf基础 SPA 配置)
```nginx
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/x-javascript application/xml+rss
application/json application/javascript;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA 路由处理:所有请求都返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
```
### nginx.conf带 API 代理)
```nginx
server {
listen 80;
server_name _;
client_max_body_size 30M;
root /usr/share/nginx/html;
index index.html;
# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/x-javascript application/xml+rss
application/json application/javascript;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA 路由处理
location / {
try_files $uri $uri/ /index.html;
}
# API 代理(根据实际后端服务修改)
location /api/ {
proxy_pass http://backend-service:8080/;
proxy_http_version 1.1;
proxy_read_timeout 300s;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
```
### nginx.conf完整生产配置
```nginx
# SPA 应用 Nginx 配置
# 所有路由都返回 index.html由前端路由处理
server {
listen 80;
server_name _;
client_max_body_size 30M;
root /usr/share/nginx/html;
index index.html;
# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/x-javascript application/xml+rss
application/json application/javascript;
# 静态资源缓存(带版本号的文件可以长期缓存)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 文件不缓存(确保用户总是获取最新版本)
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# SPA 路由处理:所有请求都返回 index.html
location / {
# 用于配合 browserHistory 使用
try_files $uri $uri/ /index.html;
}
# API 代理:平台 API
location /platform/api/ {
proxy_pass http://backend-service:2020/;
proxy_http_version 1.1;
proxy_read_timeout 300s;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API 代理:微信小程序 API
location /wxmp/api/ {
proxy_pass http://backend-service:2021/;
proxy_http_version 1.1;
proxy_read_timeout 300s;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 第三方服务代理(如高德地图)
location /_AMapService/ {
set $args "$args&jscode=YOUR_AMAP_KEY";
proxy_pass https://restapi.amap.com/;
}
# 错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
```
## 高级配置
### 支持 HTTPS
```nginx
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /usr/share/nginx/html;
index index.html;
# ... 其他配置
}
```
### 防盗链
```nginx
location ~* \.(jpg|jpeg|png|gif|svg)$ {
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
expires 1y;
}
```
### 限流
```nginx
# 在 http 块中定义限流区域
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 在 server 块中应用
location / {
limit_req zone=one burst=20 nodelay;
try_files $uri $uri/ /index.html;
}
```
### 安全头
```nginx
location / {
# 防止点击劫持
add_header X-Frame-Options "SAMEORIGIN" always;
# 防止 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# 启用 XSS 保护
add_header X-XSS-Protection "1; mode=block" always;
# CSP 策略
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
try_files $uri $uri/ /index.html;
}
```
## 镜像优化技巧
### 1. 使用多阶段构建
```dockerfile
# 分离构建和运行环境,只保留必要文件
FROM node:22-alpine AS builder
# ... 构建步骤
FROM nginx:1.28.0-alpine
# 只复制构建产物
COPY --from=builder /build/dist /usr/share/nginx/html
```
### 2. 利用构建缓存
```dockerfile
# 先复制依赖文件
COPY package.json pnpm-lock.yaml ./
RUN pnpm install
# 再复制源码(源码变化频繁)
COPY . .
```
### 3. 使用 .dockerignore
```
# .dockerignore
node_modules/
.git/
.gitignore
*.md
docs/
tests/
.github/
.gitea/
Dockerfile*
docker-compose.yml
.env*
dist/
build/
coverage/
.vscode/
.idea/
```
### 4. 优化 Nginx 配置
```nginx
# 启用 sendfile
sendfile on;
# 启用 tcp_nopush
tcp_nopush on;
# 启用 tcp_nodelay
tcp_nodelay on;
# 设置 keepalive 超时
keepalive_timeout 65;
# 禁用访问日志(生产环境可选)
access_log off;
```
## 构建命令示例
### 本地构建
```bash
# 基础构建
docker build -t frontend:latest .
# 指定 Dockerfile
docker build -f Dockerfile.ci -t frontend:latest .
# 传递构建参数
docker build --build-arg BUILD_ENV=production -t frontend:1.0.0 .
```
### CI/CD 构建
```bash
# 使用 BuildKit 缓存
export DOCKER_BUILDKIT=1
# 使用注册表缓存
docker build \
--cache-from=type=registry,ref=frontend:buildcache \
--cache-to=type=registry,ref=frontend:buildcache,mode=max \
-t frontend:latest \
.
```
## 运行容器示例
```bash
# 基础运行
docker run -d -p 80:80 frontend:latest
# 挂载自定义 Nginx 配置
docker run -d \
-p 80:80 \
-v ./nginx.conf:/etc/nginx/conf.d/default.conf \
frontend:latest
# 设置环境变量(如果 Nginx 配置需要)
docker run -d \
-p 80:80 \
-e BACKEND_HOST=api.example.com \
frontend:latest
# 查看日志
docker logs -f <container-id>
# 进入容器调试
docker exec -it <container-id> /bin/sh
```
## Docker Compose 示例
```yaml
version: '3.8'
services:
frontend:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- backend
restart: unless-stopped
backend:
image: backend-service:latest
ports:
- "8080:8080"
restart: unless-stopped
```
## 常见问题
### Q1: SPA 路由 404 错误
**原因**: Nginx 找不到对应的文件
**解决方案**:
```nginx
# 配置 try_files所有路由返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
```
### Q2: API 代理跨域问题
**原因**: CORS 配置不正确
**解决方案**:
```nginx
location /api/ {
proxy_pass http://backend:8080/;
# 添加 CORS 头
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
if ($request_method = 'OPTIONS') {
return 204;
}
}
```
### Q3: 静态资源缓存问题
**原因**: 浏览器缓存了旧版本
**解决方案**:
```nginx
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# JS/CSS 使用版本号或哈希,可以长期缓存
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
```
### Q4: 容器启动后立即退出
**原因**: Nginx 配置文件语法错误
**解决方案**:
```bash
# 测试配置文件
docker run --rm -v ./nginx.conf:/etc/nginx/conf.d/default.conf \
nginx:1.28.0-alpine nginx -t
# 查看容器日志
docker logs <container-id>
```
## 参考资源
- [Node.js 官方 Docker 指南](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)
- [Nginx 官方文档](https://nginx.org/en/docs/)
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
- [前端构建工具文档](https://vitejs.dev/guide/)

View File

@@ -0,0 +1,571 @@
# Rust 服务 Dockerfile 模板
Rust 项目的 Docker 容器化模板,支持多种构建场景。
## 模板类型
### 1. CI 构建镜像Dockerfile.ci
**适用场景**:在 CI/CD 中使用,二进制文件已在外部构建完成。
**优点**
- 镜像体积最小(~20MB
- 构建速度快
- 适合生产环境
```dockerfile
FROM alpine:3.21
# 安装运行时依赖
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
# 复制编译好的二进制文件(由 CI workflow 构建)
COPY your-binary-name .
# 环境变量
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
# 暴露端口
EXPOSE 9090
# 健康检查
# 根据你的服务实际情况修改端点和端口
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
CMD ["./your-binary-name"]
```
**使用说明**
1.`your-binary-name` 替换为实际的二进制文件名
2. 修改 `EXPOSE` 端口号
3. 修改 `HEALTHCHECK` 的端点和端口
4. 在 CI workflow 中先构建二进制,再构建镜像
### 2. 完整构建镜像Dockerfile
**适用场景**:本地开发、无 CI 环境、独立构建。
**优点**
- 自包含构建流程
- 可在任何环境构建
- 利用 Docker 缓存加速
```dockerfile
# ============================================
# 构建阶段
# ============================================
FROM rust:1.83-alpine AS builder
# 安装构建依赖
# musl-dev: musl libc 开发包(静态链接)
# openssl-dev: OpenSSL 开发包(如果项目依赖 openssl
# pkgconfig: pkg-config 工具
RUN apk add --no-cache musl-dev openssl-dev pkgconfig
# 配置 Cargo 镜像加速(使用 rsproxy.cn
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
# 先复制依赖文件,利用 Docker 层缓存
# 只要 Cargo.toml 和 Cargo.lock 不变,依赖层就会被缓存
COPY Cargo.toml Cargo.lock ./
# 创建虚拟 src 目录,预先下载和编译依赖
# 这样修改源码时不需要重新编译依赖
RUN mkdir src && \
echo "fn main() {}" > src/main.rs && \
cargo build --release --target x86_64-unknown-linux-musl && \
rm -rf src
# 复制实际源码
COPY src ./src
# 构建 Release 版本musl 静态链接)
RUN cargo build --release --target x86_64-unknown-linux-musl
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
# 安装运行时依赖
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
# 从构建阶段复制二进制文件
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
# 环境变量
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
# 暴露端口
EXPOSE 9090
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
CMD ["./your-binary-name"]
```
**使用说明**
1.`your-binary-name` 替换为 `Cargo.toml` 中的 `[[bin]]` name
2. 如果不需要 OpenSSL可移除 `openssl-dev`
3. 修改端口和健康检查配置
### 3. 依赖缓存优化版Dockerfile.cache
**适用场景**:本地开发,频繁构建。
**优点**
- 最大化利用缓存
- 依赖只编译一次
- 适合迭代开发
```dockerfile
# ============================================
# 依赖缓存阶段
# ============================================
FROM rust:1.83-alpine AS dependencies
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
# 只复制依赖文件
COPY Cargo.toml Cargo.lock ./
# 创建虚拟 main.rs编译依赖
RUN mkdir -p src && \
echo "fn main() {}" > src/main.rs && \
cargo build --release --target x86_64-unknown-linux-musl
# 删除虚拟源码编译产物
RUN rm -f target/x86_64-unknown-linux-musl/release/deps/your_binary_name*
# ============================================
# 构建阶段
# ============================================
FROM rust:1.83-alpine AS builder
RUN apk add --no-cache musl-dev openssl-dev pkgconfig
WORKDIR /build
# 从依赖阶段复制编译缓存
COPY --from=dependencies /root/.cargo /root/.cargo
COPY --from=dependencies /build/target target
# 复制所有文件
COPY . .
# 构建项目
RUN cargo build --release --target x86_64-unknown-linux-musl
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
EXPOSE 9090
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
CMD ["./your-binary-name"]
```
**使用说明**
1.`your_binary_name` 替换为二进制文件名(下划线形式)
2. 适合本地 `docker build` 使用
### 4. 多平台构建版Dockerfile.multiarch
**适用场景**:需要支持 ARM64 和 AMD64。
```dockerfile
# ============================================
# 构建阶段(支持多平台)
# ============================================
FROM --platform=$BUILDPLATFORM rust:1.83-alpine AS builder
# 安装构建依赖
RUN apk add --no-cache musl-dev openssl-dev pkgconfig clang
# 配置 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
# 安装目标架构支持
ARG TARGETARCH
RUN case "$TARGETARCH" in \
"amd64") echo "x86_64-unknown-linux-musl" > /target.txt ;; \
"arm64") echo "aarch64-unknown-linux-musl" > /target.txt ;; \
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
esac && \
rustup target add $(cat /target.txt)
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
COPY src ./src
# 构建对应架构
RUN cargo build --release --target $(cat /target.txt)
# 复制二进制到统一路径
RUN mkdir -p /app && \
cp target/$(cat /target.txt)/release/your-binary-name /app/
# ============================================
# 运行阶段
# ============================================
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
COPY --from=builder /app/your-binary-name .
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
EXPOSE 9090
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
CMD ["./your-binary-name"]
```
**使用说明**
```bash
# 构建多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 -t your-image:latest .
```
## 高级配置
### 健康检查配置
```dockerfile
# 存活探针Liveness Probe
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
# 参数说明:
# --interval=30s 每 30 秒检查一次
# --timeout=5s 单次检查超时 5 秒
# --start-period=10s 容器启动后 10 秒开始检查
# --retries=3 失败 3 次后认为不健康
```
### 多端点健康检查
```dockerfile
# 使用脚本检查多个端点
RUN echo '#!/bin/sh' > /healthcheck.sh && \
echo 'curl -sf http://localhost:9090/healthz || exit 1' >> /healthcheck.sh && \
echo 'curl -sf http://localhost:9090/readyz || exit 1' >> /healthcheck.sh && \
chmod +x /healthcheck.sh
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD ["/healthcheck.sh"]
```
### 环境变量配置
```dockerfile
# 基础环境变量
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
ENV RUST_BACKTRACE=1
# 应用配置(可在 docker run 时覆盖)
ENV CONFIG_FILE=production.yaml
ENV HTTP_PORT=9090
ENV LOG_LEVEL=info
# 数据库配置(敏感信息应通过运行时注入)
# ENV DATABASE_URL=postgresql://...
```
### 非 root 用户运行
```dockerfile
# 创建非特权用户
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
# 复制二进制文件
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
# 修改所有权
RUN chown -R appuser:appuser /app
# 切换用户
USER appuser
CMD ["./your-binary-name"]
```
### 配置文件挂载
```dockerfile
# 在 Dockerfile 中创建配置目录
RUN mkdir -p /app/configs /app/logs
# 在 docker run 时挂载
# docker run -v ./configs:/app/configs -v ./logs:/app/logs your-image
```
## 构建命令示例
### 本地构建
```bash
# 基础构建
docker build -t your-service:latest .
# 指定 Dockerfile
docker build -f Dockerfile.ci -t your-service:latest .
# 传递构建参数
docker build --build-arg GIT_TAG=1.0.0 -t your-service:1.0.0 .
# 查看构建过程
docker build --progress=plain -t your-service:latest .
```
### 多平台构建
```bash
# 创建 buildx builder
docker buildx create --name mybuilder --use
# 构建并推送
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.example.com/your-service:latest \
--push \
.
```
### 优化构建速度
```bash
# 使用 BuildKit 缓存
export DOCKER_BUILDKIT=1
# 使用外部缓存
docker build \
--cache-from=type=registry,ref=your-service:buildcache \
--cache-to=type=registry,ref=your-service:buildcache,mode=max \
-t your-service:latest \
.
```
## 镜像大小优化
### 1. 使用 Alpine 基础镜像
```dockerfile
# Alpine 镜像体积小(~5MB
FROM alpine:3.21
```
### 2. 静态链接musl
```dockerfile
# 构建时使用 musl 目标,生成静态链接二进制
RUN cargo build --release --target x86_64-unknown-linux-musl
```
### 3. Strip 二进制文件
```toml
# Cargo.toml
[profile.release]
strip = true # 去除符号表
```
或在 Dockerfile 中:
```dockerfile
# 手动 strip
RUN strip target/x86_64-unknown-linux-musl/release/your-binary-name
```
### 4. 最小化层数
```dockerfile
# 合并多个 RUN 命令
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
addgroup -g 1000 appuser
```
### 5. 使用 .dockerignore
```
# .dockerignore
target/
.git/
.gitignore
*.md
tests/
benches/
```
## 安全建议
### 1. 使用特定版本标签
```dockerfile
# 不推荐:使用 latest
FROM alpine:latest
# 推荐:使用特定版本
FROM alpine:3.21
```
### 2. 定期更新依赖
```bash
# 更新 Cargo 依赖
cargo update
# 检查安全漏洞
cargo audit
```
### 3. 最小权限原则
```dockerfile
# 使用非 root 用户
USER appuser
# 只读文件系统(根据需要)
# docker run --read-only your-service
```
### 4. 不在镜像中包含敏感信息
```dockerfile
# 错误示例
# ENV DATABASE_PASSWORD=secret123
# 正确做法:通过运行时注入
# docker run -e DATABASE_PASSWORD=secret123 your-service
```
## 常见问题
### Q1: 镜像体积过大
**原因**
- 使用 `rust:latest` 作为运行镜像(>1GB
- 没有使用多阶段构建
- 包含 debug 符号
**解决方案**
```dockerfile
# 使用多阶段构建 + Alpine
FROM rust:1.83-alpine AS builder
# ... 构建步骤 ...
FROM alpine:3.21 # 最终镜像只有 ~20MB
COPY --from=builder /build/target/.../your-binary .
```
### Q2: openssl 链接错误
**错误信息**
```
error: linking with `cc` failed
ld: library not found for -lssl
```
**解决方案**
```dockerfile
# 方案 1安装 openssl-dev
RUN apk add --no-cache openssl-dev
# 方案 2使用 rustls 替代 openssl
# 在 Cargo.toml 中使用 rustls feature
```
### Q3: 交叉编译失败
**错误信息**
```
error: linker `aarch64-linux-musl-gcc` not found
```
**解决方案**
```bash
# 安装 musl 交叉编译工具链
rustup target add aarch64-unknown-linux-musl
# 或使用 cargo-zigbuild
cargo install cargo-zigbuild
cargo zigbuild --target aarch64-unknown-linux-musl
```
### Q4: 容器启动失败
**检查步骤**
```bash
# 查看日志
docker logs your-container
# 进入容器调试
docker run -it --entrypoint /bin/sh your-image
# 检查二进制是否可执行
docker run your-image ls -lh /app/
```
## 参考资源
- [Rust 官方 Docker 指南](https://docs.docker.com/language/rust/)
- [Alpine Linux 包搜索](https://pkgs.alpinelinux.org/packages)
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
- [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild)

View File

@@ -0,0 +1,443 @@
# Rust 项目 Gitea CI/CD 快速参考
快速设置 Rust 项目的 CI/CD 流程。
## 最小化配置3 步)
### 1. 创建 Dockerfile.ci
在项目根目录创建 `Dockerfile.ci`
```dockerfile
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata curl && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
COPY your-binary-name .
ENV TZ=Asia/Shanghai
ENV RUST_LOG=info
EXPOSE 9090
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:9090/healthz || exit 1
CMD ["./your-binary-name"]
```
**修改项**
- `your-binary-name` → 你的二进制文件名(通常和 `Cargo.toml` 中的 `name` 一致)
- `9090` → 你的服务端口
### 2. 创建 workflow 文件
创建 `.gitea/workflows/your-service.yml`
```yaml
name: Your Service - Build & Publish
on:
push:
paths:
- '**' # 修改为实际监听路径
tags:
- 'v*' # 修改为实际 tag 格式
env:
SERVICE_PREFIX: your-service # 修改为实际服务名
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_DIST_SERVER: https://rsproxy.cn
RUSTUP_UPDATE_ROOT: https://rsproxy.cn/rustup
jobs:
build-and-publish:
runs-on: ubuntu-latest # 修改为你的 Runner 标签
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: |
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
source "$HOME/.cargo/env"
rustup target add x86_64-unknown-linux-musl
- name: Build
run: |
cargo build --release --target x86_64-unknown-linux-musl
cp target/x86_64-unknown-linux-musl/release/your-binary-name .
- name: Docker Login
uses: docker/login-action@v3
with:
registry: ${{ github.server_url }}
username: ${{ vars.REGISTRY_USERNAME }}
password: ${{ secrets.RELEASE_TOKEN }}
- name: Build & Push Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.ci
push: true
tags: |
${{ github.server_url }}/${{ github.repository }}:latest
${{ github.server_url }}/${{ github.repository }}:${{ github.sha }}
```
**修改项**
- `your-service` → 你的服务名
- `your-binary-name` → 你的二进制文件名
- `ubuntu-latest` → 你的 Runner 标签
- `paths` → 触发构建的路径
- `tags` → 触发构建的 tag 格式
### 3. 推送代码并打 tag
```bash
git add .gitea Dockerfile.ci
git commit -m "ci: 添加 CI/CD 配置"
git push
# 触发构建
git tag v0.1.0
git push origin v0.1.0
```
## 进阶配置
### 添加交叉编译支持
如果你的 Runner 是 ARM64但需要构建 x86_64 镜像:
```yaml
- name: Install Zig and cargo-zigbuild
run: |
wget -q https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz
tar -xf zig-linux-aarch64-0.13.0.tar.xz
mv zig-linux-aarch64-0.13.0 /opt/zig
echo "/opt/zig" >> $GITHUB_PATH
cargo install cargo-zigbuild
- name: Build
run: |
cargo zigbuild --release --target x86_64-unknown-linux-musl
```
### 添加 Rust 缓存
```yaml
- name: Cache Rust dependencies
uses: https://github.com/Swatinem/rust-cache@v2
with:
workspaces: . -> target
cache-targets: true
```
### 添加版本号注入
**1. 创建 `build.rs`**
```rust
fn main() {
if let Ok(git_tag) = std::env::var("GIT_TAG") {
println!("cargo:rustc-env=GIT_TAG={}", git_tag);
}
println!("cargo:rerun-if-env-changed=GIT_TAG");
}
```
**2. 在 workflow 中设置环境变量**
```yaml
- name: Build
env:
GIT_TAG: ${{ github.ref_name }}
run: cargo build --release
```
**3. 在代码中使用**
```rust
let version = option_env!("GIT_TAG").unwrap_or("dev");
println!("Version: {}", version);
```
### 添加 Release 创建
```yaml
release:
name: Create Release
runs-on: ubuntu-latest
needs: build-and-publish
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Create Release
run: |
curl -X POST "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${{ github.ref_name }}\",
\"name\": \"Release ${{ github.ref_name }}\",
\"body\": \"自动构建的 Release\"
}"
```
## 健康检查端点实现
如果你的服务还没有健康检查端点,快速添加:
### 使用 Axum
```rust
use axum::{Router, routing::get};
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/healthz", get(|| async { "OK" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:9090")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
```
### 使用 Actix-web
```rust
use actix_web::{web, App, HttpServer, HttpResponse};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/healthz", web::get().to(|| async { HttpResponse::Ok().body("OK") }))
})
.bind("0.0.0.0:9090")?
.run()
.await
}
```
### 使用 Rocket
```rust
#[macro_use] extern crate rocket;
#[get("/healthz")]
fn healthz() -> &'static str {
"OK"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![healthz])
}
```
## Cargo.toml 优化配置
```toml
[profile.release]
opt-level = 3 # 最高优化级别
lto = true # 链接时优化
codegen-units = 1 # 单个代码生成单元
strip = true # 去除符号表
panic = 'abort' # panic 时直接退出
```
## .dockerignore 示例
创建 `.dockerignore` 减小构建上下文:
```
target/
.git/
.gitignore
*.md
tests/
benches/
examples/
.github/
.gitea/workflows/
Dockerfile*
docker-compose.yml
```
## 环境变量配置
### 在 workflow 中
```yaml
env:
RUST_LOG: info # 日志级别
CONFIG_FILE: production.yaml # 配置文件
DATABASE_URL: ${{ secrets.DATABASE_URL }} # 数据库连接
```
### 在 Dockerfile 中
```dockerfile
ENV RUST_LOG=info
ENV CONFIG_FILE=production.yaml
```
### 在运行时
```bash
docker run -e RUST_LOG=debug -e CONFIG_FILE=custom.yaml your-image
```
## 常见 Runner 标签
| 标签 | 说明 |
|------|------|
| `ubuntu-latest` | Ubuntu 最新版本 |
| `darwin-arm64` | macOS ARM64 |
| `darwin-amd64` | macOS x86_64 |
| `linux-amd64` | Linux x86_64 |
| `linux-arm64` | Linux ARM64 |
查看可用 Runner
```bash
/gitea-list-runners
```
## 触发条件参考
### 推送到特定分支
```yaml
on:
push:
branches:
- main
- develop
```
### 推送特定路径
```yaml
on:
push:
paths:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
```
### 特定 tag 格式
```yaml
on:
push:
tags:
- 'v*' # v1.0.0, v2.1.3
- 'device-rs-*' # device-rs-1.0.0
```
### Pull Request
```yaml
on:
pull_request:
branches:
- main
```
### 定时触发
```yaml
on:
schedule:
- cron: '0 2 * * *' # 每天凌晨 2 点
```
## 调试技巧
### 查看 workflow 日志
在 Gitea 仓库页面:
1. 点击 "Actions"
2. 选择对应的 workflow run
3. 查看每个 step 的日志
### 本地测试构建
```bash
# 测试 Rust 构建
cargo build --release --target x86_64-unknown-linux-musl
# 测试 Docker 构建
docker build -f Dockerfile.ci -t test:latest .
# 测试容器运行
docker run -p 9090:9090 test:latest
```
### 使用 act 本地运行 workflow
```bash
# 安装 act如果使用 Gitea Actions
# 注意act 主要支持 GitHub ActionsGitea Actions 可能有兼容性问题
# 列出可用的 actions
act -l
# 运行特定 job
act -j build-and-publish
```
## 完整配置示例
参考 BMS 项目的 device-rs
- Workflow: [device-rs.yml](https://git.voson.top/tianchu/bms/src/branch/main/.gitea/workflows/device-rs.yml)
- Dockerfile: [Dockerfile.ci](https://git.voson.top/tianchu/bms/src/branch/main/device-rs/Dockerfile.ci)
- 项目结构: [device-rs/](https://git.voson.top/tianchu/bms/src/branch/main/device-rs)
## 故障排查清单
### 构建失败
- [ ] 检查 Rust 版本是否满足要求
- [ ] 检查依赖是否正确(`Cargo.lock` 是否提交)
- [ ] 检查 musl 目标是否安装(`rustup target list --installed`
- [ ] 查看完整构建日志
### Docker 推送失败
- [ ] 检查 Docker Registry 地址是否正确
- [ ] 检查 `REGISTRY_USERNAME` 变量是否设置
- [ ] 检查 `RELEASE_TOKEN` secret 是否正确
- [ ] 检查 Token 权限(需要 packages write 权限)
### 容器启动失败
- [ ] 检查二进制文件名是否正确
- [ ] 检查端口是否正确
- [ ] 检查健康检查端点是否存在
- [ ] 查看容器日志(`docker logs <container>`
### Runner 无法连接
- [ ] 检查 Runner 是否在运行(`/gitea-list-runners`
- [ ] 检查 Runner labels 是否匹配 workflow 中的 `runs-on`
- [ ] 检查网络连接
- [ ] 查看 Runner 日志(`~/.config/gitea/runners/<runner-name>/*.log`
## 更多资源
- [完整 Workflow 模板](./workflow-templates/rust-backend.md)
- [Dockerfile 模板详解](./dockerfile-templates/rust-service.md)
- [Gitea Actions 文档](https://docs.gitea.com/usage/actions/quickstart)
- [Rust CI 最佳实践](https://doc.rust-lang.org/cargo/guide/continuous-integration.html)

File diff suppressed because it is too large Load Diff