# Phase 2 Keycloak·MinIO AIStor 수동 배포 진입점 상세 설계는 중앙 문서 `/home/donghyeon/workspace/docs/platform`에서 관리한다. 이 파일은 선언의 의존 순서와 검증 진입점만 기록한다. ## 상태 2026-07-23 기준 Keycloak-only 경로는 DB Secret 두 사본, DatabaseRole·Database, additive NetworkPolicy, Operator·Server `26.7.0`, Ingress, `hyeonworks` realm, confidential `gitea` client와 OIDC Secret까지 실제 적용했다. Traefik 최소 trust도 완료했다. Host Nginx의 `id.learn.hyeonworks.com` proxy와 Gitea OIDC·브랜딩 live 적용도 완료했고 OAuth source·정책·redirect·브랜딩 자동 검증을 통과했다. 실제 realm 사용자의 브라우저 login/callback/logout, 비상 관리자 로그인과 재시작 지속성 검증은 남아 있다. MinIO AIStor는 NetworkPolicy, namespace, AIStor 전용 두 Secret, 900Gi Retain Local PV, 공식 Operator와 단일 ObjectStore까지 실제 적용했다. `Initialized/green`, PVC Bound, ClusterIP-only와 인증된 S3 put/get/delete checksum 검증을 통과했다. ## 적용 전 렌더 검증 Helm `3.19.4`가 `PATH`에 없다면 검증된 실행 파일의 절대 경로를 지정한다. ```sh cd /home/donghyeon/workspace/platform PLATFORM_HELM_BIN=/absolute/path/to/helm \ bash scripts/validate/render-phase2.sh ``` 이 명령은 Phase 1 검증을 먼저 수행한 뒤 Phase 2 전체를 로컬 렌더한다. 실제 클러스터의 ObjectStore CRD 설치 여부와 무관하며 어떤 리소스도 적용하지 않는다. ## Keycloak-only 적용 진입점 AIStor license와 ObjectStore 준비를 기다리지 않고 Keycloak만 먼저 배포할 때는 다음 전용 스크립트를 사용한다. Helm과 AIStor Secret은 요구하지 않는다. ```sh cd /home/donghyeon/workspace/platform bash scripts/bootstrap/apply-keycloak.sh --execute ``` 스크립트는 Keycloak namespace, Keycloak DB Secret 두 개, 공용 PostgreSQL의 `DatabaseRole`·`Database`·`NetworkPolicy`, Keycloak Operator, Keycloak CR과 Ingress를 순서대로 렌더·검증·적용하고 각 readiness를 기다린다. 먼저 현재 Kubernetes context와 API server를 출력한 뒤 `APPLY`를 정확히 입력해야 한다. 두 DB Secret이 모두 없으면 비밀번호를 화면에 표시하지 않고 두 번 입력받은 뒤 `APPLY KEYCLOAK SECRETS`를 한 번 더 확인한다. 두 Secret이 이미 있으면 값을 바꾸지 않고 계약만 검증해 재사용한다. 자동 적용 전에 두 Secret을 안전하게 미리 생성하려면 다음 명시적 비대화형 모드를 사용할 수 있다. ```sh kubectl apply --filename=infrastructure/namespaces/phase2/keycloak.yaml bash scripts/bootstrap/create-keycloak-secrets.sh --generate --execute bash scripts/bootstrap/apply-keycloak.sh --execute ``` `--generate --execute`는 OpenSSL CSPRNG로 32바이트 난수를 생성해 mode `0600` 임시 파일에만 기록한다. 생성값은 argv나 화면에 출력하지 않으며 두 플래그 자체를 자동 생성 승인으로 취급한다. 두 Secret이 이미 있으면 새 값을 만들거나 회전하지 않고 기존 계약을 검증해 그대로 재사용한다. 독립 실행하는 Secret 생성기는 `platform-data`와 `keycloak` namespace가 먼저 존재해야 하므로 위 첫 명령을 생략하지 않는다. 위 Keycloak-only 명령은 실제 완료됐으며 재실행 시 기존 Secret을 회전하지 않고 계약과 readiness를 다시 검증한다. 실제 결과는 중앙 `runbooks/2026-07-23-keycloak-gitea-oidc-cutover.md`에 기록한다. 중간 단계가 실패해도 namespace, Secret, DB 리소스, Operator 또는 Keycloak 리소스를 자동 삭제하지 않는다. DB reclaim policy는 `Retain`으로 유지하며 원인을 해결한 뒤 같은 명령을 다시 실행하는 것이 복구 경로다. Host Nginx의 Keycloak 정적 hold 제거와 Gitea OIDC 인증 소스 등록은 이 스크립트 범위가 아니다. ## AIStor 실제 적용 및 재검증 실제 적용에는 다음 진입점을 사용했다. ```sh cd /home/donghyeon/workspace/platform PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \ bash scripts/bootstrap/apply-aistor.sh \ --license-file /home/donghyeon/.secrets/aistor/minio.license \ --root-config-file /home/donghyeon/.secrets/aistor/root.env \ --execute bash scripts/validate/aistor-s3-smoke.sh --execute ``` 첫 스크립트가 수행하고 검증한 순서는 다음과 같다. 1. Phase 1과 Keycloak-only 리소스가 정상이며 Host Nginx·Gitea OIDC 작업과 AIStor 작업이 서로 독립임을 확인한다. 2. AIStor Operator·ObjectStore용 저장소 소유 NetworkPolicy를 먼저 렌더하고 k3s CNI의 승인 흐름과 차단 흐름을 검증한다. webhook, Kubernetes API, license 확인, DNS, ObjectStore 내부 통신과 승인된 S3 소비자를 빠뜨리지 않는다. - `infrastructure/controllers/aistor-operator` - `services/minio-aistor` 3. 정확히 `aistor`, `object-storage` namespace만 검토·적용한다. 4. `/srv/k3s/aistor`의 XFS mount·권한과 예상 밖 StorageClass 소비자가 없음을 확인한 뒤 `infrastructure/storage/aistor-local-pv`를 적용한다. 5. AIStor 두 Secret만 two-or-none으로 관리하는 전용 helper를 실행한다. 기존 four-or-none `create-phase2-secrets.sh`는 현재 Keycloak-only 상태에서 실행하지 않는다. 6. NetworkPolicy gate 통과 후 `infrastructure/controllers/aistor-operator`와 해당 정책을 적용하고 CRD, 두 Operator, admission webhook이 준비될 때까지 기다린다. 7. `services/minio-aistor`와 해당 정책을 적용하고 하나의 서버·하나의 900Gi 볼륨, `ClusterIP` S3/Console, 예상 PVC·PV binding을 확인한다. 실제 workload에서도 승인·차단 흐름을 다시 시험한다. 8. S3 put/get/delete와 checksum, 관리 포트 비노출을 확인한다. 1~8의 자동 검증은 완료했다. ObjectStore Pod 재생성 뒤 기존 영구 객체가 유지되는지 보는 별도 지속성 시험은 운영 데이터와 구분된 시험 버킷·승인 창을 정한 뒤 수행한다. Argo CD 인계와 Host Nginx 변경은 이 Phase 2 수동 적용 범위에 포함하지 않는다. Istio와 sidecar injection도 적용하지 않는다. ## Secret 안전 조건 - Keycloak-only 경로의 두 DB Secret 중 일부만 존재하면 `create-keycloak-secrets.sh`가 생성·회전을 거부한다. 두 Secret은 `platform-data`와 `keycloak` namespace에 동일한 `keycloak` 사용자와 동일한 비밀번호로 존재해야 한다. - Keycloak-only 스크립트는 AIStor license와 ObjectStore root Secret을 읽거나 요구하거나 수정하지 않는다. - Keycloak DB Secret과 AIStor 두 Secret은 모두 존재하지만 서로 독립 계약이다. - 기존 four-or-none helper는 이 상태에서 중단하는 것이 정상이며 우회하거나 Keycloak Secret을 삭제하지 않는다. - AIStor 전용 helper는 AIStor 두 Secret이 모두 없거나 모두 존재할 때만 진행하고 one-of-two 상태에서는 자동 삭제·덮어쓰기 없이 중단한다. - 두 AIStor Secret이 모두 존재하면 타입·정확한 키, license file 동일성, root configuration 형식만 검증하고 값을 바꾸지 않는다. - license payload, root 사용자·비밀번호, DB 비밀번호는 Git, argv, 표준 출력에 기록하지 않는다. - bootstrap helper는 credential 또는 license 회전을 수행하지 않는다.