# 🚀 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 < $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" </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" </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 - Show runner details" echo " add - Add new runner" echo " delete - Delete a runner" echo " start - Start a runner" echo " stop - Stop a runner" echo " restart - Restart a runner" echo " logs [lines] - View runner logs" echo " follow - 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 </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 < $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" <