Add GitHub staging deployment workflow
This commit is contained in:
@@ -0,0 +1,171 @@
|
|||||||
|
name: Deploy Staging
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- internal
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ghcr.io/tawcorp/hifast-server
|
||||||
|
DEPLOY_HOST: 154.12.35.103
|
||||||
|
DEPLOY_PATH: /opt/hifast-server
|
||||||
|
HEALTHCHECK_URL: http://127.0.0.1:8080/v1/common/heartbeat
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
name: Build image and deploy to staging
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
TARGETARCH=amd64
|
||||||
|
VERSION=${{ github.sha }}
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||||||
|
${{ env.IMAGE_NAME }}:staging
|
||||||
|
|
||||||
|
- name: Copy compose file
|
||||||
|
uses: appleboy/scp-action@v0.1.7
|
||||||
|
with:
|
||||||
|
host: ${{ env.DEPLOY_HOST }}
|
||||||
|
username: ${{ secrets.STAGING_SSH_USER }}
|
||||||
|
key: ${{ secrets.STAGING_SSH_KEY }}
|
||||||
|
port: ${{ secrets.STAGING_SSH_PORT || 22 }}
|
||||||
|
source: docker-compose.cloud.yml
|
||||||
|
target: /tmp/hifast-server-deploy/
|
||||||
|
|
||||||
|
- name: Deploy on staging server
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
env:
|
||||||
|
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
|
||||||
|
GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||||
|
with:
|
||||||
|
host: ${{ env.DEPLOY_HOST }}
|
||||||
|
username: ${{ secrets.STAGING_SSH_USER }}
|
||||||
|
key: ${{ secrets.STAGING_SSH_KEY }}
|
||||||
|
port: ${{ secrets.STAGING_SSH_PORT || 22 }}
|
||||||
|
timeout: 300s
|
||||||
|
command_timeout: 600s
|
||||||
|
envs: GHCR_USERNAME,GHCR_TOKEN
|
||||||
|
script: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
IMAGE_NAME="${{ env.IMAGE_NAME }}"
|
||||||
|
NEW_TAG="${{ github.sha }}"
|
||||||
|
DEPLOY_PATH="${{ env.DEPLOY_PATH }}"
|
||||||
|
HEALTHCHECK_URL="${{ env.HEALTHCHECK_URL }}"
|
||||||
|
ROLLBACK_TAG="rollback-${NEW_TAG}"
|
||||||
|
|
||||||
|
if command -v sudo >/dev/null 2>&1 && ! docker ps >/dev/null 2>&1; then
|
||||||
|
SUDO="sudo"
|
||||||
|
else
|
||||||
|
SUDO=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker_cmd() {
|
||||||
|
if [ -n "$SUDO" ]; then
|
||||||
|
sudo docker "$@"
|
||||||
|
else
|
||||||
|
docker "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compose_cmd() {
|
||||||
|
tag="$1"
|
||||||
|
shift
|
||||||
|
if docker compose version >/dev/null 2>&1; then
|
||||||
|
if [ -n "$SUDO" ]; then
|
||||||
|
sudo env PPANEL_SERVER_IMAGE="$IMAGE_NAME" PPANEL_SERVER_TAG="$tag" docker compose -f docker-compose.cloud.yml "$@"
|
||||||
|
else
|
||||||
|
PPANEL_SERVER_IMAGE="$IMAGE_NAME" PPANEL_SERVER_TAG="$tag" docker compose -f docker-compose.cloud.yml "$@"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -n "$SUDO" ]; then
|
||||||
|
sudo env PPANEL_SERVER_IMAGE="$IMAGE_NAME" PPANEL_SERVER_TAG="$tag" docker-compose -f docker-compose.cloud.yml "$@"
|
||||||
|
else
|
||||||
|
PPANEL_SERVER_IMAGE="$IMAGE_NAME" PPANEL_SERVER_TAG="$tag" docker-compose -f docker-compose.cloud.yml "$@"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
health_check() {
|
||||||
|
attempt=1
|
||||||
|
while [ "$attempt" -le 6 ]; do
|
||||||
|
if curl -fsS "$HEALTHCHECK_URL"; then
|
||||||
|
echo
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Health check ${attempt}/6 failed; retrying in 10s..."
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "${GHCR_USERNAME:-}" ] && [ -n "${GHCR_TOKEN:-}" ]; then
|
||||||
|
echo "$GHCR_TOKEN" | docker_cmd login ghcr.io -u "$GHCR_USERNAME" --password-stdin
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SUDO" ]; then
|
||||||
|
sudo mkdir -p "$DEPLOY_PATH"
|
||||||
|
sudo cp /tmp/hifast-server-deploy/docker-compose.cloud.yml "$DEPLOY_PATH/docker-compose.cloud.yml"
|
||||||
|
else
|
||||||
|
mkdir -p "$DEPLOY_PATH"
|
||||||
|
cp /tmp/hifast-server-deploy/docker-compose.cloud.yml "$DEPLOY_PATH/docker-compose.cloud.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$DEPLOY_PATH"
|
||||||
|
|
||||||
|
PREVIOUS_IMAGE_ID="$(docker_cmd inspect --format '{{.Image}}' ppanel-server 2>/dev/null || true)"
|
||||||
|
echo "Previous image ID: ${PREVIOUS_IMAGE_ID:-none}"
|
||||||
|
|
||||||
|
echo "Pulling ${IMAGE_NAME}:${NEW_TAG}"
|
||||||
|
compose_cmd "$NEW_TAG" pull ppanel-server
|
||||||
|
|
||||||
|
echo "Starting ppanel-server"
|
||||||
|
compose_cmd "$NEW_TAG" up -d ppanel-server
|
||||||
|
|
||||||
|
echo "Checking ${HEALTHCHECK_URL}"
|
||||||
|
if health_check; then
|
||||||
|
docker_cmd image prune -f || true
|
||||||
|
echo "Staging deployment succeeded"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Health check failed; attempting rollback"
|
||||||
|
if [ -n "$PREVIOUS_IMAGE_ID" ]; then
|
||||||
|
docker_cmd tag "$PREVIOUS_IMAGE_ID" "${IMAGE_NAME}:${ROLLBACK_TAG}"
|
||||||
|
compose_cmd "$ROLLBACK_TAG" up -d ppanel-server
|
||||||
|
health_check || true
|
||||||
|
else
|
||||||
|
echo "No previous image found; rollback skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker_cmd image prune -f || true
|
||||||
|
exit 1
|
||||||
@@ -19,10 +19,10 @@ services:
|
|||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# 1. 业务后端 (PPanel Server)
|
# 1. 业务后端 (PPanel Server)
|
||||||
# host 网络:可出外网,直接访问 AWS RDS/Redis;通过 127.0.0.1 访问 Tempo
|
# host 网络:可出外网,直接访问 AWS RDS/Redis;通过 127.0.0.1 访问 Tempo
|
||||||
# PPANEL_SERVER_TAG 由 CI/CD 传入不可变镜像标签(如 git SHA)
|
# PPANEL_SERVER_IMAGE/PPANEL_SERVER_TAG 由 CI/CD 传入不可变镜像(如 ghcr.io/org/repo:git SHA)
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
ppanel-server:
|
ppanel-server:
|
||||||
image: registry.kxsw.us/vpn-server:${PPANEL_SERVER_TAG:?please set PPANEL_SERVER_TAG to an immutable image tag}
|
image: ${PPANEL_SERVER_IMAGE:-registry.kxsw.us/vpn-server}:${PPANEL_SERVER_TAG:?please set PPANEL_SERVER_TAG to an immutable image tag}
|
||||||
container_name: ppanel-server
|
container_name: ppanel-server
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# GitHub Staging Deploy
|
||||||
|
|
||||||
|
This repository deploys the `internal` branch to the staging server `154.12.35.103` with GitHub Actions.
|
||||||
|
|
||||||
|
## Required GitHub secrets
|
||||||
|
|
||||||
|
- `STAGING_SSH_USER`: SSH user for `154.12.35.103`.
|
||||||
|
- `STAGING_SSH_KEY`: Private SSH key that can log in to the staging server.
|
||||||
|
- `STAGING_SSH_PORT`: SSH port. Use `22` if the server uses the default port.
|
||||||
|
- `GHCR_USERNAME`: Optional. Required only if the GHCR package is private and the server needs to log in before pulling.
|
||||||
|
- `GHCR_TOKEN`: Optional. A GitHub PAT with `read:packages` for private GHCR pulls.
|
||||||
|
|
||||||
|
## Server requirements
|
||||||
|
|
||||||
|
- Docker and either `docker compose` or `docker-compose` are installed.
|
||||||
|
- The deployment directory is `/opt/hifast-server`.
|
||||||
|
- Runtime files already exist beside `docker-compose.cloud.yml`, especially `configs/ppanel.yaml`, `logs/`, `cache/`, `loki/`, `grafana/`, `prometheus/`, and `tempo/`.
|
||||||
|
|
||||||
|
## What the workflow does
|
||||||
|
|
||||||
|
1. Builds the Docker image for `linux/amd64`.
|
||||||
|
2. Pushes `ghcr.io/tawcorp/hifast-server:<commit-sha>` and `ghcr.io/tawcorp/hifast-server:staging`.
|
||||||
|
3. Copies `docker-compose.cloud.yml` to the staging server.
|
||||||
|
4. Pulls the immutable commit image and restarts `ppanel-server`.
|
||||||
|
5. Checks `http://127.0.0.1:8080/v1/common/heartbeat`.
|
||||||
|
6. Attempts rollback to the previous container image if the health check fails.
|
||||||
Reference in New Issue
Block a user