Commit ae624632 by luoqi

fix(ops): compose 显式声明 json-file 日志驱动 + rotate —— podman 上看不到日志

【podman 差异】docker 默认就是 json-file(写不写行为一样),但 **podman rootless 默认
journald**。2026-08-04 新服务器(Ubuntu 26.04 + podman 5.7 兼容层)实测:
  · `docker logs <c>`  → 只回一行兼容层提示
  · `podman logs <c>`  → 0 行
  · 日志进用户 journal,普通用户不在 systemd-journal 组 → 读不到
等于线上出问题**完全看不到日志**。今天排查那四个增量 bug 全靠 docker logs,这在新环境行不通。
显式声明后两种运行时行为一致。

【顺带治一个既有隐患】此前没有任何 rotate —— 当前生产 /var/lib/docker/containers 已 1.4 GB,
只增不减。50m × 5 = 单容器上限 250MB:够排查近期问题,又不会把盘吃满
(2026-08-01 测试服正是被写满后 Postgres 写不了 pg_wal 崩溃重启)。

用 YAML anchor 统一 7 个服务,避免逐个漂移。对 docker 侧唯一的行为变化是**加了 rotate**,
驱动本身就是原默认值。

顺带纠正一个误判:pac-asr 早已是 `profiles: ["asr"]` 默认不启(当前生产也没跑它),
「迁移时 ASR 不部署」不需要改 deploy 脚本 —— 之前判断错了,已用测试钉住该事实。

测试 828 项(+4)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 0cf8409e
Pipeline #3529 failed in 0 seconds
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import * as yaml from 'js-yaml';
/**
* compose 日志驱动契约。
*
* 【为什么显式声明 json-file】docker 默认就是 json-file(写不写行为一样),但
* **podman rootless 默认是 journald**。2026-08-04 新服务器实测:
* - `docker logs <c>` 只回一行兼容层提示
* - `podman logs <c>` 返回 0 行
* - 日志进了用户 journal,而普通用户不在 systemd-journal 组 → 读不到
* 等于线上出问题看不到日志。显式声明让两种运行时行为一致。
*
* 【顺带治既有隐患】此前无任何 rotate —— 当前生产 /var/lib/docker/containers 已 1.4 GB
* 且只增不减。2026-08-01 测试服正是被写满后 Postgres 崩溃重启(pg_wal 写不下)。
*/
const compose = yaml.load(
readFileSync(join(__dirname, '../../../docker-compose.prod.yml'), 'utf-8'),
) as { services: Record<string, { logging?: { driver?: string; options?: Record<string, string> }; profiles?: string[] }> };
describe('docker-compose.prod.yml 日志驱动', () => {
const names = Object.keys(compose.services);
test('⭐ 每个服务都声明了 logging —— 漏一个,那个容器在 podman 上就看不到日志', () => {
const missing = names.filter((n) => !compose.services[n]!.logging);
expect(missing).toEqual([]);
});
test('驱动是 json-file(不是 podman 默认的 journald)', () => {
for (const n of names) {
expect(compose.services[n]!.logging!.driver).toBe('json-file');
}
});
test('⭐ 带 rotate 上限 —— 否则日志只增不减,最终吃满磁盘', () => {
for (const n of names) {
const opts = compose.services[n]!.logging!.options ?? {};
expect(opts['max-size']).toBeTruthy();
expect(opts['max-file']).toBeTruthy();
}
});
test('pac-asr 仍是 profile 可选服务(默认不启,迁移时无需额外处理)', () => {
expect(compose.services['pac-asr']!.profiles).toContain('asr');
});
});
...@@ -17,8 +17,24 @@ ...@@ -17,8 +17,24 @@
# #
# 内部网络:容器之间用服务名访问(postgres:5432 / redis:6379),容器内 port 用 image 默认 # 内部网络:容器之间用服务名访问(postgres:5432 / redis:6379),容器内 port 用 image 默认
# ─── 日志驱动(所有服务共用)────────────────────────────────────────────
# 【为什么显式声明 json-file】docker 的默认就是 json-file,写不写行为一样;但 **podman
# rootless 默认是 journald** —— 2026-08-04 新服务器实测:`docker logs` 只回一行兼容层提示,
# `podman logs` 返回 0 行,而容器日志进了用户 journal、普通用户不在 systemd-journal 组读不到,
# 等于**线上出问题看不到日志**。显式声明让两种运行时行为一致。
#
# 【顺带治一个既有隐患】此前没有任何 rotate —— 当前生产 /var/lib/docker/containers 已 1.4 GB,
# 且只会一直涨。50m × 5 = 单容器上限 250MB,够排查近期问题,又不会把盘吃满
# (2026-08-01 测试服就是被写满后 Postgres 崩溃重启的)。
x-logging: &default-logging
driver: json-file
options:
max-size: "50m"
max-file: "5"
services: services:
postgres: postgres:
logging: *default-logging
image: postgres:16-alpine image: postgres:16-alpine
restart: always restart: always
env_file: ./apps/pac-service/.env # 读 POSTGRES_USER/PASSWORD/DB env_file: ./apps/pac-service/.env # 读 POSTGRES_USER/PASSWORD/DB
...@@ -39,6 +55,7 @@ services: ...@@ -39,6 +55,7 @@ services:
retries: 5 retries: 5
redis: redis:
logging: *default-logging
image: redis:7-alpine image: redis:7-alpine
restart: always restart: always
command: ["redis-server", "--appendonly", "yes"] command: ["redis-server", "--appendonly", "yes"]
...@@ -51,6 +68,7 @@ services: ...@@ -51,6 +68,7 @@ services:
# Runs before pac-service so the schema is current before any traffic # Runs before pac-service so the schema is current before any traffic
# hits the API. Safe to re-run — `migrate deploy` is idempotent. # hits the API. Safe to re-run — `migrate deploy` is idempotent.
pac-migrate: pac-migrate:
logging: *default-logging
build: build:
context: . context: .
dockerfile: apps/pac-service/Dockerfile dockerfile: apps/pac-service/Dockerfile
...@@ -67,6 +85,7 @@ services: ...@@ -67,6 +85,7 @@ services:
command: ["npx", "prisma", "migrate", "deploy"] command: ["npx", "prisma", "migrate", "deploy"]
pac-service: pac-service:
logging: *default-logging
build: build:
context: . context: .
dockerfile: apps/pac-service/Dockerfile dockerfile: apps/pac-service/Dockerfile
...@@ -97,6 +116,7 @@ services: ...@@ -97,6 +116,7 @@ services:
# 启用 → docker compose --profile asr -f docker-compose.prod.yml up -d # 启用 → docker compose --profile asr -f docker-compose.prod.yml up -d
# 不启用时 pac-service 的 PAC_ASR_URL 指向的服务不在,transcribe 端点会失败(不影响其它功能/启动)。 # 不启用时 pac-service 的 PAC_ASR_URL 指向的服务不在,transcribe 端点会失败(不影响其它功能/启动)。
pac-asr: pac-asr:
logging: *default-logging
profiles: ["asr"] profiles: ["asr"]
build: build:
context: . context: .
...@@ -113,6 +133,7 @@ services: ...@@ -113,6 +133,7 @@ services:
mem_limit: 2g mem_limit: 2g
pac-web: pac-web:
logging: *default-logging
build: build:
context: . context: .
dockerfile: apps/pac-web/Dockerfile dockerfile: apps/pac-web/Dockerfile
...@@ -136,6 +157,7 @@ services: ...@@ -136,6 +157,7 @@ services:
- "127.0.0.1:3100:3100" - "127.0.0.1:3100:3100"
pac-docs: pac-docs:
logging: *default-logging
build: build:
context: . context: .
dockerfile: apps/pac-docs/Dockerfile dockerfile: apps/pac-docs/Dockerfile
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment