provision-server.sh 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. cd "$(dirname "$0")"
  4. source ./deploy.config
  5. KEY_PATH="${KEY_FILE/#\~/$HOME}"
  6. RELEASE_ID="$(date -u +%Y%m%d%H%M%S)"
  7. ARCHIVE="${TMPDIR:-/tmp}/lessncosmos-initial-${RELEASE_ID}.tar.gz"
  8. REMOTE_ARCHIVE="/tmp/lessncosmos-initial-${RELEASE_ID}.tar.gz"
  9. chmod 600 "$KEY_PATH"
  10. npm run build
  11. COPYFILE_DISABLE=1 tar -C dist -czf "$ARCHIVE" .
  12. SSH=(ssh -i "$KEY_PATH" -o BatchMode=yes -o StrictHostKeyChecking=accept-new "$SERVER_USER@$SERVER_HOST")
  13. SCP=(scp -i "$KEY_PATH" -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
  14. "${SCP[@]}" "$ARCHIVE" "$SERVER_USER@$SERVER_HOST:$REMOTE_ARCHIVE"
  15. "${SCP[@]}" nginx.conf "$SERVER_USER@$SERVER_HOST:/tmp/lessncosmos-nginx.conf"
  16. "${SSH[@]}" bash -s -- \
  17. "$REMOTE_ARCHIVE" "$REMOTE_ROOT" "$RELEASE_ID" \
  18. "$SITE_CONTAINER" "$DOCKER_NETWORK" "$PROXY_CONTAINER" "$DOMAIN" <<'REMOTE'
  19. set -euo pipefail
  20. archive="$1"
  21. remote_root="$2"
  22. release_id="$3"
  23. site_container="$4"
  24. docker_network="$5"
  25. proxy_container="$6"
  26. domain="$7"
  27. gateway_config="/opt/meishi_ccdw_life/nginx.conf"
  28. release_dir="$remote_root/releases/$release_id"
  29. begin_marker="# BEGIN MANAGED $domain"
  30. end_marker="# END MANAGED $domain"
  31. if grep -q "server_name $domain" "$gateway_config" && ! grep -qF "$begin_marker" "$gateway_config"; then
  32. echo "Refusing to continue: $domain is already configured outside the managed block." >&2
  33. exit 1
  34. fi
  35. mkdir -p "$release_dir"
  36. tar -xzf "$archive" -C "$release_dir"
  37. ln -sfn "$release_dir" "$remote_root/current"
  38. rm -f "$archive"
  39. if docker inspect "$site_container" >/dev/null 2>&1; then
  40. docker rm -f "$site_container" >/dev/null
  41. fi
  42. docker run -d \
  43. --name "$site_container" \
  44. --restart unless-stopped \
  45. --network "$docker_network" \
  46. -v "$remote_root/current:/usr/share/nginx/html:ro" \
  47. docker.m.daocloud.io/library/nginx:alpine >/dev/null
  48. backup="$gateway_config.backup-$(date -u +%Y%m%d%H%M%S)"
  49. cp "$gateway_config" "$backup"
  50. clean_config="$(mktemp)"
  51. awk -v begin="$begin_marker" -v end="$end_marker" '
  52. $0 == begin { skip=1; next }
  53. $0 == end { skip=0; next }
  54. !skip { print }
  55. ' "$gateway_config" > "$clean_config"
  56. stage_block="$(mktemp)"
  57. cat > "$stage_block" <<STAGE
  58. $begin_marker
  59. server {
  60. listen 80;
  61. server_name $domain;
  62. location / {
  63. proxy_pass http://$site_container:80;
  64. proxy_set_header Host \$host;
  65. proxy_set_header X-Real-IP \$remote_addr;
  66. proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  67. proxy_set_header X-Forwarded-Proto \$scheme;
  68. }
  69. }
  70. $end_marker
  71. STAGE
  72. head -n -1 "$clean_config" > "$gateway_config"
  73. cat "$stage_block" >> "$gateway_config"
  74. printf '}\n' >> "$gateway_config"
  75. if ! docker exec "$proxy_container" nginx -t; then
  76. cp "$backup" "$gateway_config"
  77. exit 1
  78. fi
  79. docker exec "$proxy_container" nginx -s reload
  80. mkdir -p "$remote_root/current/.well-known/acme-challenge"
  81. printf 'ready' > "$remote_root/current/.well-known/acme-challenge/deployment-check"
  82. challenge=""
  83. for _ in 1 2 3 4 5 6 7 8 9 10; do
  84. challenge="$(curl -fsS -H "Host: $domain" http://127.0.0.1/.well-known/acme-challenge/deployment-check 2>/dev/null || true)"
  85. [[ "$challenge" == "ready" ]] && break
  86. sleep 1
  87. done
  88. test "$challenge" = "ready"
  89. certbot certonly --webroot \
  90. --webroot-path "$remote_root/current" \
  91. --domain "$domain" \
  92. --non-interactive --agree-tos --register-unsafely-without-email
  93. final_block="$(mktemp)"
  94. {
  95. printf '%s\n' "$begin_marker"
  96. sed 's/^/ /' /tmp/lessncosmos-nginx.conf
  97. printf '%s\n' "$end_marker"
  98. } > "$final_block"
  99. head -n -1 "$clean_config" > "$gateway_config"
  100. cat "$final_block" >> "$gateway_config"
  101. printf '}\n' >> "$gateway_config"
  102. if ! docker exec "$proxy_container" nginx -t; then
  103. cp "$backup" "$gateway_config"
  104. docker exec "$proxy_container" nginx -s reload
  105. exit 1
  106. fi
  107. docker exec "$proxy_container" nginx -s reload
  108. cat > /etc/letsencrypt/renewal-hooks/deploy/reload-lessncosmos-nginx.sh <<HOOK
  109. #!/bin/sh
  110. docker exec $proxy_container nginx -s reload
  111. HOOK
  112. chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-lessncosmos-nginx.sh
  113. rm -f /tmp/lessncosmos-nginx.conf "$clean_config" "$stage_block" "$final_block"
  114. echo "Provisioned: https://$domain"
  115. REMOTE
  116. rm -f "$ARCHIVE"