# 인프라 이관 가이드 (집 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 ```