diff --git a/infra/compute/_index.md b/infra/compute/_index.md index 58abfb6..58bdcfb 100644 --- a/infra/compute/_index.md +++ b/infra/compute/_index.md @@ -11,4 +11,5 @@ tags: [moc, compute] | [[infra-hosts]] | 인프라 호스트 및 네트워크 (SSH 접속, Incus, Tailscale) | | [[amd-vi-iommu]] | AMD-Vi (IOMMU) Completion-Wait timeout 부분 hang 메커니즘과 차단 (`iommu=pt`) | | [[k3s-migration]] | K3s PostgreSQL 백엔드 이전 기록 | +| [[k3s-to-incus-migration]] | K3s → Incus 워크로드 이전 플랜 (2026-06 결정) | | [[zlambda]] | zlambda Linode 도쿄 NixOS VM | diff --git a/infra/compute/k3s-to-incus-migration.md b/infra/compute/k3s-to-incus-migration.md new file mode 100644 index 0000000..71ba527 --- /dev/null +++ b/infra/compute/k3s-to-incus-migration.md @@ -0,0 +1,244 @@ +--- +title: K3s → Incus 이전 플랜 +updated: + - 2026-06-01 초안 작성 (kappa 결정 — K3s 운영부담 감축 위해 Incus 전환) +tags: [infra, compute, k3s, incus, migration, plan] +status: draft +--- + +## 배경 + +K3s 운영 부담이 누적되어 Incus 전환 결정. 주요 통증: + +- Longhorn 리밸런싱·drain hang·VolumeAttachment 수동 정리 +- kine → HAProxy → Patroni 다단 의존, 장애 cascade ([[../../history/2026-05-23-kr1-k3s-stuck-cascade]]) +- kr2 RAM 30GiB에서 K3s + Incus 동시 호스팅 → OOM freeze ([[../../history/2026-05-04-amd-iommu-freeze]] 포함) +- multus·MetalLB·descheduler 튜닝, kube-system CRD/operator 다수 (cert-manager, ArgoCD 등) 유지비 +- helm/ArgoCD GitOps 학습·디버깅 비용 + +반대로 Incus는 이미 정착(Vault, Patroni, ops 에이전트 모두 incus), OpenTofu 모듈 보유, OCI 이미지 1급 지원. + +## 목표 + +- 서울 K3s 클러스터(kr1·kr2·hp1·hp2) 워크로드를 incus 컨테이너/VM으로 이전 +- K3s 자체 폐기 (kine·Patroni 의존 종료, Longhorn 폐기) +- 운영 면적 축소: GitOps·CRD·CNI·CSI·CRDS 레이어 제거 + +## 비목표 + +- jp1 Incus 호스트 그대로 유지 (도쿄, Vault·agents 정본) +- 서울 4대 Incus 클러스터링은 별도 결정 ([[infra-hosts]] 참조). 이전은 호스트 분리 상태로 진행 +- Outline/Slack 등 외부 SaaS 이전 (해당 없음) + +## 디폴트 결정 — 대체 패턴 + +플랜 단계에서 확정. 변경 시 본 문서 history에 명시. + +| 레이어 | K3s 현행 | Incus 대체 | 근거 | +|--------|---------|-----------|------| +| 진입점/라우팅 | MetalLB + Traefik + APISIX | OpenWrt HAProxy → 컨테이너 IP 직접 | 이미 80/443/5432 HAProxy 경유. APISIX 서울은 운영 면적 큼 (etcd 3-replica StatefulSet) | +| TLS 인증서 | cert-manager + Let's Encrypt | acme.sh + Cloudflare DNS-01, HAProxy/SafeLine reload | CF global key 이미 운영. cert-manager CRD 학습 비용 회피 | +| 스토리지 | Longhorn (분산 블록) | Synology NAS NFS/iSCSI 직접 마운트 + 호스트 btrfs 로컬 | 분산 복제는 앱 레벨 HA(Patroni)로 한정. NAS는 democratic-csi 없이 직접 | +| 프로비저닝 | ArgoCD + Helm (GitGitea) | OpenTofu + cloud-init (ops-agents-tofu 패턴 확장) | 기존 헤임달/씬에서 검증 | +| 시크릿 | K8s Secret + Vault Agent | Vault API 직접 (envconsul/sidecar/init) | Vault는 이전 없음 | +| 로깅 | VictoriaLogs + vector daemonset | VictoriaLogs 컨테이너 + vector systemd unit on host | 컨테이너 수집은 console.log tail | +| 메트릭 | VictoriaMetrics + node-exporter daemonset | VictoriaMetrics 컨테이너 + node-exporter systemd on host | 동일 패턴 | +| 메시지 큐 | RabbitMQ operator + cluster | RabbitMQ 단일 incus 컨테이너 (현 사용량 단일로 충분) | operator 폐기, 필요 시 페어 | +| WAF | SafeLine in K3s (6 컴포넌트) | SafeLine docker-compose → incus 단일 컨테이너 묶음 | 공식 docker-compose 그대로, K3s 분산 가치 적음 | +| HA 정책 | Pod 자동 재스케줄 | 디폴트 active-passive (호스트 다운 시 분 단위 다운타임 수용) | Patroni·Vault만 자체 HA. 나머지는 분 단위 다운 허용 | + +미결정 (Phase 진행 중 결정): +- **APISIX 서울 — 유지 vs HAProxy 통합 흡수**: 현재 ApisixRoute는 `juiceshop.keepanker.cv` 1건 + SafeLine 통합. HAProxy + SafeLine 직결로 통합 흡수 권장 (Phase 4에서 확정) +- **GitOps 대체 — OpenTofu만 vs Tofu+Ansible**: Phase 1 시범 후 결정 +- **kr1 GTX 1080 Ti 활용**: Incus GPU 컨테이너로 유지, K3s 종료 후 워크로드 미정 + +## 배치 정책 + +| 호스트 | 권장 워크로드 | 사유 | +|--------|-------------|------| +| kr1 (62GiB + GPU) | GPU 컨테이너, brokkr(현행), DB replica | GPU 자원, control-plane 부담 제거 | +| kr2 (30GiB) | inbest 7컨테이너 유지, mariadb-3/postgres-3 유지, **신규 무거운 워크로드 금지** | RAM 작음. K3s 폐기 후에도 OOM 위험 | +| hp1 (188GiB) | 메인 호스트 — Gitea, Outline, OpenMemory, SafeLine, VictoriaLogs/Metrics | RAM 여유, 베어메탈 | +| hp2 (188GiB) | hp1 짝꿍 — jarvis, trader 유지 + 분산 부담 분배, mariadb-1/postgres-1 유지 | RAM 여유, Tailscale 가입 | + +## Phase 0 — 대체 패턴 인프라 셋업 + +K3s 워크로드 이전 전 인프라 토대 구축. 약 1~2주. + +- [ ] **OpenTofu 컨테이너 모듈** 작성 (`kaffa/ops-agents-tofu`에 `modules/incus-service/` 추가): Incus 컨테이너 + 디스크 device + proxy device + cloud-init 표준화 +- [ ] **공통 베이스 이미지** (Debian 13 + vector + node-exporter + tailscale 선택적): incus image build, 캐시 push +- [ ] **acme.sh 인증서 발급기**: hp1에 incus 컨테이너 `acme` 신설, Cloudflare DNS-01 + Vault에서 CF global key 조회. hook으로 HAProxy reload +- [ ] **HAProxy 확장**: OpenWrt의 `/etc/haproxy/haproxy.cfg`에 신규 backend 추가 검증 (현재 Traefik VIP 192.168.9.53 백엔드 → 컨테이너 직접 IP로 점진 전환) +- [ ] **NAS 마운트 표준**: Synology NFS export 디렉터리 구조 결정 (`/volume1/incus-data//`), btrfs subvol 옵션, idmap 정책 +- [ ] **VictoriaLogs/Metrics incus 이전 사전 작업**: jp1 monitoring 프로젝트 또는 hp1에 신규 컨테이너 — K3s VictoriaMetrics 데이터 마이그레이션 dry-run +- [ ] **롤백 스냅샷 정책**: `incus snapshot` 자동화 — 이전 후 일정 기간 보관 + +성공 기준: Phase 1 시범 서비스를 Tofu apply → 컨테이너 RUN → HAProxy 라우팅 → 인증서 발급까지 무인 실행 + +## Phase 1 — 시범 이전 (1~2주) + +패턴 검증용. 다운타임 영향 작은 서비스만. + +| 순서 | 서비스 | 출처 ns | 목표 호스트 | 메모 | +|------|--------|--------|-----------|------| +| 1 | kroki | kroki | hp1 | stateless, 다이어그램 렌더링. 의존성 없음 | +| 2 | searxng | searxng | hp1 | stateless 검색 프록시 | +| 3 | juice-shop | juiceshop | hp2 | 테스트용, jp1 default에도 컨테이너 존재 → 통합 검토 | + +검증 항목: +- OpenTofu 모듈로 컨테이너 생성·재생성 멱등성 +- OCI 이미지 자동 업데이트 패턴 (blue-green vs in-place restart) +- HAProxy → 컨테이너 IP 직접 라우팅 + SafeLine 통과 (필요 시) +- acme.sh 인증서 발급·갱신 +- vector → VictoriaLogs 수집 (K3s 잔존 + 신규 incus 동시 수집) + +성공 기준: 3 서비스 정상 가동 + DNS 전환 후 1주 무이슈 + +## Phase 2 — Stateless·단일 컨테이너 이전 (2~3주) + +| 순서 | 서비스 | 출처 ns | 목표 호스트 | 메모 | +|------|--------|--------|-----------|------| +| 1 | smtp-relay | mail | hp1 | ArgoCD App, 단순 | +| 2 | BunnyCDN MCP | mcp | hp1 | ArgoCD App | +| 3 | cfb-manager | tools | hp1 | ArgoCD `cf-bouncer-manager` | +| 4 | Namecheap API | api | hp1 | ArgoCD | +| 5 | Vultr API | api | hp1 | ArgoCD | +| 6 | NocoDB | tools | hp1 | Patroni 사용 — DB 연결만 유지 | +| 7 | n8n | n8n | hp1 | Helm. Postgres 연결만 유지. workflow 데이터 백업 후 마이그레이션 | +| 8 | sftpgo | sftpgo | hp2 | SFTP 22 노출 — HAProxy TCP 라우팅 | +| 9 | sshpiper | sshpiper | hp2 | jp1에 sshpiper 컨테이너도 있음 — 역할 분리 확인 후 | +| 10 | teleport | teleport | hp1 | auth + proxy 2 컴포넌트, 단일 incus 컨테이너로 통합 가능 | + +각 항목 작업: +1. OpenTofu apply로 컨테이너 생성 +2. 데이터 이전 (PVC → NAS 또는 컨테이너 disk) +3. K3s 측에서 동일 DNS로 dual-run, healthcheck +4. HAProxy backend 전환 (K3s VIP → incus 컨테이너 IP) +5. 1주 모니터 후 K3s 측 helm uninstall / kubectl delete ns +6. ArgoCD Application 삭제 + +## Phase 3 — 멀티 컴포넌트 이전 (2~3주) + +| 순서 | 서비스 | 출처 ns | 목표 호스트 | 구성 | +|------|--------|--------|-----------|------| +| 1 | Gitea | gitea | hp1 | gitea + valkey 두 컨테이너. PostgreSQL은 Patroni 그대로. valkey는 컨테이너 동거 또는 별도 | +| 2 | Outline | outline | hp1 | outline + redis 두 컨테이너. PostgreSQL은 Patroni | +| 3 | OpenMemory | openmemory | hp1 | mcp + ui + qdrant 세 컨테이너. qdrant 데이터는 NAS NFS | +| 4 | SafeLine WAF | safeline | hp1 | 공식 docker-compose 기반, 단일 incus 컨테이너 안에서 compose 실행 (예외적 사용) 또는 multi-container profile | +| 5 | RabbitMQ | mq | hp1 | 단일 컨테이너로 충분. operator·cluster 폐기 | +| 6 | PgCat/ProxySQL | db | hp1 또는 jp1 db 프로젝트 | Patroni pooler 위치 재검토 | + +작업 패턴 (Phase 2와 동일하나 데이터 마이그레이션 비중 큼): +- Postgres 의존: 이전 중 connection string만 incus 컨테이너 IP로 전환 +- Redis/valkey/qdrant: 데이터 export → import 또는 NAS 볼륨 통째로 이동 +- Gitea: LFS 데이터 NAS 이전 + clone/push 무중단 테스트 + +## Phase 4 — 게이트웨이 정리 (1주) + +- [ ] **APISIX 서울 폐기 결정 확정**: HAProxy + SafeLine 직결 vs APISIX 유지 + - HAProxy 통합 시: `juiceshop.keepanker.cv` 라우트 1건을 HAProxy backend로 이전, SafeLine chaitin-waf 플러그인 → SafeLine 컨테이너 직접 통과 + - 유지 시: APISIX를 incus 단일 컨테이너로 이전 (etcd 단일 또는 sqlite backend) +- [ ] **Traefik 폐기**: 모든 HTTPRoute/IngressRoute → HAProxy backend로 이전 완료 확인. K3s Traefik helm uninstall +- [ ] **MetalLB 폐기**: LoadBalancer Service 없음 확인 후 helm uninstall +- [ ] **cert-manager 폐기**: 모든 Certificate CR 무사용 확인 후 helm uninstall + +## Phase 5 — K3s 인프라 폐기 (1~2주) + +- [ ] **Longhorn 데이터 마이그레이션 완료 확인**: 모든 PVC가 NAS NFS 또는 호스트 디스크로 이전됨. `kubectl get pv` 비어있음 +- [ ] **Longhorn helm uninstall + longhorn-system ns 삭제** +- [ ] **ArgoCD helm uninstall + argocd ns 삭제**: GitOps 저장소(`kaffa/k3s-charts` 등) 아카이브 +- [ ] **VictoriaLogs/Metrics 데이터 incus로 이전**: jp1 monitoring 또는 hp1 신규 컨테이너에서 단일 인스턴스 운영 +- [ ] **kube-system 잔여 정리**: descheduler, multus, nfs-provisioner 등 +- [ ] **Patroni → kine 의존 제거**: K3s 자체 종료 직전 단계. Patroni는 그대로 유지 (다른 워크로드가 사용) + +## Phase 6 — K3s 종료 (1주) + +- [ ] kr1·kr2 control-plane drain + `k3s-uninstall.sh` +- [ ] hp1·hp2 agent drain + `k3s-agent-uninstall.sh` +- [ ] OpenWrt HAProxy에서 K3s 관련 backend 전체 삭제 +- [ ] DNS — `k3s.inouter.com`, `*.inouter.com` 매핑 정리 +- [ ] [[infra-hosts]] 업데이트: K3s 섹션 → history로 이동, 호스트 역할 재정의 +- [ ] Obsidian K3s 관련 정본 문서 → `history/2026-XX-XX-k3s-decommission.md`로 이전 + +## 서비스 매핑 요약 + +| 서비스 | 현재 (K3s) | 이전 후 (Incus) | Phase | +|--------|-----------|----------------|-------| +| kroki | kroki ns | hp1 컨테이너 `kroki` | 1 | +| searxng | searxng ns | hp1 컨테이너 `searxng` | 1 | +| juice-shop | juiceshop ns | hp2 컨테이너 (jp1과 통합 검토) | 1 | +| smtp-relay | mail ns | hp1 컨테이너 `smtp-relay` | 2 | +| BunnyCDN MCP | mcp ns | hp1 컨테이너 `bunny-mcp` | 2 | +| cfb-manager | tools ns | hp1 컨테이너 `cfb-manager` | 2 | +| Namecheap/Vultr API | api ns | hp1 컨테이너 `dns-api` (통합 또는 분리) | 2 | +| NocoDB | tools ns | hp1 컨테이너 `nocodb` | 2 | +| n8n | n8n ns | hp1 컨테이너 `n8n` | 2 | +| sftpgo | sftpgo ns | hp2 컨테이너 `sftpgo` | 2 | +| sshpiper | sshpiper ns | hp2 컨테이너 `sshpiper-seoul` (jp1과 역할 분리) | 2 | +| Teleport | teleport ns | hp1 컨테이너 `teleport` (auth+proxy 통합) | 2 | +| Gitea | gitea ns | hp1 컨테이너 `gitea` + `gitea-valkey` | 3 | +| Outline | outline ns | hp1 컨테이너 `outline` + `outline-redis` | 3 | +| OpenMemory | openmemory ns | hp1 컨테이너 `openmemory-mcp` + `openmemory-ui` + `openmemory-qdrant` | 3 | +| SafeLine WAF | safeline ns | hp1 컨테이너 `safeline` (compose 내부) | 3 | +| RabbitMQ | mq ns | hp1 컨테이너 `rabbitmq` | 3 | +| PgCat/ProxySQL | db ns | hp1 또는 jp1 db 프로젝트 | 3 | +| VictoriaLogs/Metrics | logging/monitoring ns | hp1 컨테이너 또는 jp1 monitoring | 5 | +| APISIX 서울 | apisix ns | **폐기** (HAProxy 통합) 또는 hp1 단일 컨테이너 | 4 | +| Traefik | kube-system | **폐기** | 4 | +| MetalLB | metallb-system | **폐기** | 4 | +| cert-manager | cert-manager | **폐기** (acme.sh로 대체) | 4 | +| Longhorn | longhorn-system | **폐기** (NAS NFS로 대체) | 5 | +| ArgoCD | argocd | **폐기** (OpenTofu로 대체) | 5 | + +## 롤백 절차 + +Phase별 롤백 가능. 단 Phase 5 (Longhorn 폐기) 이후 K3s 복귀는 비현실적 — 그 이전 단계는 데이터 손실 없이 복귀 가능. + +각 서비스 단위 롤백: +1. HAProxy backend를 K3s VIP로 되돌림 +2. Incus 컨테이너 stop (삭제 금지 — `incus snapshot`으로 백업 보존) +3. K3s 측 Deployment/StatefulSet replica 복원 +4. DNS TTL 안에 트래픽 복귀 + +전체 롤백 트리거: +- 이전 후 1주 내 다운타임 > 1시간 누적 +- 데이터 무결성 사고 +- 운영 부담이 예상보다 큼 (incus 자체 OOM, idmap 깨짐 반복 등) + +## 위험요소 / 트레이드오프 + +| 위험 | 완화 | +|------|------| +| HA 손실 (호스트 다운 = 서비스 다운) | 디폴트 수용. Patroni·Vault·DNS만 자체 HA. 핵심 외 분 단위 다운타임 허용 | +| 이전 중 K3s + Incus 이중 운영 부담 | Phase 시간 단축, 시범에서 패턴 확립 후 가속 | +| ArgoCD GitOps 시각화 손실 | OpenTofu state + Tofu Cloud 또는 atlantis로 보완 | +| OCI 이미지 자동 업데이트 부재 | blue-green 패턴 표준화, renovate-bot으로 tag PR 자동 생성 | +| Longhorn 폐기 후 백업 전략 | NAS snapshot + R2 sync 기존 파이프라인 ([[backup]]) 활용 | +| kr2 OOM 재발 (Incus 단독에서도) | inbest 7컨테이너 + DB 2 + 신규 워크로드 금지. 가벼운 것만 | +| SafeLine WAF Phase 3 단일 컨테이너 통합 — chaitin-waf 트래픽 처리 성능 | 시범에서 부하 테스트, 필요 시 hp1·hp2 페어 active-passive | + +## 일정 추정 (의지적, 실제는 더 길어질 수 있음) + +| Phase | 기간 | 누적 | +|-------|------|------| +| Phase 0 (인프라 토대) | 2주 | 2주 | +| Phase 1 (시범) | 2주 | 4주 | +| Phase 2 (단일 컨테이너) | 3주 | 7주 | +| Phase 3 (멀티 컴포넌트) | 3주 | 10주 | +| Phase 4 (게이트웨이) | 1주 | 11주 | +| Phase 5 (인프라 폐기) | 2주 | 13주 | +| Phase 6 (K3s 종료) | 1주 | 14주 | + +전체 약 3.5개월. 실제 진행은 다른 작업·인시던트로 6개월~1년 범위 예상. + +## 관련 문서 + +- [[infra-hosts]] — 현재 K3s/Incus 호스트 및 워크로드 정본 +- [[../platform/longhorn]] — Longhorn 현행 운영 +- [[../network/apisix]] — APISIX 서울/오사카 라우팅 +- [[../network/metallb]] — MetalLB IP 풀 +- [[../data/postgresql-ha]] — Patroni HA (그대로 유지) +- [[../security/vault]] — Vault (그대로 유지, jp1) +- [[../data/k3s-backup]] — 현행 백업 파이프라인 +- [[../../history/2026-05-23-kr1-k3s-stuck-cascade]] — 이전 결정 동기 중 하나 +- [[../../history/2026-05-04-amd-iommu-freeze]] — kr2 freeze 이력