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