Files
photography/.gitea/workflows/deploy-backend.yml
xujiang 964126953a
Some checks failed
部署后端服务 / 🚀 构建并部署 (push) Has been cancelled
feat: 重构配置文件同步流程
- 将配置文件同步步骤从脚本改为使用,简化了部署过程
- 新增备份现有配置和生成新的及文件的逻辑
- 提升了配置文件的管理和部署效率

此更改优化了生产环境的配置文件处理流程。
2025-07-16 15:17:07 +08:00

432 lines
15 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: 部署后端服务
on:
push:
branches: [ main ]
paths:
- 'backend/**'
- '.env.example'
- '.gitea/workflows/deploy-backend.yml'
workflow_dispatch:
env:
REGISTRY: crpi-b4fqtfbvv583enk2.cn-shanghai.personal.cr.aliyuncs.com
IMAGE_NAME: photography-backend/photography
jobs:
build-and-deploy:
name: 🚀 构建并部署
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🐳 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: |
network=host
- name: 🔧 配置 Docker 镜像代理
run: |
# 创建 Docker daemon 配置文件
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://hub.skillixx.com/"
]
}
EOF
# 重启 Docker 服务
sudo systemctl restart docker || true
# 验证配置
docker info | grep -A 5 "Registry Mirrors" || true
- name: 🔑 登录到镜像仓库(使用阿里云访问令牌)
run: |
echo "🔑 使用阿里云访问令牌登录..."
# 创建 Docker 配置文件
mkdir -p ~/.docker
cat > ~/.docker/config.json << EOF
{
"auths": {
"${{ env.REGISTRY }}": {
"auth": "$(echo -n '${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }}' | base64 -w 0)"
}
}
}
EOF
# 验证登录状态
echo "🔍 验证登录状态..."
if docker pull ${{ env.REGISTRY }}/library/hello-world:latest 2>/dev/null; then
echo "✅ 镜像仓库认证成功"
else
echo "❌ 镜像仓库认证失败,尝试基础认证..."
# 使用基础认证
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login ${{ env.REGISTRY }} --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin || {
echo "❌ 所有认证方式都失败"
exit 1
}
fi
- name: 📝 提取元数据
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: 🏗️ 构建镜像(使用正确格式)
run: |
echo "🔨 开始构建 Docker 镜像..."
cd ./backend
# 设置正确的镜像标签格式
IMAGE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
IMAGE_TAG_LATEST="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
echo "🏷️ 镜像标签:"
echo " - $IMAGE_TAG"
echo " - $IMAGE_TAG_LATEST"
# 构建镜像
docker build -t $IMAGE_TAG -t $IMAGE_TAG_LATEST .
# 验证镜像
echo "🔍 验证镜像构建..."
docker images | grep photography-backend
echo "✅ 镜像构建完成"
- name: 📤 推送镜像(使用正确路径)
run: |
IMAGE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
IMAGE_TAG_LATEST="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
echo "📤 开始推送镜像..."
echo "📋 目标仓库: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
# 重试机制
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "📤 推送尝试 $attempt/$max_attempts..."
echo "🚀 推送命令: docker push $IMAGE_TAG"
if docker push $IMAGE_TAG; then
echo "✅ 版本标签推送成功"
echo "🚀 推送 latest 标签: docker push $IMAGE_TAG_LATEST"
if docker push $IMAGE_TAG_LATEST; then
echo "✅ latest 标签推送成功"
break
else
echo "⚠️ latest 标签推送失败,但版本标签已成功"
break
fi
else
echo "❌ 推送失败,等待重试..."
if [ $attempt -lt $max_attempts ]; then
echo "⏳ 等待15秒后重试..."
sleep 15
fi
fi
attempt=$((attempt + 1))
done
if [ $attempt -gt $max_attempts ]; then
echo "❌ 所有推送尝试都失败"
echo "📋 请检查以下配置:"
echo " - 仓库地址: ${{ env.REGISTRY }}"
echo " - 镜像名称: ${{ env.IMAGE_NAME }}"
echo " - 认证信息: ${{ secrets.DOCKER_USERNAME }}"
exit 1
fi
- name: 📦 同步配置文件
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.TYY_USER }}
password: ${{ secrets.TYY_PWD }}
port: ${{ secrets.PORT }}
script: |
echo "📦 开始同步配置文件..."
# 创建目录
mkdir -p /data/docker/photography/backend
cd /data/docker/photography/backend
# 备份现有配置
if [ -f docker-compose.yml ]; then
cp docker-compose.yml docker-compose.yml.backup.$(date +%Y%m%d-%H%M%S)
echo "📋 已备份现有配置"
fi
# 生成新的配置文件
cat > docker-compose.yml << 'EOF'
# Photography Portfolio Backend - Production Docker Compose
# 生产环境配置 - 使用现有 PostgreSQL 和 Redis 服务
services:
# 后端API服务 (仅API服务无数据库)
api:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
container_name: photography-api
environment:
# 数据库配置 (连接现有服务)
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: photography
DB_USER: ${{ secrets.POSTGRES_PHOTO_USER }}
DB_PASSWORD: ${{ secrets.POSTGRES_PHOTO_PWD }}
DB_SSL_MODE: disable
# Redis配置 (连接现有服务)
REDIS_HOST: localhost
REDIS_PORT: 6379
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
REDIS_DB: 0
# JWT配置
JWT_SECRET: ${{ secrets.JWT_SECRET }}
JWT_EXPIRE: 24h
# 服务配置
APP_ENV: production
APP_PORT: 8080
APP_HOST: 0.0.0.0
# CORS配置
CORS_ORIGINS: https://photography.iriver.top
# 文件上传配置
UPLOAD_PATH: /app/uploads
UPLOAD_MAX_SIZE: 10485760
# 日志配置
LOG_LEVEL: info
LOG_FORMAT: json
ports:
- "8080:8080"
networks:
- app_network
volumes:
- uploads_data:/app/uploads
- logs_data:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "/usr/local/bin/health-check.sh"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# 数据库迁移服务 (一次性运行)
migrate:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
container_name: photography-migrate
environment:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: photography
DB_USER: ${{ secrets.POSTGRES_PHOTO_USER }}
DB_PASSWORD: ${{ secrets.POSTGRES_PHOTO_PWD }}
DB_SSL_MODE: disable
entrypoint: ["/migrate", "up"]
restart: "no"
volumes:
uploads_data:
driver: local
logs_data:
driver: local
networks:
app_network:
external: true
EOF
# 创建环境变量文件
cat > .env << 'EOF'
# 生产环境配置
DB_HOST=localhost
DB_PORT=5432
DB_NAME=photography
DB_USER=${{ secrets.POSTGRES_PHOTO_USER }}
DB_PASSWORD=${{ secrets.POSTGRES_PHOTO_PWD }}
DB_SSL_MODE=disable
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}
REDIS_DB=0
JWT_SECRET=${{ secrets.JWT_SECRET }}
JWT_EXPIRE=24h
APP_ENV=production
APP_PORT=8080
APP_HOST=0.0.0.0
CORS_ORIGINS=https://photography.iriver.top
UPLOAD_PATH=/app/uploads
UPLOAD_MAX_SIZE=10485760
LOG_LEVEL=info
LOG_FORMAT=json
EOF
echo "✅ 配置文件同步完成"
echo "📋 已部署以下文件:"
ls -la /data/docker/photography/backend/
- name: 🚀 部署到生产环境
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.TYY_USER }}
password: ${{ secrets.TYY_PWD }}
port: ${{ secrets.PORT }}
script: |
# 切换到后端项目目录
cd /data/docker/photography/backend
# 检查 Docker 服务状态
echo "🐳 检查 Docker 服务状态..."
if ! docker info >/dev/null 2>&1; then
echo "❌ Docker 服务未运行或权限不足,尝试使用 sudo..."
echo '${{ secrets.TYY_PWD }}' | sudo -S systemctl start docker
sleep 5
fi
# 验证 Docker 权限
echo "🔍 验证 Docker 权限..."
docker --version || {
echo "❌ Docker 命令不可用"
exit 1
}
# 登录阿里云镜像仓库
echo "🔑 登录阿里云镜像仓库..."
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login ${{ env.REGISTRY }} --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin || {
echo "❌ Docker 登录失败,检查认证信息..."
exit 1
}
# 备份当前运行的容器 (如果存在)
if docker ps -q -f name=photography-api; then
echo "📦 备份当前后端容器..."
docker commit photography-api photography_backend_backup_$(date +%Y%m%d_%H%M%S)
fi
# 停止现有服务
echo "🛑 停止现有服务..."
docker compose down api || {
echo "⚠️ 停止服务时遇到问题,继续执行..."
docker stop photography-api || true
docker rm photography-api || true
}
# 拉取最新镜像
echo "📥 拉取最新镜像..."
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} || {
echo "❌ 镜像拉取失败,检查网络连接..."
exit 1
}
# 数据库迁移需要手动执行
echo "⚠️ 数据库迁移需要手动执行,请在部署后运行:"
echo " docker compose exec api ./main migrate"
# 启动后端服务
echo "🚀 启动后端服务..."
docker compose up -d api
# 等待服务启动
echo "⏳ 等待服务启动..."
sleep 30
# 健康检查
echo "🔍 执行健康检查..."
for i in {1..30}; do
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
echo "✅ 后端服务健康检查通过"
break
fi
echo "等待后端服务启动... ($i/30)"
sleep 10
done
# 检查服务状态
echo "📊 检查服务状态..."
docker compose ps
# 清理旧镜像 (保留最近3个)
echo "🧹 清理旧镜像..."
docker images ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | tail -n +2 | sort -k2 -r | tail -n +4 | awk '{print $1}' | xargs -r docker rmi || true
# 清理旧备份容器 (保留最近5个)
docker images photography_backend_backup_* --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | tail -n +2 | sort -k2 -r | tail -n +6 | awk '{print $1}' | xargs -r docker rmi || true
echo "🎉 后端部署完成!"
echo "📋 请记住手动运行数据库迁移:"
echo " docker compose exec api ./main migrate"
# rollback:
# name: 🔄 回滚部署
# runs-on: ubuntu-latest
# if: failure() && github.ref == 'refs/heads/main'
# needs: build-and-deploy
# steps:
# - name: 🔄 执行回滚
# uses: appleboy/ssh-action@v1.0.0
# with:
# host: ${{ secrets.HOST }}
# username: ${{ secrets.TYY_USER }}
# password: ${{ secrets.TYY_PWD }}
# port: ${{ secrets.PORT }}
# script: |
# cd /data/docker/photography/backend
# echo "🔄 开始回滚后端服务..."
# # 查找最新的备份容器
# BACKUP_IMAGE=$(docker images photography_backend_backup_* --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | tail -n +2 | sort -k2 -r | head -n 1 | awk '{print $1}')
# if [ -n "$BACKUP_IMAGE" ]; then
# echo "📦 找到备份镜像: $BACKUP_IMAGE"
# # 停止当前服务
# docker compose down api
# # 标记备份镜像为最新
# docker tag $BACKUP_IMAGE photography_backend:rollback
# # 修改 docker compose 使用回滚镜像
# sed -i 's|build: .*|image: photography_backend:rollback|g' docker-compose.yml
# # 启动回滚版本
# docker compose up -d api
# echo "✅ 回滚完成"
# else
# echo "❌ 未找到备份镜像,无法回滚"
# exit 1
# fi