10 KiB
Keycloak 기준
목적
이 문서는 Kubernetes 환경에서 Keycloak 26+ (Quarkus distribution)을 1000+ 서비스의 ID 브로커로 운영하기 위한 기준을 고정한다.
- 빌드/실행 두 단계(
kc.sh build→kc.sh start --optimized)를 전제한다 - Hostname v2, proxy-headers, management port 9000, Infinispan 캐시를 명시한다
- 단일 Deployment 수제 배포 대신 Keycloak Operator를 1차 권장 경로로 둔다
- DB / 캐시 / probe / Ingress / RealmImport 를 YAML이 아닌 "설계 결정"으로 먼저 고정한다
- auth-server(도메인 위임)와 Keycloak(IdP)의 ownership 경계를 분리한다
공식 의미 (Keycloak 26+ 기준)
- 운영 실행 방식은 두 단계다.
kc.sh build가 Quarkus augmentation을 수행해 optimized 이미지를 만들고,kc.sh start --optimized가 그 이미지를 기동한다. 빌드 시 configuration은 런타임에 변경 불가능하다. --proxy옵션은 v24에서 deprecated, v26에서 제거되었다. 대체는--proxy-headers=xforwarded또는--proxy-headers=forwarded다.- Hostname v2가 기본값이며
--hostname은 full URL을 받는다. v24+ 이후hostname-url,hostname-path,hostname-port는 제거되었다. admin 전용 주소는--hostname-admin으로 지정한다. --hostname-strict의 production 기본값은true다.--hostname-backchannel-dynamic은 기본false다.- HTTPS 종료를 Ingress/LB가 하면 Keycloak은
KC_HTTP_ENABLED=true로 HTTP를 수신한다. - DB는
KC_DB=postgres,KC_DB_URL은 JDBC URL(jdbc:postgresql://host:5432/db) 형식이다. - **Management interface는 기본 포트
9000**에서 제공되고,/health,/health/started,/health/ready,/health/live,/metrics를 호스팅한다. Pod probe와 Prometheus scrape는 모두 9000 대상이다. - Production cache type 기본은
ispn(Infinispan distributed). cache-stack 기본값이kubernetes(DNS_PING)에서 v25부터jdbc-ping으로 바뀌었다. Operator가 관리하는 StatefulSet은 Raft-less 클러스터링을 jdbc-ping으로 수행한다. - Operator가 생성하는 워크로드는 StatefulSet이다 (pod ordering이 Infinispan discovery와 맞물린다). 사용자 수제 YAML에서도 Operator 경로가 1차 권장이다.
KeycloakRealmImportCR은 Keycloak server가 준비된 후 realm JSON을 server side로 import하는 1회성 Job을 생성한다.
기본 규칙
1. 운영 실행은 start --optimized 두 단계
빌드 단계에서 feature/db/health/metrics를 굽고, 실행 단계에서 runtime config만 주입한다.
기본:
- Dockerfile에서
RUN /opt/keycloak/bin/kc.sh build로 optimized 이미지 생성 - 컨테이너 CMD는
kc.sh start --optimized - runtime-only config: hostname, DB URL/credential, log level
기본 금지:
start-dev운영 사용start단독 실행(build 없이 매 기동마다 augmentation)
2. --proxy-headers 사용, --proxy 금지
Keycloak 26에서 --proxy는 제거되었다.
기본:
- HTTPS 종료 proxy 뒤:
KC_PROXY_HEADERS=xforwarded(nginx, Traefik, ingress-nginx 등) - RFC 7239 지원 proxy:
KC_PROXY_HEADERS=forwarded - proxy가 Host / X-Forwarded-* 를 덮어쓰도록 고정
기본 금지:
KC_PROXY=edge|reencrypt|passthrough등 legacy 옵션
3. Hostname v2: full URL로 고정
기본:
KC_HOSTNAME=https://auth.example.com(full URL)- Admin Console 분리:
KC_HOSTNAME_ADMIN=https://admin-auth.example.com KC_HOSTNAME_STRICT=true(production 기본 유지)KC_HOSTNAME_BACKCHANNEL_DYNAMIC=false(기본값; 다중 cluster federation일 때만 true 검토)
기본 금지:
- 제거된 옵션 사용:
KC_HOSTNAME_URL,KC_HOSTNAME_PATH,KC_HOSTNAME_PORT - hostname 없이 요청 헤더에서 해석되도록 방치
4. HTTPS는 Ingress/LB에서 종료, Pod는 HTTP
Pod 내부에서 TLS 재암호화가 필요 없으면 Pod는 HTTP로 수신한다.
기본:
KC_HTTP_ENABLED=true,KC_HTTP_PORT=8080- Ingress가 TLS 종료 + proxy-header 주입
- passthrough TLS가 필요한 보안 요구가 있을 때만
KC_HTTPS_*경로 채택
5. DB는 외부 PostgreSQL + JDBC URL
기본:
KC_DB=postgresKC_DB_URL=jdbc:postgresql://keycloak-db-rw:5432/keycloak(CloudNativePG-rwRW endpoint 권장)KC_DB_USERNAME,KC_DB_PASSWORD→ SecretsecretKeyRef- Keycloak schema와 auth-server schema는 다른 DB 또는 다른 database로 분리
기본 금지:
- 내장 H2 (
dev-file,dev-mem) 운영 - root/superuser credential 사용
- Keycloak DB에 auth-server migration 수행
6. Management port 9000은 외부 비공개
기본:
- Pod containerPort 9000 (
KC_HTTP_MANAGEMENT_PORT=9000) - Service에 9000 expose하되 Ingress 대상 제외
- probe는 9000 대상:
/health/started,/health/ready,/health/live - Prometheus scrape는 내부 scraper가 9000/
/metrics에 직접 접근
7. Probe timing은 Keycloak 기동 특성에 맞춘다
Keycloak은 JVM + Quarkus + Infinispan + DB migration으로 cold start가 30~120초다.
기본:
startupProbe:/health/started,periodSeconds: 5,failureThreshold: 60→ 최대 5분 유예readinessProbe:/health/ready,periodSeconds: 10,failureThreshold: 3livenessProbe:/health/live,periodSeconds: 30,failureThreshold: 3,initialDelaySeconds: 60
8. Cache: Infinispan + 버전별 stack 기본값 인지
v25+ 기본 stack은 **jdbc-ping**이다. DB를 discovery 매체로 쓰므로 headless service / ServiceAccount RBAC가 필요 없다.
기본:
- Operator 관리 클러스터:
KC_CACHE=ispn,KC_CACHE_STACK=jdbc-ping(명시) - 수제 StatefulSet에서 headless service 경유 discovery를 쓰려면
KC_CACHE_STACK=kubernetes(DNS_PING) 선택 - local mode 운영 금지 (
KC_CACHE=local은 single replica 테스트 전용)
9. Operator 경로를 1차 권장으로
1000+ 서비스 규모에서 realm import, CR 기반 롤아웃, cache stack 자동 설정, StatefulSet 관리를 Operator가 담당한다.
기본:
KeycloakCR +KeycloakRealmImportCR 조합- OLM(OperatorHub) 또는 공식 manifest 설치
- 수제 StatefulSet 유지보수는 Operator 기능이 부족할 때만 허용
10. 공개 경로 최소화
Ingress에 허용하는 기본 경로:
/realms/— OIDC / SAML endpoint/resources/— Keycloak theme / JS/.well-known/— OIDC discovery, JWKS/js/— Keycloak adapter JS (필요 시)
기본 금지:
/admin/외부 공개 (별도 admin host 경유)/metrics,/health*외부 공개/전체 wildcard 공개
11. Admin Console은 별도 host로 분리
Admin 접근은 일반 SSO host와 다른 경로로 둔다.
기본:
KC_HOSTNAME_ADMIN=https://admin-auth.example.com- Admin host는 사내 IP 화이트리스트 / VPN / OIDC forward-auth로 추가 보호
- production에서
/admin/을 SSO 공용 host에 노출 금지
12. Realm은 KeycloakRealmImport CR로 선언적 관리
기본:
- realm JSON은 Git에 보관
KeycloakRealmImportCR이 Job을 생성해 server-side import- secret이 들어가는 identity provider client secret은 Vault에서 주입
기본 금지:
- Admin REST / kcadm.sh를 CI/CD pipeline이 직접 호출해 상태 변경
- realm export 파일을 Pod 내부 파일로 배포
13. High Availability: replicas ≥ 2 + PDB + topologySpread
Operator는 instances 필드로 replica를 제어한다.
기본:
instances: 3(odd quorum 아님 — cache replication 안정성)PodDisruptionBudget minAvailable: 2topologySpreadConstraints로 node/zone 분산
14. Sticky session은 성능 최적화 옵션
Infinispan이 session을 복제하므로 필수는 아니지만, login flow 중간 redirect 지연을 줄인다.
기본:
- Ingress controller에서
AUTH_SESSION_IDcookie affinity - Service
sessionAffinity: ClientIP는 2차 선택지
15. Security context: Restricted PSS 준수
기본:
runAsNonRoot: true,runAsUser: 1000readOnlyRootFilesystem: true(Keycloak은/opt/keycloak/data만 writable 요구; emptyDir 마운트)allowPrivilegeEscalation: false,capabilities.drop: [ALL]seccompProfile: RuntimeDefault
16. Resource 요청은 JVM 특성 반영
기본 단일 replica:
- requests:
cpu: 500m,memory: 1Gi - limits:
cpu: 2,memory: 2Gi - JVM:
JAVA_OPTS_APPEND=-XX:MaxRAMPercentage=70 -XX:InitialRAMPercentage=50
login throughput 요구가 높으면 replica 수평 확장 우선 (JVM heap 수직 확장 2차).
17. Observability
기본:
KC_METRICS_ENABLED=true,KC_HEALTH_ENABLED=trueServiceMonitor또는PodMonitor로 9000//metricsscrape- event metric (login failure, token issuance)은 필요한 것만 활성화 (high cardinality 방지)
18. DB credential / admin credential은 Vault 경유
기본:
KC_DB_PASSWORD: VSOVaultDynamicSecret(postgres dynamic role) 또는VaultStaticSecret→ K8s Secret 동기화- Bootstrap admin (
KEYCLOAK_ADMIN,KEYCLOAK_ADMIN_PASSWORD): 최초 기동 후 제거, 실 운영 admin은 realm-managed
기본 금지:
- Secret을 Git에 평문 저장
- 환경변수 default value로 credential 하드코딩
19. 현재 스택 기본 권장안
- 배포: Keycloak Operator +
KeycloakCR +KeycloakRealmImportCR - 워크로드: StatefulSet (Operator 생성)
- Service: ClusterIP (9000, 8080)
- Ingress: SSO host + Admin host 분리
- DB: CloudNativePG PostgreSQL cluster + Vault dynamic secret
- Cache:
ispn+jdbc-ping - Probe: 9000 management port
- Replicas: 3 + PDB + topologySpread
프로젝트 기준 요약
- Keycloak 26+ Quarkus distribution,
start --optimized두 단계 --proxy-headers사용,--proxy금지- Hostname v2 full URL, admin host 분리, strict=true 유지
- DB: 외부 PostgreSQL, JDBC URL, Vault credential
- Management port 9000 내부 전용, probe / metrics 대상
- Infinispan
ispn+jdbc-ping(v25+) - Operator 경로 1차 권장 (CR로 realm import 포함)
- Admin Console 별도 host, 공개 경로는
/realms/,/resources/,/.well-known/ - Replicas ≥ 2 + PDB + topologySpread + Restricted PSS