596 lines
10 KiB
Markdown
596 lines
10 KiB
Markdown
# CTMS 安装步骤向导
|
||
|
||
按目标分支选择对应章节执行:
|
||
|
||
- [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.*` 已被忽略。
|
||
|
||
## 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
|
||
```
|
||
|
||
### 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 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://<main-domain>/health
|
||
curl -i https://<main-domain>/api/v1/auth/login-key
|
||
```
|
||
|
||
接口应返回 `HTTP/1.1 200 OK`。
|
||
|
||
### 11. 打开系统
|
||
|
||
本机:
|
||
|
||
```text
|
||
http://localhost:8888
|
||
```
|
||
|
||
远程访问:
|
||
|
||
```text
|
||
https://<main-domain>
|
||
```
|
||
|
||
远程访问必须使用 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://<production-domain>
|
||
```
|
||
|
||
如果服务器前面有反向代理,确认代理规则:
|
||
|
||
```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
|
||
```
|
||
|
||
将 `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 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://<production-domain>/health
|
||
curl -i https://<production-domain>/api/v1/auth/login-key
|
||
```
|
||
|
||
两个接口都应返回 `HTTP/1.1 200 OK`。
|
||
|
||
### 12. 打开系统并登录
|
||
|
||
```text
|
||
https://<production-domain>
|
||
```
|
||
|
||
初始化管理员:
|
||
|
||
```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://<production-domain> EMAIL=<admin-email> PASSWORD=<admin-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://<domain>
|
||
```
|
||
|
||
本机 `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)"
|
||
```
|