--- name: chart-to-cnpg description: 카탈로그 차트가 내장한 bitnami postgresql 서브차트를 전용 cnpg-cluster 인스턴스로 전환할 때 사용한다. "postgresql 서브차트를 cnpg로 바꿔줘", "이 차트도 cnpg-cluster 쓰게 해줘", "bitnami postgresql 제거" 같은 요청, 또는 manifests/helm//의 custom-values.yaml에서 postgresql 서브차트를 끄고 외부 DB로 돌리는 작업에 해당한다. 남은 대상은 langflow-ide, langfuse, litellm, nemo, infisical-standalone다(flowise는 2026-08 전환 완료). --- # 카탈로그 앱 → cnpg-cluster 전환 내장 bitnami postgresql 서브차트를 끄고, 앱 전용 `cnpg-cluster` 인스턴스를 외부 DB로 쓰도록 바꾼다. airflow/lakekeeper/mlflow/superset 4개에서 실측 검증된 절차다(GitHub 이슈 #14). **helm/kubectl을 직접 실행하지 않는다** — 배포·검증은 `scripts/deploy-test/*.sh`가 전담한다 (CLAUDE.md 설계 원칙 #1). 이 skill은 파일 편집과 판단만 담당한다. ## 1. 대상 확인 `manifests/helm///custom-values.yaml`에 `postgresql:` 서브차트 설정이 있는지 본다. `Chart.yaml`의 `dependencies`에 postgresql이 있는지도 확인한다. **범위 밖**: gitea·keycloak·dnsup는 서브차트가 아니라 공유 `postgresql-ha`를 외부 참조하며 `paasup/dipup` 레포 관리 대상이다 — 건드리지 않는다(`doc/migrations/dipup-postgresql-ha-cnpg-handoff.md`). ## 2. 외부 DB 연결 키 찾기 차트마다 키 구조가 다르다. `values.yaml`에서 `externalDatabase` / `external*` / `postgresql.enabled` 주변을 grep 한다. 이미 해결된 4개 사례: | 차트 | 외부 DB 키 | |---|---| | `airflow/1.16.0` | `data.metadataConnection.{host,user,pass,db,port,sslmode}` | | `lakekeeper/0.11.0` | `externalDatabase.{host_read,host_write,port,database,user,password}` | | `mlflow/2.1.0` | `externalDatabase.{dialectDriver,host,port,user,password,database}` | | `superset/0.13.5` | `supersetNode.connections.db_{host,port,user,pass,name}` | **서비스명 규칙**: 쓰기는 `<릴리스>-rw`, 읽기는 `<릴리스>-r`. 읽기에 **`-ro`를 쓰면 안 된다** — `-ro`는 replica 전용이라 권장 기준인 `instances: 1`에서 엔드포인트가 0개가 되어 앱이 영구 재시도에 빠진다(lakekeeper에서 실측). ## 3. 파일 작업 (6종) 내용은 여기 복붙하지 말고 **기존 4개 사례를 열어 그대로 따른다**. 문서 중복은 유지보수 부담만 는다. | 파일 | 작업 | 레퍼런스 | |---|---|---| | `manifests/helm///custom-values.yaml` | `postgresql.enabled: false` + 위 외부 DB 키. 기존 `postgresql.auth.*`/`image.*` 등 서브차트 전용 설정은 제거 | 4개 앱 | | `manifests/helm///CUSTOM-README.md` | 전환 사실·사전조건(operator)·배포 순서 | 〃 | | `manifests/applicationset///applicationset.yaml` | 신규 — DB(syncWave 0) → 앱(syncWave 1) | `manifests/applicationset/airflow/1.16.0/` | | `manifests/applicationset///-db-values.yaml`
`-values.yaml` | 신규 — 비밀번호는 `$INFISICAL_SECRET` 플레이스홀더 | 〃 | | `manifests/applicationset///README.md` | 신규 — 짧게. 배포 테스트 절차는 복제하지 말고 참조만 | 〃 | | `scripts/deploy-test/fixtures/-deploy-test-overrides.yaml` | 신규 — 아래 4절 참고 | `scripts/deploy-test/fixtures/` 4개 | | `doc/define-chart-resources.md` | 해당 차트 섹션의 postgres 리소스/볼륨 티어를 cnpg-cluster 참조로 교체 | 기존 4개 섹션 | ## 4. 배포 테스트 fixture `custom-values.yaml`(카탈로그 권장값)은 절대 바꾸지 않고, 배포 테스트에서만 얹는 오버레이다. 격리된 테스트 네임스페이스에서 **DB 연결과 무관하게 막히는 의존성만** 끈다. - **ingress host 오버라이드는 필수다** — 카탈로그 기본 host가 이 클러스터의 다른 실제 배포와 겹치면 apisix에 동일 host 라우트가 2개 등록되어 운영 트래픽과 충돌한다(lakekeeper에서 실측). `-access-test.example.org` 같은 별도 host를 쓴다. - 외부 SSO/저장소 등 이 환경에 없는 의존성을 끈다(사례: airflow `dags.gitSync.enabled: false`, lakekeeper `auth.oauth2` 비우기, superset `configOverrides.enable_oauth`). - **왜 끄는지는 fixture 파일 주석에 남긴다** — README에 중복 기술하지 않는다. ## 5. 배포 테스트 `.claude/deploy-test-procedure.md`의 "카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다. 절차를 여기 복제하지 않는다. `APP_POD_SELECTOR`는 **DB에 실제로 연결하는 파드**를 가리켜야 한다(미지정 시 무관한 파드가 Running이라는 이유로 통과 오판). 4개 사례: airflow `component=webserver`, lakekeeper `app.kubernetes.io/component=catalog`, mlflow `app.kubernetes.io/component=tracking`, superset `app=superset`. ## 6. 마무리 체크리스트 - [ ] `helm template`으로 postgresql 서브차트 리소스가 더 이상 렌더링되지 않는지 확인 - [ ] 읽기 엔드포인트가 `-ro`가 아니라 `-r`인지 확인(해당 차트에 읽기/쓰기 분리가 있을 때) - [ ] fixture에 ingress host 오버라이드가 있는지 확인 - [ ] 배포 테스트 PASS + 테스트 네임스페이스/PVC/PV/Longhorn 볼륨 잔여 없음 - [ ] 운영 중인 네임스페이스(`platform`, `defense-llm`, `cnpg-system`)에 영향 없음 - [ ] 환경 함정은 `.claude/pitfalls.md` 참고 (Longhorn 볼륨 잔여, ingress host 충돌, `ingressClassName` 누락, 선언적 리소스 `status` 신뢰 금지)