docs: 인프라 이관 가이드 추가 (집 PC → IDC)
운영 서비스 목록, 데이터 볼륨 위치, 노출 포트, 운영 중 적용한 hotfix(Mailu DNSSEC/MariaDB 11.8 등), Phase 별 이관 체크리스트. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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/<volume_name>/_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
|
||||
```
|
||||
Reference in New Issue
Block a user