3ad430e3e3
支持两种部署模式,兼容新建 MySQL 和现有 MySQL: - Dockerfile:Python 3.12-slim 两阶段构建,非 root 运行 - docker-compose.yml:全栈模式(含 MySQL 8.0 + 可选 Nginx) - docker-compose.external-db.yml:接入现有 MySQL 模式 - docker/entrypoint.sh:自动等待 DB 就绪 → 初始化表 → 启动 Gunicorn - docker/nginx.conf:反向代理 + 静态文件直出 + 安全响应头 - .env.docker.example / .env.external-db.example:各模式配置示例 - .gitattributes:确保 entrypoint.sh 在 Windows 上保持 LF 换行 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.5 KiB
Bash
92 lines
3.5 KiB
Bash
#!/bin/sh
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 容器启动脚本
|
|
# 职责:等待数据库就绪 → 初始化表结构 → 启动 Gunicorn
|
|
# ═══════════════════════════════════════════════════════════════
|
|
set -e
|
|
|
|
# ── 颜色输出 ──────────────────────────────────────────────────
|
|
log() { echo "[entrypoint] $*"; }
|
|
info() { echo "\033[0;36m[entrypoint] $*\033[0m"; }
|
|
ok() { echo "\033[0;32m[entrypoint] ✓ $*\033[0m"; }
|
|
warn() { echo "\033[0;33m[entrypoint] ⚠ $*\033[0m"; }
|
|
err() { echo "\033[0;31m[entrypoint] ✗ $*\033[0m" >&2; }
|
|
|
|
# ── 等待数据库就绪(最多 60 秒)────────────────────────────────
|
|
wait_for_db() {
|
|
info "等待数据库连接就绪…"
|
|
MAX_WAIT=${DB_WAIT_SECONDS:-60}
|
|
elapsed=0
|
|
until python - <<'PYEOF'
|
|
import os, sys, pymysql
|
|
url = os.environ.get('DATABASE_URL', '')
|
|
# 从 DATABASE_URL 解析连接参数
|
|
# 格式: mysql+pymysql://user:pass@host:port/db
|
|
import re
|
|
m = re.match(r'mysql\+pymysql://([^:]+):([^@]+)@([^:/]+):?(\d+)?/(\S+)', url)
|
|
if not m:
|
|
sys.exit(1)
|
|
user, pwd, host, port, db = m.groups()
|
|
port = int(port or 3306)
|
|
try:
|
|
conn = pymysql.connect(host=host, port=port, user=user,
|
|
password=pwd, database=db, connect_timeout=3)
|
|
conn.close()
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
print(f" 未就绪: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
PYEOF
|
|
do
|
|
if [ "$elapsed" -ge "$MAX_WAIT" ]; then
|
|
err "数据库在 ${MAX_WAIT}s 内未就绪,退出"
|
|
exit 1
|
|
fi
|
|
elapsed=$((elapsed + 3))
|
|
sleep 3
|
|
done
|
|
ok "数据库连接成功"
|
|
}
|
|
|
|
# ── 数据库初始化(幂等)────────────────────────────────────────
|
|
init_db() {
|
|
info "执行数据库初始化…"
|
|
python init_db.py \
|
|
--admin-user "${ADMIN_USERNAME:-admin}" \
|
|
--admin-pass "${ADMIN_PASSWORD:-Admin@123456}" \
|
|
--admin-email "${ADMIN_EMAIL:-admin@example.com}"
|
|
ok "数据库初始化完成"
|
|
}
|
|
|
|
# ── 创建上传目录 ───────────────────────────────────────────────
|
|
prepare_dirs() {
|
|
mkdir -p app/static/uploads/{text,image,audio,video,temp}
|
|
ok "上传目录就绪"
|
|
}
|
|
|
|
# ── 主流程 ────────────────────────────────────────────────────
|
|
info "启动个人资料库…"
|
|
info "Python: $(python --version)"
|
|
info "Flask env: ${FLASK_ENV:-development}"
|
|
|
|
prepare_dirs
|
|
wait_for_db
|
|
init_db
|
|
|
|
# ── 启动 Gunicorn ─────────────────────────────────────────────
|
|
WORKERS=${GUNICORN_WORKERS:-4}
|
|
TIMEOUT=${GUNICORN_TIMEOUT:-120}
|
|
BIND=${GUNICORN_BIND:-0.0.0.0:5000}
|
|
|
|
ok "启动 Gunicorn (workers=${WORKERS}, bind=${BIND})"
|
|
|
|
exec gunicorn \
|
|
--workers "$WORKERS" \
|
|
--worker-class sync \
|
|
--timeout "$TIMEOUT" \
|
|
--bind "$BIND" \
|
|
--access-logfile - \
|
|
--error-logfile - \
|
|
--log-level "${LOG_LEVEL:-info}" \
|
|
"run:app"
|