Files
kra-new/deploy/docker/deploy.sh

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}}'