feat(deploy): add Ubuntu 26.04 production package
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_command docker
|
||||
require_command gzip
|
||||
require_command sha256sum
|
||||
require_env_file "$env_file"
|
||||
|
||||
backup_dir="$(env_value "$env_file" BACKUP_DIR)"
|
||||
[[ "$backup_dir" == /opt/lehuo/backups || "$backup_dir" == /opt/lehuo/backups/* ]] || {
|
||||
echo 'BACKUP_DIR 必须位于 /opt/lehuo/backups。' >&2
|
||||
exit 1
|
||||
}
|
||||
install -d -m 0700 "$backup_dir"
|
||||
|
||||
database="$(env_value "$env_file" MYSQL_DATABASE)"
|
||||
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
target="$backup_dir/lehuo-${timestamp}.sql.gz"
|
||||
temporary="${target}.tmp"
|
||||
trap 'rm -f -- "$temporary"' EXIT
|
||||
|
||||
compose "$env_file" exec -T mysql sh -c \
|
||||
'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mysqldump --single-transaction --routines --triggers --events --set-gtid-purged=OFF -u root "$MYSQL_DATABASE"' \
|
||||
| gzip -9 > "$temporary"
|
||||
|
||||
gzip -t "$temporary"
|
||||
mv -- "$temporary" "$target"
|
||||
chmod 600 "$target"
|
||||
sha256sum "$target" > "${target}.sha256"
|
||||
chmod 600 "${target}.sha256"
|
||||
find "$backup_dir" -maxdepth 1 -type f \( -name 'lehuo-*.sql.gz' -o -name 'lehuo-*.sql.gz.sha256' \) -mtime +30 -delete
|
||||
|
||||
printf '数据库备份完成:%s(数据库:%s)\n' "$target" "$database"
|
||||
echo '请将备份及 SHA-256 文件同步到异地加密存储。'
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_env_file "$env_file"
|
||||
|
||||
read -r -p '初始管理员用户名:' username
|
||||
read -r -p '初始管理员显示名:' display_name
|
||||
read -r -s -p '初始管理员密码:' password
|
||||
echo
|
||||
[[ -n "$username" && -n "$display_name" && ${#password} -ge 12 ]] || {
|
||||
echo '用户名/显示名不能为空,密码至少 12 位。' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
compose "$env_file" run --rm \
|
||||
-e "BOOTSTRAP_ADMIN_USERNAME=${username}" \
|
||||
-e "BOOTSTRAP_ADMIN_DISPLAY_NAME=${display_name}" \
|
||||
-e "BOOTSTRAP_ADMIN_PASSWORD=${password}" \
|
||||
api node apps/api/dist/scripts/bootstrap-admin.js
|
||||
|
||||
unset password
|
||||
echo '初始管理员创建完成;命令会拒绝重复初始化。'
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_env_file "$env_file"
|
||||
bash "$(dirname -- "${BASH_SOURCE[0]}")/preflight.sh" "$env_file"
|
||||
|
||||
image_tag="$(env_value "$env_file" IMAGE_TAG)"
|
||||
release_dir='/opt/lehuo/releases'
|
||||
install -d -m 0700 "$release_dir"
|
||||
|
||||
current_tag=''
|
||||
if [[ -f "$release_dir/current" ]]; then
|
||||
current_tag="$(<"$release_dir/current")"
|
||||
fi
|
||||
|
||||
echo "构建不可变镜像:${image_tag}"
|
||||
compose "$env_file" build --pull api web
|
||||
|
||||
echo '启动数据服务并初始化私有对象存储。'
|
||||
compose "$env_file" up -d mysql redis minio minio-init
|
||||
|
||||
if [[ -n "$current_tag" ]]; then
|
||||
echo '迁移前创建生产数据库备份。'
|
||||
bash "$(dirname -- "${BASH_SOURCE[0]}")/backup.sh" "$env_file"
|
||||
fi
|
||||
|
||||
echo '执行数据库 migration。'
|
||||
compose "$env_file" --profile tools run --rm migrate
|
||||
|
||||
echo '部署 API 与管理后台网关。'
|
||||
compose "$env_file" up -d --remove-orphans api web
|
||||
|
||||
bash "$(dirname -- "${BASH_SOURCE[0]}")/smoke.sh" "$env_file"
|
||||
|
||||
if [[ -n "$current_tag" && "$current_tag" != "$image_tag" ]]; then
|
||||
printf '%s\n' "$current_tag" > "$release_dir/previous"
|
||||
fi
|
||||
printf '%s\n' "$image_tag" > "$release_dir/current"
|
||||
chmod 600 "$release_dir/current"
|
||||
[[ ! -f "$release_dir/previous" ]] || chmod 600 "$release_dir/previous"
|
||||
|
||||
printf '部署完成,当前镜像标签:%s\n' "$image_tag"
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
usage() {
|
||||
echo '用法:generate-env.sh --public-host admin.example.com --files-host files.example.com [--output /etc/lehuo/lehuo.env]'
|
||||
}
|
||||
|
||||
public_host=''
|
||||
files_host=''
|
||||
output='/etc/lehuo/lehuo.env'
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--public-host) public_host="${2:-}"; shift 2 ;;
|
||||
--files-host) files_host="${2:-}"; shift 2 ;;
|
||||
--output) output="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) usage >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ "$public_host" =~ ^[A-Za-z0-9.-]+$ ]] || { echo 'PUBLIC_HOST 格式无效。' >&2; exit 1; }
|
||||
[[ "$files_host" =~ ^[A-Za-z0-9.-]+$ ]] || { echo 'FILES_HOST 格式无效。' >&2; exit 1; }
|
||||
[[ "$public_host" != "$files_host" ]] || { echo '管理后台/API 域名与文件域名必须不同。' >&2; exit 1; }
|
||||
command -v openssl >/dev/null 2>&1 || { echo '缺少 openssl。' >&2; exit 1; }
|
||||
command -v git >/dev/null 2>&1 || { echo '缺少 git。' >&2; exit 1; }
|
||||
|
||||
if [[ -e "$output" ]]; then
|
||||
printf '拒绝覆盖已有环境文件:%s\n' "$output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -d -m 0700 "$(dirname -- "$output")"
|
||||
umask 077
|
||||
|
||||
mysql_password="$(openssl rand -hex 24)"
|
||||
mysql_root_password="$(openssl rand -hex 32)"
|
||||
redis_password="$(openssl rand -hex 32)"
|
||||
minio_password="$(openssl rand -hex 32)"
|
||||
session_secret="$(openssl rand -hex 32)"
|
||||
session_pepper="$(openssl rand -hex 32)"
|
||||
audit_key="$(openssl rand -hex 32)"
|
||||
field_key="$(openssl rand -base64 32 | tr -d '\n')"
|
||||
image_tag="$(git -C "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.." && pwd)" rev-parse --short=12 HEAD)"
|
||||
|
||||
cat > "$output" <<EOF
|
||||
IMAGE_TAG=${image_tag}
|
||||
PUBLIC_HOST=${public_host}
|
||||
FILES_HOST=${files_host}
|
||||
TLS_CERT_PATH=/opt/lehuo/shared/tls/fullchain.pem
|
||||
TLS_KEY_PATH=/opt/lehuo/shared/tls/privkey.pem
|
||||
BACKUP_DIR=/opt/lehuo/backups
|
||||
MYSQL_DATABASE=lehuo
|
||||
MYSQL_USER=lehuo
|
||||
MYSQL_PASSWORD=${mysql_password}
|
||||
MYSQL_ROOT_PASSWORD=${mysql_root_password}
|
||||
DATABASE_URL=mysql://lehuo:${mysql_password}@mysql:3306/lehuo
|
||||
REDIS_PASSWORD=${redis_password}
|
||||
REDIS_URL=redis://:${redis_password}@redis:6379
|
||||
MINIO_ROOT_USER=lehuo_storage
|
||||
MINIO_ROOT_PASSWORD=${minio_password}
|
||||
OBJECT_STORAGE_ENDPOINT=https://${files_host}
|
||||
OBJECT_STORAGE_REGION=auto
|
||||
OBJECT_STORAGE_BUCKET=lehuo-private
|
||||
OBJECT_STORAGE_ACCESS_KEY=lehuo_storage
|
||||
OBJECT_STORAGE_SECRET_KEY=${minio_password}
|
||||
SESSION_SECRET=${session_secret}
|
||||
SESSION_TOKEN_PEPPER=${session_pepper}
|
||||
AUDIT_IP_HASH_KEY=${audit_key}
|
||||
FIELD_ENCRYPTION_KEY=${field_key}
|
||||
ADMIN_WEB_ORIGIN=https://${public_host}
|
||||
AUTH_SESSION_TTL_SECONDS=28800
|
||||
WECHAT_SESSION_TTL_SECONDS=7200
|
||||
WECHAT_APP_ID=
|
||||
WECHAT_APP_SECRET=
|
||||
WECHAT_TEMPLATE_APPLICATION_SUBMITTED=
|
||||
WECHAT_TEMPLATE_APPLICATION_ACCEPTED=
|
||||
WECHAT_TEMPLATE_SERVICE_ARRANGED=
|
||||
WECHAT_TEMPLATE_SERVICE_COMPLETED=
|
||||
WECHAT_TEMPLATE_FOLLOW_UP_PENDING=
|
||||
WECHAT_TEMPLATE_ACTIVITY_REGISTRATION_RESULT=
|
||||
NOTIFICATION_WORKER_ENABLED=false
|
||||
EOF
|
||||
chmod 600 "$output"
|
||||
|
||||
install -d -m 0700 /opt/lehuo/shared/tls /opt/lehuo/backups /opt/lehuo/releases
|
||||
printf '环境文件已生成:%s\n' "$output"
|
||||
echo '下一步:放置覆盖两个域名的 TLS 证书,然后运行 preflight.sh。'
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo '请使用 sudo 运行此脚本。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ubuntu_dir="$(cd -- "$script_dir/.." && pwd)"
|
||||
repo_root="$(cd -- "$ubuntu_dir/../.." && pwd)"
|
||||
[[ "$repo_root" == '/opt/lehuo/app' ]] || {
|
||||
echo '安装定时器前请把仓库部署到 /opt/lehuo/app。' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ -f /etc/lehuo/lehuo.env ]] || { echo '缺少 /etc/lehuo/lehuo.env。' >&2; exit 1; }
|
||||
|
||||
install -m 0644 "$ubuntu_dir/systemd/lehuo-backup.service" /etc/systemd/system/lehuo-backup.service
|
||||
install -m 0644 "$ubuntu_dir/systemd/lehuo-backup.timer" /etc/systemd/system/lehuo-backup.timer
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now lehuo-backup.timer
|
||||
systemctl list-timers lehuo-backup.timer --no-pager
|
||||
|
||||
echo '每日备份定时器已安装。首次正式发布前仍须手工执行一次备份和恢复演练。'
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo '请使用 sudo 运行此脚本。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r /etc/os-release ]]; then
|
||||
echo '无法识别操作系统。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source /etc/os-release
|
||||
if [[ "${ID:-}" != 'ubuntu' || "${VERSION_ID:-}" != '26.04' ]]; then
|
||||
printf '本部署包仅验证 Ubuntu 26.04;当前为 %s %s。\n' "${ID:-unknown}" "${VERSION_ID:-unknown}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if docker version >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||
echo 'Docker Engine 与 Compose plugin 已安装。'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
conflicts=()
|
||||
for package in docker.io docker-compose docker-compose-v2 podman-docker containerd runc; do
|
||||
if dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q 'install ok installed'; then
|
||||
conflicts+=("$package")
|
||||
fi
|
||||
done
|
||||
if ((${#conflicts[@]})); then
|
||||
printf '检测到可能冲突的软件包:%s\n' "${conflicts[*]}" >&2
|
||||
echo '请先人工确认并按 Docker 官方文档移除冲突包,再重新运行;脚本不会自动卸载现有容器运行时。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
chmod a+r /etc/apt/keyrings/docker.asc
|
||||
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
codename="${UBUNTU_CODENAME:-${VERSION_CODENAME}}"
|
||||
cat > /etc/apt/sources.list.d/docker.sources <<EOF
|
||||
Types: deb
|
||||
URIs: https://download.docker.com/linux/ubuntu
|
||||
Suites: ${codename}
|
||||
Components: stable
|
||||
Architectures: ${architecture}
|
||||
Signed-By: /etc/apt/keyrings/docker.asc
|
||||
EOF
|
||||
|
||||
apt-get update
|
||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable --now docker
|
||||
docker version
|
||||
docker compose version
|
||||
|
||||
echo 'Docker 安装完成。Docker 组等同 root 权限,本脚本不会自动把普通用户加入 docker 组。'
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
ubuntu_dir() {
|
||||
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd
|
||||
}
|
||||
repo_root() {
|
||||
cd -- "$(ubuntu_dir)/../.." && pwd
|
||||
}
|
||||
|
||||
compose_file() {
|
||||
printf '%s/compose.yaml\n' "$(ubuntu_dir)"
|
||||
}
|
||||
|
||||
require_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
printf '缺少命令:%s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_env_file() {
|
||||
local file="$1"
|
||||
[[ -f "$file" ]] || {
|
||||
printf '环境文件不存在:%s\n' "$file" >&2
|
||||
exit 1
|
||||
}
|
||||
local mode
|
||||
mode="$(stat -c '%a' "$file")"
|
||||
if [[ "$mode" != '600' && "$mode" != '400' ]]; then
|
||||
printf '环境文件权限必须是 600 或 400,当前为 %s:%s\n' "$mode" "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
sed -n "s/^${key}=//p" "$file" | tail -n 1
|
||||
}
|
||||
|
||||
compose() {
|
||||
local env_file="$1"
|
||||
shift
|
||||
docker compose --project-directory "$(ubuntu_dir)" --env-file "$env_file" -f "$(compose_file)" "$@"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_command docker
|
||||
require_command curl
|
||||
require_command openssl
|
||||
require_env_file "$env_file"
|
||||
|
||||
docker version >/dev/null
|
||||
docker compose version >/dev/null
|
||||
|
||||
required_keys=(IMAGE_TAG PUBLIC_HOST FILES_HOST TLS_CERT_PATH TLS_KEY_PATH BACKUP_DIR DATABASE_URL REDIS_URL FIELD_ENCRYPTION_KEY ADMIN_WEB_ORIGIN)
|
||||
for key in "${required_keys[@]}"; do
|
||||
value="$(env_value "$env_file" "$key")"
|
||||
[[ -n "$value" && "$value" != replace-* ]] || {
|
||||
printf '环境变量缺失或仍为占位值:%s\n' "$key" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
image_tag="$(env_value "$env_file" IMAGE_TAG)"
|
||||
[[ "$image_tag" != 'latest' ]] || { echo '禁止使用 latest 镜像标签。' >&2; exit 1; }
|
||||
|
||||
public_host="$(env_value "$env_file" PUBLIC_HOST)"
|
||||
files_host="$(env_value "$env_file" FILES_HOST)"
|
||||
[[ "$public_host" != "$files_host" ]] || { echo 'PUBLIC_HOST 与 FILES_HOST 必须不同。' >&2; exit 1; }
|
||||
|
||||
cert_path="$(env_value "$env_file" TLS_CERT_PATH)"
|
||||
key_path="$(env_value "$env_file" TLS_KEY_PATH)"
|
||||
[[ -r "$cert_path" && -r "$key_path" ]] || { echo 'TLS 证书或私钥不存在/不可读。' >&2; exit 1; }
|
||||
openssl x509 -in "$cert_path" -noout -checkend 604800 >/dev/null || {
|
||||
echo 'TLS 证书将在 7 天内过期或无效。' >&2
|
||||
exit 1
|
||||
}
|
||||
openssl x509 -in "$cert_path" -noout -checkhost "$public_host" >/dev/null || {
|
||||
printf 'TLS 证书不包含管理端域名:%s\n' "$public_host" >&2
|
||||
exit 1
|
||||
}
|
||||
openssl x509 -in "$cert_path" -noout -checkhost "$files_host" >/dev/null || {
|
||||
printf 'TLS 证书不包含文件域名:%s\n' "$files_host" >&2
|
||||
exit 1
|
||||
}
|
||||
openssl pkey -in "$key_path" -noout -check >/dev/null
|
||||
|
||||
compose "$env_file" config --quiet
|
||||
|
||||
available_kb="$(df -Pk "$(repo_root)" | awk 'NR==2 {print $4}')"
|
||||
((available_kb >= 10 * 1024 * 1024)) || { echo '可用磁盘不足 10 GiB。' >&2; exit 1; }
|
||||
|
||||
echo 'Ubuntu 生产部署预检通过。'
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
usage() {
|
||||
echo '用法:restore.sh <backup.sql.gz> <target_database> --confirm <target_database> [env-file]'
|
||||
}
|
||||
|
||||
backup="${1:-}"
|
||||
target_database="${2:-}"
|
||||
confirm_flag="${3:-}"
|
||||
confirm_database="${4:-}"
|
||||
env_file="${5:-/etc/lehuo/lehuo.env}"
|
||||
[[ -n "$backup" && -n "$target_database" && "$confirm_flag" == '--confirm' && "$confirm_database" == "$target_database" ]] || {
|
||||
usage >&2
|
||||
exit 1
|
||||
}
|
||||
[[ "$target_database" =~ ^[A-Za-z0-9_]+$ ]] || { echo '目标数据库名无效。' >&2; exit 1; }
|
||||
|
||||
require_command docker
|
||||
require_command gzip
|
||||
require_command sha256sum
|
||||
require_command realpath
|
||||
require_env_file "$env_file"
|
||||
|
||||
backup_dir="$(realpath -m "$(env_value "$env_file" BACKUP_DIR)")"
|
||||
backup_path="$(realpath -e "$backup")"
|
||||
[[ "$backup_path" == "$backup_dir"/* ]] || { echo '备份文件必须位于 BACKUP_DIR。' >&2; exit 1; }
|
||||
[[ -f "${backup_path}.sha256" ]] || { echo '缺少 SHA-256 校验文件。' >&2; exit 1; }
|
||||
(cd -- "$backup_dir" && sha256sum -c "$(basename -- "${backup_path}.sha256")")
|
||||
gzip -t "$backup_path"
|
||||
|
||||
production_database="$(env_value "$env_file" MYSQL_DATABASE)"
|
||||
if [[ "$target_database" == "$production_database" && "${ALLOW_PRODUCTION_RESTORE:-}" != 'YES' ]]; then
|
||||
echo '拒绝覆盖生产数据库。若已完成停机、审批和二次备份,请显式设置 ALLOW_PRODUCTION_RESTORE=YES。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
compose "$env_file" exec -T -e RESTORE_DATABASE="$target_database" mysql sh -c \
|
||||
'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mysql -u root -e "CREATE DATABASE IF NOT EXISTS \`$RESTORE_DATABASE\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci"'
|
||||
gzip -dc "$backup_path" | compose "$env_file" exec -T -e RESTORE_DATABASE="$target_database" mysql sh -c \
|
||||
'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mysql -u root "$RESTORE_DATABASE"'
|
||||
|
||||
printf '恢复完成:%s → %s\n' "$backup_path" "$target_database"
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_env_file "$env_file"
|
||||
release_dir='/opt/lehuo/releases'
|
||||
[[ -f "$release_dir/previous" ]] || { echo '没有可回滚的上一镜像标签。' >&2; exit 1; }
|
||||
|
||||
previous_tag="$(<"$release_dir/previous")"
|
||||
current_tag="$(<"$release_dir/current")"
|
||||
[[ -n "$previous_tag" && "$previous_tag" != 'latest' ]] || { echo '上一镜像标签无效。' >&2; exit 1; }
|
||||
|
||||
if ! docker image inspect "lehuo-api:${previous_tag}" >/dev/null 2>&1 || ! docker image inspect "lehuo-admin:${previous_tag}" >/dev/null 2>&1; then
|
||||
echo '上一版本镜像在本机不存在,停止回滚。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '注意:此操作只回滚应用镜像,不回滚数据库。migration 必须保持向后兼容。'
|
||||
IMAGE_TAG="$previous_tag" compose "$env_file" up -d --no-build api web
|
||||
bash "$(dirname -- "${BASH_SOURCE[0]}")/smoke.sh" "$env_file"
|
||||
printf '%s\n' "$previous_tag" > "$release_dir/current"
|
||||
printf '%s\n' "$current_tag" > "$release_dir/previous"
|
||||
chmod 600 "$release_dir/current" "$release_dir/previous"
|
||||
printf '应用已回滚到:%s\n' "$previous_tag"
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# shellcheck source=lib.sh
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
env_file="${1:-/etc/lehuo/lehuo.env}"
|
||||
require_command curl
|
||||
require_env_file "$env_file"
|
||||
|
||||
public_host="$(env_value "$env_file" PUBLIC_HOST)"
|
||||
files_host="$(env_value "$env_file" FILES_HOST)"
|
||||
|
||||
curl --fail --silent --show-error --max-time 15 "https://${public_host}/health/live" >/dev/null
|
||||
curl --fail --silent --show-error --max-time 15 "https://${public_host}/health/ready" >/dev/null
|
||||
curl --fail --silent --show-error --max-time 15 "https://${public_host}/" >/dev/null
|
||||
curl --fail --silent --show-error --max-time 15 "https://${files_host}/minio/health/live" >/dev/null
|
||||
|
||||
echo 'HTTPS、API readiness、管理后台与对象存储冒烟检查通过。'
|
||||
Reference in New Issue
Block a user