Files
life-echo/.github/workflows/docker-build-deploy.yml
2026-01-28 16:15:57 +08:00

192 lines
7.0 KiB
YAML
Raw 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: Docker Build and Deploy
on:
push:
branches:
- dev/add-agent
paths:
- 'api/**'
- '.github/workflows/**'
workflow_dispatch: # 允许手动触发
env:
IMAGE_NAME: lifecho-api
REGISTRY: crpi-u2903xccyzd6nqnc.cn-shanghai.personal.cr.aliyuncs.com
REGISTRY_NAMESPACE: huaga
jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Alibaba Cloud Container Registry
env:
REGISTRY: ${{ env.REGISTRY }}
USERNAME: ${{ secrets.ALIYUN_CR_USERNAME }}
PASSWORD: ${{ secrets.ALIYUN_CR_PASSWORD }}
run: |
echo "正在登录到阿里云容器镜像服务..."
echo "Registry: $REGISTRY"
echo "Username: $USERNAME"
echo "Password length: ${#PASSWORD}"
# 使用 printf 确保密码正确传递(包括特殊字符)
printf '%s\n' "$PASSWORD" | docker login "$REGISTRY" --username="$USERNAME" --password-stdin
echo "✅ 登录成功!"
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.REGISTRY_NAMESPACE }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./api
file: ./api/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
name: Deploy to Remote Server
runs-on: ubuntu-latest
needs: build-and-push
if: github.event_name != 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Add server to known hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H -p ${{ secrets.SSH_PORT || 22 }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Determine image tag
id: image_tag
run: |
if [ "${{ github.ref_name }}" == "main" ] || [ "${{ github.ref_name }}" == "master" ]; then
echo "tag=latest" >> $GITHUB_OUTPUT
else
# 将分支名中的斜杠替换为破折号,以符合 Docker 标签规范
BRANCH_TAG=$(echo "${{ github.ref_name }}" | sed 's/\//-/g')
echo "tag=$BRANCH_TAG" >> $GITHUB_OUTPUT
fi
- name: Deploy to remote server
env:
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT || 22 }}
IMAGE_TAG: ${{ env.REGISTRY }}/${{ env.REGISTRY_NAMESPACE }}/${{ env.IMAGE_NAME }}:${{ steps.image_tag.outputs.tag }}
COMPOSE_FILE: docker-compose.yml
COMPOSE_DIR: ${{ secrets.DEPLOY_PATH || '/opt/life-echo' }}
REGISTRY: ${{ env.REGISTRY }}
ALIYUN_CR_USERNAME: ${{ secrets.ALIYUN_CR_USERNAME }}
ALIYUN_CR_PASSWORD: ${{ secrets.ALIYUN_CR_PASSWORD }}
run: |
echo "开始部署到远程服务器..."
echo "镜像标签: $IMAGE_TAG"
echo "部署目录: $COMPOSE_DIR/api"
# 登录到阿里云容器仓库
echo "$ALIYUN_CR_PASSWORD" | ssh -p $SSH_PORT $SSH_USER@$SSH_HOST \
"docker login $REGISTRY --username=$ALIYUN_CR_USERNAME --password-stdin"
# 创建部署目录(如果不存在)
ssh -p $SSH_PORT $SSH_USER@$SSH_HOST \
"mkdir -p $COMPOSE_DIR/api"
# 第一步:先停止并删除旧容器(使用服务器上现有的 docker-compose.yml
echo "停止并删除旧容器..."
ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "
cd $COMPOSE_DIR/api 2>/dev/null || exit 0
if [ -f '$COMPOSE_FILE' ]; then
echo '使用旧配置停止容器...'
docker-compose -f '$COMPOSE_FILE' down --remove-orphans || true
fi
# 清理可能残留的容器
echo '清理残留容器...'
docker stop life-echo-api-prod life-echo-celery-worker life-echo-postgres life-echo-redis 2>/dev/null || true
docker rm -f life-echo-api-prod life-echo-celery-worker life-echo-postgres life-echo-redis 2>/dev/null || true
"
# 第二步:复制新的配置文件到远程服务器
echo "复制配置文件..."
scp -P $SSH_PORT ./api/$COMPOSE_FILE $SSH_USER@$SSH_HOST:$COMPOSE_DIR/api/
# 复制 .env.production 到远程服务器(重命名为 .env.prod
echo "复制 .env.production 文件..."
scp -P $SSH_PORT ./api/.env.production $SSH_USER@$SSH_HOST:$COMPOSE_DIR/api/.env.prod
# 第三步:在远程服务器上执行部署操作
ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "
set -e
cd $COMPOSE_DIR/api
echo '拉取最新镜像: $IMAGE_TAG'
docker pull '$IMAGE_TAG' || true
echo '备份并更新 docker-compose.yml 中的镜像标签...'
cp '$COMPOSE_FILE' '${COMPOSE_FILE}.bak'
# 更新所有包含 lifecho-api 或 life-echo-api 的 image 行
sed -i.tmp 's|image:.*lifecho-api.*|image: $IMAGE_TAG|g' '$COMPOSE_FILE'
sed -i.tmp 's|image:.*life-echo-api.*|image: $IMAGE_TAG|g' '$COMPOSE_FILE'
# 清理临时文件
rm -f '${COMPOSE_FILE}.tmp' 2>/dev/null || true
echo '启动新容器...'
docker-compose -f '$COMPOSE_FILE' pull || true
docker-compose -f '$COMPOSE_FILE' up -d
echo '等待容器启动...'
sleep 15
echo '清理旧镜像...'
docker image prune -f || true
echo '部署完成!'
echo '检查容器状态...'
docker-compose -f '$COMPOSE_FILE' ps
"
- name: Verify deployment
env:
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT || 22 }}
COMPOSE_DIR: ${{ secrets.DEPLOY_PATH || '/opt/life-echo' }}
run: |
echo "验证部署状态..."
ssh -p $SSH_PORT $SSH_USER@$SSH_HOST \
"cd $COMPOSE_DIR/api && docker-compose ps && docker-compose logs --tail=50 api"