airflow fernet key 를 콘솔이 만든 Secret 으로 넘긴다 (#38)
차트가 fernet key Secret 을 직접 만들면 helm.sh/hook: pre-install + hook-delete-policy: before-hook-creation 이 붙는다. ArgoCD 는 훅을 "삭제 후 재생성" 하는데, 변경이 없는 sync 에서는 삭제 직후 operation 이 종료되어 재생성되지 않는다. 컨트롤러 로그로 확인했다. 변경 있는 sync waiting for deletion ... -> serverside-applied -> Succeeded 'all tasks run' no-op sync waiting for deletion ... -> Succeeded (같은 초) 'no more tasks' 파드는 이미 주입된 env 로 계속 돌아 증상이 즉시 드러나지 않지만, 재시작하는 순간 CreateContainerConfigError 로 기동하지 못한다. refresh·selfHeal 로 흔히 발생하는 상황이다. fernetKeySecretName 을 주면 차트가 Secret 을 아예 만들지 않으므로 (templates/secrets/fernetkey-secret.yaml:23) 훅 자체가 사라지고, 모든 워크로드가 그 이름을 secretKeyRef 로 참조한다(templates/_helpers.yaml:395-397). $FERNET_KEY_SECRET 은 placeholder 다. dip-console-api 가 <릴리스명>-fernet-key Secret 을 만들고(없을 때만) 이 자리를 그 이름으로 치환한다. 콘솔이 만든 Secret 은 ArgoCD 추적 대상이 아니라 sync 의 영향을 받지 않는다. 값이 아니라 이름을 넘기는 이유가 하나 더 있다 — fernet key 는 메타DB 의 Connection·Variable 암호화에 쓰이므로 tenant 레포(git)에 평문으로 남기지 않는다. 업스트림도 can only be set during install, not upgrade 라고 못박고 있어, 이미 배포된 테넌트는 재배포 시 현재 Secret 을 그대로 인수한다. CUSTOM-README 5절은 이 문서의 성격(배포 방법 · custom-values.yaml 키 설명)에 맞게 키 테이블과 배포 절차 중심으로 다시 썼다(69줄 -> 48줄). 상세 근거는 이슈로 넘긴다. redis 훅 Secret 2종은 CeleryExecutor 전환 예정이 없어 조치하지 않는다. 참조 워크로드가 0개인 것을 클러스터에서 확인했다. 전환 시 처리 방법만 문서에 남긴다. 검증 service-catalog 에 선반영해 테스트 클러스터에서 확인했다. 재배포 후 sync 를 걸었을 때 훅인 redis Secret 2종은 또 삭제됐고 콘솔이 관리하는 fernet Secret 만 살아남았다(resourceVersion 미동, 값 동일). 이 레포 기준으로도 helm template 2회 렌더에서 차트가 fernet Secret 을 만들지 않고 webserver 체크섬이 동일함을 확인했다. 관련: paasup/dip-catalog#38, paasup/dip-console-api#100 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,6 +163,14 @@ dags:
|
|||||||
# 차트 안에서는 못 고친다(ArgoCD repo-server 는 클러스터 접근이 없어 helm lookup 이 빈 값).
|
# 차트 안에서는 못 고친다(ArgoCD repo-server 는 클러스터 접근이 없어 helm lookup 이 빈 값).
|
||||||
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
||||||
|
|
||||||
|
# fernet key 는 값이 아니라 Secret 이름만 넘긴다 — dip-console-api 가 배포 시점에
|
||||||
|
# <릴리스명>-fernet-key Secret 을 만들고 이 자리를 그 이름으로 치환한다.
|
||||||
|
# 비워두면 차트가 Secret 을 pre-install 훅으로 만드는데, ArgoCD 는 훅을 "삭제 후 재생성"
|
||||||
|
# 하므로 변경이 없는 sync 에서는 삭제 직후 operation 이 끝나 재생성되지 않는다. 그러면
|
||||||
|
# 파드 재시작 시 기동에 실패한다 (paasup/dip-catalog#38).
|
||||||
|
# 이름만 넘기므로 키가 tenant 레포(git)에 평문으로 남지 않는다.
|
||||||
|
fernetKeySecretName: "$FERNET_KEY_SECRET"
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
defaultUser:
|
defaultUser:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -201,70 +201,49 @@ Airflow 웹서버는 Flask-AppBuilder 구조라 번들링 없이 CSS/JS를 개
|
|||||||
|
|
||||||
### 5) 반복 재배포 방지 설정
|
### 5) 반복 재배포 방지 설정
|
||||||
|
|
||||||
`dip-values.yaml`에 있는 두 항목은 airflow가 반복 재배포되는 것을 막기 위한 것이다. 원인이
|
ArgoCD 배포에서 airflow가 반복 재배포되거나 Secret이 사라지는 것을 막기 위한 설정이다.
|
||||||
서로 다르므로 따로 설명한다 (issue #29).
|
배경과 실측 근거는 paasup/dip-catalog#29 · #38 참고.
|
||||||
|
|
||||||
#### 5.1) `webserverSecretKey` — 렌더 결정성
|
| Name | 설명 | 기본값 |
|
||||||
|
| ---- | ---- | ------ |
|
||||||
|
| `webserverSecretKey` | 세션 서명 키. 비우면 차트가 렌더할 때마다 난수를 만들어 webserver가 계속 롤링되고 웹 세션이 끊긴다 | `~` |
|
||||||
|
| `fernetKeySecretName` | fernet key Secret 이름. 비우면 차트가 `pre-install` 훅으로 Secret을 만드는데, 변경이 없는 sync에서 삭제만 되고 재생성되지 않는다 | `~` |
|
||||||
|
| `migrateDatabaseJob.ttlSecondsAfterFinished` | `useHelmHooks: false`와 같이 두면 TTL 삭제 ↔ selfHeal 재생성 루프가 생긴다. `~`로 끈다 | `300` |
|
||||||
|
|
||||||
``` yaml
|
- dip-values.yaml
|
||||||
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
``` yaml
|
||||||
```
|
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
||||||
|
fernetKeySecretName: "$FERNET_KEY_SECRET"
|
||||||
|
|
||||||
- 이 값을 주지 않으면 차트가 템플릿 안에서 직접 난수를 만든다
|
migrateDatabaseJob:
|
||||||
(`templates/secrets/webserver-secret-key-secret.yaml`의 `randAlphaNum 32`).
|
useHelmHooks: false
|
||||||
- 그 Secret은 webserver·worker Deployment의 `checksum/webserver-secret-key` 애노테이션에
|
ttlSecondsAfterFinished: ~
|
||||||
물려 있어서, **매니페스트를 다시 렌더할 때마다 Deployment spec이 바뀌고 롤링이 걸린다.**
|
```
|
||||||
실측에서 webserver가 반복 재배포되고 그때마다 웹 세션이 끊겼다. 캐시된 매니페스트를
|
- `$WEBSERVER_SECRET_KEY`와 `$FERNET_KEY_SECRET`는 placeholder다. dip-console-api가 배포
|
||||||
그대로 쓰는 reconcile로는 일어나지 않지만, **일반 refresh만으로도 일어난다.**
|
시점에 각각 난수 키와 Secret 이름으로 치환한다
|
||||||
- **차트 안에서는 못 고친다.** `lookup`으로 기존 Secret을 재사용하는 방법은 ArgoCD
|
(`catalogs/catalogtype/catalog_airflow.go`의 `DeployPreInstall`).
|
||||||
repo-server가 클러스터 접근 없이 클라이언트 렌더를 하므로 항상 빈 값이 된다(클러스터에
|
**카탈로그와 dip-console-api를 함께 반영해야 한다** — 한쪽만 반영하면 placeholder 문자열이
|
||||||
Secret이 실제로 있는데도 빈 값인 것을 실측 확인). 릴리스명·네임스페이스 기반 결정적
|
그대로 값이 되거나 아무 일도 일어나지 않는다.
|
||||||
파생은 렌더가 안정되는 대신 파생 입력이 전부 공개 정보라 **세션 서명 키를 예측할 수 있어**
|
- 같은 내용을 `manifests/applicationset/airflow/1.16.0/dip-values.yaml`(`---` 뒤 두 번째
|
||||||
부적합하다. 값이 차트 바깥에서 와야 한다.
|
YAML 문서)에도 반영한다.
|
||||||
- 그래서 `$WEBSERVER_SECRET_KEY`는 **placeholder**다. 배포 시점에 dip-console-api가 난수로
|
- 이미 배포된 테넌트는 **재배포해야 적용된다.** fernet key는 재배포해도 기존 Secret을 그대로
|
||||||
치환한다 — `catalogs/catalogtype/catalog_airflow.go`의 `DeployPreInstall`
|
인수하므로 Connection·Variable은 유지된다. webserver 세션은 끊겨 다시 로그인해야 한다.
|
||||||
(crypto/rand 32바이트, URL-safe base64). 치환된 values가 tenant 레포에 저장되므로 이후
|
|
||||||
렌더는 그 고정값을 쓴다.
|
- **helm으로 직접 배포할 때**는 dip-console-api를 거치지 않으므로 두 값을 직접 넣는다.
|
||||||
- **dip-console-api와 짝이다.** 한쪽만 반영되면 placeholder 문자열이 그대로 키가 되거나
|
|
||||||
(카탈로그만) 아무 일도 일어나지 않는다(console만).
|
|
||||||
- **helm으로 직접 배포할 때는** dip-console-api를 거치지 않으므로 치환이 일어나지 않는다.
|
|
||||||
`custom-values.yaml`에 실제 값을 직접 넣는다.
|
|
||||||
``` sh
|
``` sh
|
||||||
# 값 생성
|
# fernet key Secret 생성 (키 이름은 반드시 fernet-key)
|
||||||
|
kubectl create secret generic airflow-fernet-key -n airflow \
|
||||||
|
--from-literal=fernet-key="$(python3 -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())')"
|
||||||
|
|
||||||
|
# webserver secret key 값 생성
|
||||||
python3 -c "import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"
|
python3 -c "import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"
|
||||||
```
|
```
|
||||||
- **재배포하면 새 키가 생성되어 기존 웹 세션은 무효화된다** — 사용자는 다시 로그인해야 한다.
|
``` yaml
|
||||||
|
webserverSecretKey: "<위에서 생성한 값>"
|
||||||
#### 5.2) `migrateDatabaseJob.ttlSecondsAfterFinished` — TTL ↔ selfHeal 루프
|
fernetKeySecretName: "airflow-fernet-key"
|
||||||
|
```
|
||||||
``` yaml
|
- 참고: `<릴리스명>-redis-password` · `<릴리스명>-broker-url`도 같은 `pre-install` 훅이라
|
||||||
migrateDatabaseJob:
|
sync에서 사라진다. `executor: "KubernetesExecutor"`인 현재는 참조하는 워크로드가 없어
|
||||||
useHelmHooks: false
|
무해하다(다음 배포 때 자동으로 다시 생긴다). CeleryExecutor로 바꾼다면 차트가 제공하는
|
||||||
ttlSecondsAfterFinished: ~
|
`redis.passwordSecretName` · `data.brokerUrlSecretName`으로 위 5.2와 같게 처리한다 —
|
||||||
```
|
`broker-url`은 redis 비밀번호를 DSN에 포함하므로 두 Secret을 함께 생성해야 한다.
|
||||||
|
|
||||||
- `useHelmHooks: false`로 두면 migrations Job이 helm hook이 아니라 **ArgoCD가 추적하는 일반
|
|
||||||
리소스**가 된다. ArgoCD 환경에서 hook을 끄라는 것은 업스트림 권고다
|
|
||||||
(`values.yaml` 주석: `Disable this if you are using ArgoCD for example`).
|
|
||||||
- 그런데 차트 기본값 `ttlSecondsAfterFinished: 300`이 그대로 남아 있으면, 완료 300초 뒤 TTL
|
|
||||||
컨트롤러가 Job을 지우고 ArgoCD는 그것을 OutOfSync로 보고 selfHeal로 다시 만든다.
|
|
||||||
**migrations job이 5분 주기로 재실행되는 루프**가 된다.
|
|
||||||
- 둘의 조합이 문제다. 하나만 있으면 발생하지 않는다.
|
|
||||||
- 리소스 수명은 ArgoCD가 관리하므로 **TTL을 끈다.** `~`(null)이면 템플릿의
|
|
||||||
`kindIs "invalid"` 가드에 걸려 `ttlSecondsAfterFinished` 필드 자체가 렌더되지 않는다
|
|
||||||
(`templates/jobs/migrate-database-job.yaml`). 대신 완료된 Job 객체가 네임스페이스에 남는다.
|
|
||||||
- `createUserJob`도 `ttlSecondsAfterFinished: 300` + `useHelmHooks: true`로 구조가 같지만,
|
|
||||||
hook이 살아 있어 ArgoCD 추적 대상이 아니므로 지금은 문제가 없다. `useHelmHooks`를 끄게
|
|
||||||
되면 TTL도 같이 꺼야 한다.
|
|
||||||
|
|
||||||
#### 5.3) 반영 위치가 2곳이다
|
|
||||||
|
|
||||||
같은 내용을 아래 두 파일에 모두 반영해야 한다. 한쪽만 고치면 배포 경로에 따라 증상이 남는다.
|
|
||||||
|
|
||||||
- `manifests/helm/airflow/1.16.0/dip-values.yaml`
|
|
||||||
- `manifests/applicationset/airflow/1.16.0/dip-values.yaml` (`---` 뒤 두 번째 YAML 문서)
|
|
||||||
|
|
||||||
#### 5.4) 이미 배포된 테넌트
|
|
||||||
|
|
||||||
tenant 레포에 저장된 values에는 이 설정이 없으므로, **재배포해야 적용된다.** 그전까지 기존
|
|
||||||
테넌트의 롤링·job 재실행 루프는 계속된다.
|
|
||||||
|
|||||||
@@ -72,6 +72,14 @@ dags:
|
|||||||
# 차트 안에서는 못 고친다(ArgoCD repo-server 는 클러스터 접근이 없어 helm lookup 이 빈 값).
|
# 차트 안에서는 못 고친다(ArgoCD repo-server 는 클러스터 접근이 없어 helm lookup 이 빈 값).
|
||||||
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
webserverSecretKey: "$WEBSERVER_SECRET_KEY"
|
||||||
|
|
||||||
|
# fernet key 는 값이 아니라 Secret 이름만 넘긴다 — dip-console-api 가 배포 시점에
|
||||||
|
# <릴리스명>-fernet-key Secret 을 만들고 이 자리를 그 이름으로 치환한다.
|
||||||
|
# 비워두면 차트가 Secret 을 pre-install 훅으로 만드는데, ArgoCD 는 훅을 "삭제 후 재생성"
|
||||||
|
# 하므로 변경이 없는 sync 에서는 삭제 직후 operation 이 끝나 재생성되지 않는다. 그러면
|
||||||
|
# 파드 재시작 시 기동에 실패한다 (paasup/dip-catalog#38).
|
||||||
|
# 이름만 넘기므로 키가 tenant 레포(git)에 평문으로 남지 않는다.
|
||||||
|
fernetKeySecretName: "$FERNET_KEY_SECRET"
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
defaultUser:
|
defaultUser:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
Reference in New Issue
Block a user