카탈로그 앱 내장 bitnami postgresql → cnpg-cluster 전환 (1차 5개 차트)
이슈 #9에서 결정된 CloudNativePG 채택을 카탈로그 앱에 확장 적용한다. airflow/lakekeeper/mlflow/superset/flowise 5개 차트가 내장하던 bitnami postgresql 서브차트를 끄고 앱 전용 cnpg-cluster 인스턴스를 외부 DB로 쓰도록 전환했다. 5개 모두 dev 클러스터 격리 네임스페이스에서 배포 테스트로 실측 검증했다. gitea/keycloak/dnsup 는 서브차트가 아니라 공유 postgresql-ha 를 외부 참조하며 paasup/dipup 레포 관리 대상이라 제외했다 — 인수인계 문서만 추가했다. 배포 구조: - ArgoCD ApplicationSet 으로 DB(syncWave 0) → 앱(syncWave 1) 순서를 보장한다. 기존 openmetadata/victoria-metrics 관례를 따랐다. cnpg-cluster 차트는 범용 상태로 유지하고 앱별 값은 manifests/applicationset/<app>/ 에 둔다. 검증 중 발견해 함께 고친 문제: - lakekeeper: cnpg 의 -ro 는 replica 전용이라 instances:1 에서 엔드포인트가 0개다. 읽기 연결을 -r(전체 라운드로빈)로 교체했다. - airflow/superset: ingressClassName 누락 + kong 애노테이션 잔존으로 이 클러스터(apisix 전용)에서 ingress 접근이 아예 불가능했다. apisix + regex path 로 교체했다. - 배포 테스트가 PV 만 지우고 Longhorn Volume CR 을 남겨 storageScheduled 가 누적됐다(orphan 112개 ~1TB 로 배포 차단). 두 스크립트의 정리 로직을 고쳤다. 재사용 구조화: - .claude/skills/chart-to-cnpg/ 신규. 남은 4개 차트(langflow-ide, langfuse, litellm, nemo)에 같은 절차를 재사용한다. flowise 에 실제 적용해 검증했다. - 배포 테스트 공통 절차는 .claude/deploy-test-procedure.md, 환경 함정은 .claude/pitfalls.md 로 단일화하고 앱별 README 는 참조만 남겼다. 관련: #9, #14 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,37 @@ IMAGE_NAME=quay.io/coreos/etcd:v3.7.1 bash scripts/deploy-test/deploy-test-etcd.
|
||||
따른다. security-catalog 프로젝트에 상세 배포 테스트 기록(`doc/charts/etcd/deploy-test.md`)이
|
||||
있으나 dip-catalog 에는 아직 이관되지 않았다.
|
||||
|
||||
## 카탈로그 앱 → cnpg-cluster 전환 배포 테스트 (`deploy-test-app-with-cnpg.sh`)
|
||||
|
||||
airflow/lakekeeper/mlflow/superset처럼 내장 bitnami postgresql 서브차트를 cnpg-cluster
|
||||
전용 인스턴스로 전환한 앱은 이 공용 스크립트로 검증한다. 각 앱의 ApplicationSet 배포 구조·
|
||||
사전조건은 `manifests/applicationset/<app>/<version>/README.md`에 있다 — 여기서는 배포
|
||||
테스트 자체의 공통 절차만 다룬다.
|
||||
|
||||
```sh
|
||||
APP_CHART_DIR=manifests/helm/<app>/<version> \
|
||||
APP_RELEASE=<app> \
|
||||
DB_CUSTOM_VALUES=manifests/applicationset/<app>/<version>/<app>-db-values.yaml \
|
||||
DB_SECRET_USER=<user> DB_SECRET_PASSWORD=<password> \
|
||||
TEST_NAMESPACE=<app>-test-build \
|
||||
APP_EXTRA_VALUES=scripts/deploy-test/fixtures/<app>-deploy-test-overrides.yaml \
|
||||
APP_POD_SELECTOR="<DB에 실제로 연결하는 파드의 label selector>" \
|
||||
bash scripts/deploy-test/deploy-test-app-with-cnpg.sh /tmp/deploy-test-<app>
|
||||
```
|
||||
|
||||
- operator(`cloudnative-pg`)는 상시 컴포넌트로 재사용한다. DB(cnpg-cluster)를 먼저 띄워
|
||||
healthy 대기 후 앱을 설치하고, 실제 DB 소비 파드가 Running+Ready 상태가 되고 로그에 DB
|
||||
연결 실패 패턴이 없으면 PASS. 정리는 성공/실패와 무관하게 항상 수행한다.
|
||||
- `APP_POD_SELECTOR`는 반드시 지정한다 — 미지정 시 네임스페이스의 아무 파드나 Running이면
|
||||
통과로 오판할 수 있다(DB와 무관한 다른 파드가 떠 있으면 실제 실패를 놓친다 — 실측).
|
||||
- `APP_EXTRA_VALUES`(fixture, `scripts/deploy-test/fixtures/<app>-deploy-test-overrides.yaml`)는
|
||||
DB 연결과 무관하지만 격리된 테스트 네임스페이스에서만 막히는 의존성을 끈다. 카탈로그의
|
||||
실제 권장 설정(`custom-values.yaml`)은 건드리지 않는다. 앱별로 무엇을 왜 끄는지는 각
|
||||
fixture 파일 자체의 주석에 있다 — 문서를 이중으로 유지하지 않는다.
|
||||
- PV뿐 아니라 **Longhorn Volume 커스텀 리소스**까지 지워야 한다 — PV만 지우면 Longhorn
|
||||
쪽에 실제 디스크가 계속 쌓인다. 상세: [pitfalls.md](pitfalls.md).
|
||||
- ingress host 충돌 주의: [pitfalls.md](pitfalls.md) 참고.
|
||||
|
||||
## 그 외 차트 — 수동 절차
|
||||
|
||||
1. 전용 네임스페이스를 새로 만든다 (`pg-test-<name>`). 기존 워크로드가 있는 NS 를 쓰지 않는다.
|
||||
|
||||
@@ -51,3 +51,41 @@ kubectl -n <ns> get clusters.postgresql.cnpg.io <name>
|
||||
CNPG `Database` CRD 는 spec generation 이 바뀔 때만 reconcile 한다. 확장이 DB 에서
|
||||
사라져도 `status.applied` 는 계속 `true` 다. 검증은 항상 실제 DB 에 질의해서 한다
|
||||
(security-catalog 프로젝트에서 실측 — 상세 배포 테스트 기록은 dip-catalog 에 아직 없음).
|
||||
|
||||
## PV 를 지워도 Longhorn Volume 은 남는다
|
||||
|
||||
`kubectl delete pv` 로 k8s PersistentVolume 오브젝트를 지워도 **Longhorn 자체의
|
||||
`volumes.longhorn.io` 커스텀 리소스는 남는다.** `kubectl get pv` 로는 "깨끗하다"고 보이지만
|
||||
Longhorn 의 `storageScheduled` 는 계속 누적되고, 결국 디스크에 여유가 충분한데도
|
||||
`insufficient storage`(`ReplicaSchedulingFailure`)로 새 볼륨 스케줄링이 막힌다.
|
||||
|
||||
실측: 반복된 배포 테스트가 남긴 orphan 볼륨 112 개(~1TB)가 쌓여 airflow 배포가 볼륨
|
||||
attach 단계에서 멈췄다. PV 와 이름이 같은 Longhorn Volume 을 함께 지워야 한다.
|
||||
|
||||
```sh
|
||||
kubectl -n longhorn-system delete volumes.longhorn.io <pv-name>
|
||||
```
|
||||
|
||||
이미 삭제된 네임스페이스의 orphan 볼륨을 찾을 때는 `.status.kubernetesStatus.namespace` 가
|
||||
현존하지 않는 네임스페이스를 가리키는 것만 골라낸다. 배포 테스트 스크립트
|
||||
(`deploy-test-app-with-cnpg.sh`, `deploy-test-cnpg-cluster.sh`)에는 이 정리가 포함돼 있다.
|
||||
|
||||
## 배포 테스트의 ingress host 가 실제 배포와 충돌한다
|
||||
|
||||
카탈로그 `custom-values.yaml` 의 ingress host(`<app>.example.org`)를 그대로 쓰면, 같은 host 를
|
||||
이미 쓰는 실제 배포가 있을 때 **apisix 에 동일 host 라우트가 2 개 등록되어 운영 트래픽
|
||||
라우팅과 충돌한다.** 실측: `defense-llm` 네임스페이스에 이미 배포된 lakekeeper 와
|
||||
`lakekeeper.example.org` 가 겹쳤다.
|
||||
|
||||
배포 테스트는 항상 host 를 `<app>-access-test.example.org` 처럼 별도 값으로 오버라이드한다
|
||||
(각 앱 fixture 에 반영돼 있다). 배포 전 `kubectl get ingress -A` 로 host 중복을 먼저 확인한다.
|
||||
|
||||
## ingress 는 `ingressClassName` 을 명시해야 한다
|
||||
|
||||
이 클러스터의 ingress controller 는 **apisix 하나뿐이다**(kong 은 없다). 그런데 일부 카탈로그
|
||||
차트가 아직 `kubernetes.io/ingress.class: kong` 애노테이션 방식으로 남아 있었다 — 이 경우
|
||||
Ingress 리소스는 만들어지지만 `CLASS: <none>` 으로 뜨고 어떤 컨트롤러도 처리하지 않아
|
||||
접근 자체가 불가능하다(airflow·superset 에서 실측, 둘 다 수정함).
|
||||
|
||||
또한 `path: /` 는 정확히 `/` 만 매치되므로 앱이 `/home` 등으로 리다이렉트하면 전부 404 가
|
||||
난다. `path: /.*` + `k8s.apisix.apache.org/use-regex: "true"` 를 함께 쓴다.
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: chart-to-cnpg
|
||||
description: 카탈로그 차트가 내장한 bitnami postgresql 서브차트를 전용 cnpg-cluster 인스턴스로 전환할 때 사용한다. "postgresql 서브차트를 cnpg로 바꿔줘", "이 차트도 cnpg-cluster 쓰게 해줘", "bitnami postgresql 제거" 같은 요청, 또는 manifests/helm/<chart>/의 custom-values.yaml에서 postgresql 서브차트를 끄고 외부 DB로 돌리는 작업에 해당한다. 남은 대상은 flowise, langflow-ide, langfuse, litellm, nemo다.
|
||||
---
|
||||
|
||||
# 카탈로그 앱 → 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/<chart>/<ver>/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/<chart>/<ver>/custom-values.yaml` | `postgresql.enabled: false` + 위 외부 DB 키. 기존 `postgresql.auth.*`/`image.*` 등 서브차트 전용 설정은 제거 | 4개 앱 |
|
||||
| `manifests/helm/<chart>/<ver>/CUSTOM-README.md` | 전환 사실·사전조건(operator)·배포 순서 | 〃 |
|
||||
| `manifests/applicationset/<app>/<ver>/applicationset.yaml` | 신규 — DB(syncWave 0) → 앱(syncWave 1) | `manifests/applicationset/airflow/1.16.0/` |
|
||||
| `manifests/applicationset/<app>/<ver>/<app>-db-values.yaml`<br>`<app>-values.yaml` | 신규 — 비밀번호는 `$INFISICAL_SECRET` 플레이스홀더 | 〃 |
|
||||
| `manifests/applicationset/<app>/<ver>/README.md` | 신규 — 짧게. 배포 테스트 절차는 복제하지 말고 참조만 | 〃 |
|
||||
| `scripts/deploy-test/fixtures/<app>-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에서 실측).
|
||||
`<app>-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` 신뢰 금지)
|
||||
@@ -109,6 +109,30 @@ python3 agent/update_catalog/skills/helm_diff/scripts/run.py \
|
||||
|
||||
---
|
||||
|
||||
## Claude Code Skills (`.claude/skills/`)
|
||||
|
||||
위 `agent/update_catalog/skills/`(결정론적 Python CLI 파이프라인)와 **용도가 다른 별개
|
||||
체계**다. 혼동하지 않는다.
|
||||
|
||||
| 구분 | `agent/update_catalog/skills/` | `.claude/skills/` |
|
||||
|------|-------------------------------|-------------------|
|
||||
| 형식 | SKILL.md + `scripts/run.py` (JSON in/out) | Claude Code Agent Skill (frontmatter + 지시문) |
|
||||
| 실행 | `python3 run.py --flags`, 파이프라인이 호출 | Claude Code 세션에서 관련 작업 시 자동 로드 |
|
||||
| 용도 | 차트 버전 감지·diff·PR 생성 등 결정론적 자동화 | 차트마다 판단이 필요한 반복 편집·검증 절차 |
|
||||
|
||||
`.claude/skills/`의 Skill도 설계 원칙 #1을 지킨다 — helm/kubectl 실행은
|
||||
`scripts/deploy-test/*.sh` 같은 스크립트가 전담하고, Skill은 편집·판단·검증 절차만 담당한다.
|
||||
|
||||
| Skill | 용도 |
|
||||
|-------|------|
|
||||
| [chart-to-cnpg](.claude/skills/chart-to-cnpg/SKILL.md) | 카탈로그 차트의 내장 bitnami postgresql 서브차트를 전용 cnpg-cluster로 전환 |
|
||||
|
||||
관련 참조 문서(Skill이 절차의 단일 출처로 삼는다):
|
||||
[deploy-test-procedure.md](.claude/deploy-test-procedure.md) ·
|
||||
[pitfalls.md](.claude/pitfalls.md) · [image-authoring.md](.claude/image-authoring.md)
|
||||
|
||||
---
|
||||
|
||||
## CVE/SBOM 게이트 작업
|
||||
|
||||
`manifests/helm/` 카탈로그가 참조하는 컨테이너 이미지의 SBOM·취약점을 스캔하고
|
||||
|
||||
@@ -16,12 +16,15 @@ DIP 카탈로그의 Helm 차트에 대한 상세한 리소스 요구사항을
|
||||
| **Workers** | 500m/1Gi → 2000m/2Gi | 1000m/2Gi → 4000m/4Gi | 2000m/4Gi → 8000m/8Gi |
|
||||
| **Webserver** | 200m/256Mi → 500m/512Mi | 500m/512Mi → 1000m/1Gi | 1000m/1Gi → 2000m/2Gi |
|
||||
| **Redis** | 100m/128Mi → 200m/256Mi | 200m/256Mi → 500m/512Mi | 500m/512Mi → 1000m/1Gi |
|
||||
| **PostgreSQL** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
|
||||
> **PostgreSQL**: 내장 서브차트 대신 전용 `cnpg-cluster` 인스턴스를 외부 DB로 사용한다
|
||||
> (`postgresql.enabled: false` + `data.metadataConnection`). 리소스/볼륨 티어는
|
||||
> `manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml` ·
|
||||
> `dip-volumes-quotas.yaml` 참고.
|
||||
|
||||
**볼륨 사이즈 권장사항:**
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **PostgreSQL Data** | 10Gi | 50Gi | 200Gi |
|
||||
| **Redis Data** | 1Gi | 5Gi | 20Gi |
|
||||
| **Logs Volume** | 5Gi | 20Gi | 100Gi |
|
||||
| **DAGs Volume** | 1Gi | 5Gi | 20Gi |
|
||||
@@ -261,12 +264,15 @@ resources: {}
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **API Server** | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi | 2000m/4Gi → 4000m/8Gi |
|
||||
| **Database** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
|
||||
> **Database**: 내장 서브차트 대신 전용 `cnpg-cluster` 인스턴스를 외부 DB로 사용한다
|
||||
> (`postgresql.enabled: false` + `externalPostgresql.*`). 리소스/볼륨 티어는
|
||||
> `manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml` ·
|
||||
> `dip-volumes-quotas.yaml` 참고.
|
||||
|
||||
**볼륨 사이즈 권장사항:**
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **Database Data** | 5Gi | 20Gi | 100Gi |
|
||||
| **Flow Storage** | 2Gi | 10Gi | 50Gi |
|
||||
|
||||
**권장 사용 시나리오:**
|
||||
@@ -295,20 +301,19 @@ worker:
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# 내장 postgresql 대신 전용 cnpg-cluster 인스턴스를 외부 DB로 사용한다.
|
||||
# flowise는 postgresql.enabled: false 만으로는 부족하고 externalPostgresql.enabled: true 를
|
||||
# 함께 켜야 한다.
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
externalPostgresql:
|
||||
enabled: true
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "longhorn"
|
||||
size: 5Gi
|
||||
resources: {}
|
||||
# limits:
|
||||
# memory: 512Mi
|
||||
# cpu: 500m
|
||||
# requests:
|
||||
# memory: 256Mi
|
||||
# cpu: 250m
|
||||
host: flowise-db-rw
|
||||
port: 5432
|
||||
username: flowise
|
||||
password: flowise
|
||||
database: flowise
|
||||
```
|
||||
|
||||
---
|
||||
@@ -830,13 +835,16 @@ postgresql:
|
||||
|----------|-------|--------|-------|
|
||||
| **Tracking Server** | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi | 2000m/4Gi → 4000m/8Gi |
|
||||
| **Run Container** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
| **PostgreSQL** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
| **MinIO** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
|
||||
> **PostgreSQL**: 내장 서브차트 대신 전용 `cnpg-cluster` 인스턴스를 외부 DB로 사용한다
|
||||
> (`postgresql.enabled: false` + `externalDatabase.*`). 리소스/볼륨 티어는
|
||||
> `manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml` ·
|
||||
> `dip-volumes-quotas.yaml` 참고.
|
||||
|
||||
**볼륨 사이즈 권장사항:**
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **PostgreSQL Data** | 5Gi | 20Gi | 100Gi |
|
||||
| **MinIO Storage** | 20Gi | 200Gi | 1Ti |
|
||||
| **Artifacts** | 10Gi | 100Gi | 500Gi |
|
||||
|
||||
@@ -851,13 +859,6 @@ tracking:
|
||||
resources: {}
|
||||
run:
|
||||
resources: {}
|
||||
|
||||
postgresql:
|
||||
primary:
|
||||
resources: {}
|
||||
persistence:
|
||||
storageClass: ""
|
||||
size: 5Gi
|
||||
```
|
||||
|
||||
|
||||
@@ -1565,13 +1566,16 @@ resources:
|
||||
| **Superset Worker** | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi | 2000m/4Gi → 4000m/8Gi |
|
||||
| **Celery Beat** | 100m/256Mi → 200m/512Mi | 200m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi |
|
||||
| **Celery Flower** | 100m/256Mi → 200m/512Mi | 200m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi |
|
||||
| **PostgreSQL** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
| **Redis** | 200m/256Mi → 500m/512Mi | 500m/512Mi → 1000m/1Gi | 1000m/1Gi → 2000m/2Gi |
|
||||
|
||||
> **PostgreSQL**: 내장 서브차트 대신 전용 `cnpg-cluster` 인스턴스를 외부 DB로 사용한다
|
||||
> (`postgresql.enabled: false` + `supersetNode.connections.db_*`). 리소스/볼륨 티어는
|
||||
> `manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml` ·
|
||||
> `dip-volumes-quotas.yaml` 참고.
|
||||
|
||||
**볼륨 사이즈 권장사항:**
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **PostgreSQL Data** | 8Gi | 50Gi | 200Gi |
|
||||
| **Redis Data** | 8Gi | 20Gi | 100Gi |
|
||||
|
||||
**권장 사용 시나리오:**
|
||||
@@ -1974,14 +1978,18 @@ metadata:
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **Lakekeeper Catalog** | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi | 2000m/4Gi → 4000m/8Gi |
|
||||
| **PostgreSQL** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
| **OpenFGA** | 200m/256Mi → 500m/512Mi | 500m/512Mi → 1000m/1Gi | 1000m/1Gi → 2000m/2Gi |
|
||||
| **OpenFGA PostgreSQL** | 250m/512Mi → 500m/1Gi | 500m/1Gi → 1000m/2Gi | 1000m/2Gi → 2000m/4Gi |
|
||||
|
||||
> **Lakekeeper PostgreSQL**: 내장 `groundhog2k/postgres` 서브차트 대신 전용 `cnpg-cluster`
|
||||
> 인스턴스를 외부 DB로 사용한다(`postgresql.enabled: false` + `externalDatabase.*`). 리소스/볼륨
|
||||
> 티어는 `manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml` ·
|
||||
> `dip-volumes-quotas.yaml` 참고. **OpenFGA PostgreSQL은 여전히 내장 bitnami 서브차트를
|
||||
> 사용한다** — 이번 전환 범위에 포함되지 않았다.
|
||||
|
||||
**볼륨 사이즈 권장사항:**
|
||||
| 컴포넌트 | Small | Medium | Large |
|
||||
|----------|-------|--------|-------|
|
||||
| **Lakekeeper PostgreSQL** | 5Gi | 20Gi | 100Gi |
|
||||
| **OpenFGA PostgreSQL** | 8Gi | 30Gi | 150Gi |
|
||||
|
||||
**권장 사용 시나리오:**
|
||||
@@ -2000,18 +2008,18 @@ catalog:
|
||||
# cpu: 500m
|
||||
# memory: 1Gi
|
||||
|
||||
# lakekeeper DB
|
||||
# lakekeeper DB — 전용 cnpg-cluster 인스턴스를 외부 DB로 사용한다
|
||||
postgresql:
|
||||
storage:
|
||||
className: ""
|
||||
requestedSize: 5Gi
|
||||
resources: {}
|
||||
# limits:
|
||||
# cpu: 500m
|
||||
# memory: 1Gi
|
||||
# requests:
|
||||
# cpu: 250m
|
||||
# memory: 512Mi
|
||||
enabled: false
|
||||
|
||||
externalDatabase:
|
||||
type: postgres
|
||||
host_read: "lakekeeper-db-r" # instances:1 에서는 -ro가 엔드포인트 0개라 -r을 쓴다
|
||||
host_write: "lakekeeper-db-rw"
|
||||
port: 5432
|
||||
database: lakekeeper
|
||||
user: lakekeeper
|
||||
password: lakekeeper
|
||||
|
||||
|
||||
openfga:
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# dipup 인수인계 — postgresql-ha → cnpg-cluster (gitea/keycloak/dnsup)
|
||||
|
||||
이 문서는 실행 문서가 아니다. dip-catalog가 아닌 `paasup/dipup` 레포에서 관리되는
|
||||
gitea/keycloak/dnsup의 DB 전환 작업을 위해, dip-catalog 쪽에서 파악한 내용을 정리해
|
||||
넘기는 참고 자료다. dip-catalog는 이 3개 앱과 `postgresql-ha`를 직접 변경하지 않는다.
|
||||
|
||||
## 배경
|
||||
|
||||
카탈로그의 `postgresql-ha`(bitnami postgresql-repmgr + pgpool,
|
||||
`manifests/helm/postgresql-ha/11.9.4/`)는 현재 `platform` 네임스페이스에 배포되어 있고,
|
||||
`gitea`·`keycloak`·`dnsup` 3개 앱이 이 인스턴스의 4개 DB를 쓴다.
|
||||
|
||||
| DB | 소비 앱 | owner role |
|
||||
|---|---|---|
|
||||
| `gitea` | gitea | gitea |
|
||||
| `gitea_session` | gitea (세션 저장소) | gitea |
|
||||
| `keycloak` | keycloak | keycloak |
|
||||
| `paasup` | dnsup | paasup |
|
||||
|
||||
GitHub 이슈 [#9](https://github.com/paasup/dip-catalog/issues/9)에서 이 인스턴스를
|
||||
CloudNativePG(cnpg-cluster + cloudnative-pg operator)로 대체하기로 이미 결정되었다
|
||||
(2026-07-31). dip-catalog에는 이미 `cloudnative-pg`(0.29.0, operator)·`cnpg-cluster`
|
||||
(1.0.0, Cluster/Database 래퍼 차트)가 추가되어 있고, 이번 작업(카탈로그 앱
|
||||
airflow/lakekeeper/mlflow/superset 전환)에서 이 두 차트로 4개 앱의 내장 DB를 전용
|
||||
인스턴스로 옮기는 배포 테스트까지 실측 검증했다.
|
||||
|
||||
## 현재 연결 정보 (변경 지점)
|
||||
|
||||
3개 앱은 서브차트가 아니라 외부 호스트를 직접 참조한다. cnpg-cluster로 전환 시 아래
|
||||
지점만 바뀐다.
|
||||
|
||||
| 앱 | 파일 | 현재 값 | 변경 후 (예: 릴리스명 `platform-db`) |
|
||||
|---|---|---|---|
|
||||
| gitea | `gitea.config.database.HOST` | `postgresql-postgresql-ha-postgresql:5432` | `platform-db-rw:5432` |
|
||||
| gitea | `gitea.config.session.PROVIDER_CONFIG` | `host=postgresql-postgresql-ha-postgresql` | `host=platform-db-rw` |
|
||||
| keycloak | `extraEnv` (`DB_ADDR`) | `postgresql-postgresql-ha-postgresql` | `platform-db-rw` |
|
||||
| dnsup | `env.DB_HOST` | `postgresql-postgresql-ha-postgresql.platform.svc.cluster.local` | `platform-db-rw.platform.svc.cluster.local` |
|
||||
|
||||
cnpg-cluster의 서비스 명명 규칙은 `<릴리스명>-rw`(쓰기/기본 연결)이다 — 자세한 내용은
|
||||
`manifests/helm/cnpg-cluster/1.0.0/CUSTOM-README.md` §3 참고.
|
||||
|
||||
## 선결 과제 — 공유 인스턴스 하나로 4개 DB를 만들 수 없다
|
||||
|
||||
`cnpg-cluster` 차트의 `bootstrap.initdb`는 **DB 1개 + owner role 1개만** 최초 생성한다.
|
||||
`databases[]`(Database CRD)로 DB를 추가할 수는 있지만 **owner role은 미리 존재해야
|
||||
한다** — role 자체를 새로 만들지 못한다. `postgresql-ha`의 `initdbScripts`처럼
|
||||
gitea/keycloak/paasup 3개 role을 한 번에 만들 방법이 지금 차트에는 없다.
|
||||
|
||||
이번 dip-catalog 작업(airflow/lakekeeper/mlflow/superset)은 각 앱이 **자기 전용
|
||||
인스턴스**를 쓰는 구조라 이 문제를 만나지 않았다(인스턴스마다 role 1개만 필요).
|
||||
gitea/keycloak/dnsup는 원래 인스턴스 1개를 공유하던 구조라 선택이 필요하다.
|
||||
|
||||
### 옵션 A — 공유 인스턴스 유지 (기존 토폴로지와 동일)
|
||||
|
||||
`bootstrap.initdb.postInitSQL`(CNPG 업스트림 API에 있는 필드 — superuser로 최초 1회,
|
||||
DB 생성 전 단계에 실행되는 SQL. 기존 `postInitApplicationSQL`과 달리 `CREATE ROLE`/
|
||||
`CREATE DATABASE`에 적합)을 `manifests/helm/cnpg-cluster/1.0.0/templates/cluster.yaml`·
|
||||
`values.yaml`에 추가해야 한다. 이 필드로 keycloak/paasup role을 만들고(gitea role은
|
||||
`bootstrap.initdb.owner`로 자동 생성), `databases[]`로 gitea/gitea_session/keycloak/
|
||||
paasup 4개 DB를 선언하는 방식이다. **이 작업은 dip-catalog 쪽 차트 변경이 선행되어야
|
||||
하므로, dipup에서 이 옵션을 원하면 dip-catalog에 먼저 요청이 필요하다.**
|
||||
|
||||
### 옵션 B — 앱별 전용 인스턴스 3개 (이번 작업에서 검증된 패턴 그대로)
|
||||
|
||||
gitea용, keycloak용, dnsup(paasup)용 `cnpg-cluster` 릴리스를 각각 배포한다. 각 인스턴스는
|
||||
`bootstrap.initdb.database`/`owner`만 채우면 되므로 **차트 수정이 필요 없다** —
|
||||
`manifests/applicationset/airflow/1.16.0/airflow-db-values.yaml` 등과 동일한 패턴의
|
||||
값 오버레이 3개(dipup 쪽에 `gitea-db-values.yaml`, `keycloak-db-values.yaml`,
|
||||
`dnsup-db-values.yaml` 격)만 있으면 된다. 이 값들은 ArgoCD ApplicationSet의 `valuesPath`로
|
||||
연결하는 것을 권장한다 — `manifests/applicationset/{airflow,lakekeeper,mlflow,superset}/`가
|
||||
그 구조의 실제 예시다(DB 차트 syncWave 0 → 앱 차트 syncWave 1). gitea의 `gitea_session` DB는 같은 인스턴스
|
||||
안에서 `databases[]`에 `owner: gitea`로 추가하면 된다(role은 이미 bootstrap로 생성됨).
|
||||
|
||||
단점은 postgres 파드 수가 늘어나는 것(인스턴스 3개 × instances 설정값)이다 — 리소스는
|
||||
`manifests/helm/cnpg-cluster/1.0.0/dip-resources-quotas.yaml`의 `small`(instances:1) 기준을
|
||||
참고한다.
|
||||
|
||||
**권장**: 별도 사유가 없다면 옵션 B — dip-catalog 차트 변경 없이 바로 시작할 수 있고,
|
||||
이번 4개 앱 전환에서 동일 패턴이 실측 검증되었다.
|
||||
|
||||
## 이번 작업에서 실측된 주의사항 (그대로 적용됨)
|
||||
|
||||
- cnpg-cluster의 `-ro`(replica 전용) 서비스는 `instances:1`이면 엔드포인트가 0개다.
|
||||
읽기/쓰기를 분리해서 연결하는 앱이 있다면 읽기는 `-ro`가 아니라 `-r`(전체 인스턴스
|
||||
라운드로빈)을 쓴다 — `lakekeeper/0.11.0/custom-values.yaml`에서 실측.
|
||||
- `cloudnative-pg` operator(namespace `cnpg-system`, release `cnpg`)는 클러스터에 이미
|
||||
1회 설치되어 있으므로 재사용하면 된다 — 새로 설치할 필요 없다.
|
||||
- DB owner 계정의 비밀번호는 `bootstrap.initdb.secretName`으로 지정한 시크릿(키:
|
||||
`username`/`password`)에서 온다. 앱 쪽 값(예: keycloak의 `DB_PASSWORD`)과 반드시
|
||||
일치해야 한다.
|
||||
|
||||
## 이번 작업 범위에 포함되지 않은 것
|
||||
|
||||
- gitea/keycloak/dnsup의 `custom-values.yaml` 실제 수정 (dipup 관리 대상)
|
||||
- `postgresql-ha` 카탈로그 항목 제거 여부 판단
|
||||
- 실 데이터 마이그레이션(`pg_dump`/`pg_restore`) 및 컷오버 절차, 다운타임 계획, 롤백 계획
|
||||
— SSO(keycloak)·git(gitea) 서비스에 영향을 주는 운영 작업이라 별도 계획·승인이 필요하다.
|
||||
@@ -0,0 +1,36 @@
|
||||
# airflow ArgoCD ApplicationSet
|
||||
|
||||
`airflow-db`(cnpg-cluster, syncWave 0) → `airflow`(syncWave 1) 2단계로 배포한다.
|
||||
전체 스키마는 [manifests/applicationset/openmetadata/1.12.1/](../../openmetadata/1.12.1/)와
|
||||
[manifests/applicationset/victoria-metrics/1.0.0/](../../victoria-metrics/1.0.0/)를 따른다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `applicationset.yaml` — list generator로 Application 2개 생성.
|
||||
- `airflow-db-values.yaml` — cnpg-cluster 환경 오버레이 템플릿(DB/owner, 시크릿 참조).
|
||||
- `airflow-values.yaml` — airflow 환경 오버레이 템플릿(DB 비밀번호만).
|
||||
|
||||
두 파일 모두 **템플릿**이다 — dip-console이 렌더링해 값 repo(`dip/tenant-catalog`)의
|
||||
`airflow/*.yaml`로 커밋하는 것을 가정한다.
|
||||
|
||||
## 값 적용 순서 (2단)
|
||||
|
||||
각 Application은 `baseValues`(카탈로그 차트의 `custom-values.yaml`) + `valuesPath`(값 repo의
|
||||
환경 오버레이, 나중 파일 우선)를 병합한다.
|
||||
|
||||
## 사전 준비 (ArgoCD 밖)
|
||||
|
||||
- `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저
|
||||
설치되어 있어야 한다 — 없으면 `airflow-db`(Cluster CR)가 webhook 부재로 생성 자체가 거부된다.
|
||||
- `airflow-db-values.yaml`의 `bootstrap.initdb.secretName`과 동일한 이름·값의 K8s Secret이
|
||||
대상 네임스페이스에 미리 존재해야 한다(`username`/`password` 키).
|
||||
- ArgoCD가 `Cluster.postgresql.cnpg.io` 커스텀 리소스의 상태를 "Healthy"로 판정하려면
|
||||
`argocd-cm`에 해당 CRD용 커스텀 헬스 체크(Lua)가 등록되어 있어야 sync-wave 1이 정상
|
||||
대기한다 — 미등록 시 wave 0이 Progressing으로만 남아 wave 1로 못 넘어갈 수 있다.
|
||||
|
||||
## 배포 테스트
|
||||
|
||||
절차와 주의사항은 [.claude/deploy-test-procedure.md](../../../../.claude/deploy-test-procedure.md)
|
||||
"카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다.
|
||||
`APP_POD_SELECTOR="component=webserver"`, fixture는
|
||||
`scripts/deploy-test/fixtures/airflow-deploy-test-overrides.yaml`.
|
||||
@@ -0,0 +1,25 @@
|
||||
# airflow 전용 cnpg-cluster 환경 오버레이 템플릿.
|
||||
# dip-console이 렌더링해 값 repo(dip/tenant-catalog)의 airflow/airflow-db-values.yaml로
|
||||
# 커밋하는 것을 가정한 시작점이다. 배포 테스트로 검증된 값 그대로다
|
||||
# (지난 세션: manifests/helm/cnpg-cluster/1.0.0/custom-values-airflow.yaml).
|
||||
instances: 1
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: airflow
|
||||
owner: airflow
|
||||
secretName: "$INFISICAL_SECRET"
|
||||
|
||||
databases:
|
||||
- name: airflow
|
||||
owner: airflow
|
||||
ensure: present
|
||||
reclaimPolicy: retain
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,9 @@
|
||||
# airflow 환경 오버레이 템플릿 — custom-values.yaml(baseValues) 위에 얹힌다.
|
||||
# DB 비밀번호처럼 환경별로 달라야 하는 민감값만 담는다. 나머지 연결 정보(host/db/user 등)는
|
||||
# custom-values.yaml에 이미 있으므로 여기서 중복 기입하지 않는다.
|
||||
#
|
||||
# airflow-db-values.yaml의 bootstrap.initdb.secretName과 실제 값이 일치해야 한다
|
||||
# ($INFISICAL_SECRET 토큰은 dip-console이 같은 시크릿 값으로 렌더링하는 것을 가정).
|
||||
data:
|
||||
metadataConnection:
|
||||
pass: "$INFISICAL_SECRET"
|
||||
@@ -0,0 +1,61 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: airflow
|
||||
namespace: platform
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
generators:
|
||||
- list:
|
||||
# syncWave 0: 전용 cnpg-cluster DB 인스턴스 먼저 배포
|
||||
# syncWave 1: airflow 본체 (postgresql.enabled: false, 위 DB의 -rw 서비스를 바라봄)
|
||||
elements:
|
||||
- name: airflow-db
|
||||
chartPath: manifests/helm/cnpg-cluster/1.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: airflow/airflow-db-values.yaml
|
||||
syncWave: "0"
|
||||
- name: airflow
|
||||
chartPath: manifests/helm/airflow/1.16.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: airflow/airflow-values.yaml
|
||||
syncWave: "1"
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ .name }}"
|
||||
namespace: platform
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
# Source 1: 카탈로그 repo (Helm 차트)
|
||||
- repoURL: https://gitea.example.org/dip/service-catalog
|
||||
targetRevision: master
|
||||
path: "{{ .chartPath }}"
|
||||
helm:
|
||||
releaseName: "{{ .name }}"
|
||||
valueFiles:
|
||||
- "{{ .baseValues }}" # 카탈로그 차트의 정적 베이스값(custom-values)
|
||||
- $values/{{ .valuesPath }} # Source 2의 환경 오버레이 (나중 = 우선)
|
||||
# Source 2: 값(values) repo — dip-console이 렌더한 환경별 값
|
||||
- repoURL: https://gitea.example.org/dip/tenant-catalog
|
||||
targetRevision: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: airflow
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 10s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -0,0 +1,37 @@
|
||||
# flowise ArgoCD ApplicationSet
|
||||
|
||||
`flowise-db`(cnpg-cluster, syncWave 0) → `flowise`(syncWave 1) 2단계로 배포한다.
|
||||
전체 스키마는 [manifests/applicationset/openmetadata/1.12.1/](../../openmetadata/1.12.1/)와
|
||||
[manifests/applicationset/victoria-metrics/1.0.0/](../../victoria-metrics/1.0.0/)를 따른다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `applicationset.yaml` — list generator로 Application 2개 생성.
|
||||
- `flowise-db-values.yaml` — cnpg-cluster 환경 오버레이 템플릿(DB/owner, 시크릿 참조).
|
||||
- `flowise-values.yaml` — flowise 환경 오버레이 템플릿(DB 비밀번호만).
|
||||
|
||||
두 파일 모두 **템플릿**이다 — dip-console이 렌더링해 값 repo(`dip/tenant-catalog`)의
|
||||
`flowise/*.yaml`로 커밋하는 것을 가정한다.
|
||||
|
||||
## 값 적용 순서 (2단)
|
||||
|
||||
각 Application은 `baseValues`(카탈로그 차트의 `custom-values.yaml`) + `valuesPath`(값 repo의
|
||||
환경 오버레이, 나중 파일 우선)를 병합한다.
|
||||
|
||||
## 사전 준비 (ArgoCD 밖)
|
||||
|
||||
- `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저
|
||||
설치되어 있어야 한다.
|
||||
- `flowise-db-values.yaml`의 `bootstrap.initdb.secretName`과 동일한 이름·값의 K8s Secret이
|
||||
대상 네임스페이스에 미리 존재해야 한다(`username`/`password` 키).
|
||||
- ArgoCD가 `Cluster.postgresql.cnpg.io` 커스텀 리소스의 상태를 "Healthy"로 판정하려면
|
||||
`argocd-cm`에 해당 CRD용 커스텀 헬스 체크(Lua)가 등록되어 있어야 sync-wave 1이 정상 대기한다.
|
||||
- flowise는 내장 postgresql을 끄는 것(`postgresql.enabled: false`)만으로는 부족하고
|
||||
`externalPostgresql.enabled: true`를 함께 켜야 한다 — 다른 차트와 다른 점이다.
|
||||
|
||||
## 배포 테스트
|
||||
|
||||
절차와 주의사항은 [.claude/deploy-test-procedure.md](../../../../.claude/deploy-test-procedure.md)
|
||||
"카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다.
|
||||
`APP_POD_SELECTOR="app.kubernetes.io/component=flowise"`, fixture는
|
||||
`scripts/deploy-test/fixtures/flowise-deploy-test-overrides.yaml`.
|
||||
@@ -0,0 +1,59 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: flowise
|
||||
namespace: platform
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
generators:
|
||||
- list:
|
||||
# syncWave 0: 전용 cnpg-cluster DB 인스턴스 먼저 배포
|
||||
# syncWave 1: flowise 본체 (postgresql.enabled: false, 위 DB의 -rw 서비스를 바라봄)
|
||||
elements:
|
||||
- name: flowise-db
|
||||
chartPath: manifests/helm/cnpg-cluster/1.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: flowise/flowise-db-values.yaml
|
||||
syncWave: "0"
|
||||
- name: flowise
|
||||
chartPath: manifests/helm/flowise/6.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: flowise/flowise-values.yaml
|
||||
syncWave: "1"
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ .name }}"
|
||||
namespace: platform
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://gitea.example.org/dip/service-catalog
|
||||
targetRevision: master
|
||||
path: "{{ .chartPath }}"
|
||||
helm:
|
||||
releaseName: "{{ .name }}"
|
||||
valueFiles:
|
||||
- "{{ .baseValues }}"
|
||||
- $values/{{ .valuesPath }}
|
||||
- repoURL: https://gitea.example.org/dip/tenant-catalog
|
||||
targetRevision: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: flowise
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 10s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -0,0 +1,24 @@
|
||||
# flowise 전용 cnpg-cluster 환경 오버레이 템플릿.
|
||||
# dip-console이 렌더링해 값 repo(dip/tenant-catalog)의 flowise/flowise-db-values.yaml로
|
||||
# 커밋하는 것을 가정한 시작점이다.
|
||||
instances: 1
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: flowise
|
||||
owner: flowise
|
||||
secretName: "$INFISICAL_SECRET"
|
||||
|
||||
databases:
|
||||
- name: flowise
|
||||
owner: flowise
|
||||
ensure: present
|
||||
reclaimPolicy: retain
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,8 @@
|
||||
# flowise 환경 오버레이 템플릿 — custom-values.yaml(baseValues) 위에 얹힌다.
|
||||
# DB 비밀번호처럼 환경별로 달라야 하는 민감값만 담는다. 나머지 연결 정보(host/db/user 등)는
|
||||
# custom-values.yaml에 이미 있으므로 여기서 중복 기입하지 않는다.
|
||||
#
|
||||
# flowise-db-values.yaml의 bootstrap.initdb.secretName과 실제 값이 일치해야 한다
|
||||
# ($INFISICAL_SECRET 토큰은 dip-console이 같은 시크릿 값으로 렌더링하는 것을 가정).
|
||||
externalPostgresql:
|
||||
password: "$INFISICAL_SECRET"
|
||||
@@ -0,0 +1,35 @@
|
||||
# lakekeeper ArgoCD ApplicationSet
|
||||
|
||||
`lakekeeper-db`(cnpg-cluster, syncWave 0) → `lakekeeper`(syncWave 1) 2단계로 배포한다.
|
||||
전체 스키마는 [manifests/applicationset/openmetadata/1.12.1/](../../openmetadata/1.12.1/)와
|
||||
[manifests/applicationset/victoria-metrics/1.0.0/](../../victoria-metrics/1.0.0/)를 따른다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `applicationset.yaml` — list generator로 Application 2개 생성.
|
||||
- `lakekeeper-db-values.yaml` — cnpg-cluster 환경 오버레이 템플릿(DB/owner, 시크릿 참조).
|
||||
- `lakekeeper-values.yaml` — lakekeeper 환경 오버레이 템플릿(DB 비밀번호만).
|
||||
|
||||
두 파일 모두 **템플릿**이다 — dip-console이 렌더링해 값 repo(`dip/tenant-catalog`)의
|
||||
`lakekeeper/*.yaml`로 커밋하는 것을 가정한다.
|
||||
|
||||
## 값 적용 순서 (2단)
|
||||
|
||||
각 Application은 `baseValues`(카탈로그 차트의 `custom-values.yaml`) + `valuesPath`(값 repo의
|
||||
환경 오버레이, 나중 파일 우선)를 병합한다.
|
||||
|
||||
## 사전 준비 (ArgoCD 밖)
|
||||
|
||||
- `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저
|
||||
설치되어 있어야 한다.
|
||||
- `lakekeeper-db-values.yaml`의 `bootstrap.initdb.secretName`과 동일한 이름·값의 K8s Secret이
|
||||
대상 네임스페이스에 미리 존재해야 한다(`username`/`password` 키).
|
||||
- ArgoCD가 `Cluster.postgresql.cnpg.io` 커스텀 리소스의 상태를 "Healthy"로 판정하려면
|
||||
`argocd-cm`에 해당 CRD용 커스텀 헬스 체크(Lua)가 등록되어 있어야 sync-wave 1이 정상 대기한다.
|
||||
|
||||
## 배포 테스트
|
||||
|
||||
절차와 주의사항은 [.claude/deploy-test-procedure.md](../../../../.claude/deploy-test-procedure.md)
|
||||
"카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다.
|
||||
`APP_POD_SELECTOR="app.kubernetes.io/component=catalog"`, fixture는
|
||||
`scripts/deploy-test/fixtures/lakekeeper-deploy-test-overrides.yaml`.
|
||||
@@ -0,0 +1,59 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: lakekeeper
|
||||
namespace: platform
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
generators:
|
||||
- list:
|
||||
# syncWave 0: 전용 cnpg-cluster DB 인스턴스 먼저 배포
|
||||
# syncWave 1: lakekeeper 본체 (postgresql.enabled: false, 위 DB의 -rw/-r 서비스를 바라봄)
|
||||
elements:
|
||||
- name: lakekeeper-db
|
||||
chartPath: manifests/helm/cnpg-cluster/1.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: lakekeeper/lakekeeper-db-values.yaml
|
||||
syncWave: "0"
|
||||
- name: lakekeeper
|
||||
chartPath: manifests/helm/lakekeeper/0.11.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: lakekeeper/lakekeeper-values.yaml
|
||||
syncWave: "1"
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ .name }}"
|
||||
namespace: platform
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://gitea.example.org/dip/service-catalog
|
||||
targetRevision: master
|
||||
path: "{{ .chartPath }}"
|
||||
helm:
|
||||
releaseName: "{{ .name }}"
|
||||
valueFiles:
|
||||
- "{{ .baseValues }}"
|
||||
- $values/{{ .valuesPath }}
|
||||
- repoURL: https://gitea.example.org/dip/tenant-catalog
|
||||
targetRevision: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: lakekeeper
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 10s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -0,0 +1,25 @@
|
||||
# lakekeeper 전용 cnpg-cluster 환경 오버레이 템플릿.
|
||||
# dip-console이 렌더링해 값 repo(dip/tenant-catalog)의 lakekeeper/lakekeeper-db-values.yaml로
|
||||
# 커밋하는 것을 가정한 시작점이다. 배포 테스트로 검증된 값 그대로다
|
||||
# (지난 세션: manifests/helm/cnpg-cluster/1.0.0/custom-values-lakekeeper.yaml).
|
||||
instances: 1
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: lakekeeper
|
||||
owner: lakekeeper
|
||||
secretName: "$INFISICAL_SECRET"
|
||||
|
||||
databases:
|
||||
- name: lakekeeper
|
||||
owner: lakekeeper
|
||||
ensure: present
|
||||
reclaimPolicy: retain
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,8 @@
|
||||
# lakekeeper 환경 오버레이 템플릿 — custom-values.yaml(baseValues) 위에 얹힌다.
|
||||
# DB 비밀번호처럼 환경별로 달라야 하는 민감값만 담는다. 나머지 연결 정보(host_read/host_write/
|
||||
# database/user 등)는 custom-values.yaml에 이미 있으므로 여기서 중복 기입하지 않는다.
|
||||
#
|
||||
# lakekeeper-db-values.yaml의 bootstrap.initdb.secretName과 실제 값이 일치해야 한다
|
||||
# ($INFISICAL_SECRET 토큰은 dip-console이 같은 시크릿 값으로 렌더링하는 것을 가정).
|
||||
externalDatabase:
|
||||
password: "$INFISICAL_SECRET"
|
||||
@@ -0,0 +1,35 @@
|
||||
# mlflow ArgoCD ApplicationSet
|
||||
|
||||
`mlflow-db`(cnpg-cluster, syncWave 0) → `mlflow`(syncWave 1) 2단계로 배포한다.
|
||||
전체 스키마는 [manifests/applicationset/openmetadata/1.12.1/](../../openmetadata/1.12.1/)와
|
||||
[manifests/applicationset/victoria-metrics/1.0.0/](../../victoria-metrics/1.0.0/)를 따른다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `applicationset.yaml` — list generator로 Application 2개 생성.
|
||||
- `mlflow-db-values.yaml` — cnpg-cluster 환경 오버레이 템플릿(DB/owner, 시크릿 참조).
|
||||
- `mlflow-values.yaml` — mlflow 환경 오버레이 템플릿(DB 비밀번호만).
|
||||
|
||||
두 파일 모두 **템플릿**이다 — dip-console이 렌더링해 값 repo(`dip/tenant-catalog`)의
|
||||
`mlflow/*.yaml`로 커밋하는 것을 가정한다.
|
||||
|
||||
## 값 적용 순서 (2단)
|
||||
|
||||
각 Application은 `baseValues`(카탈로그 차트의 `custom-values.yaml`) + `valuesPath`(값 repo의
|
||||
환경 오버레이, 나중 파일 우선)를 병합한다.
|
||||
|
||||
## 사전 준비 (ArgoCD 밖)
|
||||
|
||||
- `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저
|
||||
설치되어 있어야 한다.
|
||||
- `mlflow-db-values.yaml`의 `bootstrap.initdb.secretName`과 동일한 이름·값의 K8s Secret이
|
||||
대상 네임스페이스에 미리 존재해야 한다(`username`/`password` 키).
|
||||
- ArgoCD가 `Cluster.postgresql.cnpg.io` 커스텀 리소스의 상태를 "Healthy"로 판정하려면
|
||||
`argocd-cm`에 해당 CRD용 커스텀 헬스 체크(Lua)가 등록되어 있어야 sync-wave 1이 정상 대기한다.
|
||||
|
||||
## 배포 테스트
|
||||
|
||||
절차와 주의사항은 [.claude/deploy-test-procedure.md](../../../../.claude/deploy-test-procedure.md)
|
||||
"카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다.
|
||||
`APP_POD_SELECTOR="app.kubernetes.io/component=tracking"`, fixture는
|
||||
`scripts/deploy-test/fixtures/mlflow-deploy-test-overrides.yaml`.
|
||||
@@ -0,0 +1,59 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: mlflow
|
||||
namespace: platform
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
generators:
|
||||
- list:
|
||||
# syncWave 0: 전용 cnpg-cluster DB 인스턴스 먼저 배포
|
||||
# syncWave 1: mlflow 본체 (postgresql.enabled: false, 위 DB의 -rw 서비스를 바라봄)
|
||||
elements:
|
||||
- name: mlflow-db
|
||||
chartPath: manifests/helm/cnpg-cluster/1.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: mlflow/mlflow-db-values.yaml
|
||||
syncWave: "0"
|
||||
- name: mlflow
|
||||
chartPath: manifests/helm/mlflow/2.1.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: mlflow/mlflow-values.yaml
|
||||
syncWave: "1"
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ .name }}"
|
||||
namespace: platform
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://gitea.example.org/dip/service-catalog
|
||||
targetRevision: master
|
||||
path: "{{ .chartPath }}"
|
||||
helm:
|
||||
releaseName: "{{ .name }}"
|
||||
valueFiles:
|
||||
- "{{ .baseValues }}"
|
||||
- $values/{{ .valuesPath }}
|
||||
- repoURL: https://gitea.example.org/dip/tenant-catalog
|
||||
targetRevision: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: mlflow
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 10s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -0,0 +1,25 @@
|
||||
# mlflow 전용 cnpg-cluster 환경 오버레이 템플릿.
|
||||
# dip-console이 렌더링해 값 repo(dip/tenant-catalog)의 mlflow/mlflow-db-values.yaml로
|
||||
# 커밋하는 것을 가정한 시작점이다. 배포 테스트로 검증된 값 그대로다
|
||||
# (지난 세션: manifests/helm/cnpg-cluster/1.0.0/custom-values-mlflow.yaml).
|
||||
instances: 1
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: mlflow
|
||||
owner: mlflow
|
||||
secretName: "$INFISICAL_SECRET"
|
||||
|
||||
databases:
|
||||
- name: mlflow
|
||||
owner: mlflow
|
||||
ensure: present
|
||||
reclaimPolicy: retain
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,8 @@
|
||||
# mlflow 환경 오버레이 템플릿 — custom-values.yaml(baseValues) 위에 얹힌다.
|
||||
# DB 비밀번호처럼 환경별로 달라야 하는 민감값만 담는다. 나머지 연결 정보(host/db/user 등)는
|
||||
# custom-values.yaml에 이미 있으므로 여기서 중복 기입하지 않는다.
|
||||
#
|
||||
# mlflow-db-values.yaml의 bootstrap.initdb.secretName과 실제 값이 일치해야 한다
|
||||
# ($INFISICAL_SECRET 토큰은 dip-console이 같은 시크릿 값으로 렌더링하는 것을 가정).
|
||||
externalDatabase:
|
||||
password: "$INFISICAL_SECRET"
|
||||
@@ -0,0 +1,39 @@
|
||||
# superset ArgoCD ApplicationSet
|
||||
|
||||
`superset-db`(cnpg-cluster, syncWave 0) → `superset`(syncWave 1) 2단계로 배포한다.
|
||||
전체 스키마는 [manifests/applicationset/openmetadata/1.12.1/](../../openmetadata/1.12.1/)와
|
||||
[manifests/applicationset/victoria-metrics/1.0.0/](../../victoria-metrics/1.0.0/)를 따른다.
|
||||
|
||||
## 구성
|
||||
|
||||
- `applicationset.yaml` — list generator로 Application 2개 생성.
|
||||
- `superset-db-values.yaml` — cnpg-cluster 환경 오버레이 템플릿(DB/owner, 시크릿 참조).
|
||||
- `superset-values.yaml` — superset 환경 오버레이 템플릿(DB 비밀번호만).
|
||||
|
||||
두 파일 모두 **템플릿**이다 — dip-console이 렌더링해 값 repo(`dip/tenant-catalog`)의
|
||||
`superset/*.yaml`로 커밋하는 것을 가정한다.
|
||||
|
||||
## 값 적용 순서 (2단)
|
||||
|
||||
각 Application은 `baseValues`(카탈로그 차트의 `custom-values.yaml`) + `valuesPath`(값 repo의
|
||||
환경 오버레이, 나중 파일 우선)를 병합한다.
|
||||
|
||||
## 사전 준비 (ArgoCD 밖)
|
||||
|
||||
- `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저
|
||||
설치되어 있어야 한다.
|
||||
- `superset-db-values.yaml`의 `bootstrap.initdb.secretName`과 동일한 이름·값의 K8s Secret이
|
||||
대상 네임스페이스에 미리 존재해야 한다(`username`/`password` 키).
|
||||
- ArgoCD가 `Cluster.postgresql.cnpg.io` 커스텀 리소스의 상태를 "Healthy"로 판정하려면
|
||||
`argocd-cm`에 해당 CRD용 커스텀 헬스 체크(Lua)가 등록되어 있어야 sync-wave 1이 정상 대기한다.
|
||||
- `custom-values.yaml`은 Keycloak OIDC 연동(`AUTH_TYPE = AUTH_OAUTH`)이 기본 활성화되어
|
||||
있고 `client_secret`/keycloak URL이 플레이스홀더다 — 실제 배포 전 반드시 실제 값으로
|
||||
채워야 한다. 채우지 않으면 로그인 화면에 "Keycloak으로 로그인" 버튼만 뜨고 실제로는
|
||||
로그인이 불가능하다(아이디/비번 입력폼 자체가 사라짐).
|
||||
|
||||
## 배포 테스트
|
||||
|
||||
절차와 주의사항은 [.claude/deploy-test-procedure.md](../../../../.claude/deploy-test-procedure.md)
|
||||
"카탈로그 앱 → cnpg-cluster 전환 배포 테스트" 절을 따른다.
|
||||
`APP_POD_SELECTOR="app=superset"`, fixture는
|
||||
`scripts/deploy-test/fixtures/superset-deploy-test-overrides.yaml`.
|
||||
@@ -0,0 +1,59 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: superset
|
||||
namespace: platform
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
generators:
|
||||
- list:
|
||||
# syncWave 0: 전용 cnpg-cluster DB 인스턴스 먼저 배포
|
||||
# syncWave 1: superset 본체 (postgresql.enabled: false, 위 DB의 -rw 서비스를 바라봄)
|
||||
elements:
|
||||
- name: superset-db
|
||||
chartPath: manifests/helm/cnpg-cluster/1.0.0
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: superset/superset-db-values.yaml
|
||||
syncWave: "0"
|
||||
- name: superset
|
||||
chartPath: manifests/helm/superset/0.13.5
|
||||
baseValues: custom-values.yaml
|
||||
valuesPath: superset/superset-values.yaml
|
||||
syncWave: "1"
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ .name }}"
|
||||
namespace: platform
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://gitea.example.org/dip/service-catalog
|
||||
targetRevision: master
|
||||
path: "{{ .chartPath }}"
|
||||
helm:
|
||||
releaseName: "{{ .name }}"
|
||||
valueFiles:
|
||||
- "{{ .baseValues }}"
|
||||
- $values/{{ .valuesPath }}
|
||||
- repoURL: https://gitea.example.org/dip/tenant-catalog
|
||||
targetRevision: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: superset
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 10s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -0,0 +1,25 @@
|
||||
# superset 전용 cnpg-cluster 환경 오버레이 템플릿.
|
||||
# dip-console이 렌더링해 값 repo(dip/tenant-catalog)의 superset/superset-db-values.yaml로
|
||||
# 커밋하는 것을 가정한 시작점이다. 배포 테스트로 검증된 값 그대로다
|
||||
# (지난 세션: manifests/helm/cnpg-cluster/1.0.0/custom-values-superset.yaml).
|
||||
instances: 1
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: superset
|
||||
owner: superset
|
||||
secretName: "$INFISICAL_SECRET"
|
||||
|
||||
databases:
|
||||
- name: superset
|
||||
owner: superset
|
||||
ensure: present
|
||||
reclaimPolicy: retain
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,9 @@
|
||||
# superset 환경 오버레이 템플릿 — custom-values.yaml(baseValues) 위에 얹힌다.
|
||||
# DB 비밀번호처럼 환경별로 달라야 하는 민감값만 담는다. 나머지 연결 정보(host/db/user 등)는
|
||||
# custom-values.yaml에 이미 있으므로 여기서 중복 기입하지 않는다.
|
||||
#
|
||||
# superset-db-values.yaml의 bootstrap.initdb.secretName과 실제 값이 일치해야 한다
|
||||
# ($INFISICAL_SECRET 토큰은 dip-console이 같은 시크릿 값으로 렌더링하는 것을 가정).
|
||||
supersetNode:
|
||||
connections:
|
||||
db_pass: "$INFISICAL_SECRET"
|
||||
@@ -122,17 +122,40 @@ logs:
|
||||
|
||||
### 4) Postgresql 설정
|
||||
|
||||
- 내장 postgresql을 사용하여 배포할 수 있다.
|
||||
- 내장 bitnami postgresql 서브차트 대신, `cnpg-cluster`(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
- **사전 조건**: `cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에
|
||||
먼저 설치되어 있어야 한다.
|
||||
- **배포 순서**
|
||||
``` sh
|
||||
# 1) airflow용 DB 계정 시크릿 생성 (아래 password는 metadataConnection.pass와 동일해야 한다)
|
||||
kubectl create secret generic airflow-db-app-secret \
|
||||
--from-literal=username=airflow --from-literal=password=airflow -n airflow
|
||||
|
||||
# 2) 전용 cnpg-cluster 배포
|
||||
helm upgrade airflow-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
-f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
-f manifests/applicationset/airflow/1.16.0/airflow-db-values.yaml \
|
||||
--install -n airflow
|
||||
|
||||
# 3) airflow 배포 (기존 배포 방법과 동일)
|
||||
helm upgrade airflow ./ -f custom-values.yaml --install -n airflow --create-namespace
|
||||
```
|
||||
- ArgoCD로 배포한다면 `manifests/applicationset/airflow/1.16.0/`의 ApplicationSet을 쓴다 —
|
||||
`airflow-db`(syncWave 0) → `airflow`(syncWave 1) 순서로 자동 배포된다. 자세한 내용은
|
||||
해당 디렉토리의 `README.md` 참고.
|
||||
- custom-values.yaml
|
||||
``` yaml
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
enablePostgresUser: true
|
||||
postgresPassword: postgres # postgres 유저의 패스워드 설정
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi # airflow에서 사용하는 DB의 볼륨 사이즈 설정
|
||||
storageClass: "" # DB 볼륨 배포를 위한 storage class 설정
|
||||
enabled: false
|
||||
|
||||
data:
|
||||
metadataConnection:
|
||||
user: airflow
|
||||
pass: airflow # 위 시크릿 생성 시 사용한 password와 동일해야 한다
|
||||
protocol: postgresql
|
||||
host: airflow-db-rw # cnpg-cluster 릴리스명이 airflow-db일 때의 rw 서비스명
|
||||
port: 5432
|
||||
db: airflow
|
||||
sslmode: disable
|
||||
```
|
||||
- 참고: `manifests/applicationset/airflow/1.16.0/airflow-db-values.yaml`
|
||||
@@ -4,11 +4,17 @@
|
||||
ingress:
|
||||
web:
|
||||
enabled: true
|
||||
# Kong → APISIX 마이그레이션(issue #139, mlflow/lakekeeper와 동일). 이 클러스터에는
|
||||
# kong ingress controller가 없어 `kubernetes.io/ingress.class: kong` 애노테이션 방식은
|
||||
# 어떤 컨트롤러도 인식하지 않아 접근 자체가 불가능했다(실측 — CLASS: <none>).
|
||||
ingressClassName: "apisix"
|
||||
# 기본 path "/" 는 정확히 "/" 만 매치되어(exact) airflow가 "/home" 등으로 리다이렉트한
|
||||
# 뒤에는 전부 404가 난다(실측). lakekeeper와 동일하게 regex 전체 매치로 바꾼다.
|
||||
path: "/.*"
|
||||
annotations:
|
||||
cert-manager.io/issuer: "root-ca-issuer"
|
||||
kubernetes.io/ingress.class: kong
|
||||
konghq.com/protocols: https
|
||||
konghq.com/https-redirect-status-code: "301"
|
||||
cert-manager.io/cluster-issuer: "root-ca-issuer"
|
||||
k8s.apisix.apache.org/ssl-redirect: "true"
|
||||
k8s.apisix.apache.org/use-regex: "true"
|
||||
hosts:
|
||||
- name: "airflow.example.org"
|
||||
tls:
|
||||
@@ -81,13 +87,19 @@ logs:
|
||||
statsd:
|
||||
enabled: false
|
||||
|
||||
# 내장 bitnami postgresql 서브차트 대신 cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
# 사전 배포: helm install airflow-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
# -f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
# -f manifests/applicationset/airflow/1.16.0/airflow-db-values.yaml -n <ns>
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
enablePostgresUser: true
|
||||
postgresPassword: postgres
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi # airflow에서 사용하는 DB의 볼륨 사이즈 설정
|
||||
storageClass: ""
|
||||
enabled: false
|
||||
|
||||
data:
|
||||
metadataConnection:
|
||||
user: airflow
|
||||
pass: airflow
|
||||
protocol: postgresql
|
||||
host: airflow-db-rw
|
||||
port: 5432
|
||||
db: airflow
|
||||
sslmode: disable
|
||||
@@ -34,12 +34,20 @@ ingress:
|
||||
- flowise.example.org
|
||||
secretName: flowise-tls-secret
|
||||
|
||||
# 내장 bitnami postgresql 서브차트 대신 cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
# 사전 배포: helm install flowise-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
# -f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
# -f manifests/applicationset/flowise/6.0.0/flowise-db-values.yaml -n <ns>
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
externalPostgresql:
|
||||
enabled: true
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi
|
||||
host: flowise-db-rw # cnpg-cluster 릴리스명이 flowise-db일 때의 rw 서비스명
|
||||
port: 5432
|
||||
username: flowise
|
||||
password: flowise # cnpg-cluster 부트스트랩 시크릿의 password와 동일해야 한다
|
||||
database: flowise
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
|
||||
@@ -68,12 +68,20 @@ catalog:
|
||||
secret:
|
||||
secretName: root-ca-secret
|
||||
|
||||
# lakekeeper DB
|
||||
# lakekeeper DB — 내장 postgres 서브차트 대신 cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
postgresql:
|
||||
storage:
|
||||
className:
|
||||
requestedSize: 5Gi
|
||||
resources: {}
|
||||
enabled: false
|
||||
|
||||
externalDatabase:
|
||||
type: postgres
|
||||
# -ro는 replica 전용이라 instances:1(권장 기준, small 티어)에서는 엔드포인트가 없다 —
|
||||
# -r(전체 인스턴스 라운드로빈, primary 포함)을 대신 쓴다. 배포 테스트로 실측된 문제다.
|
||||
host_read: "lakekeeper-db-r"
|
||||
host_write: "lakekeeper-db-rw"
|
||||
port: 5432
|
||||
database: lakekeeper
|
||||
user: lakekeeper
|
||||
password: lakekeeper
|
||||
|
||||
# OIDC 인증
|
||||
auth:
|
||||
@@ -162,11 +170,35 @@ extraVolumes:
|
||||
|
||||
### 4) PostgreSQL 데이터베이스 설정
|
||||
|
||||
- 내장 postgres 서브차트(`groundhog2k/postgres`) 대신 `cnpg-cluster`(전용 인스턴스)를 외부 DB로
|
||||
사용한다. **사전 조건**: `cloudnative-pg` operator가 클러스터에 먼저 설치되어 있어야 한다.
|
||||
- **배포 순서**
|
||||
``` sh
|
||||
kubectl create secret generic lakekeeper-db-app-secret \
|
||||
--from-literal=username=lakekeeper --from-literal=password=lakekeeper -n lakekeeper
|
||||
|
||||
helm upgrade lakekeeper-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
-f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
-f manifests/applicationset/lakekeeper/0.11.0/lakekeeper-db-values.yaml \
|
||||
--install -n lakekeeper
|
||||
|
||||
helm upgrade lakekeeper ./ -f custom-values.yaml --install -n lakekeeper --create-namespace
|
||||
```
|
||||
- ArgoCD로 배포한다면 `manifests/applicationset/lakekeeper/0.11.0/`의 ApplicationSet을 쓴다 —
|
||||
`lakekeeper-db`(syncWave 0) → `lakekeeper`(syncWave 1) 순서로 자동 배포된다. 자세한 내용은
|
||||
해당 디렉토리의 `README.md` 참고.
|
||||
|
||||
| Name | 설명 | 기본값 |
|
||||
| ------------------------- | ------------------------------------------------------------ | ----------- |
|
||||
| `postgresql.storage.className` | 스토리지 클래스 | `""` |
|
||||
| `postgresql.storage.requestedSize` | PostgreSQL 스토리지 크기 | `5Gi` |
|
||||
| `postgresql.resources` | PostgreSQL 리소스 설정 | `{}` |
|
||||
| `postgresql.enabled` | 내장 postgres 서브차트 사용 여부. cnpg-cluster 사용 시 `false` | `false` |
|
||||
| `externalDatabase.host_write` | cnpg-cluster의 rw 서비스명(`<release>-rw`) | `lakekeeper-db-rw` |
|
||||
| `externalDatabase.host_read` | cnpg-cluster의 ro 서비스명(`<release>-ro`) | `lakekeeper-db-ro` |
|
||||
| `externalDatabase.database`, `.user`, `.password` | DB 계정 정보. cnpg-cluster 부트스트랩 시크릿과 동일해야 한다 | 참고 예시 |
|
||||
|
||||
> 참고: `manifests/applicationset/lakekeeper/0.11.0/lakekeeper-db-values.yaml`
|
||||
>
|
||||
> OpenFGA 인증/권한 스토어(`internalOpenFGA: true`)는 여전히 자체 bitnami postgresql
|
||||
> 서브차트(`openfga.postgresql`)를 내장 사용한다 — 이번 전환 범위에는 포함되지 않았다.
|
||||
|
||||
### 5) OIDC 인증 설정
|
||||
|
||||
|
||||
@@ -29,13 +29,27 @@ catalog:
|
||||
secret:
|
||||
secretName: root-ca-secret
|
||||
|
||||
# lakekeeper DB
|
||||
# lakekeeper DB — 내장 postgres 서브차트(groundhog2k/postgres) 대신
|
||||
# cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
# 사전 배포: helm install lakekeeper-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
# -f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
# -f manifests/applicationset/lakekeeper/0.11.0/lakekeeper-db-values.yaml -n <ns>
|
||||
postgresql:
|
||||
enabled: true
|
||||
storage:
|
||||
className:
|
||||
requestedSize: 5Gi
|
||||
resources: {}
|
||||
enabled: false
|
||||
|
||||
externalDatabase:
|
||||
type: postgres
|
||||
# cnpg-cluster의 -ro 서비스는 replica 파드에만 연결되는데, 권장 배포 기준인
|
||||
# instances: 1(small 티어)에서는 replica가 없어 -ro가 엔드포인트 0개인 서비스가
|
||||
# 된다 — lakekeeper의 read-connection이 영구히 재시도만 하며 멈춘다(배포 테스트로 실측).
|
||||
# -r은 인스턴스 수와 무관하게 항상 primary를 포함해 라운드로빈하므로 이 값을 쓴다.
|
||||
# instances를 3 이상으로 올려 실제 replica를 운용하는 경우에도 -r로 충분하다.
|
||||
host_read: "lakekeeper-db-r"
|
||||
host_write: "lakekeeper-db-rw"
|
||||
port: 5432
|
||||
database: lakekeeper
|
||||
user: lakekeeper
|
||||
password: lakekeeper
|
||||
|
||||
|
||||
# OIDC 인증
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# MLflow 배포
|
||||
|
||||
bitnami/mlflow 차트(2.1.0) 기반 배포 가이드다.
|
||||
|
||||
## 1. 배포 방법
|
||||
|
||||
### 1) 사전 조건
|
||||
|
||||
- 내장 bitnami postgresql 서브차트 대신 `cnpg-cluster`(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
`cloudnative-pg` operator(`manifests/helm/cloudnative-pg/0.29.0`)가 클러스터에 먼저 설치되어
|
||||
있어야 한다.
|
||||
|
||||
### 2) 배포 순서
|
||||
|
||||
```sh
|
||||
git clone https://github.com/paasup/dip-catalog.git
|
||||
cd dip-catalog
|
||||
|
||||
# 1) mlflow용 DB 계정 시크릿 생성 (password는 externalDatabase.password와 동일해야 한다)
|
||||
kubectl create secret generic mlflow-db-app-secret \
|
||||
--from-literal=username=mlflow --from-literal=password=mlflow1234 -n mlflow
|
||||
|
||||
# 2) 전용 cnpg-cluster 배포
|
||||
helm upgrade mlflow-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
-f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
-f manifests/applicationset/mlflow/2.1.0/mlflow-db-values.yaml \
|
||||
--install -n mlflow --create-namespace
|
||||
|
||||
# 3) mlflow 배포
|
||||
helm upgrade mlflow manifests/helm/mlflow/2.1.0 -f custom-values.yaml --install -n mlflow
|
||||
```
|
||||
|
||||
ArgoCD로 배포한다면 `manifests/applicationset/mlflow/2.1.0/`의 ApplicationSet을 쓴다 —
|
||||
`mlflow-db`(syncWave 0) → `mlflow`(syncWave 1) 순서로 자동 배포된다. 자세한 내용은 해당
|
||||
디렉토리의 `README.md` 참고.
|
||||
|
||||
## 2. custom-values.yaml 설명
|
||||
|
||||
### 1) 이미지 설정
|
||||
|
||||
private 환경 배포 시 registry를 변경한다.
|
||||
|
||||
```yaml
|
||||
image:
|
||||
repository: bitnamilegacy/mlflow
|
||||
gitImage:
|
||||
repository: bitnamilegacy/git
|
||||
waitContainer:
|
||||
image:
|
||||
repository: bitnamilegacy/os-shell
|
||||
```
|
||||
|
||||
### 2) PostgreSQL 설정
|
||||
|
||||
내장 postgresql 서브차트를 끄고 `externalDatabase`로 cnpg-cluster를 가리킨다.
|
||||
|
||||
```yaml
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
externalDatabase:
|
||||
dialectDriver: "postgresql"
|
||||
host: "mlflow-db-rw" # cnpg-cluster 릴리스명이 mlflow-db일 때의 rw 서비스명
|
||||
port: 5432
|
||||
user: mlflow
|
||||
password: mlflow1234 # 위 시크릿 생성 시 사용한 password와 동일해야 한다
|
||||
database: mlflow
|
||||
```
|
||||
|
||||
참고: `manifests/applicationset/mlflow/2.1.0/mlflow-db-values.yaml`
|
||||
|
||||
### 3) MinIO 설정
|
||||
|
||||
아티팩트 저장소로 내장 minio 서브차트를 사용한다(`minio.enabled: true`). private 환경에서는
|
||||
`minio.image.repository`를 내부 registry로 변경한다.
|
||||
|
||||
```yaml
|
||||
minio:
|
||||
enabled: true
|
||||
auth:
|
||||
rootUser: admin
|
||||
rootPassword: admin1234
|
||||
image:
|
||||
repository: bitnamilegacy/minio
|
||||
```
|
||||
@@ -45,20 +45,20 @@ run:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
|
||||
# 내장 bitnami postgresql 서브차트 대신 cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
# 사전 배포: helm install mlflow-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
# -f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
# -f manifests/applicationset/mlflow/2.1.0/mlflow-db-values.yaml -n <ns>
|
||||
postgresql:
|
||||
auth:
|
||||
username: mlflow
|
||||
enabled: false
|
||||
|
||||
externalDatabase:
|
||||
dialectDriver: "postgresql"
|
||||
host: "mlflow-db-rw"
|
||||
port: 5432
|
||||
user: mlflow
|
||||
password: mlflow1234
|
||||
image:
|
||||
repository: bitnamilegacy/postgresql
|
||||
primary:
|
||||
reousrces:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
database: mlflow
|
||||
minio:
|
||||
enabled: true
|
||||
auth:
|
||||
|
||||
@@ -247,34 +247,38 @@ bootstrapScript: |
|
||||
|
||||
### 6) postgresql
|
||||
|
||||
- subchart로 포함된 postgresql을 배포하기 위한 설정이다.
|
||||
- 내장 bitnami postgresql 서브차트 대신 `cnpg-cluster`(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
- **사전 조건**: `cloudnative-pg` operator가 클러스터에 먼저 설치되어 있어야 한다.
|
||||
- **배포 순서**
|
||||
``` sh
|
||||
kubectl create secret generic superset-db-app-secret \
|
||||
--from-literal=username=superset --from-literal=password=superset -n superset
|
||||
|
||||
``` yaml
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
# DB의 계정 및 기본 생성되는 database를 설정한다.
|
||||
username: superset
|
||||
password: superset
|
||||
database: superset
|
||||
image:
|
||||
# private 환경에서 배포할 때 사용할 registry로 설정을 변경한다.
|
||||
registry: docker.io
|
||||
primary:
|
||||
# DB의 리소스를 설정한다.
|
||||
resources:
|
||||
limits: {}
|
||||
requests:
|
||||
memory: 256Mi
|
||||
cpu: 250m
|
||||
# DB에서 사용할 Disk 설정으로 StorageClass와 사이즈를 설정한다.
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: ""
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
size: 8Gi
|
||||
```
|
||||
helm upgrade superset-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
-f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
-f manifests/applicationset/superset/0.13.5/superset-db-values.yaml \
|
||||
--install -n superset
|
||||
|
||||
helm upgrade superset ../superset --install -n superset --create-namespace \
|
||||
-f ./custom-values.yaml
|
||||
```
|
||||
- ArgoCD로 배포한다면 `manifests/applicationset/superset/0.13.5/`의 ApplicationSet을 쓴다 —
|
||||
`superset-db`(syncWave 0) → `superset`(syncWave 1) 순서로 자동 배포된다. 자세한 내용은
|
||||
해당 디렉토리의 `README.md` 참고.
|
||||
- custom-values.yaml
|
||||
``` yaml
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
supersetNode:
|
||||
connections:
|
||||
db_host: 'superset-db-rw' # cnpg-cluster 릴리스명이 superset-db일 때의 rw 서비스명
|
||||
db_port: "5432"
|
||||
db_user: superset
|
||||
db_pass: superset # 위 시크릿 생성 시 사용한 password와 동일해야 한다
|
||||
db_name: superset
|
||||
```
|
||||
- 참고: `manifests/applicationset/superset/0.13.5/superset-db-values.yaml`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -99,11 +99,17 @@ tolerations: []
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
# airflow와 동일하게 실측된 문제 — ingressClassName 미지정 시 이 클러스터의 apisix가
|
||||
# 아예 인식하지 않아 CLASS: <none>으로 뜨고 접근이 불가능하다. path도 exact "/" 만
|
||||
# 매치되면 superset의 내부 리다이렉트(예: /login/) 이후 전부 404가 나 regex로 바꾼다.
|
||||
ingressClassName: "apisix"
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "selfsigned-issuer"
|
||||
cert-manager.io/duration: 8760h
|
||||
cert-manager.io/renew-before: 720h
|
||||
path: /
|
||||
k8s.apisix.apache.org/use-regex: "true"
|
||||
k8s.apisix.apache.org/ssl-redirect: "true"
|
||||
path: /.*
|
||||
pathType: ImplementationSpecific
|
||||
hosts:
|
||||
- superset.example.org
|
||||
@@ -133,8 +139,11 @@ supersetNode:
|
||||
redis_ssl:
|
||||
enabled: false
|
||||
ssl_cert_reqs: CERT_NONE
|
||||
# You need to change below configuration incase bringing own PostgresSQL instance and also set postgresql.enabled:false
|
||||
db_host: '{{ .Release.Name }}-postgresql'
|
||||
# 내장 bitnami postgresql 서브차트 대신 cnpg-cluster(전용 인스턴스)를 외부 DB로 사용한다.
|
||||
# 사전 배포: helm install superset-db manifests/helm/cnpg-cluster/1.0.0 \
|
||||
# -f manifests/helm/cnpg-cluster/1.0.0/custom-values.yaml \
|
||||
# -f manifests/applicationset/superset/0.13.5/superset-db-values.yaml -n <ns>
|
||||
db_host: 'superset-db-rw'
|
||||
db_port: "5432"
|
||||
db_user: superset
|
||||
db_pass: superset
|
||||
@@ -160,23 +169,7 @@ supersetCeleryFlower:
|
||||
resources: {}
|
||||
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
username: superset
|
||||
password: superset
|
||||
database: superset
|
||||
image:
|
||||
registry: docker.io
|
||||
primary:
|
||||
resources:
|
||||
limits: {}
|
||||
requests:
|
||||
memory: 256Mi
|
||||
cpu: 250m
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: ""
|
||||
size: 8Gi
|
||||
enabled: false
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# deploy-test-app-with-cnpg.sh
|
||||
# 내장 bitnami postgresql 서브차트를 끄고 전용 cnpg-cluster 인스턴스를 외부 DB로
|
||||
# 쓰도록 바꾼 카탈로그 앱(airflow/lakekeeper/mlflow/superset)이 실제로 그 DB에
|
||||
# 연결해 기동하는지 격리된 테스트 네임스페이스에서 확인한다.
|
||||
#
|
||||
# scripts/deploy-test/deploy-test-cnpg-cluster.sh 와 같은 원칙을 따른다 —
|
||||
# operator(cloudnative-pg)는 상시 컴포넌트로 재사용하고, 앱/DB 릴리스와 테스트
|
||||
# 네임스페이스는 매 실행 재생성한다. 정리는 성공/실패와 무관하게 항상 수행한다(trap).
|
||||
#
|
||||
# 이 스크립트가 확인하는 것은 "DB 연결이 되는가"이지 앱의 전체 기능 검증이 아니다.
|
||||
# ingress/cert-manager/OIDC(keycloak) 등 DB와 무관한 의존성 때문에 앱 차트의
|
||||
# `helm install --wait`가 타임아웃할 수 있어, 앱 설치는 --wait 없이 진행하고
|
||||
# 파드 상태 + 로그의 DB 연결 실패 패턴 부재로 판단한다.
|
||||
#
|
||||
# 사용:
|
||||
# APP_CHART_DIR=manifests/helm/airflow/1.16.0 \
|
||||
# APP_RELEASE=airflow \
|
||||
# DB_CUSTOM_VALUES=manifests/applicationset/airflow/1.16.0/airflow-db-values.yaml \
|
||||
# DB_SECRET_USER=airflow DB_SECRET_PASSWORD=airflow \
|
||||
# TEST_NAMESPACE=airflow-test-build \
|
||||
# bash scripts/deploy-test/deploy-test-app-with-cnpg.sh /tmp/deploy-test-out
|
||||
#
|
||||
# 환경변수:
|
||||
# APP_CHART_DIR (필수) 앱 차트 경로 (예: manifests/helm/airflow/1.16.0)
|
||||
# APP_RELEASE (필수) 앱 helm 릴리스명
|
||||
# APP_CUSTOM_VALUES 기본 "$APP_CHART_DIR/custom-values.yaml"
|
||||
# DB_CUSTOM_VALUES (필수) cnpg-cluster용 앱 전용 오버레이 — 각 앱의
|
||||
# manifests/applicationset/<app>/<version>/<app>-db-values.yaml
|
||||
# (ArgoCD ApplicationSet의 valuesPath와 동일한 파일)
|
||||
# DB_RELEASE 기본 "${APP_RELEASE}-db"
|
||||
# DB_SECRET_NAME 기본 "${DB_RELEASE}-app-secret" (각 앱 <app>-db-values.yaml의
|
||||
# bootstrap.initdb.secretName과 반드시 일치해야 한다)
|
||||
# DB_SECRET_USER (필수) DB owner 계정명
|
||||
# DB_SECRET_PASSWORD (필수) DB owner 비밀번호 — 앱 custom-values.yaml의 평문 값과 동일해야 함
|
||||
# OPERATOR_CHART_DIR 기본 manifests/helm/cloudnative-pg/0.29.0
|
||||
# OPERATOR_NAMESPACE 기본 cnpg-system (상시 — 이 스크립트가 지우지 않는다)
|
||||
# OPERATOR_RELEASE 기본 cnpg
|
||||
# TEST_NAMESPACE (필수) 격리된 테스트 네임스페이스 (매 실행 재생성)
|
||||
# DB_WAIT_TIMEOUT 기본 300 (초) — Cluster healthy 대기 한도
|
||||
# APP_WAIT_SECONDS 기본 180 (초) — 앱 파드 상태 관찰 시간
|
||||
# APP_EXTRA_VALUES 선택. custom-values.yaml 위에 추가로 얹을 배포-테스트 전용 values 파일
|
||||
# (예: 외부 keycloak 등 이 테스트 환경에 없는 의존성을 끄는 용도).
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 건드리지 않는다 —
|
||||
# scripts/deploy-test/fixtures/ 에 보관한다.
|
||||
# APP_POD_SELECTOR (강력 권장) DB에 실제로 연결하는 파드를 가리키는 label selector
|
||||
# (예: airflow "component=webserver", mlflow
|
||||
# "app.kubernetes.io/component=tracking", superset "app=superset",
|
||||
# lakekeeper "app.kubernetes.io/component=catalog"). 지정하면 이
|
||||
# selector 에 매치되는 파드가 Running+Ready 가 될 때까지만 기다린다 —
|
||||
# git-sync/ingress 등 DB 와 무관한 다른 파드의 상태는 무시한다.
|
||||
# 미지정 시 네임스페이스의 아무 파드나 1개 Running 이면 통과로 간주하는
|
||||
# 느슨한 판정으로 대체된다(권장하지 않음 — 실제 DB 소비 파드를 못 볼 수 있다).
|
||||
#
|
||||
# 산출물 (OUT_DIR): deploy-test.log
|
||||
# 종료 코드: 0 = DB 연결 성공(파드 Running + 로그에 DB 연결 실패 패턴 없음), 그 외 = 실패.
|
||||
# 정리는 종료 코드와 무관하게 항상 수행된다.
|
||||
# =============================================================================
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
OUT_DIR="${1:?사용법: deploy-test-app-with-cnpg.sh <OUT_DIR>}"
|
||||
mkdir -p "$OUT_DIR"
|
||||
LOG="$OUT_DIR/deploy-test.log"
|
||||
: > "$LOG"
|
||||
|
||||
APP_CHART_DIR="${APP_CHART_DIR:?APP_CHART_DIR 환경변수가 필요하다 (앱 차트 경로)}"
|
||||
APP_RELEASE="${APP_RELEASE:?APP_RELEASE 환경변수가 필요하다}"
|
||||
APP_CUSTOM_VALUES="${APP_CUSTOM_VALUES:-$APP_CHART_DIR/custom-values.yaml}"
|
||||
DB_CUSTOM_VALUES="${DB_CUSTOM_VALUES:?DB_CUSTOM_VALUES 환경변수가 필요하다 (cnpg-cluster 앱 전용 오버레이)}"
|
||||
DB_RELEASE="${DB_RELEASE:-${APP_RELEASE}-db}"
|
||||
DB_SECRET_NAME="${DB_SECRET_NAME:-${DB_RELEASE}-app-secret}"
|
||||
DB_SECRET_USER="${DB_SECRET_USER:?DB_SECRET_USER 환경변수가 필요하다}"
|
||||
DB_SECRET_PASSWORD="${DB_SECRET_PASSWORD:?DB_SECRET_PASSWORD 환경변수가 필요하다}"
|
||||
OPERATOR_CHART_DIR="${OPERATOR_CHART_DIR:-$REPO_ROOT/manifests/helm/cloudnative-pg/0.29.0}"
|
||||
OPERATOR_NAMESPACE="${OPERATOR_NAMESPACE:-cnpg-system}"
|
||||
OPERATOR_RELEASE="${OPERATOR_RELEASE:-cnpg}"
|
||||
TEST_NAMESPACE="${TEST_NAMESPACE:?TEST_NAMESPACE 환경변수가 필요하다 (격리된 테스트 네임스페이스)}"
|
||||
DB_WAIT_TIMEOUT="${DB_WAIT_TIMEOUT:-300}"
|
||||
# 이미지가 큰 앱(flowise 등)은 최초 pull 에만 5분 넘게 걸려 300s 로는 ImagePullBackOff 상태에서
|
||||
# 타임아웃한다(실측). 캐시된 뒤에는 1분 내로 끝나므로 넉넉히 잡아도 손해가 없다.
|
||||
APP_WAIT_SECONDS="${APP_WAIT_SECONDS:-600}"
|
||||
APP_EXTRA_VALUES="${APP_EXTRA_VALUES:-}"
|
||||
APP_POD_SELECTOR="${APP_POD_SELECTOR:-}"
|
||||
CNPG_CLUSTER_CHART_DIR="${CNPG_CLUSTER_CHART_DIR:-$REPO_ROOT/manifests/helm/cnpg-cluster/1.0.0}"
|
||||
|
||||
log() { echo "$(date -u +%H:%M:%S) $*" | tee -a "$LOG"; }
|
||||
|
||||
log "== 사전 확인 =="
|
||||
for t in kubectl helm python3; do
|
||||
command -v "$t" >/dev/null || { log "::error:: $t 없음"; exit 2; }
|
||||
done
|
||||
[ -d "$OPERATOR_CHART_DIR" ] || { log "::error:: operator 차트 없음: $OPERATOR_CHART_DIR"; exit 2; }
|
||||
[ -d "$APP_CHART_DIR" ] || { log "::error:: 앱 차트 없음: $APP_CHART_DIR"; exit 2; }
|
||||
[ -f "$APP_CUSTOM_VALUES" ] || { log "::error:: 앱 custom-values 없음: $APP_CUSTOM_VALUES"; exit 2; }
|
||||
[ -f "$DB_CUSTOM_VALUES" ] || { log "::error:: DB custom-values 없음: $DB_CUSTOM_VALUES"; exit 2; }
|
||||
if ! kubectl get ns >/dev/null 2>>"$LOG"; then
|
||||
log "::error:: 클러스터 연결 실패 — kubeconfig/네트워크 확인"
|
||||
exit 2
|
||||
fi
|
||||
log " OK (kubectl context: $(kubectl config current-context 2>/dev/null))"
|
||||
|
||||
# --- operator: 상시 컴포넌트. 없으면 1회 설치, 있으면 재사용 --------------------
|
||||
ensure_operator() {
|
||||
if helm status "$OPERATOR_RELEASE" -n "$OPERATOR_NAMESPACE" >/dev/null 2>>"$LOG"; then
|
||||
log "== operator: 기존 릴리스 재사용 ($OPERATOR_RELEASE/$OPERATOR_NAMESPACE) =="
|
||||
return 0
|
||||
fi
|
||||
if kubectl get validatingwebhookconfiguration cnpg-validating-webhook-configuration >/dev/null 2>&1; then
|
||||
owner_ns="$(kubectl get validatingwebhookconfiguration cnpg-validating-webhook-configuration \
|
||||
-o jsonpath='{.metadata.annotations.meta\.helm\.sh/release-namespace}' 2>/dev/null)"
|
||||
owner_rel="$(kubectl get validatingwebhookconfiguration cnpg-validating-webhook-configuration \
|
||||
-o jsonpath='{.metadata.annotations.meta\.helm\.sh/release-name}' 2>/dev/null)"
|
||||
if [ "$owner_ns" != "$OPERATOR_NAMESPACE" ] || [ "$owner_rel" != "$OPERATOR_RELEASE" ]; then
|
||||
log "::error:: 다른 CNPG operator 가 이미 클러스터에 있다 (release=$owner_rel ns=$owner_ns)."
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
log "== operator: 신규 설치 ($OPERATOR_RELEASE/$OPERATOR_NAMESPACE, 차트 기본값) =="
|
||||
helm install "$OPERATOR_RELEASE" "$OPERATOR_CHART_DIR" \
|
||||
-n "$OPERATOR_NAMESPACE" --create-namespace --wait --timeout 600s \
|
||||
>>"$LOG" 2>&1 || { log "::error:: operator 설치 실패 — $LOG 확인"; exit 1; }
|
||||
log " OK"
|
||||
}
|
||||
|
||||
# --- 테스트 네임스페이스 정리 (선-정리·후-정리 공용) --------------------------
|
||||
cleanup_test_ns() {
|
||||
kubectl get ns "$TEST_NAMESPACE" >/dev/null 2>&1 || return 0
|
||||
|
||||
log "== 정리: $TEST_NAMESPACE =="
|
||||
if helm status "$APP_RELEASE" -n "$TEST_NAMESPACE" >/dev/null 2>&1; then
|
||||
helm uninstall "$APP_RELEASE" -n "$TEST_NAMESPACE" >>"$LOG" 2>&1
|
||||
fi
|
||||
if helm status "$DB_RELEASE" -n "$TEST_NAMESPACE" >/dev/null 2>&1; then
|
||||
helm uninstall "$DB_RELEASE" -n "$TEST_NAMESPACE" >>"$LOG" 2>&1
|
||||
fi
|
||||
kubectl -n "$TEST_NAMESPACE" delete pvc --all --wait --timeout=120s >>"$LOG" 2>&1
|
||||
|
||||
# storageClass longhorn은 reclaimPolicy Retain이라 PVC 삭제만으로는 볼륨이 안 지워진다
|
||||
# (.claude/pitfalls.md) — 이 네임스페이스가 남긴 PV만 골라 지운다.
|
||||
local pv
|
||||
for pv in $(kubectl get pv -o json 2>/dev/null | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
for p in d.get('items', []):
|
||||
cr = p['spec'].get('claimRef') or {}
|
||||
if cr.get('namespace') == '$TEST_NAMESPACE':
|
||||
print(p['metadata']['name'])
|
||||
" 2>/dev/null); do
|
||||
kubectl delete pv "$pv" --ignore-not-found >>"$LOG" 2>&1 \
|
||||
|| log "::warning:: PV $pv 삭제 실패 — 수동 확인 필요"
|
||||
# k8s PV 오브젝트를 지워도 Longhorn 자체의 Volume 커스텀리소스는 남는다(실측 —
|
||||
# storageScheduled가 계속 누적돼 결국 "insufficient storage"로 다른 배포가 막힘).
|
||||
# PV와 이름이 같은 volumes.longhorn.io를 명시적으로 지워야 실제 디스크가 반환된다.
|
||||
kubectl -n longhorn-system delete volumes.longhorn.io "$pv" --ignore-not-found >>"$LOG" 2>&1
|
||||
done
|
||||
|
||||
kubectl delete ns "$TEST_NAMESPACE" --wait --timeout=120s >>"$LOG" 2>&1
|
||||
log " 완료"
|
||||
}
|
||||
|
||||
RESULT=1
|
||||
trap 'cleanup_test_ns' EXIT
|
||||
|
||||
ensure_operator
|
||||
cleanup_test_ns # 직전 실행이 비정상 종료했을 경우를 대비한 선-정리
|
||||
|
||||
log "== 테스트 네임스페이스 생성: $TEST_NAMESPACE =="
|
||||
kubectl create ns "$TEST_NAMESPACE" >>"$LOG" 2>&1
|
||||
|
||||
log "== DB 계정 시크릿 생성: $DB_SECRET_NAME (user=$DB_SECRET_USER) =="
|
||||
kubectl -n "$TEST_NAMESPACE" create secret generic "$DB_SECRET_NAME" \
|
||||
--from-literal=username="$DB_SECRET_USER" --from-literal=password="$DB_SECRET_PASSWORD" \
|
||||
>>"$LOG" 2>&1 || { log "::error:: 시크릿 생성 실패"; exit 1; }
|
||||
|
||||
log "== DB: cnpg-cluster 설치 ($DB_RELEASE/$TEST_NAMESPACE) =="
|
||||
# DB_CUSTOM_VALUES는 ArgoCD ApplicationSet용 템플릿(manifests/applicationset/<app>/<version>/
|
||||
# <app>-db-values.yaml)이라 secretName이 "$INFISICAL_SECRET" 플레이스홀더로 되어 있다 —
|
||||
# dip-console이 실제 배포 시 렌더링해 치환하는 값이다. 여기서는 위에서 실제로 만든
|
||||
# $DB_SECRET_NAME 시크릿으로 --set 오버라이드한다.
|
||||
if ! helm install "$DB_RELEASE" "$CNPG_CLUSTER_CHART_DIR" \
|
||||
-f "$CNPG_CLUSTER_CHART_DIR/custom-values.yaml" -f "$DB_CUSTOM_VALUES" \
|
||||
--set "bootstrap.initdb.secretName=$DB_SECRET_NAME" \
|
||||
-n "$TEST_NAMESPACE" --wait --timeout "${DB_WAIT_TIMEOUT}s" \
|
||||
>>"$LOG" 2>&1; then
|
||||
log "::error:: cnpg-cluster 설치 실패 — $LOG 확인"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "== DB: Cluster healthy 대기 (최대 ${DB_WAIT_TIMEOUT}s) =="
|
||||
# FQN 필수 — kubectl get cluster 는 Rancher/CAPI 의 동명 CRD 와 충돌한다(.claude/pitfalls.md)
|
||||
instances="$(kubectl -n "$TEST_NAMESPACE" get clusters.postgresql.cnpg.io "$DB_RELEASE" \
|
||||
-o jsonpath='{.spec.instances}' 2>/dev/null)"
|
||||
deadline=$((SECONDS + DB_WAIT_TIMEOUT))
|
||||
phase=""
|
||||
ready=0
|
||||
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||
phase="$(kubectl -n "$TEST_NAMESPACE" get clusters.postgresql.cnpg.io "$DB_RELEASE" \
|
||||
-o jsonpath='{.status.phase}' 2>/dev/null)"
|
||||
ready="$(kubectl -n "$TEST_NAMESPACE" get clusters.postgresql.cnpg.io "$DB_RELEASE" \
|
||||
-o jsonpath='{.status.readyInstances}' 2>/dev/null)"
|
||||
log " phase=$phase ready=${ready:-0}/$instances"
|
||||
[ "$phase" = "Cluster in healthy state" ] && [ "${ready:-0}" = "$instances" ] && break
|
||||
sleep 10
|
||||
done
|
||||
if [ "$phase" != "Cluster in healthy state" ] || [ "${ready:-0}" != "$instances" ]; then
|
||||
log "::error:: 타임아웃 — DB Cluster 가 healthy 상태에 도달하지 못함"
|
||||
kubectl -n "$TEST_NAMESPACE" get pods -l "cnpg.io/cluster=$DB_RELEASE" >>"$LOG" 2>&1
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "== DB: 부트스트랩 확인 (role/database) =="
|
||||
primary_pod="$(kubectl -n "$TEST_NAMESPACE" get pods \
|
||||
-l "cnpg.io/cluster=$DB_RELEASE,cnpg.io/instanceRole=primary" \
|
||||
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null)"
|
||||
kubectl -n "$TEST_NAMESPACE" exec "$primary_pod" -c postgres -- \
|
||||
psql -U postgres -Atc "\du" >>"$LOG" 2>&1
|
||||
kubectl -n "$TEST_NAMESPACE" exec "$primary_pod" -c postgres -- \
|
||||
psql -U postgres -Atc "\l" >>"$LOG" 2>&1
|
||||
|
||||
log "== 앱: 설치 ($APP_RELEASE/$TEST_NAMESPACE, --wait 없이 진행) =="
|
||||
# ingress/cert-manager/OIDC 등 DB 와 무관한 의존성 때문에 helm --wait 가 타임아웃할 수 있어
|
||||
# 설치만 하고 파드 상태는 아래에서 직접 관찰한다.
|
||||
app_values_args=(-f "$APP_CUSTOM_VALUES")
|
||||
if [ -n "$APP_EXTRA_VALUES" ]; then
|
||||
[ -f "$APP_EXTRA_VALUES" ] || { log "::error:: APP_EXTRA_VALUES 파일 없음: $APP_EXTRA_VALUES"; exit 2; }
|
||||
log " 추가 배포-테스트 전용 values 사용: $APP_EXTRA_VALUES"
|
||||
app_values_args+=(-f "$APP_EXTRA_VALUES")
|
||||
fi
|
||||
if ! helm install "$APP_RELEASE" "$APP_CHART_DIR" "${app_values_args[@]}" \
|
||||
-n "$TEST_NAMESPACE" --timeout 300s \
|
||||
>>"$LOG" 2>&1; then
|
||||
log "::error:: 앱 차트 설치 실패 — $LOG 확인"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "== 앱: 파드 상태 관찰 (최대 ${APP_WAIT_SECONDS}s) — DB 연결 여부 판단 =="
|
||||
if [ -n "$APP_POD_SELECTOR" ]; then
|
||||
log " 대상 파드 selector: $APP_POD_SELECTOR"
|
||||
else
|
||||
log " ::warning:: APP_POD_SELECTOR 미지정 — 네임스페이스의 아무 파드나 1개 Running 이면 통과로 판정한다"
|
||||
fi
|
||||
deadline=$((SECONDS + APP_WAIT_SECONDS))
|
||||
DB_ERROR_PATTERN='password authentication failed|could not connect to server|connection refused|no pg_hba.conf entry|FATAL:.*database|OperationalError|could not translate host name'
|
||||
db_connected=""
|
||||
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||
kubectl -n "$TEST_NAMESPACE" get pods >>"$LOG" 2>&1
|
||||
target_ready_count="$(kubectl -n "$TEST_NAMESPACE" get pods ${APP_POD_SELECTOR:+-l "$APP_POD_SELECTOR"} -o json 2>/dev/null | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
n=0
|
||||
for p in d.get('items', []):
|
||||
phase = p['status'].get('phase')
|
||||
statuses = p['status'].get('containerStatuses') or []
|
||||
ready = bool(statuses) and all(c.get('ready') for c in statuses)
|
||||
if phase == 'Running' and ready:
|
||||
n += 1
|
||||
print(n)
|
||||
" 2>/dev/null)"
|
||||
# DB 관련 여부와 무관하게 네임스페이스 전체 로그에서 연결 실패 패턴을 찾는다(넓게 감지).
|
||||
db_errors="$(kubectl -n "$TEST_NAMESPACE" get pods -o name 2>/dev/null | while read -r pod; do
|
||||
kubectl -n "$TEST_NAMESPACE" logs "$pod" --all-containers --tail=200 2>/dev/null
|
||||
done | grep -Ei "$DB_ERROR_PATTERN" | head -5)"
|
||||
|
||||
if [ -n "$db_errors" ]; then
|
||||
log "::error:: 파드 로그에서 DB 연결 실패 패턴 발견:"
|
||||
echo "$db_errors" | tee -a "$LOG"
|
||||
db_connected="false"
|
||||
break
|
||||
fi
|
||||
if [ "${target_ready_count:-0}" -ge 1 ]; then
|
||||
log " 대상 파드 Running/Ready ${target_ready_count}개 확인, DB 연결 실패 로그 없음"
|
||||
db_connected="true"
|
||||
break
|
||||
fi
|
||||
sleep 15
|
||||
done
|
||||
|
||||
if [ "$db_connected" = "true" ]; then
|
||||
log "== 결과: PASS (DB 연결 확인) =="
|
||||
RESULT=0
|
||||
else
|
||||
log "::error:: 타임아웃 또는 DB 연결 실패 — 파드/로그 상세는 $LOG 참고"
|
||||
kubectl -n "$TEST_NAMESPACE" get pods -o wide >>"$LOG" 2>&1
|
||||
RESULT=1
|
||||
fi
|
||||
|
||||
exit "$RESULT"
|
||||
@@ -128,6 +128,10 @@ for p in d.get('items', []):
|
||||
" 2>/dev/null); do
|
||||
kubectl delete pv "$pv" --ignore-not-found >>"$LOG" 2>&1 \
|
||||
|| log "::warning:: PV $pv 삭제 실패 — 수동 확인 필요"
|
||||
# k8s PV 오브젝트를 지워도 Longhorn 자체의 Volume 커스텀리소스는 남는다(실측 —
|
||||
# storageScheduled가 계속 누적돼 결국 "insufficient storage"로 다른 배포가 막힘).
|
||||
# PV와 이름이 같은 volumes.longhorn.io를 명시적으로 지워야 실제 디스크가 반환된다.
|
||||
kubectl -n longhorn-system delete volumes.longhorn.io "$pv" --ignore-not-found >>"$LOG" 2>&1
|
||||
done
|
||||
|
||||
kubectl delete ns "$TEST_NAMESPACE" --wait --timeout=120s >>"$LOG" 2>&1
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# airflow 배포 테스트 전용 오버라이드 — custom-values.yaml 위에 얹어서 쓴다.
|
||||
# 목적: DB(cnpg-cluster) 연결 여부만 확인한다. custom-values.yaml의 dags.gitSync는
|
||||
# 실제 운영 gitea 저장소(http://gitea-http.platform.svc.cluster.local:3000/dip/airflow-dags.git)를
|
||||
# 가리키므로, 격리된 테스트 네임스페이스에는 그 저장소가 없어 scheduler의 git-sync-init
|
||||
# 컨테이너가 "Repository not found"로 영구 CrashLoopBackOff에 빠진다(실측 — DB 연결과 무관).
|
||||
#
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 변경하지 않는다.
|
||||
dags:
|
||||
gitSync:
|
||||
enabled: false
|
||||
|
||||
# 배포 테스트 시 host는 반드시 별도 값으로 덮어쓴다 — 카탈로그 기본값(airflow.example.org)이
|
||||
# 이 클러스터의 다른 실제 배포와 겹칠 수 있다(lakekeeper에서 실측 — defense-llm 네임스페이스가
|
||||
# 동일 패턴의 host를 이미 쓰고 있었다).
|
||||
ingress:
|
||||
web:
|
||||
hosts:
|
||||
- name: "airflow-access-test.example.org"
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: "airflow-tls"
|
||||
@@ -0,0 +1,16 @@
|
||||
# flowise 배포 테스트 전용 오버라이드 — custom-values.yaml 위에 얹어서 쓴다.
|
||||
#
|
||||
# 배포 테스트 시 host는 반드시 별도 값으로 덮어쓴다 — 카탈로그 기본값(flowise.example.org)이
|
||||
# 이 클러스터의 다른 실제 배포와 겹칠 수 있다(lakekeeper에서 실측 — defense-llm 네임스페이스가
|
||||
# 동일 패턴의 host를 이미 쓰고 있었다).
|
||||
#
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 변경하지 않는다.
|
||||
ingress:
|
||||
hosts:
|
||||
- host: flowise-access-test.example.org
|
||||
paths:
|
||||
- /
|
||||
tls:
|
||||
- hosts:
|
||||
- flowise-access-test.example.org
|
||||
secretName: flowise-tls-secret
|
||||
@@ -0,0 +1,34 @@
|
||||
# lakekeeper 배포 테스트 전용 오버라이드 — custom-values.yaml 위에 얹어서 쓴다.
|
||||
# 목적: DB(cnpg-cluster) 연결 여부만 확인한다. custom-values.yaml의 auth.oauth2는
|
||||
# 실제 운영 keycloak(https://keycloak.example.org/realms/paasup)을 가리키므로,
|
||||
# 격리된 테스트 네임스페이스에서는 도달 불가능해 파드가 기동하지 못한다.
|
||||
#
|
||||
# lakekeeper values.yaml 주석(line 492-494): auth.oauth2.providerUri가 비어 있고
|
||||
# auth.kubernetes.enabled가 false면 인증 자체가 꺼진다 — 이 테스트에서 노리는 상태.
|
||||
#
|
||||
# custom-values.yaml의 catalog.extraVolumes(keycloak-tls)는 root-ca-secret을 참조하는데
|
||||
# 이 시크릿도 실제 keycloak 배포 시에만 생성되는 것이라 격리된 테스트 네임스페이스에는
|
||||
# 없다 — db-migration Job의 init 컨테이너가 FailedMount로 멈춘다. oauth2를 끄면서 이 볼륨도
|
||||
# 같이 비운다(Helm은 배열 값을 병합하지 않고 통째로 교체하므로 빈 배열로 덮어써야 한다).
|
||||
#
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 변경하지 않는다.
|
||||
auth:
|
||||
oauth2:
|
||||
providerUri: ""
|
||||
audience: ""
|
||||
ui:
|
||||
clientID: ""
|
||||
scopes: ""
|
||||
|
||||
catalog:
|
||||
extraEnv: []
|
||||
extraVolumeMounts: []
|
||||
extraVolumes: []
|
||||
|
||||
# custom-values.yaml의 host(lakekeeper.example.org)는 이 클러스터에 실제 배포된 다른
|
||||
# lakekeeper 인스턴스(defense-llm 네임스페이스)가 이미 쓰고 있다 — 같은 host로 배포하면
|
||||
# apisix에 동일 host의 라우트가 2개 등록되어 실제 서비스의 트래픽 라우팅과 충돌한다
|
||||
# (실측 — apisix admin API에 두 서비스가 동일 host로 동시 등록된 것 확인).
|
||||
# 배포 테스트는 반드시 별도 host를 쓴다.
|
||||
ingress:
|
||||
host: "lakekeeper-access-test.example.org"
|
||||
@@ -0,0 +1,14 @@
|
||||
# mlflow 배포 테스트 전용 오버라이드 — custom-values.yaml 위에 얹어서 쓴다.
|
||||
#
|
||||
# 배포 테스트 시 host는 반드시 별도 값으로 덮어쓴다 — 카탈로그 기본값(mlflow.example.org)이
|
||||
# 이 클러스터의 다른 실제 배포와 겹칠 수 있다(lakekeeper에서 실측 — defense-llm 네임스페이스가
|
||||
# 동일 패턴의 host를 이미 쓰고 있었다).
|
||||
#
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 변경하지 않는다.
|
||||
tracking:
|
||||
ingress:
|
||||
hostname: mlflow-access-test.example.org
|
||||
extraTls:
|
||||
- hosts:
|
||||
- mlflow-access-test.example.org
|
||||
secretName: mlflow-tls-secret
|
||||
@@ -0,0 +1,28 @@
|
||||
# superset 배포 테스트 전용 오버라이드 — custom-values.yaml 위에 얹어서 쓴다.
|
||||
# 목적: DB(cnpg-cluster) 연결 여부만 확인한다. custom-values.yaml의 configOverrides.enable_oauth는
|
||||
# 실제 운영 keycloak(플레이스홀더 URL/client_secret)을 가리키는 AUTH_TYPE = AUTH_OAUTH 설정이라,
|
||||
# 격리된 테스트 네임스페이스에서 로그인 화면에 "Keycloak으로 로그인" 버튼만 뜨고 실제로는
|
||||
# 가짜 URL이라 로그인이 불가능하다(실측). 이 키를 비워 기본 AUTH_DB(아이디/비번) 로그인으로
|
||||
# 되돌린다 — 다른 configOverrides 키(secret, log_level)는 그대로 둔다(Helm은 맵을 키 단위로
|
||||
# 병합하므로 이 키만 덮어써진다).
|
||||
#
|
||||
# 카탈로그의 실제 권장 설정(custom-values.yaml)은 변경하지 않는다.
|
||||
#
|
||||
# 주의: configOverrides의 모든 키는 superset_config.py 파일 하나로 합쳐진다. log_level
|
||||
# 키(LOG_LEVEL = logging.INFO)는 자체적으로 import를 안 하고 이 enable_oauth 키 맨 위의
|
||||
# `import logging`에 의존하고 있었다(실측 — 완전히 비우면 NameError로 전체 기동 실패).
|
||||
# 그래서 완전히 비우지 않고 import만 남긴다.
|
||||
configOverrides:
|
||||
enable_oauth: |
|
||||
import logging
|
||||
|
||||
# 배포 테스트 시 host는 반드시 별도 값으로 덮어쓴다 — 카탈로그 기본값(superset.example.org)이
|
||||
# 이 클러스터의 다른 실제 배포와 겹칠 수 있다(lakekeeper에서 실측 — defense-llm 네임스페이스가
|
||||
# 동일 패턴의 host를 이미 쓰고 있었다).
|
||||
ingress:
|
||||
hosts:
|
||||
- superset-access-test.example.org
|
||||
tls:
|
||||
- hosts:
|
||||
- superset-access-test.example.org
|
||||
secretName: superset-tls-secret
|
||||
Reference in New Issue
Block a user