712 lines
15 KiB
Markdown
712 lines
15 KiB
Markdown
# 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 <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;保留已有身份与登录密钥,补齐并默认启用标准 ONLYOFFICE 配置,不存在则新建。
|
||
3. 所有环境自动生成独立的 ONLYOFFICE JWT 与稳定实例标识;新建 main/release 的 .env 时同时生成登录 JWT 和 RSA 私钥。
|
||
4. main/release 先构建并执行 backend-init,确保数据库迁移使用当前代码中的 Alembic revision。
|
||
5. 默认构建并启动数据库、后端、前端/Nginx 和 ONLYOFFICE;ONLYOFFICE 不再使用可选 Compose Profile。
|
||
6. 执行 docker compose run --rm backend python -m alembic upgrade head。
|
||
7. 检查全部容器状态、后端环境、/health、/api/v1/auth/login-key 和 /onlyoffice/healthcheck。
|
||
```
|
||
|
||
更新进入“执行部署更新”后会持续显示 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=
|
||
ONLYOFFICE_ENABLED=true
|
||
ONLYOFFICE_JWT_SECRET=<替换为独立的随机密钥,至少 32 个字符>
|
||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||
ONLYOFFICE_INSTANCE_ID=ctms-dev-<替换为随机实例标识>
|
||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||
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 私钥单行文本>
|
||
ONLYOFFICE_ENABLED=true
|
||
ONLYOFFICE_JWT_SECRET=<替换为不同于 JWT_SECRET_KEY 的独立随机密钥>
|
||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||
ONLYOFFICE_INSTANCE_ID=ctms-main-<替换为随机实例标识>
|
||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||
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://<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
|
||
```
|
||
|
||
也可以直接生成完整的 `.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 私钥单行文本>
|
||
ONLYOFFICE_ENABLED=true
|
||
ONLYOFFICE_JWT_SECRET=<替换为不同于 JWT_SECRET_KEY 的独立生产随机密钥>
|
||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||
ONLYOFFICE_INSTANCE_ID=ctms-release-<替换为随机实例标识>
|
||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||
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://<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)"
|
||
```
|