102 lines
3.7 KiB
Bash
102 lines
3.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
TAG="${1:?Usage: deploy.sh TAG [--build-only]}"
|
|
MODE="${2:-}"
|
|
[[ "$TAG" =~ ^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,100}$ ]] || { echo 'Invalid release tag' >&2; exit 1; }
|
|
[[ -z "$MODE" || "$MODE" == '--build-only' ]] || { echo 'Unknown mode' >&2; exit 1; }
|
|
BASE_DIR=/www/wwwroot/sw2.fuhua91.com
|
|
RELEASE="$BASE_DIR/releases/$TAG"
|
|
BACKEND_IMAGE="sw2/backend:$TAG"
|
|
FRONTEND_IMAGE="sw2/frontend:$TAG"
|
|
NETWORK=sw2-net
|
|
NAMES=(sw2-backend sw2-frontend)
|
|
|
|
command -v docker >/dev/null
|
|
command -v curl >/dev/null
|
|
test -f "$RELEASE/backend/Dockerfile"
|
|
test -f "$RELEASE/frontend/Dockerfile"
|
|
|
|
docker build --build-arg "VERSION=$TAG" -t "$BACKEND_IMAGE" "$RELEASE/backend"
|
|
docker build --build-arg "VERSION=$TAG" -t "$FRONTEND_IMAGE" "$RELEASE/frontend"
|
|
docker run --rm "$BACKEND_IMAGE" /app/kratos-admin -h >/dev/null
|
|
docker run --rm --add-host sw2-backend:127.0.0.1 "$FRONTEND_IMAGE" nginx -t
|
|
if [[ "$MODE" == '--build-only' ]]; then exit 0; fi
|
|
|
|
test -s "$BASE_DIR/config/config.yaml"
|
|
test -f "$BASE_DIR/runtime.env"
|
|
install -d -o 1000 -g 1000 -m 0750 "$BASE_DIR/config" "$BASE_DIR/log" "$BASE_DIR/uploads"
|
|
chown 1000:1000 "$BASE_DIR/config/config.yaml"
|
|
chmod 0600 "$BASE_DIR/config/config.yaml" "$BASE_DIR/runtime.env"
|
|
if ! docker network inspect "$NETWORK" >/dev/null 2>&1; then
|
|
docker network create --label com.sw2.application=sw2 "$NETWORK"
|
|
fi
|
|
|
|
managed() {
|
|
[[ "$(docker inspect --format '{{index .Config.Labels "com.sw2.application"}}' "$1")" == sw2 ]]
|
|
}
|
|
exists() { docker inspect "$1" >/dev/null 2>&1; }
|
|
for name in "${NAMES[@]}"; do
|
|
for candidate in "$name" "$name-previous"; do
|
|
if exists "$candidate"; then
|
|
managed "$candidate" || { echo "Refusing to replace unmanaged container: $candidate" >&2; exit 1; }
|
|
fi
|
|
done
|
|
done
|
|
|
|
moved=()
|
|
created=()
|
|
rollback() {
|
|
result=$?
|
|
trap - ERR
|
|
set +eu
|
|
for name in "${created[@]}"; do docker rm -f "$name" >/dev/null 2>&1; done
|
|
for name in "${moved[@]}"; do
|
|
if exists "$name-previous"; then docker rename "$name-previous" "$name"; fi
|
|
docker start "$name"
|
|
done
|
|
echo 'Deployment failed; previous containers restored where present.' >&2
|
|
exit "$result"
|
|
}
|
|
trap rollback ERR
|
|
for name in "${NAMES[@]}"; do
|
|
if exists "$name-previous"; then docker rm "$name-previous" >/dev/null; fi
|
|
if exists "$name"; then
|
|
moved+=("$name")
|
|
docker stop -t 30 "$name" >/dev/null
|
|
docker rename "$name" "$name-previous"
|
|
fi
|
|
done
|
|
|
|
created+=(sw2-backend)
|
|
docker run -d --name sw2-backend --label com.sw2.application=sw2 \
|
|
--restart unless-stopped --network "$NETWORK" \
|
|
--log-opt max-size=20m --log-opt max-file=3 \
|
|
-p 127.0.0.1:9602:8000 --env-file "$BASE_DIR/runtime.env" \
|
|
-v "$BASE_DIR/config:/data/conf" -v "$BASE_DIR/log:/app/logs" -v "$BASE_DIR/uploads:/app/uploads" \
|
|
"$BACKEND_IMAGE"
|
|
|
|
ready=false
|
|
for attempt in $(seq 1 45); do
|
|
if curl -fsS --max-time 3 http://127.0.0.1:9602/health >/dev/null 2>&1; then ready=true; break; fi
|
|
sleep 2
|
|
done
|
|
[[ "$ready" == true ]] || { echo 'Backend health check failed' >&2; false; }
|
|
|
|
created+=(sw2-frontend)
|
|
docker run -d --name sw2-frontend --label com.sw2.application=sw2 \
|
|
--restart unless-stopped --network "$NETWORK" \
|
|
--log-opt max-size=20m --log-opt max-file=3 \
|
|
-p 127.0.0.1:9601:80 "$FRONTEND_IMAGE"
|
|
|
|
ready=false
|
|
for attempt in $(seq 1 15); do
|
|
if curl -fsS --max-time 3 http://127.0.0.1:9601/api/health >/dev/null 2>&1; then ready=true; break; fi
|
|
sleep 2
|
|
done
|
|
[[ "$ready" == true ]] || { echo 'Frontend API proxy health check failed' >&2; false; }
|
|
trap - ERR
|
|
printf '%s\n' "$TAG" > "$BASE_DIR/current-release"
|
|
install -m 0750 "$RELEASE/deploy.sh" "$BASE_DIR/deploy.sh"
|
|
docker ps --filter label=com.sw2.application=sw2 --format '{{.Names}} {{.Image}} {{.Status}} {{.Ports}}'
|