Git 服务器搭建(Gitea)及自动化部署(Webhook)实战

659 阅读10分钟

1. 环境准备与规划

1.1 系统要求

  • 操作系统: Ubuntu 20.04 LTS 或 CentOS 8
  • 内存: 至少 2GB RAM
  • 存储: 至少 20GB 可用空间
  • 网络: 固定的 IP 地址或域名

1.2 架构规划

以下是 Gitea 与自动化部署的完整架构流程图:

graph TD
    A[开发者] -->|Git Push| B[Gitea 服务器]
    B -->|触发 Webhook| C[Webhook 接收器]
    C -->|执行部署脚本| D[应用服务器]
    D -->|拉取代码| B
    D -->|构建应用| E[生产环境]
    F[用户] -->|访问应用| E
    
    style A fill:#4CAF50,stroke:#388E3C,stroke-width:2px,color:white
    style B fill:#2196F3,stroke:#1976D2,stroke-width:2px,color:white
    style C fill:#FF9800,stroke:#F57C00,stroke-width:2px,color:white
    style D fill:#9C27B0,stroke:#7B1FA2,stroke-width:2px,color:white
    style E fill:#F44336,stroke:#D32F2F,stroke-width:2px,color:white
    style F fill:#607D8B,stroke:#455A64,stroke-width:2px,color:white

2. Gitea 服务器安装与配置

2.1 系统环境准备

创建安装脚本文件:setup_gitea.sh

#!/bin/bash

# 设置颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo -e "${GREEN}开始安装 Gitea 服务器...${NC}"

# 检查系统类型
if [ -f /etc/redhat-release ]; then
    OS="centos"
elif [ -f /etc/lsb-release ]; then
    OS="ubuntu"
else
    echo -e "${RED}不支持的操作系统${NC}"
    exit 1
fi

# 更新系统包
echo -e "${YELLOW}更新系统包...${NC}"
if [ "$OS" = "centos" ]; then
    yum update -y
    yum install -y wget curl git vim
else
    apt-get update
    apt-get upgrade -y
    apt-get install -y wget curl git vim
fi

# 创建 Gitea 用户
echo -e "${YELLOW}创建 Gitea 用户...${NC}"
if ! id "gitea" &>/dev/null; then
    useradd --system --shell /bin/bash --create-home --home-dir /home/gitea --comment 'Git Version Control' gitea
    echo -e "${GREEN}Gitea 用户创建成功${NC}"
else
    echo -e "${YELLOW}Gitea 用户已存在${NC}"
fi

# 创建必要的目录
echo -e "${YELLOW}创建目录结构...${NC}"
mkdir -p /var/lib/gitea/{custom,data,log}
mkdir -p /etc/gitea
chown -R gitea:gitea /var/lib/gitea
chmod -R 750 /var/lib/gitea
chown -R root:gitea /etc/gitea
chmod 770 /etc/gitea

echo -e "${GREEN}系统环境准备完成${NC}"

2.2 安装 MySQL 数据库

创建数据库安装脚本:setup_mysql.sh

#!/bin/bash

# 设置颜色输出
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${YELLOW}安装 MySQL 数据库...${NC}"

if [ -f /etc/redhat-release ]; then
    # CentOS 安装 MySQL
    yum install -y mysql-server mysql-client
    systemctl start mysqld
    systemctl enable mysqld
    
    # 获取初始密码
    MYSQL_TEMP_PWD=$(grep 'temporary password' /var/log/mysqld.log | awk '{print $NF}')
    echo -e "${YELLOW}MySQL 初始密码: $MYSQL_TEMP_PWD${NC}"
    
elif [ -f /etc/lsb-release ]; then
    # Ubuntu 安装 MySQL
    apt-get install -y mysql-server mysql-client
    systemctl start mysql
    systemctl enable mysql
fi

# 创建 Gitea 数据库
echo -e "${YELLOW}配置 Gitea 数据库...${NC}"
mysql -u root -p <<EOF
CREATE DATABASE IF NOT EXISTS gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'gitea'@'localhost' IDENTIFIED BY 'GiteaPassword123!';
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost';
FLUSH PRIVILEGES;
EXIT;
EOF

echo -e "${GREEN}MySQL 数据库安装完成${NC}"

2.3 下载并安装 Gitea

创建 Gitea 安装脚本:install_gitea.sh

#!/bin/bash

# 设置颜色输出
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${YELLOW}下载并安装 Gitea...${NC}"

# 获取最新版本号
GITEA_VERSION=$(curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')

echo -e "${YELLOW}当前最新版本: $GITEA_VERSION${NC}"

# 下载 Gitea
cd /tmp
wget -O gitea https://dl.gitea.io/gitea/$GITEA_VERSION/gitea-$GITEA_VERSION-linux-amd64
chmod +x gitea

# 安装到系统路径
cp gitea /usr/local/bin/gitea

# 验证安装
/usr/local/bin/gitea --version

# 创建系统服务文件
echo -e "${YELLOW}创建系统服务...${NC}"
cat > /etc/systemd/system/gitea.service <<EOF
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
After=mysql.service

[Service]
RestartSec=2s
Type=simple
User=gitea
Group=gitea
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=gitea HOME=/home/gitea GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target
EOF

# 重新加载 systemd
systemctl daemon-reload
systemctl enable gitea

echo -e "${GREEN}Gitea 安装完成${NC}"

2.4 配置 Gitea

创建 Gitea 配置文件:/etc/gitea/app.ini

APP_NAME = 我的 Gitea 服务器
RUN_USER = gitea
RUN_MODE = prod

[server]
APP_DATA_PATH    = /var/lib/gitea/data
HTTP_PORT        = 3000
ROOT_URL         = http://your-domain.com:3000/
DISABLE_SSH      = false
SSH_PORT         = 22
SSH_LISTEN_PORT  = 22
LFS_START_SERVER = true
LFS_CONTENT_PATH = /var/lib/gitea/data/lfs
DOMAIN           = localhost
SSH_DOMAIN       = localhost
OFFLINE_MODE     = false

[database]
DB_TYPE  = mysql
HOST     = 127.0.0.1:3306
NAME     = gitea
USER     = gitea
PASSWD   = GiteaPassword123!
SCHEMA   = 
SSL_MODE = disable
CHARSET  = utf8mb4
PATH     = /var/lib/gitea/data/gitea.db

[repository]
ROOT = /var/lib/gitea/data/gitea-repositories

[session]
PROVIDER = file
PROVIDER_CONFIG = /var/lib/gitea/data/sessions

[picture]
AVATAR_UPLOAD_PATH      = /var/lib/gitea/data/avatars
DISABLE_GRAVATAR        = false
ENABLE_FEDERATED_AVATAR = false

[attachment]
PATH = /var/lib/gitea/data/attachments

[log]
ROOT_PATH = /var/lib/gitea/log
MODE      = file
LEVEL     = Info

[security]
INSTALL_LOCK   = true
SECRET_KEY     = your-secret-key-here
INTERNAL_TOKEN = your-internal-token-here

[service]
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL     = false
DISABLE_REGISTRATION   = false
REQUIRE_SIGNIN_VIEW    = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS       = noreply.example.org

2.5 启动 Gitea 服务

执行启动命令:

# 设置配置文件权限
chown gitea:gitea /etc/gitea/app.ini
chmod 660 /etc/gitea/app.ini

# 启动 Gitea 服务
systemctl start gitea
systemctl status gitea

# 检查服务是否正常运行
netstat -tlnp | grep 3000

3. Gitea 初始配置

3.1 通过 Web 界面完成安装

  1. 打开浏览器访问 http://your-server-ip:3000
  2. 按照安装向导完成配置:
    • 数据库类型选择 MySQL
    • 填写数据库连接信息
    • 设置管理员账户
    • 配置服务器域名和端口

3.2 创建第一个仓库

通过 Web 界面创建:

  1. 登录 Gitea
  2. 点击右上角 "+" 号
  3. 选择 "新建仓库"
  4. 填写仓库名称和描述
  5. 选择仓库可见性
  6. 点击 "创建仓库"

4. Webhook 自动化部署配置

4.1 部署服务器准备

创建部署服务器设置脚本:setup_deploy_server.sh

#!/bin/bash

# 设置颜色输出
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${YELLOW}设置部署服务器...${NC}"

# 创建部署用户
if ! id "deploy" &>/dev/null; then
    useradd -m -s /bin/bash deploy
    echo -e "${GREEN}部署用户创建成功${NC}"
    
    # 设置部署用户密码
    echo "deploy:DeployPassword123!" | chpasswd
else
    echo -e "${YELLOW}部署用户已存在${NC}"
fi

# 安装必要的软件
if [ -f /etc/redhat-release ]; then
    yum install -y nginx git nodejs npm python3 python3-pip
elif [ -f /etc/lsb-release ]; then
    apt-get install -y nginx git nodejs npm python3 python3-pip
fi

# 创建部署目录
mkdir -p /home/deploy/apps
mkdir -p /home/deploy/scripts
mkdir -p /home/deploy/logs
chown -R deploy:deploy /home/deploy

# 生成 SSH 密钥对
echo -e "${YELLOW}生成 SSH 密钥...${NC}"
sudo -u deploy ssh-keygen -t rsa -b 4096 -f /home/deploy/.ssh/id_rsa -N ""

echo -e "${GREEN}部署服务器设置完成${NC}"
echo -e "${YELLOW}请将以下公钥添加到 Gitea 的部署密钥中:${NC}"
cat /home/deploy/.ssh/id_rsa.pub

4.2 创建 Webhook 接收器

创建 Flask Webhook 接收器:webhook_receiver.py

#!/usr/bin/env python3
"""
Gitea Webhook 接收器
用于接收 Gitea 的 Webhook 请求并触发自动化部署
"""

from flask import Flask, request, jsonify
import json
import hmac
import hashlib
import subprocess
import logging
import os
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('/home/deploy/logs/webhook.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

app = Flask(__name__)

# Webhook 密钥(需要与 Gitea 中设置的密钥一致)
WEBHOOK_SECRET = 'YourWebhookSecretKey123!'

class WebhookProcessor:
    def __init__(self):
        self.deploy_scripts = {
            'frontend': '/home/deploy/scripts/deploy_frontend.sh',
            'backend': '/home/deploy/scripts/deploy_backend.sh',
            'api': '/home/deploy/scripts/deploy_api.sh'
        }
    
    def verify_signature(self, payload, signature):
        """验证 Webhook 签名"""
        expected_signature = hmac.new(
            WEBHOOK_SECRET.encode('utf-8'),
            payload,
            hashlib.sha256
        ).hexdigest()
        
        return hmac.compare_digest(expected_signature, signature)
    
    def parse_payload(self, payload):
        """解析 Webhook 负载"""
        try:
            data = json.loads(payload)
            event_type = request.headers.get('X-Gitea-Event')
            repository = data.get('repository', {})
            
            return {
                'event': event_type,
                'repo_name': repository.get('name'),
                'repo_url': repository.get('clone_url'),
                'branch': data.get('ref', '').split('/')[-1],
                'commit_id': data.get('after'),
                'committer': data.get('committer', {}).get('name', ''),
                'commit_message': data.get('head_commit', {}).get('message', '')
            }
        except Exception as e:
            logger.error(f"解析负载失败: {e}")
            return None
    
    def determine_deploy_script(self, repo_name, branch):
        """根据仓库名和分支确定部署脚本"""
        # 这里可以根据实际项目结构进行定制
        if 'frontend' in repo_name.lower():
            return self.deploy_scripts['frontend']
        elif 'api' in repo_name.lower() or 'backend' in repo_name.lower():
            return self.deploy_scripts['backend']
        else:
            return self.deploy_scripts.get(repo_name)
    
    def execute_deploy_script(self, script_path, repo_info):
        """执行部署脚本"""
        if not script_path or not os.path.exists(script_path):
            logger.error(f"部署脚本不存在: {script_path}")
            return False
        
        try:
            # 设置环境变量
            env = os.environ.copy()
            env.update({
                'REPO_NAME': repo_info['repo_name'],
                'REPO_URL': repo_info['repo_url'],
                'BRANCH': repo_info['branch'],
                'COMMIT_ID': repo_info['commit_id'],
                'COMMITTER': repo_info['committer']
            })
            
            # 执行部署脚本
            result = subprocess.run(
                ['bash', script_path],
                env=env,
                capture_output=True,
                text=True,
                timeout=300  # 5分钟超时
            )
            
            if result.returncode == 0:
                logger.info(f"部署成功: {repo_info['repo_name']} - {repo_info['branch']}")
                logger.info(f"部署输出: {result.stdout}")
                return True
            else:
                logger.error(f"部署失败: {result.stderr}")
                return False
                
        except subprocess.TimeoutExpired:
            logger.error("部署脚本执行超时")
            return False
        except Exception as e:
            logger.error(f"执行部署脚本时发生错误: {e}")
            return False

# 初始化处理器
processor = WebhookProcessor()

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    """处理 Webhook 请求"""
    try:
        # 获取请求数据
        payload = request.get_data()
        signature = request.headers.get('X-Gitea-Signature', '').replace('sha256=', '')
        
        # 验证签名
        if not processor.verify_signature(payload, signature):
            logger.warning("Webhook 签名验证失败")
            return jsonify({'error': 'Invalid signature'}), 401
        
        # 解析负载
        repo_info = processor.parse_payload(payload)
        if not repo_info:
            return jsonify({'error': 'Invalid payload'}), 400
        
        logger.info(f"接收到 Webhook 事件: {repo_info['event']}")
        logger.info(f"仓库: {repo_info['repo_name']}, 分支: {repo_info['branch']}")
        
        # 只处理 push 事件到特定分支
        if repo_info['event'] == 'push' and repo_info['branch'] in ['main', 'master', 'develop']:
            # 确定部署脚本
            deploy_script = processor.determine_deploy_script(
                repo_info['repo_name'], 
                repo_info['branch']
            )
            
            # 执行部署
            success = processor.execute_deploy_script(deploy_script, repo_info)
            
            if success:
                return jsonify({'status': 'success', 'message': 'Deployment triggered'})
            else:
                return jsonify({'status': 'error', 'message': 'Deployment failed'}), 500
        else:
            logger.info(f"忽略事件: {repo_info['event']} 分支: {repo_info['branch']}")
            return jsonify({'status': 'ignored', 'message': 'Event ignored'})
            
    except Exception as e:
        logger.error(f"处理 Webhook 时发生错误: {e}")
        return jsonify({'error': 'Internal server error'}), 500

@app.route('/health', methods=['GET'])
def health_check():
    """健康检查端点"""
    return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

4.3 创建部署脚本

创建前端项目部署脚本:/home/deploy/scripts/deploy_frontend.sh

#!/bin/bash

# 设置颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}

error() {
    echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
}

warning() {
    echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
}

# 环境变量
REPO_NAME=${REPO_NAME:-"unknown"}
REPO_URL=${REPO_URL:-""}
BRANCH=${BRANCH:-"main"}
COMMIT_ID=${COMMIT_ID:-""}
DEPLOY_USER="deploy"
APP_NAME="$REPO_NAME"
DEPLOY_DIR="/home/deploy/apps/$APP_NAME"
BACKUP_DIR="/home/deploy/backups/$APP_NAME"
LOG_FILE="/home/deploy/logs/${APP_NAME}_deploy.log"
NGINX_CONF="/etc/nginx/sites-available/$APP_NAME"
NGINX_ENABLED="/etc/nginx/sites-enabled/$APP_NAME"

# 创建目录
mkdir -p $DEPLOY_DIR $BACKUP_DIR $(dirname $LOG_FILE)

# 记录开始时间
START_TIME=$(date +%s)

log "开始部署项目: $APP_NAME"
log "仓库: $REPO_URL"
log "分支: $BRANCH"
log "提交ID: $COMMIT_ID"

# 检查必要的环境变量
if [ -z "$REPO_URL" ]; then
    error "REPO_URL 环境变量未设置"
    exit 1
fi

# 备份当前版本(如果存在)
backup_current_version() {
    if [ -d "$DEPLOY_DIR" ] && [ "$(ls -A $DEPLOY_DIR)" ]; then
        log "备份当前版本..."
        BACKUP_FILE="$BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
        tar -czf "$BACKUP_FILE" -C "$DEPLOY_DIR" . >> $LOG_FILE 2>&1
        
        if [ $? -eq 0 ]; then
            log "备份成功: $BACKUP_FILE"
        else
            error "备份失败"
            exit 1
        fi
    fi
}

# 克隆或更新代码
update_code() {
    if [ ! -d "$DEPLOY_DIR/.git" ]; then
        log "首次克隆代码库..."
        git clone --branch $BRANCH $REPO_URL $DEPLOY_DIR >> $LOG_FILE 2>&1
        
        if [ $? -ne 0 ]; then
            error "克隆代码库失败"
            exit 1
        fi
    else
        log "更新代码库..."
        cd $DEPLOY_DIR
        git fetch origin >> $LOG_FILE 2>&1
        git checkout $BRANCH >> $LOG_FILE 2>&1
        git reset --hard origin/$BRANCH >> $LOG_FILE 2>&1
        
        if [ $? -ne 0 ]; then
            error "更新代码库失败"
            exit 1
        fi
    fi
    
    # 切换到特定提交(如果指定)
    if [ ! -z "$COMMIT_ID" ]; then
        log "切换到提交: $COMMIT_ID"
        cd $DEPLOY_DIR
        git reset --hard $COMMIT_ID >> $LOG_FILE 2>&1
    fi
}

# 安装依赖
install_dependencies() {
    log "安装项目依赖..."
    cd $DEPLOY_DIR
    
    if [ -f "package.json" ]; then
        log "检测到 Node.js 项目,安装 npm 依赖..."
        npm install >> $LOG_FILE 2>&1
        
        if [ $? -ne 0 ]; then
            error "npm 依赖安装失败"
            exit 1
        fi
    fi
    
    if [ -f "requirements.txt" ]; then
        log "检测到 Python 项目,安装 pip 依赖..."
        pip3 install -r requirements.txt >> $LOG_FILE 2>&1
        
        if [ $? -ne 0 ]; then
            error "pip 依赖安装失败"
            exit 1
        fi
    fi
}

# 构建项目
build_project() {
    log "构建项目..."
    cd $DEPLOY_DIR
    
    # Node.js 项目构建
    if [ -f "package.json" ]; then
        if grep -q "\"build\"" package.json; then
            log "执行 npm build..."
            npm run build >> $LOG_FILE 2>&1
            
            if [ $? -ne 0 ]; then
                error "项目构建失败"
                exit 1
            fi
        fi
    fi
}

# 配置 Nginx
setup_nginx() {
    log "配置 Nginx..."
    
    # 创建 Nginx 配置文件
    cat > $NGINX_CONF <<EOF
server {
    listen 80;
    server_name ${APP_NAME}.example.com;
    
    root ${DEPLOY_DIR}/dist;
    index index.html index.htm;
    
    # 前端路由支持
    location / {
        try_files \$uri \$uri/ /index.html;
    }
    
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # API 代理(如果有后端)
    location /api/ {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_cache_bypass \$http_upgrade;
    }
    
    access_log /var/log/nginx/${APP_NAME}_access.log;
    error_log /var/log/nginx/${APP_NAME}_error.log;
}
EOF
    
    # 启用站点
    if [ ! -f "$NGINX_ENABLED" ]; then
        ln -s $NGINX_CONF $NGINX_ENABLED
    fi
    
    # 测试并重新加载 Nginx
    nginx -t >> $LOG_FILE 2>&1
    if [ $? -eq 0 ]; then
        systemctl reload nginx >> $LOG_FILE 2>&1
        log "Nginx 配置已重载"
    else
        error "Nginx 配置测试失败"
        exit 1
    fi
}

# 设置文件权限
set_permissions() {
    log "设置文件权限..."
    chown -R $DEPLOY_USER:$DEPLOY_USER $DEPLOY_DIR
    chmod -R 755 $DEPLOY_DIR
}

# 主部署流程
main() {
    log "=== 开始部署流程 ==="
    
    # 执行部署步骤
    backup_current_version
    update_code
    install_dependencies
    build_project
    setup_nginx
    set_permissions
    
    # 计算部署时间
    END_TIME=$(date +%s)
    DEPLOY_TIME=$((END_TIME - START_TIME))
    
    log "=== 部署完成 ==="
    log "项目: $APP_NAME"
    log "分支: $BRANCH"
    log "提交: $COMMIT_ID"
    log "部署时间: ${DEPLOY_TIME}秒"
    log "部署目录: $DEPLOY_DIR"
    log "日志文件: $LOG_FILE"
    
    # 写入部署记录
    echo "$(date +'%Y-%m-%d %H:%M:%S') - $APP_NAME - $BRANCH - $COMMIT_ID - 成功 - ${DEPLOY_TIME}秒" >> /home/deploy/logs/deploy_history.log
}

# 执行主函数
main "$@"

4.4 配置 Webhook 服务

创建 Webhook 服务配置:/etc/systemd/system/webhook.service

[Unit]
Description=Gitea Webhook Receiver
After=network.target
Wants=network.target

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/home/deploy
ExecStart=/usr/bin/python3 /home/deploy/webhook_receiver.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

启动 Webhook 服务:

# 设置脚本权限
chmod +x /home/deploy/scripts/*.sh
chmod +x /home/deploy/webhook_receiver.py

# 安装 Python 依赖
pip3 install flask

# 启动 Webhook 服务
systemctl daemon-reload
systemctl enable webhook
systemctl start webhook
systemctl status webhook

5. Gitea Webhook 配置

5.1 在 Gitea 中配置 Webhook

  1. 登录 Gitea 并进入你的仓库
  2. 点击 设置Webhook添加 Webhook
  3. 选择 Gitea 类型
  4. 填写配置信息:

目标 URL: http://your-deploy-server:5000/webhook 密钥: YourWebhookSecretKey123! 触发事件: 选择 Push 事件 分支过滤: main,master,develop

5.2 测试 Webhook

创建测试脚本:test_webhook.sh

#!/bin/bash

# Webhook 测试脚本

GITEA_URL="http://your-gitea-server:3000"
WEBHOOK_URL="http://your-deploy-server:5000/webhook"
SECRET="YourWebhookSecretKey123!"

# 模拟 Webhook 负载
PAYLOAD='{
  "ref": "refs/heads/main",
  "before": "d4c7a6b2f9c7e8b1a0d5e8f7a6b5c4d3e2f1a0b9",
  "after": "a1b2c3d4e5f6789012345678901234567890abcd",
  "repository": {
    "name": "test-frontend",
    "clone_url": "http://your-gitea-server:3000/username/test-frontend.git"
  },
  "committer": {
    "name": "Test User"
  },
  "head_commit": {
    "message": "Test commit message"
  }
}'

# 生成签名
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

# 发送测试请求
curl -X POST \
  -H "Content-Type: application/json" \
  -H "X-Gitea-Event: push" \
  -H "X-Gitea-Signature: sha256=$SIGNATURE" \
  -d "$PAYLOAD" \
  "$WEBHOOK_URL"

echo -e "\nWebhook 测试完成"

6. 完整部署流程验证

6.1 部署流程验证

以下是完整的自动化部署流程验证步骤:

sequenceDiagram
    participant D as 开发者
    participant G as Gitea服务器
    participant W as Webhook接收器
    participant S as 部署服务器
    participant N as Nginx
    participant U as 最终用户

    Note over D,S: 初始设置阶段
    D->>G: 推送代码到仓库
    G->>W: 发送Webhook通知
    W->>S: 触发部署脚本
    S->>G: 拉取最新代码
    S->>S: 安装依赖和构建
    S->>N: 更新Nginx配置
    N->>N: 重载服务配置
    S->>W: 返回部署结果
    W->>G: 更新部署状态
    U->>N: 访问应用
    N->>U: 返回应用内容
    
    Note over D,U: 自动化部署完成

6.2 监控和日志查看

创建监控脚本:monitor_deployments.sh

#!/bin/bash

# 部署监控脚本

echo "=== 部署服务状态 ==="
systemctl status webhook --no-pager -l
echo ""

echo "=== 最近部署记录 ==="
tail -20 /home/deploy/logs/deploy_history.log
echo ""

echo "=== Webhook 访问日志 ==="
tail -10 /home/deploy/logs/webhook.log
echo ""

echo "=== Nginx 错误日志 ==="
tail -10 /var/log/nginx/error.log
echo ""

echo "=== 磁盘使用情况 ==="
df -h /home/deploy
echo ""

echo "=== 内存使用情况 ==="
free -h

7. 高级配置和优化

7.1 数据库优化

创建数据库优化脚本:optimize_mysql.sh

#!/bin/bash

# MySQL 优化配置

# 备份原配置
cp /etc/mysql/my.cnf /etc/mysql/my.cnf.backup.$(date +%Y%m%d)

# 优化配置
cat >> /etc/mysql/my.cnf <<EOF

# Gitea 优化配置
[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
max_connections = 100
query_cache_size = 32M
query_cache_type = 1
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

[gitea]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
EOF

# 重启 MySQL
systemctl restart mysql

echo "MySQL 优化配置完成"

7.2 SSL 证书配置

创建 SSL 配置脚本:setup_ssl.sh

#!/bin/bash

# SSL 证书配置脚本

DOMAIN="your-domain.com"
EMAIL="admin@your-domain.com"

# 安装 Certbot
if [ -f /etc/redhat-release ]; then
    yum install -y certbot python3-certbot-nginx
elif [ -f /etc/lsb-release ]; then
    apt-get install -y certbot python3-certbot-nginx
fi

# 获取 SSL 证书
certbot --nginx -d $DOMAIN -d www.$DOMAIN --non-interactive --agree-tos -m $EMAIL

# 设置自动续期
echo "0 12 * * * root /usr/bin/certbot renew --quiet" >> /etc/crontab

echo "SSL 证书配置完成"

8. 故障排除和维护

8.1 常见问题解决

创建故障排除脚本:troubleshoot.sh

#!/bin/bash

# Gitea 和部署系统故障排除

echo "=== 系统检查 ==="
echo "1. 检查服务状态..."
systemctl is-active gitea
systemctl is-active webhook
systemctl is-active nginx
systemctl is-active mysql

echo -e "\n2. 检查端口监听..."
netstat -tlnp | grep -E ':(3000|5000|80|3306)'

echo -e "\n3. 检查磁盘空间..."
df -h

echo -e "\n4. 检查内存使用..."
free -h

echo -e "\n5. 检查最近错误..."
journalctl -u gitea --since "1 hour ago" | grep -i error
journalctl -u webhook --since "1 hour ago" | grep -i error

echo -e "\n6. 检查 Webhook 日志..."
tail -20 /home/deploy/logs/webhook.log

echo -e "\n7. 检查部署日志..."
tail -20 /home/deploy/logs/deploy_history.log

8.2 备份和恢复

创建备份脚本:backup_gitea.sh

#!/bin/bash

# Gitea 数据备份脚本

BACKUP_DIR="/home/deploy/backups/gitea"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="gitea_backup_$DATE.tar.gz"

# 创建备份目录
mkdir -p $BACKUP_DIR

echo "开始备份 Gitea..."

# 停止 Gitea 服务
systemctl stop gitea

# 备份数据
tar -czf $BACKUP_DIR/$BACKUP_FILE \
    /var/lib/gitea \
    /etc/gitea \
    /home/gitea

# 备份数据库
mysqldump -u root -p gitea > $BACKUP_DIR/gitea_db_$DATE.sql

# 启动 Gitea 服务
systemctl start gitea

echo "备份完成: $BACKUP_DIR/$BACKUP_FILE"
echo "数据库备份: $BACKUP_DIR/gitea_db_$DATE.sql"

# 清理旧备份(保留最近7天)
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete

总结

通过本教程,已经成功搭建了一个完整的 Gitea Git 服务器,并配置了完整的自动化部署系统。这个系统具有以下特点:

  1. 完整的 Git 服务: 使用 Gitea 提供企业级的 Git 仓库管理
  2. 自动化部署: 通过 Webhook 实现代码推送后的自动部署
  3. 灵活的部署脚本: 支持多种项目类型的自动化部署
  4. 安全可靠: 包含签名验证、错误处理和回滚机制
  5. 易于维护: 提供完整的监控、备份和故障排除工具

这个解决方案可以满足中小型团队的代码管理和自动化部署需求,提供了生产环境级别的稳定性和可靠性。