1752 lines
44 KiB
Markdown
1752 lines
44 KiB
Markdown
# 🚀 Gitea Runner Docker 部署完整教程
|
||
|
||
## 📋 目录
|
||
|
||
- [简介](#-简介)
|
||
- [版本选择](#-版本选择)
|
||
- [版本 1:标准版](#-版本-1标准版推荐)
|
||
- [版本 2:Buildx 多架构版](#-版本-2buildx-多架构版)
|
||
- [版本对比](#️-版本对比)
|
||
- [常见问题](#-常见问题)
|
||
|
||
---
|
||
|
||
## 💡 简介
|
||
|
||
Gitea Runner 是 Gitea 的 CI/CD 执行器,类似于 GitLab Runner 或 GitHub Actions Runner,用于执行 Gitea Actions 工作流。
|
||
|
||
---
|
||
|
||
## 🎯 版本选择
|
||
|
||
### 📦 版本 1:标准版(推荐)
|
||
|
||
**适合场景:**
|
||
|
||
- ✅ 大多数日常使用场景
|
||
- ✅ 只需在 arm64 架构上运行应用
|
||
- ✅ 简单的 CI/CD 流程
|
||
- ✅ 不需要构建多架构容器镜像
|
||
|
||
**特点:**
|
||
|
||
- 🪶 轻量级,镜像体积约 400MB
|
||
- ⚡ 配置简单,启动快速(5-10 秒)
|
||
- 🔒 无需特权模式,更安全
|
||
- 🎯 易于维护
|
||
|
||
### 🚀 版本 2:Buildx 多架构版
|
||
|
||
**适合场景:**
|
||
|
||
- ✅ 需要构建 arm64 + amd64 双架构镜像
|
||
- ✅ 发布容器镜像到公共仓库
|
||
- ✅ 跨平台应用开发和测试
|
||
- ✅ 高级 CI/CD 需求
|
||
|
||
**特点:**
|
||
|
||
- 🌐 支持 Docker Buildx 多架构构建
|
||
- 🔧 内置 QEMU 跨架构模拟
|
||
- 🤖 自动配置 Buildx builder
|
||
- ⚠️ 需要特权模式和 Docker socket
|
||
|
||
---
|
||
|
||
## 📦 版本 1:标准版(推荐)
|
||
|
||
### 📂 目录结构
|
||
|
||
```txt
|
||
gitea-runner/
|
||
├── Dockerfile # 容器构建文件
|
||
├── docker-compose.yml # Docker Compose 配置
|
||
├── entrypoint.sh # 容器启动脚本
|
||
├── setup.sh # Runner 安装脚本
|
||
├── register.sh # Runner 注册脚本
|
||
├── manage.sh # Runner 管理脚本
|
||
└── runner-data/ # 数据持久化目录(自动创建)
|
||
└── runners/ # 多个 runner 存储目录
|
||
├── runner-1/
|
||
│ ├── .runner
|
||
│ ├── config.yaml
|
||
│ └── cache/
|
||
└── ...
|
||
```
|
||
|
||
### 📝 文件配置
|
||
|
||
#### 1️⃣ Dockerfile
|
||
|
||
创建 `Dockerfile` 文件:
|
||
|
||
```dockerfile
|
||
FROM ubuntu:22.04
|
||
|
||
# 设置环境变量避免交互式安装
|
||
ENV DEBIAN_FRONTEND=noninteractive
|
||
|
||
# 更新系统并安装必要软件
|
||
RUN apt-get update && apt-get install -y \
|
||
curl \
|
||
git \
|
||
python3 \
|
||
python3-yaml \
|
||
supervisor \
|
||
ca-certificates \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
|
||
# 创建必要目录
|
||
RUN mkdir -p /data /etc/supervisor/conf.d /var/log/supervisor
|
||
|
||
# 设置工作目录
|
||
WORKDIR /data
|
||
|
||
# 使用自定义入口点
|
||
ENTRYPOINT ["/data/entrypoint.sh"]
|
||
```
|
||
|
||
#### 2️⃣ docker-compose.yml
|
||
|
||
创建 `docker-compose.yml` 文件:
|
||
|
||
```yaml
|
||
services:
|
||
gitea-runner:
|
||
build: .
|
||
container_name: gitea-runner
|
||
restart: unless-stopped
|
||
volumes:
|
||
- ./runner-data:/data
|
||
- ./setup.sh:/data/setup.sh:ro
|
||
- ./register.sh:/data/register.sh:ro
|
||
- ./manage.sh:/data/manage.sh:ro
|
||
- ./entrypoint.sh:/data/entrypoint.sh:ro
|
||
|
||
# 如果需要在容器内运行 Docker,取消下面的注释
|
||
# - /var/run/docker.sock:/var/run/docker.sock
|
||
|
||
environment:
|
||
- TZ=Asia/Shanghai
|
||
|
||
# 如果需要使用代理,取消下面的注释并修改端口
|
||
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
||
# - http_proxy=http://host.docker.internal:20122
|
||
# - https_proxy=http://host.docker.internal:20122
|
||
# - HTTP_PROXY=http://host.docker.internal:20122
|
||
# - HTTPS_PROXY=http://host.docker.internal:20122
|
||
# - no_proxy=localhost,127.0.0.1
|
||
|
||
# Linux 系统需要取消下面的注释以支持 host.docker.internal
|
||
# extra_hosts:
|
||
# - "host.docker.internal:host-gateway"
|
||
```
|
||
|
||
#### 3️⃣ entrypoint.sh
|
||
|
||
创建 `entrypoint.sh` 文件:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
echo "==================================="
|
||
echo "Gitea Runner Container Starting..."
|
||
echo "==================================="
|
||
|
||
# 定义路径
|
||
PERSISTENT_BIN="/data/bin"
|
||
RUNNER_PATH="$PERSISTENT_BIN/act_runner"
|
||
SYSTEM_LINK="/usr/local/bin/act_runner"
|
||
|
||
# 创建必要目录
|
||
mkdir -p /data/runners
|
||
mkdir -p "$PERSISTENT_BIN"
|
||
mkdir -p /var/log/supervisor
|
||
mkdir -p /var/run
|
||
|
||
# 创建主 supervisor 配置文件
|
||
cat > /etc/supervisor/supervisord.conf <<EOF
|
||
[supervisord]
|
||
nodaemon=true
|
||
logfile=/var/log/supervisor/supervisord.log
|
||
pidfile=/var/run/supervisord.pid
|
||
|
||
[unix_http_server]
|
||
file=/var/run/supervisor.sock
|
||
chmod=0700
|
||
|
||
[supervisorctl]
|
||
serverurl=unix:///var/run/supervisor.sock
|
||
|
||
[rpcinterface:supervisor]
|
||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||
|
||
[include]
|
||
files = /etc/supervisor/conf.d/*.conf
|
||
EOF
|
||
|
||
# 检查持久化目录中的 act_runner
|
||
if [ -f "$RUNNER_PATH" ]; then
|
||
echo "✓ Found act_runner in persistent storage"
|
||
echo " Location: $RUNNER_PATH"
|
||
|
||
# 创建软链接到系统路径(如果不存在)
|
||
if [ ! -L "$SYSTEM_LINK" ] || [ ! -e "$SYSTEM_LINK" ]; then
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo " Created system link: $SYSTEM_LINK -> $RUNNER_PATH"
|
||
fi
|
||
|
||
# 验证版本
|
||
RUNNER_VERSION=$("$SYSTEM_LINK" --version 2>/dev/null || echo "unknown")
|
||
echo " Version: $RUNNER_VERSION"
|
||
elif [ -f "$SYSTEM_LINK" ]; then
|
||
# 旧版本可能在系统路径,迁移到持久化目录
|
||
echo "⚠ Found act_runner in system path, migrating to persistent storage..."
|
||
cp "$SYSTEM_LINK" "$RUNNER_PATH"
|
||
chmod +x "$RUNNER_PATH"
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo " ✓ Migrated to: $RUNNER_PATH"
|
||
else
|
||
# 没有找到 act_runner
|
||
echo "⚠ act_runner not installed yet!"
|
||
echo ""
|
||
echo "Please run the setup script first:"
|
||
echo " docker-compose exec gitea-runner /data/setup.sh"
|
||
echo ""
|
||
echo "Container is waiting..."
|
||
|
||
# 等待 act_runner 安装
|
||
while [ ! -f "$RUNNER_PATH" ] && [ ! -f "$SYSTEM_LINK" ]; do
|
||
sleep 10
|
||
done
|
||
|
||
# 再次检查并创建链接
|
||
if [ -f "$RUNNER_PATH" ]; then
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo "✓ act_runner detected and linked!"
|
||
elif [ -f "$SYSTEM_LINK" ]; then
|
||
cp "$SYSTEM_LINK" "$RUNNER_PATH"
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo "✓ act_runner detected and migrated!"
|
||
fi
|
||
fi
|
||
|
||
# 为每个已注册的 runner 创建 supervisor 配置
|
||
echo ""
|
||
echo "Scanning for registered runners..."
|
||
RUNNER_COUNT=0
|
||
|
||
if [ -d "/data/runners" ]; then
|
||
for runner_dir in /data/runners/*/; do
|
||
if [ -d "$runner_dir" ]; then
|
||
runner_name=$(basename "$runner_dir")
|
||
|
||
if [ -f "$runner_dir/.runner" ] && [ -f "$runner_dir/config.yaml" ]; then
|
||
echo "Found runner: $runner_name"
|
||
|
||
# 创建该 runner 的 supervisor 配置
|
||
cat > "/etc/supervisor/conf.d/runner-${runner_name}.conf" <<EOF
|
||
[program:runner-${runner_name}]
|
||
command=/usr/local/bin/act_runner daemon --config ${runner_dir}/config.yaml
|
||
directory=${runner_dir}
|
||
autostart=true
|
||
autorestart=true
|
||
stderr_logfile=/var/log/supervisor/runner-${runner_name}.err.log
|
||
stdout_logfile=/var/log/supervisor/runner-${runner_name}.out.log
|
||
user=root
|
||
environment=HOME="/root"
|
||
EOF
|
||
RUNNER_COUNT=$((RUNNER_COUNT + 1))
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
if [ $RUNNER_COUNT -eq 0 ]; then
|
||
echo "⚠ No runners registered yet!"
|
||
echo ""
|
||
echo "Please run the register script to add a runner:"
|
||
echo " docker-compose exec gitea-runner /data/register.sh"
|
||
echo ""
|
||
fi
|
||
|
||
echo "Total runners configured: $RUNNER_COUNT"
|
||
echo ""
|
||
echo "==================================="
|
||
echo "Starting Supervisor..."
|
||
echo "==================================="
|
||
|
||
# 启动 supervisord(使用主配置文件)
|
||
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||
```
|
||
|
||
#### 4️⃣ setup.sh
|
||
|
||
创建 `setup.sh` 文件:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
echo "=========================================="
|
||
echo " Gitea Runner Installation Script "
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# 持久化安装路径
|
||
INSTALL_PATH="/data/bin/act_runner"
|
||
SYSTEM_PATH="/usr/local/bin/act_runner"
|
||
|
||
# 创建目录
|
||
mkdir -p /data/bin
|
||
|
||
# 检查是否已安装
|
||
if [ -f "$INSTALL_PATH" ]; then
|
||
CURRENT_VERSION=$($INSTALL_PATH --version 2>/dev/null | grep -oP 'version \K[0-9.]+' || echo "unknown")
|
||
echo "⚠ act_runner already installed (version: $CURRENT_VERSION)"
|
||
echo " Location: $INSTALL_PATH"
|
||
echo ""
|
||
read -p "Do you want to reinstall/upgrade? (y/N): " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
echo "Installation cancelled."
|
||
exit 0
|
||
fi
|
||
rm -f "$INSTALL_PATH" "$SYSTEM_PATH"
|
||
fi
|
||
|
||
# 获取要安装的版本
|
||
echo "Available versions: https://dl.gitea.com/act_runner/"
|
||
echo ""
|
||
read -p "Enter version to install (default: 0.2.13): " RUNNER_VERSION
|
||
RUNNER_VERSION=${RUNNER_VERSION:-0.2.13}
|
||
|
||
ARCH=$(uname -m)
|
||
case "$ARCH" in
|
||
x86_64)
|
||
RUNNER_ARCH="amd64"
|
||
;;
|
||
aarch64|arm64)
|
||
RUNNER_ARCH="arm64"
|
||
;;
|
||
armv7l)
|
||
RUNNER_ARCH="arm-7"
|
||
;;
|
||
*)
|
||
echo "⚠ Unknown architecture: $ARCH"
|
||
RUNNER_ARCH="arm64"
|
||
;;
|
||
esac
|
||
|
||
# 确认架构
|
||
echo ""
|
||
echo "Detected architecture: $ARCH -> $RUNNER_ARCH"
|
||
read -p "Is this correct? (Y/n): " -n 1 -r
|
||
echo
|
||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||
echo ""
|
||
echo "Available architectures:"
|
||
echo " 1. amd64 (x86_64)"
|
||
echo " 2. arm64 (aarch64)"
|
||
echo " 3. arm-7 (armv7l)"
|
||
read -p "Select architecture (1-3): " ARCH_CHOICE
|
||
case "$ARCH_CHOICE" in
|
||
1) RUNNER_ARCH="amd64" ;;
|
||
2) RUNNER_ARCH="arm64" ;;
|
||
3) RUNNER_ARCH="arm-7" ;;
|
||
*)
|
||
echo "Invalid choice. Exiting."
|
||
exit 1
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
DOWNLOAD_URL="https://dl.gitea.com/act_runner/${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-${RUNNER_ARCH}"
|
||
|
||
echo ""
|
||
echo "Download Configuration:"
|
||
echo " Version: $RUNNER_VERSION"
|
||
echo " Architecture: $RUNNER_ARCH"
|
||
echo " URL: $DOWNLOAD_URL"
|
||
echo " Install Location: $INSTALL_PATH (persistent)"
|
||
echo ""
|
||
read -p "Proceed with download? (Y/n): " -n 1 -r
|
||
echo
|
||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||
echo "Installation cancelled."
|
||
exit 0
|
||
fi
|
||
|
||
echo ""
|
||
echo "Downloading act_runner..."
|
||
echo ""
|
||
|
||
# 下载到持久化目录
|
||
if curl -L "$DOWNLOAD_URL" -o "$INSTALL_PATH"; then
|
||
chmod +x "$INSTALL_PATH"
|
||
|
||
# 同时创建软链接到系统路径
|
||
ln -sf "$INSTALL_PATH" "$SYSTEM_PATH"
|
||
|
||
# 验证安装
|
||
if $INSTALL_PATH --version; then
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "✓ act_runner installed successfully!"
|
||
echo "=========================================="
|
||
echo ""
|
||
echo "Version: $($INSTALL_PATH --version)"
|
||
echo "Location: $INSTALL_PATH (persistent storage)"
|
||
echo ""
|
||
echo "Next steps:"
|
||
echo "1. Register the runner:"
|
||
echo " docker-compose exec gitea-runner /data/register.sh"
|
||
echo ""
|
||
echo "2. Restart the container:"
|
||
echo " docker-compose restart"
|
||
echo ""
|
||
echo "Note: act_runner is saved in persistent storage"
|
||
echo " and will survive container restarts."
|
||
echo ""
|
||
else
|
||
echo ""
|
||
echo "✗ Installation verification failed!"
|
||
rm -f "$INSTALL_PATH" "$SYSTEM_PATH"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo ""
|
||
echo "✗ Download failed!"
|
||
echo "Please check:"
|
||
echo " - Internet connection"
|
||
echo " - Version number is correct: $RUNNER_VERSION"
|
||
echo " - Architecture is correct: $RUNNER_ARCH"
|
||
echo " - URL is accessible: $DOWNLOAD_URL"
|
||
echo ""
|
||
echo "You can check available versions at:"
|
||
echo " https://dl.gitea.com/act_runner/"
|
||
exit 1
|
||
fi
|
||
```
|
||
|
||
#### 5️⃣ register.sh
|
||
|
||
创建 `register.sh` 文件:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
echo "=========================================="
|
||
echo " Gitea Runner Registration Script "
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# 检查 act_runner 是否安装
|
||
if ! command -v act_runner &> /dev/null; then
|
||
echo "✗ act_runner is not installed!"
|
||
echo ""
|
||
echo "Please run the setup script first:"
|
||
echo " docker-compose exec gitea-runner /data/setup.sh"
|
||
exit 1
|
||
fi
|
||
|
||
echo "✓ act_runner found: $(act_runner --version)"
|
||
echo ""
|
||
|
||
# 获取注册信息并验证
|
||
while true; do
|
||
read -p "Enter Gitea instance URL (e.g., https://gitea.example.com): " GITEA_INSTANCE
|
||
|
||
# 验证 URL 格式
|
||
if [[ ! "$GITEA_INSTANCE" =~ ^https?:// ]]; then
|
||
echo "✗ Error: URL must start with http:// or https://"
|
||
echo ""
|
||
continue
|
||
fi
|
||
|
||
# 移除末尾的斜杠
|
||
GITEA_INSTANCE="${GITEA_INSTANCE%/}"
|
||
echo "✓ URL validated: $GITEA_INSTANCE"
|
||
break
|
||
done
|
||
|
||
read -p "Enter registration token: " GITEA_TOKEN
|
||
|
||
if [ -z "$GITEA_TOKEN" ]; then
|
||
echo "✗ Error: Token cannot be empty!"
|
||
exit 1
|
||
fi
|
||
|
||
read -p "Enter runner name (default: docker-runner): " RUNNER_NAME
|
||
RUNNER_NAME=${RUNNER_NAME:-docker-runner}
|
||
|
||
# 多个 label(逗号分隔,无空格)
|
||
# ubuntu-22.04:host://ubuntu:22.04,ubuntu-20.04:host://ubuntu:20.04,node:docker://node:18
|
||
read -p "Enter runner labels (default: ubuntu-22.04:docker://ubuntu:22.04): " RUNNER_LABELS
|
||
RUNNER_LABELS=${RUNNER_LABELS:-ubuntu-22.04:host://ubuntu:22.04}
|
||
|
||
# 创建 runner 目录
|
||
RUNNER_DIR="/data/runners/${RUNNER_NAME}"
|
||
mkdir -p "$RUNNER_DIR"
|
||
cd "$RUNNER_DIR"
|
||
|
||
echo ""
|
||
echo "Registration Information:"
|
||
echo " Instance: $GITEA_INSTANCE"
|
||
echo " Name: $RUNNER_NAME"
|
||
echo " Labels: $RUNNER_LABELS"
|
||
echo " Directory: $RUNNER_DIR"
|
||
echo ""
|
||
|
||
# 检查是否已经注册
|
||
if [ -f ".runner" ] || [ -f "config.yaml" ]; then
|
||
echo "⚠ Runner already exists in this directory!"
|
||
read -p "Do you want to re-register? This will overwrite existing configuration. (y/N): " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
echo "Registration cancelled."
|
||
exit 0
|
||
fi
|
||
|
||
echo "Cleaning up existing runner..."
|
||
|
||
# 停止现有 runner
|
||
echo "Stopping existing runner..."
|
||
supervisorctl stop "runner-${RUNNER_NAME}" 2>/dev/null || true
|
||
|
||
# 删除 supervisor 配置
|
||
rm -f "/etc/supervisor/conf.d/runner-${RUNNER_NAME}.conf"
|
||
|
||
# 重新加载 supervisor
|
||
supervisorctl reread 2>/dev/null || true
|
||
supervisorctl update 2>/dev/null || true
|
||
|
||
# 删除日志
|
||
rm -f "/var/log/supervisor/runner-${RUNNER_NAME}".*.log*
|
||
|
||
# 删除旧配置和缓存
|
||
rm -f .runner config.yaml
|
||
rm -rf cache
|
||
fi
|
||
|
||
# 执行注册
|
||
echo ""
|
||
echo "Registering runner..."
|
||
act_runner register \
|
||
--instance "$GITEA_INSTANCE" \
|
||
--token "$GITEA_TOKEN" \
|
||
--name "$RUNNER_NAME" \
|
||
--labels "$RUNNER_LABELS" \
|
||
--no-interactive
|
||
|
||
if [ ! -f ".runner" ]; then
|
||
echo ""
|
||
echo "✗ Registration failed! .runner file not created."
|
||
exit 1
|
||
fi
|
||
|
||
echo "✓ Registration successful!"
|
||
|
||
# 生成配置文件
|
||
echo ""
|
||
echo "Generating config.yaml..."
|
||
act_runner generate-config > config.yaml
|
||
|
||
echo "✓ Configuration file generated!"
|
||
|
||
# 创建缓存目录
|
||
mkdir -p cache
|
||
|
||
# 使用 Python 修改配置(最可靠的方法)
|
||
echo ""
|
||
echo "Configuring runner settings..."
|
||
|
||
if command -v python3 &> /dev/null; then
|
||
python3 << PYEOF
|
||
import yaml
|
||
import sys
|
||
|
||
try:
|
||
# 读取生成的配置
|
||
with open('config.yaml', 'r') as f:
|
||
config = yaml.safe_load(f)
|
||
|
||
# 读取 .runner 获取实际注册的 labels
|
||
import json
|
||
with open('.runner', 'r') as f:
|
||
runner_data = json.load(f)
|
||
|
||
# 使用 .runner 中的 labels(这是实际注册的)
|
||
registered_labels = runner_data.get('labels', [])
|
||
|
||
# 修改配置
|
||
if 'runner' not in config:
|
||
config['runner'] = {}
|
||
|
||
# 使用实际注册的 labels
|
||
config['runner']['labels'] = registered_labels
|
||
config['runner']['capacity'] = 2
|
||
|
||
# 启用缓存
|
||
if 'cache' not in config:
|
||
config['cache'] = {}
|
||
config['cache']['enabled'] = True
|
||
config['cache']['dir'] = './cache'
|
||
|
||
# 保存配置
|
||
with open('config.yaml', 'w') as f:
|
||
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
||
|
||
print("✓ Configuration updated using Python")
|
||
print(f" - Labels: {registered_labels}")
|
||
print(f" - Capacity: 2")
|
||
print(f" - Cache enabled: ./cache")
|
||
sys.exit(0)
|
||
|
||
except Exception as e:
|
||
print(f"✗ Python configuration failed: {e}", file=sys.stderr)
|
||
sys.exit(1)
|
||
PYEOF
|
||
|
||
PYTHON_EXIT=$?
|
||
|
||
if [ $PYTHON_EXIT -ne 0 ]; then
|
||
echo ""
|
||
echo "⚠ Python configuration failed, using basic sed..."
|
||
|
||
# 基本的 sed 修改(只修改简单的值,不动 labels)
|
||
sed -i 's/capacity: 1/capacity: 2/g' config.yaml || true
|
||
sed -i 's/enabled: false/enabled: true/g' config.yaml || true
|
||
sed -i 's|dir: ""|dir: ./cache|g' config.yaml || true
|
||
|
||
echo "✓ Basic configuration applied"
|
||
echo " Note: Please manually verify labels in config.yaml match .runner"
|
||
fi
|
||
else
|
||
echo "⚠ Python3 not found, applying basic configuration..."
|
||
|
||
# 基本的 sed 修改
|
||
sed -i 's/capacity: 1/capacity: 2/g' config.yaml || true
|
||
sed -i 's/enabled: false/enabled: true/g' config.yaml || true
|
||
sed -i 's|dir: ""|dir: ./cache|g' config.yaml || true
|
||
|
||
echo "✓ Basic configuration applied"
|
||
echo " Note: Labels will use act_runner defaults"
|
||
fi
|
||
|
||
# 验证配置文件
|
||
echo ""
|
||
echo "Validating configuration..."
|
||
|
||
# 检查 YAML 语法
|
||
if command -v python3 &> /dev/null; then
|
||
python3 << PYEOF
|
||
import yaml
|
||
import sys
|
||
try:
|
||
with open('config.yaml', 'r') as f:
|
||
yaml.safe_load(f)
|
||
print("✓ config.yaml syntax is valid")
|
||
sys.exit(0)
|
||
except Exception as e:
|
||
print(f"✗ config.yaml syntax error: {e}", file=sys.stderr)
|
||
sys.exit(1)
|
||
PYEOF
|
||
|
||
if [ $? -ne 0 ]; then
|
||
echo ""
|
||
echo "✗ Configuration file has syntax errors!"
|
||
echo " Backup available at: config.yaml.bak"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# 显示配置摘要
|
||
echo ""
|
||
echo "Configuration Summary:"
|
||
echo "-------------------------------------------"
|
||
echo ".runner labels:"
|
||
cat .runner | grep -A 10 '"labels"' | head -15
|
||
echo ""
|
||
echo "config.yaml labels:"
|
||
grep -A 5 "^ labels:" config.yaml | head -10
|
||
|
||
# 创建 supervisor 配置
|
||
echo ""
|
||
echo "Creating supervisor configuration..."
|
||
cat > "/etc/supervisor/conf.d/runner-${RUNNER_NAME}.conf" <<EOF
|
||
[program:runner-${RUNNER_NAME}]
|
||
command=/usr/local/bin/act_runner daemon --config ${RUNNER_DIR}/config.yaml
|
||
directory=${RUNNER_DIR}
|
||
autostart=true
|
||
autorestart=true
|
||
stderr_logfile=/var/log/supervisor/runner-${RUNNER_NAME}.err.log
|
||
stdout_logfile=/var/log/supervisor/runner-${RUNNER_NAME}.out.log
|
||
stdout_logfile_maxbytes=50MB
|
||
stdout_logfile_backups=10
|
||
stderr_logfile_maxbytes=50MB
|
||
stderr_logfile_backups=10
|
||
user=root
|
||
environment=HOME="/root"
|
||
EOF
|
||
|
||
echo "✓ Supervisor configuration created"
|
||
|
||
# 重新加载 supervisor
|
||
echo ""
|
||
echo "Reloading supervisor..."
|
||
supervisorctl reread
|
||
supervisorctl update
|
||
|
||
echo "Starting runner..."
|
||
sleep 2
|
||
# 启动 runner
|
||
supervisorctl restart "runner-${RUNNER_NAME}" 2>/dev/null || \
|
||
supervisorctl start "runner-${RUNNER_NAME}"
|
||
|
||
# 等待启动
|
||
sleep 3
|
||
|
||
# 显示状态
|
||
RUNNER_STATUS=$(supervisorctl status "runner-${RUNNER_NAME}" 2>/dev/null || echo "UNKNOWN")
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "✓ Runner registered and started!"
|
||
echo "=========================================="
|
||
echo ""
|
||
echo "Runner Information:"
|
||
echo " Name: $RUNNER_NAME"
|
||
echo " Directory: $RUNNER_DIR"
|
||
echo " Status: $RUNNER_STATUS"
|
||
echo ""
|
||
echo "Configuration files:"
|
||
echo " .runner: $(ls -lh .runner 2>/dev/null | awk '{print $5}' || echo 'N/A')"
|
||
echo " config.yaml: $(ls -lh config.yaml 2>/dev/null | awk '{print $5}' || echo 'N/A')"
|
||
echo ""
|
||
echo "Useful commands:"
|
||
echo " View logs: docker-compose exec gitea-runner /data/manage.sh logs ${RUNNER_NAME}"
|
||
echo " Follow logs: docker-compose exec gitea-runner /data/manage.sh follow ${RUNNER_NAME}"
|
||
echo " Check status: docker-compose exec gitea-runner /data/manage.sh status"
|
||
echo " Restart: docker-compose exec gitea-runner /data/manage.sh restart ${RUNNER_NAME}"
|
||
echo ""
|
||
|
||
# 显示最近的日志
|
||
if [ -f "/var/log/supervisor/runner-${RUNNER_NAME}.out.log" ]; then
|
||
echo "Recent logs:"
|
||
echo "-------------------------------------------"
|
||
tail -n 20 "/var/log/supervisor/runner-${RUNNER_NAME}.out.log" 2>/dev/null || echo "No logs yet"
|
||
echo ""
|
||
fi
|
||
|
||
# 检查是否有错误
|
||
if [ -f "/var/log/supervisor/runner-${RUNNER_NAME}.err.log" ]; then
|
||
# 只查找 error/fatal/panic 级别的日志
|
||
ERR_CONTENT=$(grep -E 'level=(error|fatal|panic)' \
|
||
"/var/log/supervisor/runner-${RUNNER_NAME}.err.log" | tail -n 5 2>/dev/null)
|
||
|
||
if [ -n "$ERR_CONTENT" ]; then
|
||
echo "❌ Recent errors detected:"
|
||
echo "-------------------------------------------"
|
||
echo "$ERR_CONTENT"
|
||
echo ""
|
||
echo "Check full error log with:"
|
||
echo " docker-compose exec gitea-runner cat /var/log/supervisor/runner-${RUNNER_NAME}.err.log"
|
||
fi
|
||
fi
|
||
```
|
||
|
||
#### 6️⃣ manage.sh
|
||
|
||
创建 `manage.sh` 文件:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
echo "=========================================="
|
||
echo " Gitea Runner Management Tool "
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# 函数:列出所有 runners
|
||
list_runners() {
|
||
echo "Registered Runners:"
|
||
echo "-------------------------------------------"
|
||
|
||
if [ ! -d "/data/runners" ] || [ -z "$(ls -A /data/runners 2>/dev/null)" ]; then
|
||
echo "No runners registered yet."
|
||
return
|
||
fi
|
||
|
||
printf "%-20s %-15s %-30s\n" "Name" "Status" "Log File"
|
||
echo "-------------------------------------------"
|
||
|
||
for runner_dir in /data/runners/*/; do
|
||
if [ -d "$runner_dir" ]; then
|
||
runner_name=$(basename "$runner_dir")
|
||
|
||
if [ -f "$runner_dir/.runner" ]; then
|
||
# 获取状态
|
||
status=$(supervisorctl status "runner-${runner_name}" 2>/dev/null | awk '{print $2}')
|
||
[ -z "$status" ] && status="NOT_LOADED"
|
||
|
||
log_file="/var/log/supervisor/runner-${runner_name}.out.log"
|
||
|
||
printf "%-20s %-15s %-30s\n" "$runner_name" "$status" "$log_file"
|
||
fi
|
||
fi
|
||
done
|
||
echo ""
|
||
}
|
||
|
||
# 函数:查看 runner 详细信息
|
||
show_runner() {
|
||
local runner_name=$1
|
||
local runner_dir="/data/runners/${runner_name}"
|
||
|
||
if [ ! -d "$runner_dir" ]; then
|
||
echo "✗ Runner '$runner_name' not found!"
|
||
return 1
|
||
fi
|
||
|
||
echo "Runner Details: $runner_name"
|
||
echo "-------------------------------------------"
|
||
echo "Directory: $runner_dir"
|
||
|
||
if [ -f "$runner_dir/config.yaml" ]; then
|
||
echo ""
|
||
echo "Configuration:"
|
||
cat "$runner_dir/config.yaml"
|
||
fi
|
||
|
||
echo ""
|
||
echo "Status:"
|
||
supervisorctl status "runner-${runner_name}" 2>/dev/null | awk '{print $2}'
|
||
echo ""
|
||
}
|
||
|
||
# 函数:删除 runner
|
||
delete_runner() {
|
||
local runner_name=$1
|
||
local runner_dir="/data/runners/${runner_name}"
|
||
|
||
if [ ! -d "$runner_dir" ]; then
|
||
echo "✗ Runner '$runner_name' not found!"
|
||
return 1
|
||
fi
|
||
|
||
echo "⚠ Warning: This will permanently delete runner '$runner_name'"
|
||
read -p "Are you sure? (y/N): " -n 1 -r
|
||
echo
|
||
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
echo "Deletion cancelled."
|
||
return 0
|
||
fi
|
||
|
||
echo "Stopping runner..."
|
||
supervisorctl stop "runner-${runner_name}" 2>/dev/null || true
|
||
|
||
echo "Removing configuration..."
|
||
rm -f "/etc/supervisor/conf.d/runner-${runner_name}.conf"
|
||
|
||
echo "Deleting runner directory..."
|
||
rm -rf "$runner_dir"
|
||
|
||
echo "Updating supervisor..."
|
||
supervisorctl reread
|
||
supervisorctl update
|
||
|
||
echo ""
|
||
echo "✓ Runner '$runner_name' deleted successfully!"
|
||
}
|
||
|
||
# 函数:查看 runner 日志
|
||
logs_runner() {
|
||
local runner_name=$1
|
||
local lines=${2:-50}
|
||
|
||
local log_file="/var/log/supervisor/runner-${runner_name}.out.log"
|
||
|
||
if [ ! -f "$log_file" ]; then
|
||
echo "✗ Log file not found for runner '$runner_name'"
|
||
return 1
|
||
fi
|
||
|
||
echo "Showing last $lines lines of '$runner_name' logs:"
|
||
echo "-------------------------------------------"
|
||
tail -n "$lines" "$log_file"
|
||
}
|
||
|
||
# 函数:实时查看日志
|
||
follow_logs() {
|
||
local runner_name=$1
|
||
local log_file="/var/log/supervisor/runner-${runner_name}.out.log"
|
||
|
||
if [ ! -f "$log_file" ]; then
|
||
echo "✗ Log file not found for runner '$runner_name'"
|
||
return 1
|
||
fi
|
||
|
||
echo "Following logs for '$runner_name' (Press Ctrl+C to exit):"
|
||
echo "-------------------------------------------"
|
||
tail -f "$log_file"
|
||
}
|
||
|
||
# 函数:启动/停止/重启 runner
|
||
control_runner() {
|
||
local action=$1
|
||
local runner_name=$2
|
||
|
||
case $action in
|
||
start|stop|restart)
|
||
echo "${action^}ing runner '$runner_name'..."
|
||
supervisorctl "$action" "runner-${runner_name}"
|
||
;;
|
||
*)
|
||
echo "✗ Invalid action: $action"
|
||
return 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# 函数:显示所有 runner 状态
|
||
status_all() {
|
||
echo "All Runners Status:"
|
||
echo "-------------------------------------------"
|
||
supervisorctl status | grep "^runner-" || echo "No runners running."
|
||
echo ""
|
||
}
|
||
|
||
# 主菜单
|
||
show_menu() {
|
||
echo "Choose an action:"
|
||
echo " 1) List all runners"
|
||
echo " 2) Show runner details"
|
||
echo " 3) Add new runner"
|
||
echo " 4) Delete runner"
|
||
echo " 5) Start runner"
|
||
echo " 6) Stop runner"
|
||
echo " 7) Restart runner"
|
||
echo " 8) View runner logs"
|
||
echo " 9) Follow runner logs (real-time)"
|
||
echo " 10) Show all runners status"
|
||
echo " 0) Exit"
|
||
echo ""
|
||
}
|
||
|
||
# 主程序
|
||
if [ $# -eq 0 ]; then
|
||
# 交互模式
|
||
while true; do
|
||
show_menu
|
||
read -p "Enter your choice: " choice
|
||
echo ""
|
||
|
||
case $choice in
|
||
1)
|
||
list_runners
|
||
;;
|
||
2)
|
||
read -p "Enter runner name: " runner_name
|
||
show_runner "$runner_name"
|
||
;;
|
||
3)
|
||
echo "Starting registration process..."
|
||
/data/register.sh
|
||
;;
|
||
4)
|
||
read -p "Enter runner name to delete: " runner_name
|
||
delete_runner "$runner_name"
|
||
;;
|
||
5)
|
||
read -p "Enter runner name to start: " runner_name
|
||
control_runner start "$runner_name"
|
||
;;
|
||
6)
|
||
read -p "Enter runner name to stop: " runner_name
|
||
control_runner stop "$runner_name"
|
||
;;
|
||
7)
|
||
read -p "Enter runner name to restart: " runner_name
|
||
control_runner restart "$runner_name"
|
||
;;
|
||
8)
|
||
read -p "Enter runner name: " runner_name
|
||
read -p "Number of lines (default 50): " lines
|
||
logs_runner "$runner_name" "${lines:-50}"
|
||
;;
|
||
9)
|
||
read -p "Enter runner name: " runner_name
|
||
follow_logs "$runner_name"
|
||
;;
|
||
10)
|
||
status_all
|
||
;;
|
||
0)
|
||
echo "Goodbye!"
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo "Invalid choice!"
|
||
;;
|
||
esac
|
||
|
||
echo ""
|
||
read -p "Press Enter to continue..."
|
||
clear
|
||
done
|
||
else
|
||
# 命令行模式
|
||
case $1 in
|
||
list|ls)
|
||
list_runners
|
||
;;
|
||
show|info)
|
||
show_runner "$2"
|
||
;;
|
||
add|register)
|
||
/data/register.sh
|
||
;;
|
||
delete|rm|remove)
|
||
delete_runner "$2"
|
||
;;
|
||
start)
|
||
control_runner start "$2"
|
||
;;
|
||
stop)
|
||
control_runner stop "$2"
|
||
;;
|
||
restart)
|
||
control_runner restart "$2"
|
||
;;
|
||
logs)
|
||
logs_runner "$2" "${3:-50}"
|
||
;;
|
||
follow)
|
||
follow_logs "$2"
|
||
;;
|
||
status)
|
||
status_all
|
||
;;
|
||
*)
|
||
echo "Usage: $0 [command] [runner_name]"
|
||
echo ""
|
||
echo "Commands:"
|
||
echo " list - List all runners"
|
||
echo " show <name> - Show runner details"
|
||
echo " add - Add new runner"
|
||
echo " delete <name> - Delete a runner"
|
||
echo " start <name> - Start a runner"
|
||
echo " stop <name> - Stop a runner"
|
||
echo " restart <name> - Restart a runner"
|
||
echo " logs <name> [lines] - View runner logs"
|
||
echo " follow <name> - Follow runner logs (real-time)"
|
||
echo " status - Show all runners status"
|
||
echo ""
|
||
echo "Or run without arguments for interactive mode."
|
||
exit 1
|
||
;;
|
||
esac
|
||
fi
|
||
```
|
||
|
||
### 🚀 部署步骤
|
||
|
||
#### 1. 设置脚本权限
|
||
|
||
```bash
|
||
chmod +x entrypoint.sh setup.sh register.sh manage.sh
|
||
```
|
||
|
||
#### 2. 构建并启动容器
|
||
|
||
```bash
|
||
docker-compose build
|
||
docker-compose up -d
|
||
```
|
||
|
||
#### 3. 安装 Runner
|
||
|
||
```bash
|
||
docker-compose exec gitea-runner /data/setup.sh
|
||
```
|
||
|
||
按照提示选择版本和架构。
|
||
|
||
#### 4. 注册 Runner
|
||
|
||
```bash
|
||
docker-compose exec gitea-runner /data/register.sh
|
||
```
|
||
|
||
输入你的 Gitea 实例 URL 和注册令牌。
|
||
|
||
#### 5. 重启容器启动 Runner
|
||
|
||
```bash
|
||
docker-compose restart
|
||
```
|
||
|
||
#### 6. 验证运行状态
|
||
|
||
```bash
|
||
# 查看日志
|
||
docker-compose logs -f
|
||
|
||
# 查看 runner 状态
|
||
docker-compose exec gitea-runner /data/manage.sh list
|
||
docker-compose exec gitea-runner /data/manage.sh status
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 版本 2:Buildx 多架构版
|
||
|
||
### 📝 文件配置差异
|
||
|
||
Buildx 版本与标准版的主要区别在于以下文件:
|
||
|
||
#### 1️⃣ Dockerfile(Buildx 版)
|
||
|
||
```dockerfile
|
||
FROM ubuntu:22.04
|
||
|
||
# 设置环境变量避免交互式安装
|
||
ENV DEBIAN_FRONTEND=noninteractive
|
||
|
||
# 更新系统并安装必要软件
|
||
RUN apt-get update && apt-get install -y \
|
||
curl \
|
||
git \
|
||
python3 \
|
||
python3-yaml \
|
||
supervisor \
|
||
ca-certificates \
|
||
gnupg \
|
||
lsb-release \
|
||
qemu-user-static \
|
||
binfmt-support \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
|
||
# 安装 Docker(包含 Buildx 插件)
|
||
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
|
||
sh get-docker.sh && \
|
||
rm get-docker.sh
|
||
|
||
# 验证安装
|
||
RUN docker --version && \
|
||
qemu-aarch64-static --version && \
|
||
qemu-x86_64-static --version
|
||
|
||
# 创建必要目录
|
||
RUN mkdir -p /data /etc/supervisor/conf.d /var/log/supervisor
|
||
|
||
# 设置工作目录
|
||
WORKDIR /data
|
||
|
||
# 使用自定义入口点
|
||
ENTRYPOINT ["/data/entrypoint.sh"]
|
||
```
|
||
|
||
#### 2️⃣ docker-compose.yml(Buildx 版)
|
||
|
||
```yaml
|
||
services:
|
||
gitea-runner:
|
||
build: .
|
||
container_name: gitea-runner
|
||
restart: unless-stopped
|
||
privileged: true
|
||
volumes:
|
||
- ./runner-data:/data
|
||
- ./setup.sh:/data/setup.sh:ro
|
||
- ./register.sh:/data/register.sh:ro
|
||
- ./manage.sh:/data/manage.sh:ro
|
||
- ./entrypoint.sh:/data/entrypoint.sh:ro
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
|
||
environment:
|
||
- TZ=Asia/Shanghai
|
||
|
||
# 如果需要使用代理,取消下面的注释并修改为你的代理地址
|
||
# 注意:容器内访问宿主机需要使用 host.docker.internal 或宿主机IP
|
||
- http_proxy=http://host.docker.internal:20122
|
||
- https_proxy=http://host.docker.internal:20122
|
||
- HTTP_PROXY=http://host.docker.internal:20122
|
||
- HTTPS_PROXY=http://host.docker.internal:20122
|
||
# - no_proxy=localhost,127.0.0.1
|
||
|
||
# Linux 系统需要取消下面的注释以支持 host.docker.internal
|
||
# extra_hosts:
|
||
# - "host.docker.internal:host-gateway"
|
||
```
|
||
|
||
#### 3️⃣ entrypoint.sh(Buildx 版)
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
echo "==================================="
|
||
echo "Gitea Runner Container Starting..."
|
||
echo "==================================="
|
||
|
||
# 定义路径
|
||
PERSISTENT_BIN="/data/bin"
|
||
RUNNER_PATH="$PERSISTENT_BIN/act_runner"
|
||
SYSTEM_LINK="/usr/local/bin/act_runner"
|
||
|
||
# 创建必要目录
|
||
mkdir -p /data/runners
|
||
mkdir -p "$PERSISTENT_BIN"
|
||
mkdir -p /data/buildx
|
||
mkdir -p /var/log/supervisor
|
||
mkdir -p /var/run
|
||
|
||
# ============================================
|
||
# 初始化 Docker Buildx 支持
|
||
# ============================================
|
||
echo ""
|
||
echo "Initializing Docker Buildx..."
|
||
|
||
# 启动 Docker 守护进程(如果使用主机 socket 则跳过)
|
||
if [ -S /var/run/docker.sock ]; then
|
||
echo "✓ Using host Docker socket"
|
||
else
|
||
echo "Starting Docker daemon..."
|
||
dockerd > /var/log/dockerd.log 2>&1 &
|
||
sleep 5
|
||
fi
|
||
|
||
# 等待 Docker 就绪
|
||
echo "Waiting for Docker daemon..."
|
||
for i in {1..30}; do
|
||
if docker info > /dev/null 2>&1; then
|
||
echo "✓ Docker daemon is ready"
|
||
break
|
||
fi
|
||
if [ $i -eq 30 ]; then
|
||
echo "✗ Docker daemon failed to start"
|
||
[ -f /var/log/dockerd.log ] && cat /var/log/dockerd.log
|
||
exit 1
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
# 注册 QEMU binfmt
|
||
echo ""
|
||
echo "Registering QEMU binary formats..."
|
||
update-binfmts --enable 2>/dev/null || {
|
||
echo "⚠ binfmt_misc not available, trying to mount..."
|
||
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null || true
|
||
update-binfmts --enable
|
||
}
|
||
|
||
# 验证多架构支持
|
||
echo "Verifying multi-arch support..."
|
||
if docker run --rm arm64v8/alpine uname -m > /dev/null 2>&1; then
|
||
echo " ✓ arm64 support verified"
|
||
else
|
||
echo " ⚠ arm64 verification failed"
|
||
fi
|
||
|
||
if docker run --rm amd64/alpine uname -m > /dev/null 2>&1; then
|
||
echo " ✓ amd64 support verified"
|
||
else
|
||
echo " ⚠ amd64 verification failed"
|
||
fi
|
||
|
||
# 配置 Buildx
|
||
if [ ! -f "/data/buildx/.configured" ]; then
|
||
echo ""
|
||
echo "Setting up Buildx for the first time..."
|
||
|
||
# 创建 BuildKit 配置
|
||
cat > /data/buildx/buildkitd.toml <<EOF
|
||
[worker.oci]
|
||
max-parallelism = 4
|
||
EOF
|
||
|
||
# 创建 Buildx builder
|
||
docker buildx create \
|
||
--name gitea-multiarch \
|
||
--driver docker-container \
|
||
--bootstrap \
|
||
--use \
|
||
--config /data/buildx/buildkitd.toml 2>/dev/null || \
|
||
docker buildx use gitea-multiarch 2>/dev/null
|
||
|
||
# 验证
|
||
echo "Verifying Buildx..."
|
||
docker buildx inspect --bootstrap > /dev/null 2>&1
|
||
|
||
# 标记为已配置
|
||
touch /data/buildx/.configured
|
||
|
||
echo "✓ Buildx configured successfully!"
|
||
docker buildx inspect | grep "Platforms:" | head -1
|
||
else
|
||
echo "✓ Buildx already configured"
|
||
|
||
# 确保 builder 可用
|
||
docker buildx use gitea-multiarch 2>/dev/null || {
|
||
echo "⚠ Recreating Buildx builder..."
|
||
docker buildx rm gitea-multiarch 2>/dev/null || true
|
||
docker buildx create \
|
||
--name gitea-multiarch \
|
||
--driver docker-container \
|
||
--bootstrap \
|
||
--use 2>/dev/null
|
||
}
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 检查 act_runner 安装
|
||
# ============================================
|
||
# 创建主 supervisor 配置文件
|
||
cat > /etc/supervisor/supervisord.conf <<EOF
|
||
[supervisord]
|
||
nodaemon=true
|
||
logfile=/var/log/supervisor/supervisord.log
|
||
pidfile=/var/run/supervisord.pid
|
||
|
||
[unix_http_server]
|
||
file=/var/run/supervisor.sock
|
||
chmod=0700
|
||
|
||
[supervisorctl]
|
||
serverurl=unix:///var/run/supervisor.sock
|
||
|
||
[rpcinterface:supervisor]
|
||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||
|
||
[include]
|
||
files = /etc/supervisor/conf.d/*.conf
|
||
EOF
|
||
|
||
# 检查持久化目录中的 act_runner
|
||
if [ -f "$RUNNER_PATH" ]; then
|
||
echo "✓ Found act_runner in persistent storage"
|
||
echo " Location: $RUNNER_PATH"
|
||
|
||
if [ ! -L "$SYSTEM_LINK" ] || [ ! -e "$SYSTEM_LINK" ]; then
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo " Created system link: $SYSTEM_LINK -> $RUNNER_PATH"
|
||
fi
|
||
|
||
RUNNER_VERSION=$("$SYSTEM_LINK" --version 2>/dev/null || echo "unknown")
|
||
echo " Version: $RUNNER_VERSION"
|
||
elif [ -f "$SYSTEM_LINK" ]; then
|
||
echo "⚠ Found act_runner in system path, migrating to persistent storage..."
|
||
cp "$SYSTEM_LINK" "$RUNNER_PATH"
|
||
chmod +x "$RUNNER_PATH"
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo " ✓ Migrated to: $RUNNER_PATH"
|
||
else
|
||
echo "⚠ act_runner not installed yet!"
|
||
echo ""
|
||
echo "Please run the setup script first:"
|
||
echo " docker-compose exec gitea-runner /data/setup.sh"
|
||
echo ""
|
||
echo "Container is waiting..."
|
||
|
||
while [ ! -f "$RUNNER_PATH" ] && [ ! -f "$SYSTEM_LINK" ]; do
|
||
sleep 10
|
||
done
|
||
|
||
if [ -f "$RUNNER_PATH" ]; then
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo "✓ act_runner detected and linked!"
|
||
elif [ -f "$SYSTEM_LINK" ]; then
|
||
cp "$SYSTEM_LINK" "$RUNNER_PATH"
|
||
ln -sf "$RUNNER_PATH" "$SYSTEM_LINK"
|
||
echo "✓ act_runner detected and migrated!"
|
||
fi
|
||
fi
|
||
|
||
# ============================================
|
||
# 配置已注册的 Runners
|
||
# ============================================
|
||
echo ""
|
||
echo "Scanning for registered runners..."
|
||
RUNNER_COUNT=0
|
||
|
||
if [ -d "/data/runners" ]; then
|
||
for runner_dir in /data/runners/*/; do
|
||
if [ -d "$runner_dir" ]; then
|
||
runner_name=$(basename "$runner_dir")
|
||
|
||
if [ -f "$runner_dir/.runner" ] && [ -f "$runner_dir/config.yaml" ]; then
|
||
echo "Found runner: $runner_name"
|
||
|
||
cat > "/etc/supervisor/conf.d/runner-${runner_name}.conf" <<EOF
|
||
[program:runner-${runner_name}]
|
||
command=/usr/local/bin/act_runner daemon --config ${runner_dir}/config.yaml
|
||
directory=${runner_dir}
|
||
autostart=true
|
||
autorestart=true
|
||
stderr_logfile=/var/log/supervisor/runner-${runner_name}.err.log
|
||
stdout_logfile=/var/log/supervisor/runner-${runner_name}.out.log
|
||
stdout_logfile_maxbytes=50MB
|
||
stdout_logfile_backups=10
|
||
stderr_logfile_maxbytes=50MB
|
||
stderr_logfile_backups=10
|
||
user=root
|
||
environment=HOME="/root",PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||
EOF
|
||
RUNNER_COUNT=$((RUNNER_COUNT + 1))
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
if [ $RUNNER_COUNT -eq 0 ]; then
|
||
echo "⚠ No runners registered yet!"
|
||
echo ""
|
||
echo "Please run the register script to add a runner:"
|
||
echo " docker-compose exec gitea-runner /data/register.sh"
|
||
echo ""
|
||
fi
|
||
|
||
echo "Total runners configured: $RUNNER_COUNT"
|
||
echo ""
|
||
echo "==================================="
|
||
echo "Starting Supervisor..."
|
||
echo "==================================="
|
||
|
||
# 启动 supervisord
|
||
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||
```
|
||
|
||
#### 4️⃣ setup.sh, register.sh, manage.sh
|
||
|
||
这三个文件与标准版**完全相同**。
|
||
|
||
### 🚀 部署步骤(Buildx 版)
|
||
|
||
#### 1. 设置权限
|
||
|
||
```bash
|
||
chmod +x entrypoint.sh setup.sh register.sh manage.sh
|
||
```
|
||
|
||
#### 2. 构建并启动
|
||
|
||
```bash
|
||
docker-compose build
|
||
docker-compose up -d
|
||
```
|
||
|
||
#### 3. 验证 Buildx
|
||
|
||
```bash
|
||
# 查看日志
|
||
docker-compose logs -f
|
||
|
||
# 应该看到:
|
||
# ✓ Docker daemon is ready
|
||
# ✓ arm64 support verified
|
||
# ✓ amd64 support verified
|
||
# ✓ Buildx configured successfully!
|
||
|
||
# 进入容器验证
|
||
docker-compose exec gitea-runner docker buildx ls
|
||
# 应该看到 gitea-multiarch builder
|
||
```
|
||
|
||
#### 4. 安装和注册
|
||
|
||
```bash
|
||
docker-compose exec gitea-runner /data/setup.sh
|
||
docker-compose exec gitea-runner /data/register.sh
|
||
docker-compose restart
|
||
```
|
||
|
||
### 🔧 Buildx 多架构构建示例
|
||
|
||
#### 示例 1:基础构建
|
||
|
||
在 Gitea Actions workflow 中使用:
|
||
|
||
```yaml
|
||
name: Multi-Arch Build
|
||
|
||
on: [push]
|
||
|
||
jobs:
|
||
build:
|
||
runs-on: ubuntu-22.04
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
|
||
- name: Set up QEMU
|
||
uses: docker/setup-qemu-action@v2
|
||
|
||
- name: Set up Buildx
|
||
uses: docker/setup-buildx-action@v2
|
||
|
||
- name: Build and Push
|
||
uses: docker/build-push-action@v4
|
||
with:
|
||
context: .
|
||
platforms: linux/amd64,linux/arm64
|
||
tags: myapp:latest
|
||
push: true
|
||
```
|
||
|
||
#### 示例 2:Go 应用优化构建
|
||
|
||
使用交叉编译可以大幅提升构建速度:
|
||
|
||
```dockerfile
|
||
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder
|
||
ARG TARGETOS TARGETARCH
|
||
WORKDIR /app
|
||
COPY . .
|
||
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o app
|
||
|
||
FROM alpine
|
||
COPY --from=builder /app/app /app
|
||
CMD ["/app"]
|
||
```
|
||
|
||
---
|
||
|
||
## ⚖️ 版本对比
|
||
|
||
### 功能对比表
|
||
|
||
| 功能 | 标准版 | Buildx 版 |
|
||
| ---------------- | ------- | --------- |
|
||
| 基础 Runner 功能 | ✅ | ✅ |
|
||
| arm64 原生构建 | ✅ | ✅ |
|
||
| amd64 模拟构建 | ❌ | ✅ |
|
||
| 多架构镜像 | ❌ | ✅ |
|
||
| Buildx 支持 | ❌ | ✅ |
|
||
| QEMU 内置 | ❌ | ✅ |
|
||
| 特权模式要求 | ❌ | ✅ 必需 |
|
||
| Docker socket | 可选 | ✅ 必需 |
|
||
| 镜像大小 | ~400MB | ~850MB |
|
||
| 启动时间 | 5-10 秒 | 15-20 秒 |
|
||
| 复杂度 | 简单 | 中等 |
|
||
|
||
### 性能对比(Buildx 版)
|
||
|
||
在 ARM64 主机上使用 Buildx 构建不同架构的性能:
|
||
|
||
| 项目类型 | ARM64 (原生) | AMD64 (QEMU) | 双架构并行 |
|
||
| -------------- | ------------ | ------------ | ---------- |
|
||
| Alpine 镜像 | 5s | 15s | 18s |
|
||
| Node.js 应用 | 2min | 8min | 9min |
|
||
| Go 应用 | 1.5min | 6min | 7min |
|
||
| Go(交叉编译) | 1min | 1.5min | 2min |
|
||
|
||
**关键发现:**
|
||
|
||
- 🏆 ARM64 原生构建最快
|
||
- 🐌 AMD64 模拟慢 3-4 倍(QEMU)
|
||
- ⚡ 交叉编译比纯 QEMU 快 4 倍
|
||
- 💾 使用缓存可提速 80-90%
|
||
|
||
### 🎯 选择建议
|
||
|
||
#### 选择标准版
|
||
|
||
- ✅ 只在 arm64 上运行 arm64 应用
|
||
- ✅ 不需要发布多架构镜像
|
||
- ✅ 追求简单和轻量
|
||
- ✅ 不需要 Docker-in-Docker
|
||
|
||
#### 选择 Buildx 版
|
||
|
||
- ✅ 需要构建 amd64 + arm64 镜像
|
||
- ✅ 发布到 Docker Hub 等公共仓库
|
||
- ✅ 跨平台应用开发
|
||
- ✅ CI/CD 需要多架构支持
|
||
|
||
---
|
||
|
||
## ❓ 常见问题
|
||
|
||
### Q1: 如何更新 Runner 版本?
|
||
|
||
```bash
|
||
docker-compose exec gitea-runner /data/setup.sh
|
||
# 脚本会询问是否升级
|
||
```
|
||
|
||
### Q2: 如何添加多个 Runners?
|
||
|
||
```bash
|
||
# 多次运行注册脚本
|
||
docker-compose exec gitea-runner /data/register.sh
|
||
# 每次使用不同名称
|
||
|
||
# 查看所有 runners
|
||
docker-compose exec gitea-runner /data/manage.sh list
|
||
```
|
||
|
||
### Q3: 容器启动失败 "exec format error"
|
||
|
||
**原因:** Windows 换行符问题
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 转换换行符
|
||
sed -i 's/\r$//' *.sh
|
||
chmod +x *.sh
|
||
|
||
# 重新构建
|
||
docker-compose down
|
||
docker-compose build --no-cache
|
||
docker-compose up -d
|
||
```
|
||
|
||
### Q4: 如何完全重置?
|
||
|
||
```bash
|
||
docker-compose down -v
|
||
rm -rf runner-data
|
||
docker-compose build --no-cache
|
||
docker-compose up -d
|
||
```
|
||
|
||
### Q5: 如何清理 Docker 资源?
|
||
|
||
#### 🧹 清理未使用的镜像
|
||
|
||
```bash
|
||
# 清理悬空镜像(dangling images)
|
||
docker image prune
|
||
|
||
# 清理所有未使用的镜像
|
||
docker image prune -a
|
||
|
||
# 查看镜像占用空间
|
||
docker system df
|
||
```
|
||
|
||
#### 🗑️ 清理 Buildx 缓存(仅 Buildx 版)
|
||
|
||
```bash
|
||
# 清理构建缓存(保留 builder)
|
||
docker buildx prune
|
||
|
||
# 清理所有构建缓存
|
||
docker buildx prune -a -f
|
||
|
||
# 删除 builder(会自动清理相关容器和镜像)
|
||
docker buildx rm gitea-multiarch
|
||
```
|
||
|
||
#### 🔄 完整清理流程
|
||
|
||
```bash
|
||
# 1. 停止所有容器
|
||
docker-compose down
|
||
|
||
# 2. 清理 Buildx(如果使用 Buildx 版)
|
||
docker buildx rm gitea-multiarch
|
||
|
||
# 3. 清理未使用的镜像
|
||
docker image prune -a
|
||
|
||
# 4. 清理系统(慎用!会清理所有未使用资源)
|
||
docker system prune -a --volumes
|
||
|
||
# 5. 查看清理效果
|
||
docker system df
|
||
```
|
||
|
||
#### ⚠️ 注意事项
|
||
|
||
- `docker system prune -a` 会删除**所有未使用的镜像**,包括其他项目的
|
||
- 如果只想清理 Gitea Runner 相关资源,建议单独删除:
|
||
|
||
```bash
|
||
docker rmi buildx-gitea-runner:latest
|
||
docker rmi arm64v8/alpine:latest
|
||
docker rmi moby/buildkit:buildx-stable-1
|
||
```
|
||
|
||
### Q6: 如何查看日志?
|
||
|
||
```bash
|
||
# 容器日志
|
||
docker-compose logs -f
|
||
|
||
# Runner 日志
|
||
docker-compose exec gitea-runner /data/manage.sh logs runner-name
|
||
|
||
# 实时跟踪日志
|
||
docker-compose exec gitea-runner /data/manage.sh follow runner-name
|
||
```
|
||
|
||
### Q7: Buildx 版 - 构建速度慢?
|
||
|
||
**优化方法:**
|
||
|
||
#### 1. 启用缓存
|
||
|
||
```yaml
|
||
- name: Build with cache
|
||
uses: docker/build-push-action@v4
|
||
with:
|
||
cache-from: type=registry,ref=myimage:buildcache
|
||
cache-to: type=registry,ref=myimage:buildcache,mode=max
|
||
platforms: linux/amd64,linux/arm64
|
||
```
|
||
|
||
#### 2. 使用交叉编译
|
||
|
||
```dockerfile
|
||
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder
|
||
ARG TARGETOS TARGETARCH
|
||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o app
|
||
```
|
||
|
||
#### 3. 按需构建
|
||
|
||
```yaml
|
||
# 开发分支只构建 arm64,生产分支构建双架构
|
||
platforms: ${{ github.ref == 'refs/heads/main' && 'linux/amd64,linux/arm64' || 'linux/arm64' }}
|
||
```
|
||
|
||
### Q8: Buildx 版 - 如何验证 Buildx?
|
||
|
||
```bash
|
||
# 进入容器
|
||
docker-compose exec gitea-runner bash
|
||
|
||
# 检查 builder
|
||
docker buildx ls
|
||
|
||
# 测试构建
|
||
echo 'FROM alpine' | docker buildx build --platform linux/amd64,linux/arm64 -
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 参考资源
|
||
|
||
- 📖 [Gitea Actions 官方文档](https://docs.gitea.com/usage/actions/overview)
|
||
- 🚀 [Gitea Runner 下载](https://dl.gitea.com/act_runner/)
|
||
- 🐳 [Docker Buildx 文档](https://docs.docker.com/reference/cli/docker/buildx/)
|
||
|
||
---
|
||
|
||
## 💬 反馈与支持
|
||
|
||
如果你在使用过程中遇到问题,欢迎:
|
||
|
||
- 📝 提交 Issue
|
||
- 💡 分享你的使用经验
|
||
- 🌟 给项目点个 Star
|
||
|
||
---
|