nodebbs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -19
- package/dist/commands/pack/index.js +4 -3
- package/dist/commands/rebuild/index.js +3 -3
- package/dist/commands/start/index.d.ts +1 -1
- package/dist/commands/start/index.js +61 -70
- package/dist/templates/docker-compose.lowmem.yml +49 -3
- package/dist/templates/docker-compose.prod.yml +120 -0
- package/dist/templates/docker-compose.yml +34 -91
- package/dist/templates/env +11 -26
- package/dist/templates/init-db.sql +14 -0
- package/dist/utils/docker.d.ts +1 -1
- package/dist/utils/docker.js +5 -2
- package/dist/utils/env.d.ts +1 -1
- package/dist/utils/selection.d.ts +1 -1
- package/dist/utils/selection.js +6 -5
- package/oclif.manifest.json +112 -146
- package/package.json +1 -1
- package/dist/commands/db/import.d.ts +0 -10
- package/dist/commands/db/import.js +0 -87
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ NodeBBS CLI 是一个专为全栈开发者设计的命令行工具,用于简
|
|
|
15
15
|
- 🎯 **全栈友好** - 命令设计贴近开发者思维
|
|
16
16
|
- 📊 **实时日志** - 方便查看各服务日志
|
|
17
17
|
- 💾 **数据库管理** - 内置数据库迁移和管理工具
|
|
18
|
-
- 🌐 **内置模板** -
|
|
18
|
+
- 🌐 **内置模板** - 无需本地配置文件即可使用
|
|
19
19
|
|
|
20
20
|
## 📦 安装
|
|
21
21
|
|
|
@@ -46,7 +46,7 @@ npx nodebbs [command]
|
|
|
46
46
|
npx nodebbs
|
|
47
47
|
```
|
|
48
48
|
- **start**: 开始部署(首次使用推荐选择此项)
|
|
49
|
-
- **rebuild**:
|
|
49
|
+
- **rebuild**: 重新构建并启动(跳过检查,通常在代码更新后选择此项)
|
|
50
50
|
|
|
51
51
|
## 📚 命令参考
|
|
52
52
|
|
|
@@ -60,25 +60,21 @@ npx nodebbs start
|
|
|
60
60
|
# 指定环境启动
|
|
61
61
|
npx nodebbs start -e production
|
|
62
62
|
|
|
63
|
+
# 重新构建并启动(跳过检查)
|
|
64
|
+
npx nodebbs start --build
|
|
65
|
+
```
|
|
66
|
+
|
|
63
67
|
**参数**:
|
|
64
|
-
- `-e, --env` - 部署环境(production, lowmem)
|
|
65
|
-
- `-
|
|
68
|
+
- `-e, --env` - 部署环境(production, lowmem, basic)
|
|
69
|
+
- `-b, --build` - 重新构建镜像并启动(跳过健康检查和初始化)
|
|
66
70
|
|
|
67
71
|
启动流程包含:
|
|
68
72
|
- Docker 环境检查
|
|
69
73
|
- 环境变量验证
|
|
70
|
-
-
|
|
74
|
+
- 镜像构建
|
|
71
75
|
- 服务启动
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
#### `nodebbs rebuild`
|
|
76
|
-
拉取最新镜像并重启服务 (Update & Restart)
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
npx nodebbs rebuild
|
|
80
|
-
```
|
|
81
|
-
> 此命令是 `start` 的别名,用于快速更新部署。
|
|
76
|
+
- 健康检查(默认开启,使用 `--build` 跳过)
|
|
77
|
+
- 数据库初始化(默认开启,使用 `--build` 跳过)
|
|
82
78
|
|
|
83
79
|
#### `nodebbs restart`
|
|
84
80
|
重启所有服务(不更新镜像,支持环境选择)
|
|
@@ -276,8 +272,9 @@ vi .env
|
|
|
276
272
|
|
|
277
273
|
### 支持的环境
|
|
278
274
|
|
|
275
|
+
- **basic** - 基础环境(仅用于测试)
|
|
279
276
|
- **lowmem** - 低配环境(1C1G/1C2G)
|
|
280
|
-
- **production** -
|
|
277
|
+
- **production** - 生产环境(2C4G+,推荐)
|
|
281
278
|
|
|
282
279
|
### 环境变量
|
|
283
280
|
|
|
@@ -312,7 +309,7 @@ CLI 会自动记住您上次启动的环境:
|
|
|
312
309
|
2. 后续运行 `nodebbs logs`, `nodebbs status` 等命令时,会自动使用该环境,无需再次指定 `-e production`。
|
|
313
310
|
3. 运行 `nodebbs stop` 成功停止服务后,会自动删除 `.nodebbs-env` 文件。
|
|
314
311
|
|
|
315
|
-
**注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e
|
|
312
|
+
**注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e basic`。
|
|
316
313
|
|
|
317
314
|
## 🛠️ 高级用法
|
|
318
315
|
|
|
@@ -333,8 +330,9 @@ CLI 会自动:
|
|
|
333
330
|
### 自定义配置
|
|
334
331
|
|
|
335
332
|
如果需要自定义配置,在项目根目录创建:
|
|
336
|
-
- `docker-compose.yml` -
|
|
337
|
-
- `docker-compose.
|
|
333
|
+
- `docker-compose.yml` - 基础配置
|
|
334
|
+
- `docker-compose.prod.yml` - 生产环境配置
|
|
335
|
+
- `docker-compose.lowmem.yml` - 低配环境配置
|
|
338
336
|
|
|
339
337
|
CLI 会优先使用本地配置文件。
|
|
340
338
|
|
|
@@ -74,6 +74,9 @@ export default class Pack extends Command {
|
|
|
74
74
|
return line;
|
|
75
75
|
});
|
|
76
76
|
fs.writeFileSync(path.join(tmpDir, 'docker-compose.yml'), cleanedLines.join('\n'));
|
|
77
|
+
if (fs.existsSync('docker-compose.prod.yml')) {
|
|
78
|
+
fs.copyFileSync('docker-compose.prod.yml', path.join(tmpDir, 'docker-compose.prod.yml'));
|
|
79
|
+
}
|
|
77
80
|
if (fs.existsSync('.env')) {
|
|
78
81
|
this.warn('检测到 .env 文件,出于安全考虑,不会默认打包 .env 文件。请在部署时手动配置环境变量。');
|
|
79
82
|
}
|
|
@@ -114,9 +117,7 @@ services:
|
|
|
114
117
|
image: nodebbs-web:latest
|
|
115
118
|
EOF
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
docker compose \$COMPOSE_FILES up -d
|
|
120
|
+
docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.override.yml up -d
|
|
120
121
|
|
|
121
122
|
echo "部署完成!"
|
|
122
123
|
`;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import Start from '../start/index.js';
|
|
3
3
|
export default class Rebuild extends Command {
|
|
4
|
-
static description = '
|
|
4
|
+
static description = '重新构建并启动服务 (start --build)';
|
|
5
5
|
// Allow passing flags like -e to the underlying start command
|
|
6
6
|
static strict = false;
|
|
7
7
|
async run() {
|
|
8
|
-
// Invoke Start command
|
|
9
|
-
await Start.run(this.argv);
|
|
8
|
+
// Invoke Start command with --build flag and any other arguments
|
|
9
|
+
await Start.run(['--build', ...this.argv]);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -3,7 +3,7 @@ export default class Start extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
-
|
|
6
|
+
build: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
};
|
|
8
8
|
run(): Promise<void>;
|
|
9
9
|
}
|
|
@@ -4,20 +4,25 @@ import { checkDocker, runCompose, waitForHealth, execCompose, getComposeFiles }
|
|
|
4
4
|
import { initEnv, checkEnv } from '../../utils/env.js';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
6
|
import dotenv from 'dotenv';
|
|
7
|
-
import { input } from '@inquirer/prompts';
|
|
8
7
|
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
9
8
|
export default class Start extends Command {
|
|
10
9
|
static description = '开始部署';
|
|
11
10
|
static flags = {
|
|
12
11
|
env: EnvFlag,
|
|
13
|
-
|
|
14
|
-
char: '
|
|
15
|
-
description: '
|
|
12
|
+
build: Flags.boolean({
|
|
13
|
+
char: 'b',
|
|
14
|
+
description: '重新构建并启动服务 (跳过健康检查和数据初始化)',
|
|
15
|
+
default: false,
|
|
16
16
|
}),
|
|
17
17
|
};
|
|
18
18
|
async run() {
|
|
19
19
|
const { flags } = await this.parse(Start);
|
|
20
|
-
|
|
20
|
+
if (flags.build) {
|
|
21
|
+
logger.header('NodeBBS 重新构建启动');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
logger.header('NodeBBS Docker 部署');
|
|
25
|
+
}
|
|
21
26
|
// 1. 选择环境
|
|
22
27
|
const env = await selectEnvironment(flags.env);
|
|
23
28
|
// 2. 获取 Compose 文件
|
|
@@ -34,83 +39,69 @@ export default class Start extends Command {
|
|
|
34
39
|
else if (env === 'lowmem') {
|
|
35
40
|
logger.success('已选择:低配环境');
|
|
36
41
|
}
|
|
42
|
+
else {
|
|
43
|
+
logger.success('已选择:基础环境');
|
|
44
|
+
if (!flags.build) {
|
|
45
|
+
logger.warning('注意:无资源限制,不推荐用于生产环境。');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
// 3. 检查 Docker 和环境变量
|
|
38
49
|
await checkDocker();
|
|
39
50
|
// initEnv 保证 .env 存在(或退出)
|
|
40
51
|
await initEnv();
|
|
41
52
|
await checkEnv(env);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!tag) {
|
|
47
|
-
tag = await input({
|
|
48
|
-
message: '请选择要部署的镜像版本:',
|
|
49
|
-
default: 'latest'
|
|
53
|
+
if (!flags.build) {
|
|
54
|
+
const continueDeploy = await confirm({
|
|
55
|
+
message: '是否继续启动?',
|
|
56
|
+
default: true
|
|
50
57
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// 否则使用默认 registry
|
|
55
|
-
const defaultApiImage = 'ghcr.io/aiprojecthub/nodebbs-api';
|
|
56
|
-
const defaultWebImage = 'ghcr.io/aiprojecthub/nodebbs-web';
|
|
57
|
-
let currentApiImage = envConfig.API_IMAGE || defaultApiImage;
|
|
58
|
-
let currentWebImage = envConfig.WEB_IMAGE || defaultWebImage;
|
|
59
|
-
const replaceTag = (image, newTag) => {
|
|
60
|
-
const parts = image.split(':');
|
|
61
|
-
if (parts.length > 1 && !parts[parts.length - 1].includes('/')) {
|
|
62
|
-
// 最后一个部分不包含 '/',认为是 tag
|
|
63
|
-
parts.pop();
|
|
58
|
+
if (!continueDeploy) {
|
|
59
|
+
logger.info('操作已取消。');
|
|
60
|
+
this.exit(0);
|
|
64
61
|
}
|
|
65
|
-
return `${parts.join(':')}:${newTag}`;
|
|
66
|
-
};
|
|
67
|
-
process.env.API_IMAGE = replaceTag(currentApiImage, tag);
|
|
68
|
-
process.env.WEB_IMAGE = replaceTag(currentWebImage, tag);
|
|
69
|
-
logger.info(`将部署版本: ${tag}`);
|
|
70
|
-
logger.info(`API Image: ${process.env.API_IMAGE}`);
|
|
71
|
-
logger.info(`Web Image: ${process.env.WEB_IMAGE}`);
|
|
72
|
-
const continueDeploy = await confirm({
|
|
73
|
-
message: '是否继续启动?',
|
|
74
|
-
default: true
|
|
75
|
-
});
|
|
76
|
-
if (!continueDeploy) {
|
|
77
|
-
logger.info('操作已取消。');
|
|
78
|
-
this.exit(0);
|
|
79
62
|
}
|
|
80
|
-
// 4.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await runCompose(composeFiles, ['up', '-d'], isBuiltIn);
|
|
86
|
-
logger.success('服务启动指令已发送');
|
|
87
|
-
// 5. 启动后操作
|
|
88
|
-
await waitForHealth(composeFiles, isBuiltIn);
|
|
89
|
-
const pushDb = await confirm({
|
|
90
|
-
message: '是否推送数据库 schema?',
|
|
91
|
-
default: false
|
|
92
|
-
});
|
|
93
|
-
if (pushDb) {
|
|
94
|
-
logger.info('正在推送数据库 schema...');
|
|
95
|
-
await execCompose(composeFiles, 'api', ['npm', 'run', 'db:push'], isBuiltIn);
|
|
96
|
-
logger.success('数据库 schema 推送完成');
|
|
63
|
+
// 4. 构建并启动服务
|
|
64
|
+
if (flags.build) {
|
|
65
|
+
logger.info('正在重新构建并启动服务...');
|
|
66
|
+
await runCompose(composeFiles, ['up', '-d', '--build'], isBuiltIn);
|
|
67
|
+
logger.success('服务已重新构建并启动');
|
|
97
68
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
logger.success('数据初始化完成');
|
|
69
|
+
else {
|
|
70
|
+
logger.info('正在构建 Docker 镜像...');
|
|
71
|
+
await runCompose(composeFiles, ['build', '--no-cache'], isBuiltIn);
|
|
72
|
+
logger.success('镜像构建完成');
|
|
73
|
+
logger.info('正在启动服务...');
|
|
74
|
+
await runCompose(composeFiles, ['up', '-d'], isBuiltIn);
|
|
75
|
+
logger.success('服务启动指令已发送');
|
|
106
76
|
}
|
|
107
|
-
|
|
77
|
+
// 5. 启动后操作 (仅完整部署)
|
|
78
|
+
if (!flags.build) {
|
|
79
|
+
await waitForHealth(composeFiles, isBuiltIn);
|
|
80
|
+
const pushDb = await confirm({
|
|
81
|
+
message: '是否推送数据库 schema?',
|
|
82
|
+
default: false
|
|
83
|
+
});
|
|
84
|
+
if (pushDb) {
|
|
85
|
+
logger.info('正在推送数据库 schema...');
|
|
86
|
+
await execCompose(composeFiles, 'api', ['npm', 'run', 'db:push'], isBuiltIn);
|
|
87
|
+
logger.success('数据库 schema 推送完成');
|
|
88
|
+
}
|
|
89
|
+
const seedDb = await confirm({
|
|
90
|
+
message: '是否初始化种子数据?',
|
|
91
|
+
default: false
|
|
92
|
+
});
|
|
93
|
+
if (seedDb) {
|
|
94
|
+
logger.info('正在初始化数据...');
|
|
95
|
+
await execCompose(composeFiles, 'api', ['npm', 'run', 'seed'], isBuiltIn);
|
|
96
|
+
logger.success('数据初始化完成');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
logger.header(flags.build ? '启动成功!' : 'NodeBBS 启动成功!');
|
|
108
100
|
// 6. 显示信息
|
|
109
101
|
logger.info(`环境: ${env}`);
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const apiPort = finalEnvConfig.API_PORT || '7100';
|
|
102
|
+
const envConfig = dotenv.config().parsed || {};
|
|
103
|
+
const webPort = envConfig.WEB_PORT || '3100';
|
|
104
|
+
const apiPort = envConfig.API_PORT || '7100';
|
|
114
105
|
console.log(` Web 前端: http://localhost:${webPort}`);
|
|
115
106
|
console.log(` API 服务: http://localhost:${apiPort}`);
|
|
116
107
|
console.log(` API 文档: http://localhost:${apiPort}/docs`);
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
# 低内存环境 Docker Compose 覆盖配置
|
|
2
|
+
# 适用于 1C1G 或 1C2G 的低配服务器
|
|
3
|
+
# 使用方式: docker compose -f docker-compose.yml -f docker-compose.lowmem.yml up -d
|
|
4
|
+
|
|
1
5
|
services:
|
|
2
6
|
# PostgreSQL 数据库 - 低内存优化
|
|
3
7
|
postgres:
|
|
8
|
+
restart: always
|
|
9
|
+
ports: [] # 不暴露端口到主机,只在内部网络访问
|
|
10
|
+
environment:
|
|
11
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
12
|
+
healthcheck:
|
|
13
|
+
interval: 30s
|
|
14
|
+
timeout: 10s
|
|
15
|
+
retries: 5
|
|
16
|
+
start_period: 40s
|
|
4
17
|
deploy:
|
|
5
18
|
resources:
|
|
6
19
|
limits:
|
|
@@ -10,13 +23,14 @@ services:
|
|
|
10
23
|
cpus: '0.1'
|
|
11
24
|
memory: 128M
|
|
12
25
|
logging:
|
|
26
|
+
driver: "json-file"
|
|
13
27
|
options:
|
|
14
28
|
max-size: "5m"
|
|
15
29
|
max-file: "2"
|
|
16
30
|
|
|
17
31
|
# Redis 缓存 - 低内存优化
|
|
18
32
|
redis:
|
|
19
|
-
|
|
33
|
+
restart: always
|
|
20
34
|
command: >
|
|
21
35
|
redis-server
|
|
22
36
|
--requirepass ${REDIS_PASSWORD}
|
|
@@ -26,6 +40,12 @@ services:
|
|
|
26
40
|
--maxmemory-policy allkeys-lru
|
|
27
41
|
--save 900 1
|
|
28
42
|
--save 300 10
|
|
43
|
+
ports: [] # 不暴露端口到主机
|
|
44
|
+
healthcheck:
|
|
45
|
+
interval: 30s
|
|
46
|
+
timeout: 10s
|
|
47
|
+
retries: 5
|
|
48
|
+
start_period: 20s
|
|
29
49
|
deploy:
|
|
30
50
|
resources:
|
|
31
51
|
limits:
|
|
@@ -35,15 +55,23 @@ services:
|
|
|
35
55
|
cpus: '0.1'
|
|
36
56
|
memory: 64M
|
|
37
57
|
logging:
|
|
58
|
+
driver: "json-file"
|
|
38
59
|
options:
|
|
39
60
|
max-size: "5m"
|
|
40
61
|
max-file: "2"
|
|
41
62
|
|
|
42
63
|
# API 服务 - 低内存优化
|
|
43
64
|
api:
|
|
65
|
+
restart: always
|
|
44
66
|
environment:
|
|
45
|
-
|
|
67
|
+
USER_CACHE_TTL: ${USER_CACHE_TTL:-300}
|
|
68
|
+
JWT_SECRET: ${JWT_SECRET}
|
|
69
|
+
CORS_ORIGIN: ${CORS_ORIGIN}
|
|
70
|
+
APP_URL: ${APP_URL}
|
|
71
|
+
# Node.js 内存限制
|
|
46
72
|
NODE_OPTIONS: "--max-old-space-size=384"
|
|
73
|
+
volumes:
|
|
74
|
+
- api_uploads:/app/apps/api/uploads
|
|
47
75
|
healthcheck:
|
|
48
76
|
start_period: 90s # 低内存环境启动更慢
|
|
49
77
|
interval: 60s
|
|
@@ -56,13 +84,22 @@ services:
|
|
|
56
84
|
cpus: '0.2'
|
|
57
85
|
memory: 256M
|
|
58
86
|
logging:
|
|
87
|
+
driver: "json-file"
|
|
59
88
|
options:
|
|
60
89
|
max-size: "10m"
|
|
90
|
+
max-file: "3"
|
|
61
91
|
|
|
62
92
|
# Web 前端服务 - 低内存优化
|
|
63
93
|
web:
|
|
94
|
+
restart: always
|
|
95
|
+
build:
|
|
96
|
+
args:
|
|
97
|
+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
|
98
|
+
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
|
|
64
99
|
environment:
|
|
65
|
-
|
|
100
|
+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
|
101
|
+
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
|
|
102
|
+
# Node.js 内存限制
|
|
66
103
|
NODE_OPTIONS: "--max-old-space-size=384"
|
|
67
104
|
healthcheck:
|
|
68
105
|
start_period: 90s # 低内存环境启动更慢
|
|
@@ -76,5 +113,14 @@ services:
|
|
|
76
113
|
cpus: '0.2'
|
|
77
114
|
memory: 256M
|
|
78
115
|
logging:
|
|
116
|
+
driver: "json-file"
|
|
79
117
|
options:
|
|
80
118
|
max-size: "10m"
|
|
119
|
+
max-file: "3"
|
|
120
|
+
|
|
121
|
+
# 网络配置
|
|
122
|
+
networks:
|
|
123
|
+
nodebbs-network:
|
|
124
|
+
ipam:
|
|
125
|
+
config:
|
|
126
|
+
- subnet: 172.28.0.0/16
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# 生产环境 Docker Compose 覆盖配置
|
|
2
|
+
# 使用方式: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
|
3
|
+
# 本文件只包含与 docker-compose.yml 的差异部分
|
|
4
|
+
|
|
5
|
+
services:
|
|
6
|
+
# PostgreSQL 数据库 - 生产环境优化
|
|
7
|
+
postgres:
|
|
8
|
+
restart: always
|
|
9
|
+
ports: [] # 生产环境不暴露端口到主机,只在内部网络访问
|
|
10
|
+
environment:
|
|
11
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # 生产环境必须设置强密码
|
|
12
|
+
healthcheck:
|
|
13
|
+
interval: 30s
|
|
14
|
+
timeout: 10s
|
|
15
|
+
retries: 5
|
|
16
|
+
start_period: 40s
|
|
17
|
+
deploy:
|
|
18
|
+
resources:
|
|
19
|
+
limits:
|
|
20
|
+
cpus: '1'
|
|
21
|
+
memory: 512M
|
|
22
|
+
reservations:
|
|
23
|
+
cpus: '0.25'
|
|
24
|
+
memory: 256M
|
|
25
|
+
logging:
|
|
26
|
+
driver: "json-file"
|
|
27
|
+
options:
|
|
28
|
+
max-size: "10m"
|
|
29
|
+
max-file: "3"
|
|
30
|
+
|
|
31
|
+
# Redis 缓存 - 生产环境优化
|
|
32
|
+
redis:
|
|
33
|
+
restart: always
|
|
34
|
+
command: >
|
|
35
|
+
redis-server
|
|
36
|
+
--requirepass ${REDIS_PASSWORD}
|
|
37
|
+
--appendonly yes
|
|
38
|
+
--appendfsync everysec
|
|
39
|
+
--maxmemory 256mb
|
|
40
|
+
--maxmemory-policy allkeys-lru
|
|
41
|
+
ports: [] # 生产环境不暴露端口到主机
|
|
42
|
+
healthcheck:
|
|
43
|
+
interval: 30s
|
|
44
|
+
timeout: 10s
|
|
45
|
+
retries: 5
|
|
46
|
+
start_period: 20s
|
|
47
|
+
deploy:
|
|
48
|
+
resources:
|
|
49
|
+
limits:
|
|
50
|
+
cpus: '0.5'
|
|
51
|
+
memory: 256M
|
|
52
|
+
reservations:
|
|
53
|
+
cpus: '0.1'
|
|
54
|
+
memory: 128M
|
|
55
|
+
logging:
|
|
56
|
+
driver: "json-file"
|
|
57
|
+
options:
|
|
58
|
+
max-size: "10m"
|
|
59
|
+
max-file: "3"
|
|
60
|
+
|
|
61
|
+
# API 服务 - 生产环境优化
|
|
62
|
+
api:
|
|
63
|
+
restart: always
|
|
64
|
+
environment:
|
|
65
|
+
USER_CACHE_TTL: ${USER_CACHE_TTL:-300} # 生产环境缓存时间更长
|
|
66
|
+
JWT_SECRET: ${JWT_SECRET} # 生产环境必须设置
|
|
67
|
+
CORS_ORIGIN: ${CORS_ORIGIN} # 生产环境必须明确设置
|
|
68
|
+
APP_URL: ${APP_URL} # 生产环境实际域名
|
|
69
|
+
NODE_OPTIONS: "--max-old-space-size=512" # 限制 Node.js 内存使用
|
|
70
|
+
volumes:
|
|
71
|
+
- api_uploads:/app/apps/api/uploads # 生产环境不挂载源代码
|
|
72
|
+
healthcheck:
|
|
73
|
+
start_period: 60s
|
|
74
|
+
deploy:
|
|
75
|
+
resources:
|
|
76
|
+
limits:
|
|
77
|
+
cpus: '1'
|
|
78
|
+
memory: 768M
|
|
79
|
+
reservations:
|
|
80
|
+
cpus: '0.3'
|
|
81
|
+
memory: 384M
|
|
82
|
+
logging:
|
|
83
|
+
driver: "json-file"
|
|
84
|
+
options:
|
|
85
|
+
max-size: "20m"
|
|
86
|
+
max-file: "5"
|
|
87
|
+
|
|
88
|
+
# Web 前端服务 - 生产环境优化
|
|
89
|
+
web:
|
|
90
|
+
restart: always
|
|
91
|
+
build:
|
|
92
|
+
args:
|
|
93
|
+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
|
94
|
+
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
|
|
95
|
+
environment:
|
|
96
|
+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
|
97
|
+
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
|
|
98
|
+
NODE_OPTIONS: "--max-old-space-size=512" # 限制 Node.js 内存使用
|
|
99
|
+
healthcheck:
|
|
100
|
+
start_period: 60s
|
|
101
|
+
deploy:
|
|
102
|
+
resources:
|
|
103
|
+
limits:
|
|
104
|
+
cpus: '1'
|
|
105
|
+
memory: 768M
|
|
106
|
+
reservations:
|
|
107
|
+
cpus: '0.3'
|
|
108
|
+
memory: 384M
|
|
109
|
+
logging:
|
|
110
|
+
driver: "json-file"
|
|
111
|
+
options:
|
|
112
|
+
max-size: "20m"
|
|
113
|
+
max-file: "5"
|
|
114
|
+
|
|
115
|
+
# 网络 - 生产环境使用固定子网
|
|
116
|
+
networks:
|
|
117
|
+
nodebbs-network:
|
|
118
|
+
ipam:
|
|
119
|
+
config:
|
|
120
|
+
- subnet: 172.28.0.0/16
|