generated from ttipo/project-base
feat(docker): 13단계 운영 기반 — 백업 · 감시 · 관리자 단단히 · 휴지통 30일 · 메일 프로그램 자동 설정
전 직원 전환 전에 서버가 스스로 지키고 되살릴 수 있게 했다. - 관리자 단단히(setup): 관리자 역할에서 impersonate 를 빼고 관리자 허용 IP 를 앱 네트워크로 — 관리자 비밀번호로 바깥 993 · JMAP 에서 직원 메일함에 대리 로그인되던 길을 막았다. 운영에서 처음 걸 때는 되돌릴 복구 관리자가 있을 때만 건다(관리자 잠김 방지). - 휴지통 · 스팸 30일 정리를 KST 03:00 으로 명시. imapsync 로 옮긴 메일도 옮긴 때부터 센다(임시 엔진 실측). - restic 백업(호스트 systemd 03:30 — 메일 볼륨 warm → 엔진 멈춤 9초 안팎 → final, pg_dump, 업로드, env) · 복원(새 서버 차례를 빈 클론에서 실측) · 15분 점검 · 부팅 되살리기, Healthchecks 하트비트. - 메일 프로그램 자동 설정: frontend nginx 의 autoconfig · autodiscover 고정 응답(엔진 것은 POP3 를 알리고 입력을 되돌려 쓴다). - 모든 서비스 docker 로그 보존, DB 이미지 판 · digest 고정. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+41
-4
@@ -1,3 +1,30 @@
|
||||
# ----------------------------------------
|
||||
# 컨테이너 로그 보존 (13단계 — mail/README.md "로그 보존") — 모든 서비스에 명시한다
|
||||
#
|
||||
# 도커 기본(json-file · 회전 없음)이면 로그 파일이 디스크를 끝없이 채운다. 크기로 회전하고(max-size), 정한 개수(max-file)만 남기며,
|
||||
# 회전한 파일은 gzip 으로 줄인다(compress — docker logs 는 그대로 읽는다). json-file 은 줄마다 JSON 껍데기(시각 · stream)가 붙어 글의 1.4배 안팎이다.
|
||||
# 기본(logging-default) 10m × 5 (컨테이너마다 글 35MB 안팎) — db · redis · mail-resolver · clamav · clamav-updater · (개발) mailpit
|
||||
# 길게(logging-long) 20m × 10 (글 140MB 안팎) — mail · frontend · backend
|
||||
# 메일 엔진 인증 · 거절 · 배달 기록. 직원 50명 · 하루 받는 메일 3,000통 · 보내는 메일 750통이면 하루 10MB 안팎 → 2주쯤
|
||||
# frontend nginx 접근 로그(쿼리 · 토큰 없음). 한 사람이 하루 1,500 요청이면 50명에 하루 20MB 안팎 → 1주쯤
|
||||
# backend 경고 · 오류 · 주요 일만(요청마다 남기지 않는다) — 몇 주
|
||||
# (줄 길이는 개발 엔진 151바이트 · backend 176바이트 — 2026-10-31. 직원 수 · 메일 양은 가정이다 — 운영에서 docker logs --since 24h <컨테이너> | wc -c 로 다시 잰다)
|
||||
# 디스크는 모두 차도 0.75GB 안팎(회전한 파일은 압축돼 실제로는 그 몇 분의 1). 더 오래 둬야 하는 기록은 로그 수집기로 — 지금은 두지 않았다
|
||||
# 바꾼 값은 컨테이너를 다시 만들어야 걸린다(up -d 가 다시 만든다)
|
||||
# ----------------------------------------
|
||||
x-logging: &logging-default
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
compress: "true"
|
||||
x-logging-long: &logging-long
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "10"
|
||||
compress: "true"
|
||||
|
||||
services:
|
||||
# ----------------------------------------
|
||||
# 1. Frontend (Nginx — 화면 정적 호스팅 + /api 프록시) — 운영의 유일한 입구
|
||||
@@ -40,7 +67,12 @@ services:
|
||||
# 그 이름의 server 가 공개 화면(/s/)· 공개 API 만 열고 포털은 그 둘을 404 로 닫는다. 비었거나 같으면 포털이 함께 연다 (nginx-env.envsh)
|
||||
- SHARE_PUBLIC_URL=${SHARE_PUBLIC_URL:-}
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-}
|
||||
# 메일 프로그램 자동 설정(13단계) — autoconfig. · autodiscover.<MAIL_DOMAIN> 에 IMAP · SMTP 호스트 MAIL_HOSTNAME 을 고정 XML 로 낸다.
|
||||
# 둘 중 하나라도 비면 끈다 (nginx-env.envsh · mail/README.md "메일 프로그램 자동 설정")
|
||||
- MAIL_DOMAIN=${MAIL_DOMAIN:-}
|
||||
- MAIL_HOSTNAME=${MAIL_HOSTNAME:-}
|
||||
restart: unless-stopped
|
||||
logging: *logging-long
|
||||
# 열린 파일 수 — nginx 의 연결마다 하나(워커마다 worker_connections 4096 — frontend/Dockerfile). 데몬 기본값(배포판 · 판마다 1024 일 수 있다)에 기대지 않는다
|
||||
ulimits:
|
||||
nofile:
|
||||
@@ -163,6 +195,7 @@ services:
|
||||
- uploads:/app/uploads
|
||||
- files:/app/files
|
||||
restart: unless-stopped
|
||||
logging: *logging-long
|
||||
# 바깥에서는 프론트 nginx 의 /api 로만 들어온다 — edge(frontend 전용)와 app-network(db·mail)에만 붙고 proxy 에는 붙지 않는다.
|
||||
# clamav 는 악성 검사기와만 잇는 internal 네트워크다 (7번)
|
||||
networks:
|
||||
@@ -198,6 +231,8 @@ services:
|
||||
# ----------------------------------------
|
||||
db:
|
||||
container_name: ${PROJECT_NAME}-db
|
||||
# 판 · digest 까지 고정한다(루트 env 의 DB_IMAGE — docker 스킬 "이미지 버전 고정"). 메이저(15)는 데이터 형식이라 그대로 둔다 —
|
||||
# 올리려면 pg_dump → 새 메이저에 복원(mail/README.md "백업"의 복원 차례). 아래 watchtower 표시는 13단계에 뺐다(판을 사람이 올린다)
|
||||
image: ${DB_IMAGE}
|
||||
expose:
|
||||
- "5432"
|
||||
@@ -208,6 +243,7 @@ services:
|
||||
volumes:
|
||||
- ${DB_DATA_DIR}:/var/lib/postgresql/data
|
||||
restart: always
|
||||
logging: *logging-default
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
@@ -221,8 +257,6 @@ services:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512m
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
# ----------------------------------------
|
||||
# 4. Redis — 내부 네트워크 전용 · 기본 비활성
|
||||
@@ -241,6 +275,7 @@ services:
|
||||
volumes:
|
||||
- ${REDIS_DATA_DIR}:/data
|
||||
restart: always
|
||||
logging: *logging-default
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
@@ -254,8 +289,6 @@ services:
|
||||
limits:
|
||||
cpus: '0.25'
|
||||
memory: 256m
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
# ----------------------------------------
|
||||
# 5. Mail (Stalwart) — 사내 메일 엔진 · profile `mail`
|
||||
@@ -297,6 +330,7 @@ services:
|
||||
- mail-config:/etc/stalwart
|
||||
- mail-data:/var/lib/stalwart
|
||||
restart: unless-stopped
|
||||
logging: *logging-long
|
||||
# 컨테이너의 DNS — 도커 DNS(127.0.0.11)가 도커 내부 이름(mailpit 등)은 직접 풀고 바깥 이름은 메일 전용 리졸버에 묻는다.
|
||||
# 엔진 자신의 DNS(x:DnsResolver)는 mail setup 이 맞춘다 — 운영은 이 리졸버에 바로(도커 DNS 를 거치지 않는다), 개발은 이 길(System)
|
||||
dns:
|
||||
@@ -348,6 +382,7 @@ services:
|
||||
# 이미지의 기본 설정을 통째로 바꾼다. DNSSEC 신뢰 앵커(/var/unbound/root.key)는 이미지의 것을 쓰고 컨테이너 안에서 고쳐 쓴다
|
||||
- ./mail/resolver/unbound.conf:/etc/unbound/unbound.conf:ro
|
||||
restart: unless-stopped
|
||||
logging: *logging-default
|
||||
networks:
|
||||
mail-dns:
|
||||
ipv4_address: ${MAIL_RESOLVER_IP:-172.16.73.254}
|
||||
@@ -400,6 +435,7 @@ services:
|
||||
- clamav-db:/var/lib/clamav:ro
|
||||
- ./mail/clamav/clamd.conf:/etc/clamav/clamd.conf:ro
|
||||
restart: unless-stopped
|
||||
logging: *logging-default
|
||||
networks:
|
||||
- clamav
|
||||
# 서명이 볼륨에 생긴 뒤에 뜬다 — 처음 기동은 clamav-updater 가 서명을 받는 몇 분을 기다린다(없으면 clamd 가 바로 끝난다).
|
||||
@@ -441,6 +477,7 @@ services:
|
||||
- clamav-db:/var/lib/clamav
|
||||
- ./mail/clamav/freshclam.conf:/etc/clamav/freshclam.conf:ro
|
||||
restart: unless-stopped
|
||||
logging: *logging-default
|
||||
networks:
|
||||
- clamav-egress
|
||||
healthcheck:
|
||||
|
||||
Reference in New Issue
Block a user