Files
kefu_cloud/deploy/scripts/deploy.sh
T
yml2213 d744fc790c 增加腾讯云生产 Docker 部署编排与一键脚本
- 提供 API/Web 多阶段镜像、Nginx 反代与 WS/文件路径
- docker-compose.prod 含 Postgres、MinIO、API、前端
- deploy.sh 生成密钥、构建启动、可选种子与备份脚本
2026-07-15 15:19:04 +08:00

253 lines
6.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 客服云 — 腾讯云 / 任意 Linux 一键生产部署
# 用法:
# ./deploy/scripts/deploy.sh # 构建并启动
# ./deploy/scripts/deploy.sh --seed # 启动后导入演示账号数据
# ./deploy/scripts/deploy.sh --pull-only # 仅 pull/up,不强制 rebuild(需已有镜像)
# ./deploy/scripts/deploy.sh --down # 停止并移除容器(保留数据卷)
# ./deploy/scripts/deploy.sh --status # 查看状态
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"
COMPOSE_FILE="docker-compose.prod.yml"
ENV_FILE="deploy/.env.prod"
ENV_EXAMPLE="deploy/.env.prod.example"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[deploy]${NC} $*"; }
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
err() { echo -e "${RED}[error]${NC} $*" >&2; }
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
err "缺少命令: $1"
exit 1
fi
}
compose() {
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" "$@"
}
rand_hex() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 24
else
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' | head -c 48
fi
}
ensure_env() {
if [[ ! -f "$ENV_FILE" ]]; then
if [[ ! -f "$ENV_EXAMPLE" ]]; then
err "找不到 $ENV_EXAMPLE"
exit 1
fi
log "未找到 $ENV_FILE,从示例生成…"
cp "$ENV_EXAMPLE" "$ENV_FILE"
local jwt dbpass minio_pass
jwt="$(rand_hex)"
dbpass="$(rand_hex)"
minio_pass="K$(rand_hex | head -c 16)"
# macOS / GNU sed 兼容
if sed --version >/dev/null 2>&1; then
sed -i "s|^JWT_SECRET=.*|JWT_SECRET=${jwt}|" "$ENV_FILE"
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${dbpass}|" "$ENV_FILE"
sed -i "s|^STORAGE_SECRET_KEY=.*|STORAGE_SECRET_KEY=${minio_pass}|" "$ENV_FILE"
else
sed -i '' "s|^JWT_SECRET=.*|JWT_SECRET=${jwt}|" "$ENV_FILE"
sed -i '' "s|^DB_PASSWORD=.*|DB_PASSWORD=${dbpass}|" "$ENV_FILE"
sed -i '' "s|^STORAGE_SECRET_KEY=.*|STORAGE_SECRET_KEY=${minio_pass}|" "$ENV_FILE"
fi
warn "已生成随机 JWT_SECRET / DB_PASSWORD / STORAGE_SECRET_KEY"
warn "请编辑 $ENV_FILE,至少设置:"
warn " APP_BASE_URL=http://你的公网IP或域名"
warn " STORAGE_PUBLIC_BASE_URL=http://你的公网IP或域名/files"
echo
read -r -p "设置完成后按回车继续(或 Ctrl+C 退出先改配置): " _
fi
# shellcheck disable=SC1090
set -a
# 去除 Windows CRLF
source <(sed 's/\r$//' "$ENV_FILE" | grep -v '^\s*#' | grep -v '^\s*$' || true)
set +a
if [[ -z "${JWT_SECRET:-}" || "${JWT_SECRET}" == *"请改"* ]]; then
err "请在 $ENV_FILE 中设置有效的 JWT_SECRET"
exit 1
fi
if [[ -z "${DB_PASSWORD:-}" || "${DB_PASSWORD}" == *"请改"* ]]; then
err "请在 $ENV_FILE 中设置有效的 DB_PASSWORD"
exit 1
fi
if [[ -z "${STORAGE_PUBLIC_BASE_URL:-}" || "${STORAGE_PUBLIC_BASE_URL}" == *"你的"* ]]; then
err "请在 $ENV_FILE 中设置 STORAGE_PUBLIC_BASE_URL(如 http://1.2.3.4/files"
exit 1
fi
if [[ -z "${STORAGE_ACCESS_KEY:-}" || -z "${STORAGE_SECRET_KEY:-}" ]]; then
err "请设置 STORAGE_ACCESS_KEY / STORAGE_SECRET_KEY"
exit 1
fi
if [[ ${#STORAGE_SECRET_KEY} -lt 8 ]]; then
err "STORAGE_SECRET_KEY 长度至少 8 位(MinIO 要求)"
exit 1
fi
}
wait_healthy() {
local url="${1:-http://127.0.0.1:${HTTP_PORT:-80}/health}"
local i
log "等待服务就绪: $url"
for i in $(seq 1 60); do
if curl -fsS "$url" >/dev/null 2>&1; then
log "健康检查通过"
return 0
fi
sleep 2
done
err "健康检查超时,请执行: docker compose -f $COMPOSE_FILE --env-file $ENV_FILE logs"
return 1
}
print_summary() {
local base="${APP_BASE_URL:-http://127.0.0.1:${HTTP_PORT:-80}}"
echo
log "========== 部署完成 =========="
echo " 访问地址: ${base}"
echo " 登录页: ${base}/login"
echo " 健康检查: ${base}/health"
echo " Widget 预览: ${base}/widget/preview"
echo " 图片前缀: ${STORAGE_PUBLIC_BASE_URL}"
echo
echo " 常用命令:"
echo " 查看状态 ./deploy/scripts/deploy.sh --status"
echo " 查看日志 docker compose -f $COMPOSE_FILE --env-file $ENV_FILE logs -f"
echo " 导入种子 ./deploy/scripts/deploy.sh --seed-only"
echo " 停止服务 ./deploy/scripts/deploy.sh --down"
echo
echo " 安全提示: 安全组仅放行 80/443;改默认密码;生产务必使用 HTTPS。"
echo "=============================="
}
cmd_up() {
local seed=0 rebuild=1
while [[ $# -gt 0 ]]; do
case "$1" in
--seed) seed=1 ;;
--pull-only) rebuild=0 ;;
*) ;;
esac
shift
done
need_cmd docker
need_cmd curl
if ! docker compose version >/dev/null 2>&1; then
err "需要 Docker Compose V2docker compose"
exit 1
fi
ensure_env
log "拉取基础镜像…"
compose pull postgres minio minio-init || true
if [[ "$rebuild" -eq 1 ]]; then
log "构建并启动全部服务(api + web + db + minio)…"
compose up -d --build --remove-orphans
else
log "启动服务(不强制 rebuild)…"
compose up -d --remove-orphans
fi
wait_healthy "http://127.0.0.1:${HTTP_PORT:-80}/health" || true
wait_healthy "http://127.0.0.1:${HTTP_PORT:-80}/healthz" || true
if [[ "$seed" -eq 1 ]]; then
log "导入演示种子数据…"
compose --profile seed run --rm seed || warn "种子导入失败(可能已存在数据)"
fi
compose ps
print_summary
}
cmd_seed_only() {
need_cmd docker
ensure_env
log "运行 seed…"
compose --profile seed run --rm seed
log "种子完成。默认租户管理员 kefu_admin / kefu_admin123 (若 seed 成功)"
}
cmd_down() {
need_cmd docker
ensure_env
log "停止容器(保留数据卷)…"
compose down
log "已停止。删除数据卷请手动: docker volume rm kefu-prod_kefu_pgdata kefu-prod_kefu_miniodata"
}
cmd_status() {
need_cmd docker
if [[ -f "$ENV_FILE" ]]; then
compose ps
echo
curl -fsS "http://127.0.0.1:${HTTP_PORT:-80}/health" && echo || warn "health 不可达"
else
err "缺少 $ENV_FILE"
exit 1
fi
}
main() {
local action="${1:-up}"
case "$action" in
up|--up|"")
shift || true
cmd_up "$@"
;;
--seed)
# 兼容:./deploy.sh --seed
cmd_up --seed
;;
--seed-only)
cmd_seed_only
;;
--down|down)
cmd_down
;;
--status|status)
# 尝试读 HTTP_PORT
if [[ -f "$ENV_FILE" ]]; then
# shellcheck disable=SC1090
set -a; source <(sed 's/\r$//' "$ENV_FILE" | grep -v '^\s*#' | grep -v '^\s*$' || true); set +a
fi
cmd_status
;;
--pull-only)
cmd_up --pull-only
;;
-h|--help|help)
sed -n '2,12p' "$0"
;;
*)
err "未知参数: $action (见 --help"
exit 1
;;
esac
}
main "$@"