14 KiB
CTMS 安装步骤向导
推荐使用安装脚本完成 Docker 部署、环境变量、健康检查和数据迁移。手工步骤保留为排障附录。
安装前准备
三个分支都先完成本节。
1. 准备安装目录
mkdir -p /opt/ctms
cd /opt/ctms
2. 安装 Docker
Linux 可使用 Docker Engine 或 Docker Desktop。安装完成后确认 Docker Compose v2 可用:
docker --version
docker compose version
如果当前用户不能执行 Docker 命令,将用户加入 docker 组后重新登录:
sudo usermod -aG docker "$USER"
macOS 安装 Docker Desktop 后执行:
docker --version
docker compose version
3. 安装基础工具
Linux:
sudo apt-get update
sudo apt-get install -y git openssl curl lsof
macOS:
brew install git openssl curl
4. 检查端口
lsof -nP -iTCP:8888 -sTCP:LISTEN
lsof -nP -iTCP:8000 -sTCP:LISTEN
lsof -nP -iTCP:5432 -sTCP:LISTEN
如果命令有输出,先停止占用这些端口的服务,或调整 docker-compose.yaml 中的端口映射。
5. 拉取代码
首次安装:
git clone https://github.com/chengchengzhou7/CTMS.git
cd CTMS
已有仓库:
cd /opt/ctms/CTMS
git fetch --prune origin
6. 确认本地配置不会提交
grep -n "^\\.env$" .gitignore
grep -n "^\\.env\\.\\*$" .gitignore
预期能看到 .env 和 .env.* 已被忽略。
推荐脚本安装
推荐使用根目录交互入口:
./install.sh
执行后通过键盘 ↑/↓ 选择安装、更新、卸载、资源状态等操作;install.sh 不接受命令行参数。菜单顶部会常驻显示当前 CTMS 容器部署状态,包括已检测到的环境、Compose 项目和容器运行数量。
底层脚本入口(适合自动化或排障时直接调用):
scripts/install.sh 安装:依赖检查、配置准备、构建启动、迁移、健康检查
scripts/update.sh 更新:复用安装流程,不执行 git pull
scripts/uninstall.sh 卸载:默认仅停止并移除容器,不删除数据
scripts/status.sh 状态:使用 docker compose stats --no-stream 采集资源快照并美化展示
scripts/common.sh 公共函数:环境映射、输出样式、危险操作确认
安装脚本直接调用格式:
bash scripts/install.sh dev|main|release [选项]
更新、卸载、状态脚本直接调用格式:
bash scripts/update.sh dev|main|release [安装脚本选项]
bash scripts/uninstall.sh dev|main|release
bash scripts/status.sh dev|main|release
常用示例:
bash scripts/install.sh dev
bash scripts/install.sh main --base-url http://127.0.0.1:8888
bash scripts/install.sh release --base-url https://ctms.example.com
可选参数:
--base-url <url> 健康检查使用的访问地址。dev/main 默认 http://127.0.0.1:8888,release 必填或交互输入。
--yes 跳过交互确认,适合自动化执行。
--skip-build 跳过镜像构建,仍会执行 docker compose up -d。
--skip-migrate 跳过 alembic upgrade head。
脚本会执行:
1. 检查 docker、docker compose、openssl、curl。
2. 检查 .env;已存在则保留不改,不存在则新建。
3. 新建 main/release 的 .env 时自动生成 JWT 和 RSA 私钥。
4. main/release 先构建并执行 backend-init,确保数据库迁移使用当前代码中的 Alembic revision。
5. 启动容器;默认执行 docker compose up -d --build,--skip-build 时执行 docker compose up -d。
6. 执行 docker compose run --rm backend python -m alembic upgrade head。
7. 检查容器状态、后端环境、/health、/api/v1/auth/login-key。
更新进入“执行部署更新”后会持续显示 Docker 镜像拉取与构建进度:交互终端使用滚动实时输出窗口,CI、远程面板或管道环境直接流式输出构建日志,并在两种模式下显示累计耗时。
脚本不会执行:
git checkout
git pull
docker compose down -v
删除 pg_data
删除数据库 volume
自动安装系统依赖
如果只想查看参数说明:
bash scripts/install.sh --help
手工安装/排障:dev 分支
1. 切换分支
git checkout dev
git pull --rebase origin dev
2. 写入环境配置
在仓库根目录创建或覆盖 .env:
cat > .env <<'EOF'
COMPOSE_PROJECT_NAME=ctms_dev
ENV=development
JWT_SECRET_KEY=dev-secret
LOGIN_RSA_KEY_ID=default
LOGIN_RSA_PRIVATE_KEY=
EOF
3. 构建镜像并启动
docker compose up -d --build
4. 执行数据库迁移
已有数据库升级到新代码时,启动后执行一次迁移:
docker compose run --rm backend python -m alembic upgrade head
首次空库安装也可以执行该命令;如果已是最新版本,Alembic 不会重复应用迁移。
5. 检查容器状态
docker compose ps
确认 ctms_db、ctms_backend、ctms_nginx 都是 Up,且 ctms_db 为 healthy。
6. 检查后端环境
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY)))"
预期输出:
ENV=development
JWT_SECRET_IS_DEV=True
LOGIN_RSA_PRIVATE_KEY_SET=False
7. 检查接口
curl -i http://127.0.0.1:8888/health
curl -i http://127.0.0.1:8888/api/v1/auth/login-key
两个接口都应返回 HTTP/1.1 200 OK。
8. 打开系统
http://localhost:8888
9. 运行验证
后端:
docker compose run --rm -v "$PWD/backend/tests:/code/tests:ro" backend python -m pytest
前端:
cd frontend
npm install
npm run test:unit
npm run type-check
npm run build
cd ..
手工安装/排障:main 分支
1. 切换分支
git checkout main
git pull --rebase origin main
2. 生成密钥
生成 JWT 密钥:
openssl rand -hex 32
生成 RSA 私钥文件:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out login_rsa_main.pem
生成可写入 .env 的 RSA 私钥单行文本:
awk '{printf "%s\\n", $0}' login_rsa_main.pem
也可以直接生成完整的 .env 配置行,复制输出结果替换 .env 中的 LOGIN_RSA_PRIVATE_KEY:
printf 'LOGIN_RSA_PRIVATE_KEY=%s\n' "$(awk '{printf "%s\\n", $0}' login_rsa_main.pem)"
3. 写入环境配置
在仓库根目录创建或覆盖 .env,将占位符替换为上一步生成的值:
cat > .env <<'EOF'
COMPOSE_PROJECT_NAME=ctms_main
ENV=production
JWT_SECRET_KEY=<替换为 JWT 密钥>
LOGIN_RSA_KEY_ID=main-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY=<替换为 RSA 私钥单行文本>
EOF
4. 检查 Compose 配置
docker compose config
确认输出中包含:
ENV: production
LOGIN_RSA_KEY_ID: main-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY: '-----BEGIN PRIVATE KEY-----...'
5. 初始化数据库
docker compose run --rm --build backend-init
6. 构建镜像并启动
docker compose up -d --build
7. 执行数据库迁移
已有数据库升级到新代码时,启动后执行一次迁移:
docker compose run --rm backend python -m alembic upgrade head
backend-init 会在初始化时处理空库和已有库迁移;这里单独执行迁移用于确认部署代码已落到最新数据库版本。
8. 检查容器状态
docker compose ps
确认 ctms_db、ctms_backend、ctms_nginx 都是 Up,且 ctms_db 为 healthy。
9. 检查后端环境
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY))); print('LOGIN_RSA_KEY_ID=' + settings.LOGIN_RSA_KEY_ID)"
预期输出:
ENV=production
JWT_SECRET_IS_DEV=False
LOGIN_RSA_PRIVATE_KEY_SET=True
LOGIN_RSA_KEY_ID=main-YYYYMMDD
10. 检查接口
本机:
curl -i http://127.0.0.1:8888/health
curl -i http://127.0.0.1:8888/api/v1/auth/login-key
远程域名:
curl -i https://<main-domain>/health
curl -i https://<main-domain>/api/v1/auth/login-key
接口应返回 HTTP/1.1 200 OK。
11. 打开系统
本机:
http://localhost:8888
远程访问:
https://<main-domain>
远程访问必须使用 HTTPS。
12. 运行验证
后端:
docker compose run --rm -v "$PWD/backend/tests:/code/tests:ro" backend python -m pytest
前端:
cd frontend
npm install
npm run test:unit
npm run type-check
npm run build
cd ..
手工安装/排障:release 分支
1. 切换分支
git checkout release
git pull --rebase origin release
2. 准备 HTTPS 域名
正式访问地址必须是:
https://<production-domain>
如果服务器前面有反向代理,确认代理规则:
/ -> CTMS nginx 8888
/api/* -> CTMS nginx 8888
/health -> CTMS nginx 8888
3. 生成密钥
生成 JWT 密钥:
openssl rand -hex 32
生成 RSA 私钥文件:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out login_rsa_release.pem
生成可写入 .env 的 RSA 私钥单行文本:
awk '{printf "%s\\n", $0}' login_rsa_release.pem
也可以直接生成完整的 .env 配置行,复制输出结果替换 .env 中的 LOGIN_RSA_PRIVATE_KEY:
printf 'LOGIN_RSA_PRIVATE_KEY=%s\n' "$(awk '{printf "%s\\n", $0}' login_rsa_release.pem)"
将 login_rsa_release.pem 保存到服务器安全目录或密钥管理系统。
4. 写入环境配置
在仓库根目录创建或覆盖 .env,将占位符替换为生产值:
cat > .env <<'EOF'
COMPOSE_PROJECT_NAME=ctms_release
ENV=production
JWT_SECRET_KEY=<替换为生产 JWT 密钥>
LOGIN_RSA_KEY_ID=release-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY=<替换为生产 RSA 私钥单行文本>
EOF
确认 .env 没有进入 Git 暂存区:
git status --short .env
预期无输出。
5. 检查 Compose 配置
docker compose config
确认输出中包含:
ENV: production
JWT_SECRET_KEY: <不是 dev-secret>
LOGIN_RSA_KEY_ID: release-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY: '-----BEGIN PRIVATE KEY-----...'
6. 初始化数据库
docker compose run --rm --build backend-init
确认命令成功后再启动服务。
7. 构建镜像并启动
docker compose up -d --build
8. 执行数据库迁移
已有数据库升级到新代码时,启动后执行一次迁移:
docker compose run --rm backend python -m alembic upgrade head
backend-init 会在初始化时处理空库和已有库迁移;这里单独执行迁移用于确认部署代码已落到最新数据库版本。
9. 检查容器状态
docker compose ps
确认 ctms_db、ctms_backend、ctms_nginx 都是 Up,且 ctms_db 为 healthy。
10. 检查后端环境
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY))); print('LOGIN_RSA_KEY_ID=' + settings.LOGIN_RSA_KEY_ID)"
预期输出:
ENV=production
JWT_SECRET_IS_DEV=False
LOGIN_RSA_PRIVATE_KEY_SET=True
LOGIN_RSA_KEY_ID=release-YYYYMMDD
11. 检查生产接口
curl -i https://<production-domain>/health
curl -i https://<production-domain>/api/v1/auth/login-key
两个接口都应返回 HTTP/1.1 200 OK。
12. 打开系统并登录
https://<production-domain>
初始化管理员:
admin@huapont.cn / admin123
首次登录后立即修改初始密码。
13. 发布后检查
查看容器:
docker compose ps
查看后端日志:
docker compose logs --tail=200 backend
查看 nginx 日志:
docker compose logs --tail=200 nginx
执行 smoke:
BASE_URL=https://<production-domain> EMAIL=<admin-email> PASSWORD=<admin-password> bash docs/setup-config-curl-smoke.sh
常见安装问题
端口被占用
检查:
lsof -nP -iTCP:8888 -sTCP:LISTEN
lsof -nP -iTCP:8000 -sTCP:LISTEN
lsof -nP -iTCP:5432 -sTCP:LISTEN
处理:停止占用进程,或调整 docker-compose.yaml 端口映射。
容器名冲突
检查:
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
处理:
docker compose down
如果同一台机器要同时运行多套 CTMS,需为每套环境调整 docker-compose.yaml 中的 container_name 和端口。
production 启动失败
检查:
grep -n "ENV\\|JWT_SECRET_KEY\\|LOGIN_RSA" .env
确认:
ENV=production
JWT_SECRET_KEY 不是 dev-secret
LOGIN_RSA_PRIVATE_KEY 不是空值
LOGIN_RSA_PRIVATE_KEY 包含 -----BEGIN PRIVATE KEY-----
远程访问无法登录
检查当前浏览器地址。如果不是 https://,改用 HTTPS 域名访问:
https://<domain>
本机 dev 可使用:
http://localhost:8888
切换分支后环境不正确
重新执行对应分支的 .env 配置,然后重建:
docker compose up -d --build backend nginx
再检查:
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"