Add platform infrastructure configuration
This commit is contained in:
@@ -1,2 +1,243 @@
|
||||
# platform-core
|
||||
# 플랫폼 인프라
|
||||
|
||||
단일 노드 k3s에서 사용하는 공통 플랫폼 인프라의 선언형 구성을 관리하는
|
||||
저장소입니다. 상세 아키텍처, 운영 절차, 의사결정 기록은
|
||||
[중앙 Platform 문서](/home/donghyeon/workspace/docs/platform)에서 관리합니다.
|
||||
|
||||
## 현재 상태
|
||||
|
||||
Phase 1과 Phase 2의 선언 및 수동 초기 구축 진입점이 구현되어 있습니다.
|
||||
Phase 1에는 namespace, SSD Local PV, CloudNativePG, 공용 PostgreSQL, Gitea,
|
||||
Traefik Ingress와 NetworkPolicy가 포함됩니다. Phase 2 선언에는 Keycloak, 공용
|
||||
PostgreSQL의 Keycloak DB·Role, AIStor Operator, 단일 MinIO AIStor ObjectStore,
|
||||
namespace와 Local PV가 포함됩니다.
|
||||
|
||||
Phase 1은 실제 클러스터에 적용했습니다. CloudNativePG·PostgreSQL·Gitea,
|
||||
PV/PVC와 내부 Traefik health가 정상이며 Gitea 네 컨테이너의 restricted
|
||||
securityContext도 검증했습니다. Host Nginx의 Gitea proxy 전환과 로컬·public
|
||||
HTTP/HTTPS 검증도 통과했습니다.
|
||||
|
||||
Phase 2는 전체를 한 번에 적용하지 않았습니다. **Keycloak-only 범위**인 전용 DB와
|
||||
Role, NetworkPolicy, 공식 Operator, 단일 Keycloak 인스턴스, `hyeonworks` realm,
|
||||
confidential `gitea` client와 `gitea/gitea-keycloak-oidc` Secret까지 실제 적용을
|
||||
완료했습니다. Traefik `web` entrypoint에는 실제 관측한 Host Nginx source
|
||||
`10.42.0.1/32`만 forwarded-header trusted IP로 적용했습니다.
|
||||
|
||||
`id.learn.hyeonworks.com`의 Host Nginx static hold를 Keycloak proxy로 바꾸는
|
||||
전환과 Gitea OIDC·브랜딩 rollout도 완료했습니다. Host Nginx 후보 SHA-256은
|
||||
`5c5cd74b4992f537fd27c50cf2209573a80a9904e0b19b58c3154717be6ff4a5`, 백업은
|
||||
`/etc/nginx/sites-available/learn-services.before-keycloak-20260723160519`이며,
|
||||
적용한 Gitea manifest SHA-256은 `d25a757...a157`입니다. 활성 OAuth source,
|
||||
외부 인증 전용 가입 정책, authorization-code redirect와 브랜딩 자동 검증을 모두
|
||||
통과했습니다. 실제 realm 사용자의 브라우저 login/callback/logout와 비상 관리자
|
||||
로그인은 별도 수용 시험으로 남아 있습니다.
|
||||
|
||||
AIStor는 Keycloak 경로와 분리해 실제 적용했습니다. `aistor`와 `object-storage`
|
||||
namespace, 900Gi Retain Local PV, 공식 Operator와 단일 ObjectStore, 기본 차단
|
||||
NetworkPolicy가 동작 중입니다. ObjectStore는 `Initialized/green`, PVC는
|
||||
`aistor-data-local-pv`에 Bound이며 S3와 Console은 `ClusterIP`로만 노출됩니다.
|
||||
고정 다이제스트의 공식 AIStor Client로 버킷 생성, 객체 쓰기·읽기 checksum,
|
||||
객체·버킷 삭제까지 인증된 스모크 테스트를 통과했습니다.
|
||||
|
||||
| 구성요소 | 고정 버전 | 상태 |
|
||||
| --- | --- | --- |
|
||||
| CloudNativePG | Operator `1.30.0`, Chart `0.29.0` | Phase 1 적용, Ready/Available `1/1` |
|
||||
| PostgreSQL | `17.9-standard-trixie` | Phase 1 적용, healthy `1/1`, PVC Bound |
|
||||
| Gitea | 애플리케이션 `1.27.0`, Chart `12.7.0` | OIDC·브랜딩 rollout 완료; OAuth source·정책·redirect·자산 자동 검증 통과 |
|
||||
| Keycloak | Operator·애플리케이션 `26.7.0` | Ready `1/1`; realm·client·OIDC Secret·Host Nginx 공개 전환 완료 |
|
||||
| Traefik | Chart `40.1.3+up40.1.0`, 이미지 `3.7.4` | NodePort `30080/30443`, `web` trust `10.42.0.1/32` 적용 |
|
||||
| AIStor Operator | Chart `5.10.0` | 적용, Operator·AdminJob·Webhook Ready `1/1` |
|
||||
| AIStor ObjectStore | Chart `1.0.16` | 적용, `Initialized/green`, 900Gi PVC Bound, 인증 S3 스모크 통과 |
|
||||
| 렌더 도구 | Kustomize `5.8.1`, Helm `3.19.4` | 정확히 일치해야 함 |
|
||||
|
||||
Argo CD와 Istio는 현재 단계에서 구현하거나 배포하지 않습니다.
|
||||
|
||||
## 실제 Kustomize 빌드 루트
|
||||
|
||||
Phase 1 최초 적용은 다음 경로만 빌드 루트로 사용합니다.
|
||||
|
||||
| 순서 | 빌드 루트 | 렌더 방식 |
|
||||
| ---: | --- | --- |
|
||||
| 1 | `infrastructure/namespaces/overlays/home` | Kustomize |
|
||||
| 2 | `infrastructure/storage/ssd-local-pv` | Kustomize |
|
||||
| 3 | `infrastructure/controllers/cloudnative-pg` | Kustomize + Helm |
|
||||
| 4 | `services/platform-postgres` | Kustomize |
|
||||
| 5 | `services/gitea` | Kustomize + Helm |
|
||||
|
||||
`services/gitea`는 신규 설치용 baseline 빌드 루트이며 렌더 결과는
|
||||
`gitea.yaml`입니다. Keycloak OIDC와 브랜딩은 최초 Phase 1 적용 대상이 아니며,
|
||||
Host Nginx ID 전환 뒤 전용 빌드 루트 `services/gitea/profiles/oidc`에서
|
||||
`gitea-oidc.yaml`로 렌더링하고 수명주기 스크립트로만 적용합니다.
|
||||
|
||||
Phase 2는 Phase 1 전체를 먼저 검증한 뒤 다음 빌드 루트를 추가로 렌더합니다.
|
||||
|
||||
| 순서 | 빌드 루트 | 렌더 방식 |
|
||||
| ---: | --- | --- |
|
||||
| 1 | `infrastructure/namespaces/phase2` | Kustomize |
|
||||
| 2 | `infrastructure/storage/aistor-local-pv` | Kustomize |
|
||||
| 3 | `infrastructure/controllers/keycloak-operator` | Kustomize 원격 리소스 |
|
||||
| 4 | `services/platform-postgres-keycloak` | Kustomize |
|
||||
| 5 | `services/keycloak` | Kustomize |
|
||||
| 6 | `infrastructure/controllers/aistor-operator` | Kustomize + Helm |
|
||||
| 7 | `services/minio-aistor` | Kustomize + Helm |
|
||||
|
||||
하위 `base`와 `overlays/home`는 직접 적용 대상이 아닐 수 있습니다. Helm values와
|
||||
환경 patch가 형제 디렉터리에 있는 구성은 Kustomize 기본 `RootOnly` 제한을
|
||||
유지하기 위해 서비스 또는 컨트롤러 루트에서 렌더합니다.
|
||||
|
||||
## Secret 원칙
|
||||
|
||||
평문 Secret, 비밀번호, 라이선스, 개인 키와 소스 관리되는 `kind: Secret` YAML을
|
||||
저장소에 두지 않습니다. 각 단계의 초기 구축 스크립트가 계약 전체의
|
||||
존재·타입·키·교차 namespace 동일성을 검증하고, 전체가 없을 때만 명시 확인 후
|
||||
생성합니다. 일부만 존재하면 중단하며 자동 회전하지 않습니다.
|
||||
|
||||
최초 생성은 여러 Kubernetes API 요청으로 수행되므로 중간 실패 시 일부 객체가 남을
|
||||
수 있습니다. 후속 실행은 이를 삭제하거나 덮어쓰지 않고 부분 상태로 감지해
|
||||
중단합니다.
|
||||
|
||||
Phase 1 계약:
|
||||
|
||||
- `platform-data/gitea-db-credentials`
|
||||
- `gitea/gitea-db-credentials`
|
||||
- `gitea/gitea-admin`
|
||||
|
||||
Phase 2 계약:
|
||||
|
||||
- `platform-data/keycloak-db-credentials`
|
||||
- `keycloak/keycloak-db-credentials`
|
||||
- `gitea/gitea-keycloak-oidc`
|
||||
- `aistor/minio-license`
|
||||
- `object-storage/aistor-root-configuration`
|
||||
|
||||
`gitea/gitea-keycloak-oidc`는 `Opaque` 유형이며 정확히 `key`, `secret` 두 key만
|
||||
갖습니다. 현재 Keycloak-only 작업으로 생성됐지만 payload는 문서, values와 렌더
|
||||
결과에 기록하지 않습니다.
|
||||
|
||||
자세한 계약은 [Secret 문서](infrastructure/security/secrets/README.md)를 따릅니다.
|
||||
Helm 차트가 렌더하는 초기화 스크립트·비민감 설정용 내부 Secret은 애플리케이션
|
||||
자격 증명 계약과 구분합니다.
|
||||
|
||||
## k3s Secret 저장 암호화와 복구 증거
|
||||
|
||||
k3s Secret encryption at rest의 상태 확인과 fail-stop 활성화 절차는
|
||||
[암호화 수동 운영 절차](bootstrap/manual/k3s-secret-encryption.md)를 따릅니다. datastore나
|
||||
snapshot 단독 탈취를 완화하지만 같은 host의 root 침해나 full-disk 탈취를 해결하지는
|
||||
않습니다. server token과 backup을 함께 얻으면 복구·복호화할 수 있으므로 recovery bundle
|
||||
전체를 Secret으로 취급합니다.
|
||||
|
||||
2026-08-09 live 실행에서 수동 LUKS header 복구 proof, recovery close·잔류 없음 검사,
|
||||
closed validator, Secret encryption `Enabled/reencrypt_finished`, hash·integrity·API·node
|
||||
검사와 post bundle 기록을 모두 통과했고 최신 bundle marker도 검증했습니다. 다만 일반
|
||||
lifecycle 자동화, 격리 restore drill, off-host 복제와 암호화 escrow 검증은 아직
|
||||
완료되지 않았으므로 관측성 Phase 4 gate는 열리지 않았습니다.
|
||||
|
||||
Phase 4 Secret 생성 전에는 live `--expect-reencrypted`와 restore evidence `--check`를 매번
|
||||
독립 호출합니다. backend별 수동 복구, 일회용 격리 환경, 세 mode 결과 생성·등록·검사와
|
||||
파기 기준은 [k3s Secret 복구 drill](bootstrap/manual/k3s-secret-encryption-restore-drill.md),
|
||||
중앙 보호 경계는
|
||||
[보안과 Secret 관리](/home/donghyeon/workspace/docs/platform/architecture/security-and-secrets.md)와
|
||||
[백업과 복구 설계](/home/donghyeon/workspace/docs/platform/architecture/backup-and-recovery.md)를
|
||||
따릅니다. 완료된 live hardening의 비민감 정정은
|
||||
[2026-08-08 복구 명령 원장](/home/donghyeon/workspace/docs/platform/runbooks/2026-08-08-k3s-local-recovery-command-log.md)의
|
||||
2026-08-09 addendum과
|
||||
[2026-08-09 로컬 정책 기록](/home/donghyeon/workspace/docs/platform/runbooks/2026-08-09-k3s-secret-encryption-local-policy.md)에
|
||||
남긴다. 별도의 Task 6 실행 runbook은 만들지 않았으며, 이를 암시하지 않는다.
|
||||
|
||||
## 검증과 수동 적용
|
||||
|
||||
아무 리소스도 적용하지 않는 렌더 검증 명령은 다음과 같습니다.
|
||||
|
||||
```sh
|
||||
cd /home/donghyeon/workspace/platform
|
||||
bash scripts/validate/render-phase1.sh
|
||||
bash scripts/validate/render-phase2.sh
|
||||
```
|
||||
|
||||
Phase 1 검증은 baseline과 OIDC profile을 포함한 manifest 여섯 개를 생성합니다.
|
||||
최초 구축 스크립트는 그중 `gitea.yaml` baseline만, OIDC 수명주기 스크립트는
|
||||
`gitea-oidc.yaml` 하나만 각각 적용합니다.
|
||||
|
||||
Helm이 시스템 `PATH`에 없다면 검증된 `3.19.4` 실행 파일의 절대 경로를
|
||||
`PLATFORM_HELM_BIN`으로 지정합니다. Phase 2 검증은 Phase 1을 먼저 검증한 뒤
|
||||
공식 AIStor Chart 패키지 다이제스트, Keycloak 단일 인스턴스·HTTP hostname/Ingress,
|
||||
AIStor 1x1·900Gi·PVC 보호·ClusterIP, RootOnly 유지와 Secret 소스·렌더링 부재를
|
||||
확인합니다. 실제 클러스터의 ObjectStore CRD는 로컬 렌더에 필요하지 않습니다.
|
||||
|
||||
Phase 1 상세 절차는
|
||||
[중앙 Phase 1 런북](/home/donghyeon/workspace/docs/platform/runbooks/2026-07-22-phase1-gitea-bootstrap.md)과
|
||||
[짧은 실행 진입점](bootstrap/manual/phase1-gitea.md)을 따릅니다.
|
||||
|
||||
Keycloak에서 Gitea OIDC까지의 실제 실행 순서와 현재 완료·대기 경계는
|
||||
[중앙 Keycloak-Gitea OIDC 전환 런북](/home/donghyeon/workspace/docs/platform/runbooks/2026-07-23-keycloak-gitea-oidc-cutover.md)에
|
||||
기록합니다. 실행 진입점은 다음 네 개입니다.
|
||||
|
||||
```sh
|
||||
cd /home/donghyeon/workspace/platform
|
||||
|
||||
# 완료: Keycloak DB·Operator·인스턴스
|
||||
bash scripts/bootstrap/apply-keycloak.sh --execute
|
||||
|
||||
# 완료: realm·confidential Gitea client·OIDC Secret
|
||||
bash scripts/bootstrap/configure-keycloak-gitea-oidc.sh --execute
|
||||
|
||||
# 완료: id.learn.hyeonworks.com Host Nginx 전환
|
||||
sudo bash scripts/bootstrap/apply-host-nginx-keycloak.sh --execute
|
||||
|
||||
# 완료: 고정 SHA 렌더 결과의 gitea-oidc.yaml만 적용하고 OIDC·브랜딩 검증
|
||||
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
|
||||
bash scripts/bootstrap/apply-gitea-oidc.sh --execute
|
||||
```
|
||||
|
||||
Traefik 신뢰 경계의 적용 값과 첫 실패·복구 내용은
|
||||
[중앙 Traefik trust 런북](/home/donghyeon/workspace/docs/platform/runbooks/2026-07-23-traefik-forwarded-header-trust-boundary.md)에
|
||||
기록합니다. AIStor의 실제 배포 명령·결과·실패와 복구 과정은
|
||||
[중앙 AIStor 배포 기록](/home/donghyeon/workspace/docs/platform/runbooks/2026-07-23-aistor-deployment.md)과
|
||||
[Phase 2 수동 진입점](bootstrap/manual/phase2-keycloak-aistor.md)에 기록합니다.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
첫 명령은 기존 Secret과 Retain PV를 회전·삭제하지 않고 계약과 readiness를
|
||||
재검증한다. 두 번째 명령은 임시 버킷과 객체를 만들었다가 삭제하며, 자격 증명
|
||||
payload는 출력하지 않는다.
|
||||
|
||||
## 호스트 Nginx
|
||||
|
||||
[Gitea Nginx 설정](infrastructure/networking/host-nginx/learn-services.conf)을
|
||||
기반으로 한 Gitea proxy는 현재 Host Nginx에 적용되어 있고 로컬·public HTTPS
|
||||
health를 통과했습니다. `id.learn.hyeonworks.com`도
|
||||
[Keycloak 포함 설정](infrastructure/networking/host-nginx/learn-services-keycloak.conf)으로
|
||||
전환되어 공개 discovery issuer 검사를 통과했습니다.
|
||||
|
||||
ID 전환은 `scripts/bootstrap/apply-host-nginx-keycloak.sh --execute`가 공개
|
||||
discovery 사전 검사, 기존 설정 백업, `nginx -t`, 다시 불러오기와 HTTPS 사후
|
||||
검사를 수행하도록 합니다. 실패 시 후보를 방치하지 않고 백업 설정을 복원합니다.
|
||||
실제 전환 성공 뒤 `scripts/bootstrap/apply-gitea-oidc.sh --execute`로 Gitea
|
||||
OIDC source와 브랜딩도 live Deployment에 적용했습니다. 세부 경계는
|
||||
[Nginx 수동 전환 절차](infrastructure/networking/host-nginx/README.md)를 따릅니다.
|
||||
|
||||
## 저장소 규칙
|
||||
|
||||
- Kubernetes 리소스는 Kustomize `base`와 환경별 `overlays`로 구분합니다.
|
||||
- 공식 Helm 차트 원본은 수정하지 않고 고정 버전, SHA-256, 저장소 관리
|
||||
values·patch로 설정합니다.
|
||||
- 클러스터 진입점과 향후 GitOps 애플리케이션 선언은 `clusters/home`에서
|
||||
관리합니다.
|
||||
- 공통 컨트롤러와 기반 리소스는 `infrastructure`, 플랫폼 서비스는
|
||||
`services`에서 관리합니다.
|
||||
- 반복 정책과 향후 서비스 메시 확장 지점은 `components`에서 관리합니다.
|
||||
- 자동화는 `scripts`, 사람이 검토하는 최초 적용 절차는 `bootstrap/manual`,
|
||||
상세 운영 기록은 중앙 문서에 둡니다.
|
||||
- 생성된 Helm 캐시와 렌더 산출물 대신 재현 가능한 선언만 버전 관리합니다.
|
||||
|
||||
Reference in New Issue
Block a user