diff --git a/INFRA_MIGRATION.md b/INFRA_MIGRATION.md new file mode 100644 index 0000000..0589de9 --- /dev/null +++ b/INFRA_MIGRATION.md @@ -0,0 +1,320 @@ +# 인프라 이관 가이드 (집 PC → IDC) + +> 작성: 2026-05-12 / 대상 호스트: `chparkserver` (183.99.177.40) +> 목적: 운영 중인 서비스 전부 IDC 서버로 옮길 때 필요한 정보와 절차 정리 + +--- + +## 1. 현재 호스트 사양 / 환경 + +| 항목 | 값 | +|---|---| +| Hostname | `chparkserver` | +| OS | Ubuntu 24.04.4 LTS (kernel 6.8) | +| Docker | 29.1.3 | +| K3s | v1.34.6+k3s1 | +| 디스크 | `/` 116G / `/data` 916G (사용 121G) | +| 외부 IP | 183.99.177.40 | +| 접속 | SSH (chpark@) | + +**디스크 사용량 분포 (`/data`)**: +``` +51G containerd (K3s container runtime) +31G docker (docker engine data-root) +56G opt (docker compose 운영 파일들) +544M transfer +490M dumps +303M k3s +90M mailu +87M backups ← 이번 사고 대응으로 만든 백업 +``` + +--- + +## 2. 운영 중인 서비스 전체 목록 + +### 2.1 Docker Compose 기반 (직접 운영) + +| Project | 경로 | 도메인/포트 | 비고 | +|---|---|---|---| +| **traefik** | `/data/opt/docker/traefik/` | 80, 443 | HTTPS reverse proxy (Let's Encrypt). 모든 도메인 입구 | +| **mailufinal** | `/data/mailu/` | mail.coa-soft.com (25/110/143/465/587/993/995) | Mailu 메일 서버 9개 컨테이너 | +| **nextcloudfinal** | `/home/chpark/nextcloud/` | cloud.junggomoa.com | Nextcloud + MariaDB 11.8 + Redis | +| **gitea2** | `/data/opt/docker/gitea/` | git.junggomoa.com / 2222 (SSH) | Gitea + PostgreSQL | +| **mattermost2** | `/data/opt/docker/mattermost/` | 8065 | Mattermost + PostgreSQL | +| **registry2** | `/data/opt/docker/registry/` | localhost:5000 | Docker registry (K3s 가 image pull 용) | +| **source** | `/home/chpark/momo-erp/source/` | momotogether.com | momo-erp | +| **tradeing2** | `/home/chpark/tradeing/` | (도메인 확인) | Tradeing 서비스 + PostgreSQL | +| **portainer** | `/data/opt/docker/portainer/` | 9000 (관리 UI) | Docker 관리 UI | + +> ⚠️ **stopped 상태로 남은 옛 프로젝트**: `gitea`, `mailu`, `mattermost`, `nextcloud`, `registry`, `tradeing` — 이전 버전. **이관 시 무시**. + +### 2.2 K3s 위에서 운영 (kubectl 로 관리) + +| Namespace | 서비스 | 도메인 | +|---|---|---| +| `invyone` | frontend / backend-spring / backend-node | www.invyone.com / solution.invyone.com | +| `invyone-homepage` | homepage | (invyone homepage) | +| `insurance` | api / web / postgres-0 | (insurance 도메인) | +| `kubernetes-dashboard` | dashboard / metrics-scraper | (관리용) | +| `portainer` | portainer-agent | (관리용) | +| `kube-system` | coredns / metrics-server / local-path-provisioner | (인프라) | + +> K3s manifests: `/data/k8s-manifests/` 또는 Gitea 의 repo 확인. + +--- + +## 3. 데이터 볼륨 (이관 시 반드시 옮길 것) + +### 3.1 Docker Named Volumes +``` +nextcloud_nextcloud_db ← Nextcloud DB (MariaDB 11.8) +nextcloud_nextcloud_data ← Nextcloud 사용자 파일 +insurance_postgres_data ← Insurance K3s PostgreSQL +insurance_uploads ← Insurance 업로드 파일 +invyone-db-data ← Invyone PostgreSQL +source_momo_data_storage ← Momo ERP 데이터 +``` +호스트 경로: `/data/docker/volumes//_data/` + +### 3.2 Bind Mount 경로 +``` +/data/mailu/ ← Mailu compose + env + 인증서 +/mailu/ ← Mailu data (메일함 실제 저장 위치, /mailu/data) +/data/opt/docker/ ← 각 서비스의 compose 디렉토리 +/home/chpark/nextcloud/ ← Nextcloud compose +/home/chpark/momo-erp/ ← Momo ERP compose + source +/home/chpark/tradeing/ ← Tradeing compose +``` + +### 3.3 K3s/containerd +``` +/data/containerd/ ← K3s container runtime data (이관 비추, K3s 새로 설치 + manifest 재배포 권장) +/data/k3s/ ← K3s state +/data/k8s-manifests/ ← K8s manifest 파일들 (직접 이관) +``` + +--- + +## 4. 외부 노출 포트 (방화벽 규칙) + +| 포트 | 프로토콜 | 용도 | +|---|---|---| +| 80, 443 | TCP | Traefik (HTTPS 리버스 프록시) | +| 25 | TCP | SMTP (메일 수신) | +| 465 | TCP | SMTPS (메일 송신 TLS) | +| 587 | TCP | SMTP Submission | +| 993 | TCP | IMAPS | +| 995 | TCP | POP3S | +| 110, 143 | TCP | POP3/IMAP plain (보안상 disable 권장) | +| 2222 | TCP | Gitea SSH | +| 8065 | TCP | Mattermost | +| 5432 | TCP | **⚠️ invyone-db PostgreSQL 외부 노출** — IDC 이관 시 내부 IP 만 허용하도록 변경 권장 | + +--- + +## 5. 운영 중 적용된 Hotfix 내역 (이관 시 그대로 가져가야 함) + +이관 시 단순히 compose 파일만 복사하면 안 됨. 아래는 컨테이너/네트워크 quirk 대응으로 추가한 패치. + +### 5.1 Mailu (`/data/mailu/docker-compose.yml`) +- **admin / front / antispam** 의 `entrypoint` override: + ```yaml + entrypoint: + - /bin/sh + - -c + - | + echo "nameserver 127.0.0.11" > /etc/resolv.conf + echo "nameserver 192.168.203.2" >> /etc/resolv.conf + echo "search ." >> /etc/resolv.conf + echo "options edns0 trust-ad ndots:0" >> /etc/resolv.conf + exec /start.py + ``` +- **admin / front / antispam** 에 `extra_hosts` (mailu 내부 service IP 직접 박음): + ```yaml + extra_hosts: + - "redis:192.168.203.4" + - "imap:192.168.203.9" + - "smtp:192.168.203.6" + - "antispam:192.168.203.8" + - "webmail:192.168.203.10" + - "front:192.168.203.3" + - "resolver:192.168.203.2" + ``` + > 이유: docker user-defined bridge network 에서 admin 만 DNSSEC AD flag 강제. embedded DNS(127.0.0.11) 가 DNSSEC 안 함 → admin 시작 실패. mailu unbound resolver(192.168.203.2) 가 DNSSEC capable 이라 그쪽으로 박았고, internal service hostname 은 `extra_hosts` 로 박아 NXDOMAIN 방지. + +- **resolver** service 의 `ipv4_address: 192.168.203.254` **제거됨** (자동 할당). + > 이유: 192.168.203.254 IP 가 packet drop 되는 docker bridge quirk 발견. 자동 할당으로 .2 받음. + +- **`/data/mailu/mailu.env`** 에 추가: + ``` + WEBROOT_REDIRECT=/webmail/ + ``` + > 이유: 로그인 후 admin 계정이라 가끔 admin UI 로 redirect 됨. 항상 webmail 로 일관시킴. + +### 5.2 Nextcloud (`/home/chpark/nextcloud/docker-compose.yml`) +- DB 컨테이너 image: `mariadb:10.5` → **`mariadb:11.8`** (원본 데이터 버전 일치) +- `command:` 라인 중복 제거 (이전엔 두 줄이라 첫 번째가 덮어써짐) +- 백업 위치: `/data/backups/nextcloud_db_20260512_080401.tgz` (87MB) +- 추가 mariadb-dump: `/data/backups/nc_db_*.sql` + +### 5.3 Docker daemon (`/etc/docker/daemon.json`) +- 사용자가 이전에 추가한 `"dns": ["8.8.8.8", "1.1.1.1"]` 가 모든 컨테이너 dns 옵션과 합쳐져 충돌. **이관 시 daemon.json 의 dns 키는 빼는 게 안전** (Mailu 가 의존하는 docker embedded DNS 만 사용하도록). + +### 5.4 cron +``` +*/5 * * * * /mailu/sync-certs.sh +0 4 * * * /usr/bin/docker builder prune -af --filter until=72h +5 4 * * * /usr/bin/docker image prune -af --filter until=168h +``` +이관 시 root crontab 그대로 복사. `sync-certs.sh` 는 traefik 발급 인증서를 Mailu 형식으로 변환하는 스크립트. + +--- + +## 6. 이관 절차 (체크리스트) + +### Phase 1 — 신규 IDC 호스트 준비 +- [ ] Ubuntu 24.04 LTS 설치 (현재와 동일 버전 권장) +- [ ] Docker 29.x + Docker Compose v2 설치 +- [ ] K3s 설치 (`curl -sfL https://get.k3s.io | sh -`) +- [ ] 디스크 파티션: `/` (시스템) + `/data` (운영 데이터, **916G 이상 권장**) +- [ ] systemd: `docker`, `k3s`, `containerd` 자동 시작 enable + +### Phase 2 — 데이터 백업 (현재 호스트) +```bash +# 1. 모든 docker compose 정지 (downtime 시작) +for proj in mailufinal nextcloudfinal gitea2 mattermost2 registry2 source tradeing2 traefik portainer; do + docker compose -p $proj down +done + +# 2. K3s 도 정지 (시작 시 컨테이너 자동 stop) +sudo systemctl stop k3s + +# 3. volume + 운영 디렉토리 통째로 백업 +sudo tar -czf /data/backups/all_volumes_$(date +%Y%m%d).tgz \ + /data/docker/volumes \ + /data/mailu \ + /data/opt \ + /data/k8s-manifests \ + /home/chpark/nextcloud \ + /home/chpark/momo-erp \ + /home/chpark/tradeing \ + /mailu + +# 4. crontab 백업 +sudo crontab -l > /data/backups/root_crontab.txt + +# 5. daemon.json 백업 +sudo cp /etc/docker/daemon.json /data/backups/daemon.json +``` + +### Phase 3 — IDC 로 전송 +```bash +# rsync 권장 (재시도 가능) +rsync -avzP --partial --append-verify \ + /data/backups/all_volumes_*.tgz \ + user@idc-host:/data/backups/ +``` + +### Phase 4 — IDC 에서 복원 +```bash +cd / +sudo tar -xzf /data/backups/all_volumes_*.tgz + +# crontab 복원 +sudo crontab /data/backups/root_crontab.txt + +# 각 서비스 기동 +for path in /data/opt/docker/traefik /data/mailu /home/chpark/nextcloud /data/opt/docker/gitea /data/opt/docker/mattermost /data/opt/docker/registry /home/chpark/momo-erp/source /home/chpark/tradeing /data/opt/docker/portainer; do + cd "$path" && docker compose up -d +done + +# K3s manifest 재적용 +kubectl apply -f /data/k8s-manifests/ +``` + +### Phase 5 — DNS 변경 +- 도메인 A 레코드를 새 IDC IP 로 변경 (TTL 짧게 미리 줄여놓으면 좋음) + - `mail.coa-soft.com` + - `cloud.junggomoa.com` + - `git.junggomoa.com` + - `momotogether.com` + - `www.invyone.com` / `solution.invyone.com` + - 기타 +- Mailu MX 레코드도 새 IP 로 + +### Phase 6 — 검증 +```bash +# 컨테이너 상태 +docker ps --filter "name=mailufinal\|nextcloud\|traefik\|gitea" --format "{{.Names}} {{.Status}}" + +# HTTP 확인 +for url in https://mail.coa-soft.com/ https://cloud.junggomoa.com/ https://git.junggomoa.com/ https://momotogether.com/; do + curl -ksS -o /dev/null -w "$url -> %{http_code}\n" "$url" +done + +# SMTP banner +echo "QUIT" | nc -w 3 새IDC_IP 25 +``` + +--- + +## 7. 이관 후 정리할 것 (선택) + +- [ ] 옛 compose project (gitea/mailu/mattermost/nextcloud/registry/tradeing) 삭제 — `docker compose -p <이름> down --remove-orphans` +- [ ] dangling image 정리 — 이미 cron 으로 자동화됨 +- [ ] `/data/backups/` 의 오래된 tarball 정리 +- [ ] `daemon.json` 의 `"dns"` 키 제거 (Mailu admin DNS 충돌 방지) +- [ ] Mailu admin/front/antispam 의 entrypoint override 가 더 이상 필요한지 재확인 (IDC 의 docker network 환경에서는 안 필요할 수도) + +--- + +## 8. 핵심 비밀번호 / 인증 정보 + +> ⚠️ 이 문서에는 비밀번호 직접 안 박는다. 아래 위치에서 확인: + +- Mailu 계정: `/data/mailu/mailu.env` (SECRET_KEY, DB_PW 등) +- Nextcloud DB: `/home/chpark/nextcloud/docker-compose.yml` (MYSQL_ROOT_PASSWORD) +- 각 서비스 admin 계정: 해당 compose 의 environment 또는 env_file +- Traefik basic auth: `/data/opt/docker/traefik/` 안 설정 +- SSH 비밀번호: 운영 PC 관리자가 보관 + +이관 시 새 IDC 에서 비밀번호 **모두 회전(rotation) 권장**. + +--- + +## 9. 알려진 이슈 / 주의사항 + +1. **Mailu resolver 192.168.203.254 IP 회피**: 이번 사고에서 .254 IP 가 packet drop 되는 docker bridge quirk 발견. IDC 에서도 발생할 수 있으니 resolver 는 자동 할당으로 두는 게 안전. + +2. **MariaDB 버전 일치**: Nextcloud DB 는 11.8.x. 이관 시 절대 다운그레이드 금지 (이번에 10.5 로 갔다가 plugin load fail 로 사고남). + +3. **K3s 와 docker 의 iptables/nftables 공존**: K3s 의 kube-router 가 FORWARD chain 에 끼어들어서 docker bridge 통신에 영향을 줄 수 있음. mailu 의 192.168.203.0/24 subnet 이 다른 서비스의 K3s pod CIDR 와 안 겹치는지 확인. + +4. **메일 데이터 위치**: 사용자 메일함 파일은 `/mailu/data/` (호스트 root). `/data/mailu/` 와 다른 위치. 이관 시 둘 다 옮겨야 함. + +5. **인증서**: Let's Encrypt 인증서는 Traefik 이 자동 발급. 이관 후 도메인 가리키면 자동 재발급. 단 rate limit 주의 (도메인당 주 5건). + +--- + +## 10. 빠른 참조 — 자주 쓰는 명령 + +```bash +# 전체 컨테이너 상태 +docker ps --format "{{.Names}}\t{{.Status}}" | sort + +# 특정 서비스 로그 +docker logs --tail 50 mailufinal-admin-1 + +# 한 서비스만 재기동 +cd /data/mailu && docker compose up -d --force-recreate admin + +# Mailu 계정 추가 (admin 컨테이너 안) +docker exec mailufinal-admin-1 flask mailu user $USER $DOMAIN $PASSWORD + +# Nextcloud occ +docker exec -u www-data nextcloud-app-new php occ status + +# K3s pod 상태 +kubectl get pods -A +```