Keycloak 설치 가이드
1. 개요
본 문서는 gitops 저장소의 /keycloak 디렉터리 구조를 기반으로 Keycloak(SSO 서버)을 Kubernetes 클러스터에 설치하는 전체 과정을 안내합니다. Kustomize를 사용하여 환경별 설정을 관리하고, Makefile을 이용한 배포 자동화 방법을 다룹니다.
2. 사전 요구사항
- Kubernetes 클러스터: v1.20 이상
- 로컬 도구: kubectl, kustomize, helm 설치 완료
- 인증서 관리: TLS Secret(
cnapcloud.com-tls) 준비 - DNS 등록
<INGRESS_LB_IP> keycloak-admin.cnapcloud.com <INGRESS_LB_IP> keycloak.cnapcloud.com
3. 디렉터리 구조 및 역할
keycloak/
├── Makefile # 배포 자동화 스크립트
├── kustomize/
│ ├── base/ # 공통 리소스
│ | └── helm/keycloak # Keycloak helm chart
│ └── overlays/
│ └── dev/ # dev 환경 설정
│ ├── kustomization.yaml # Kustomize 구성
│ └── helm/
│ └── values.yaml # Keycloak 설정
Makefile:apply,delete,preview등 배포 자동화 명령어kustomize/base/: 모든 환경에 공통으로 적용될 리소스kustomize/overlays/dev/:dev환경 특화 설정helm/values.yaml: Keycloak Helm Chart 커스터마이징 설정
4. 설치 단계
4.1. values.yaml 커스터마이징
kustomize/overlays/dev/helm/values.yaml 파일을 열어 Keycloak의 기본 설정을 수정합니다.
4.1.1. 기본 이미지 설정
fullnameOverride: "keycloak"
image:
registry: docker.io
repository: bitnami/keycloak
tag: 25.0.4-debian-12-r1
pullPolicy: IfNotPresent
4.1.2. 관리자 계정 설정
Keycloak 관리 콘솔에 로그인할 admin 계정의 초기 비밀번호를 설정합니다.
auth:
adminUser: admin
adminPassword: password # 운영 환경에서는 반드시 강력한 비밀번호로 변경
보안 경고: 운영 환경에서는 이 값을 직접 입력하는 대신, Kubernetes Secret을 생성하고
existingSecret옵션을 사용하는 것을 강력히 권장합니다.
4.1.3. PostgreSQL 설정
내장 PostgreSQL 데이터베이스를 사용하여 Keycloak 데이터를 저장합니다.
postgresql:
enabled: true
auth:
username: keycloak
password: password
database: keycloak
primary:
persistence:
size: 4Gi # 데이터베이스 스토리지 크기
4.1.4. Ingress 설정 (사용자용)
사용자가 인증 및 로그인에 사용할 Keycloak 도메인을 설정합니다.
ingress:
enabled: true
ingressClassName: nginx
hostname: keycloak.cnapcloud.com # 사용자 접근 도메인
pathType: ImplementationSpecific
path: /
servicePort: http
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
tls: true
extraTls:
- hosts:
- keycloak.cnapcloud.com
secretName: cnapcloud.com-tls # cert-manager가 생성한 TLS Secret
4.1.5. Ingress 설정 (관리자용)
Keycloak 관리 콘솔에 접근할 별도의 도메인을 설정합니다.
adminIngress:
enabled: true
ingressClassName: nginx
hostname: keycloak-admin.cnapcloud.com # 관리자 콘솔 도메인
pathType: ImplementationSpecific
path: /
servicePort: http
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
tls: true
extraTls:
- hosts:
- keycloak-admin.cnapcloud.com
secretName: cnapcloud.com-tls
4.1.6. Realm 자동 구성
Keycloak 설치 시 Master Realm과 사용자 정의 Realm을 자동으로 생성합니다.
keycloakConfigCli:
enabled: true
auth:
username: admin
password: password
configuration:
master.json: |
{
"realm" : "master",
"enabled": true,
"attributes": {
"frontendUrl": "https://keycloak-admin.cnapcloud.com"
}
}
cnap.json: |
{
"realm" : "cnap",
"enabled": true,
"attributes": {
"frontendUrl": "https://keycloak.cnapcloud.com"
}
}
4.1.7. 프로덕션 환경 설정
리버스 프록시 환경에서 Keycloak을 프로덕션 모드로 실행합니다.
production: true # 프로덕션 모드 활성화
proxy: edge # nginx ingress 등 리버스 프록시 환경
adminRealm: master
4.1.8. SPI 확장 설치
Keycloak의 커스텀 확장 기능(SPI)을 추가로 설치합니다.
initContainers:
- name: keycloak-extensions-spi
image: cnapcloud/keycloak-extensions-spi:25.0.4-1
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- |
echo "Copying Keycloak Extensions SPI JAR..."
cp /resources/spi/keycloak-extensions-spi-1.0.0-SNAPSHOT.jar /opt/bitnami/keycloak/providers/
cp /resources/spi/keycloak-metrics-spi-7.0.0.jar /opt/bitnami/keycloak/providers/
volumeMounts:
- name: keycloak-extensions-spi-dir
mountPath: /opt/bitnami/keycloak/providers
extraVolumes:
- name: keycloak-extensions-spi-dir
emptyDir: {}
extraVolumeMounts:
- name: keycloak-extensions-spi-dir
mountPath: /opt/bitnami/keycloak/providers
4.1.9. 환경 변수 설정
Java 옵션, 클러스터링, 이벤트 저장 등 Keycloak 실행 환경을 구성합니다.
extraEnvVars:
# Java 옵션 (ARM 아키텍처 호환성)
- name: _JAVA_OPTIONS
value: -XX:UseSVE=0 # ARM에서 SVE(Scalable Vector Extension) 비활성화
# 클러스터링 설정
- name: KEYCLOAK_CACHE_STACK
value: kubernetes
- name: jgroups.dns.query
value: "keycloak-headless.security.svc.cluster.local" # 클러스터 디스커버리 DNS
- name: JAVA_OPTS_APPEND
value: "-Djgroups.dns.query=keycloak-headless.security.svc.cluster.local"
# 이벤트 저장 설정
- name: KC_EVENT_STORE
value: "true" # 사용자 로그인/로그아웃 이벤트 저장
- name: KC_EVENT_STORE_SPI
value: "jpa" # JPA를 통해 PostgreSQL에 저장
4.1.10. 고가용성 설정
여러 Keycloak 인스턴스를 실행하여 고가용성을 확보합니다.
replicaCount: 2 # Pod 복제본 수
cache:
enabled: true
stackName: kubernetes # Kubernetes용 분산 캐시
stackFile: ""
pdb:
create: true # Pod Disruption Budget 생성
minAvailable: 1 # 업데이트 중에도 최소 1개 Pod 유지
affinity:
podAntiAffinity: # 서로 다른 노드에 Pod 분산 배치
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
topologyKey: kubernetes.io/hostname
4.1.11. 리소스 제한
각 Keycloak Pod의 CPU 및 메모리 사용량을 제한합니다.
resources:
limits:
cpu: 1000m
memory: 1024Mi
requests:
cpu: 500m
memory: 512Mi
4.1.12. Metrics 설정
Prometheus를 통해 Keycloak의 메트릭을 수집합니다.
metrics:
enabled: true
serviceMonitor:
enabled: true
port: http
endpoints:
- path: '{{ include "keycloak.httpPath" . }}realms/{{ .Values.adminRealm }}/metrics'
interval: 30s
namespace: security
labels:
prometheus: main # Prometheus가 이 ServiceMonitor를 인식하도록 레이블 지정
4.2. Kustomize 구성
kustomize/overlays/dev/kustomization.yaml 파일을 구성합니다.
4.2.1. 기본 구성
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: security
generators:
- ./helm/helm-chart.yaml
4.2.2. ARM 아키텍처용 패치 (선택사항)
ARM 기반 클러스터에서만 다음 패치를 추가합니다:
patches:
- target:
kind: Job
name: keycloak-keycloak-config-cli
patch: |-
- op: add
path: /spec/template/spec/containers/0/env/0
value:
name: _JAVA_OPTIONS
value: -XX:UseSVE=0
참고: AMD64 아키텍처에서는 이 패치가 필요하지 않습니다. Realm 자동 구성 Job이 ARM 환경에서도 정상 동작하도록 Java 옵션을 추가합니다.
4.3. 배포 프로세스 (Makefile 활용)
Step 1: Namespace 생성
Keycloak을 배포할 네임스페이스를 생성합니다.
make namespace DEPLOY_ENV=dev
Step 2: 배포 미리보기 (권장)
실제 적용 전에 생성될 리소스를 확인합니다.
make preview DEPLOY_ENV=dev
Step 3: 배포 실행
Keycloak을 Kubernetes 클러스터에 배포합니다.
make apply DEPLOY_ENV=dev
Step 4: 배포 상태 확인
kubectl get pods -n security -w
모든 Pod이 Running 상태가 될 때까지 대기합니다.
5. 설치 후 검증
5.1. 리소스 상태 확인
배포된 모든 리소스의 상태를 확인합니다.
kubectl get pods,svc,ingress,pvc -n security
확인 사항:
- 모든 Pod이
Running상태 keycloak-0,keycloak-1Pod이 각각 실행 중keycloak-postgresql-0Pod이Running상태- PVC
data-keycloak-postgresql-0가Bound상태 - Ingress
keycloak,keycloak-admin이 생성됨
5.2. 웹 UI 접속
브라우저에서 관리자 콘솔에 접속합니다.
https://keycloak-admin.cnapcloud.com
5.3. 관리자 로그인
values.yaml에서 설정한 관리자 계정으로 로그인합니다.
- Username:
admin - Password:
password(설정한 값)
5.4. Realm 확인
좌측 상단 드롭다운에서 다음 Realm이 생성되었는지 확인합니다:
mastercnap
5.5 Metrics Event Listener 설정
메트릭 수집 대상인 Realm(cnap)에서 이벤트 기반 메트릭 수집을 활성화하기 위해 metrics-listener를 설정합니다. Realm Settings → Events → Event Listeners 메뉴에서 metrics-listener를 추가합니다.
메트릭 수집은 cnapcloud/keycloak-extensions-spi:25.0.4-1가 추가된 경우에만 가능합니다.
6. Realm 및 Client 설정
6.1. Realm 선택
관리 콘솔 좌측 상단 드롭다운에서 cnap Realm을 선택합니다.
6.2. Client 등록 (Grafana 연동 예시)
Step 1: Client 생성
- Clients → Create client
- 기본 설정:
- Client ID:
grafana - Client type:
OpenID Connect - Next
- Client ID:
Step 2: Capability 설정
- Capability config:
- Client authentication:
On - Save
- Client authentication:
Step 3: Access 설정
- Access settings:
- Home URL:
https://grafana.cnapcloud.com - Valid redirect URIs:
* - Valid post logout redirect URIs:
+ - Web origins:
https://grafana.cnapcloud.com - Save
- Home URL:
Step 4: Client Secret 확인
- Credentials 탭
- Client Secret 값 복사 (API 호출 시 필요)
6.3. Groups Mapper 추가
Step 1: Client Scope 생성
- Client scopes → Create client scope
- Name:
groups - Type:
Default - Protocol:
OpenID Connect - Include in token scope:
On - Save
- Name:
Step 2: Group Membership Mapper 추가
- Mappers 탭 → Add mapper → By configuration → Group Membership
- 설정:
- Name:
groups - Token Claim Name:
groups - Full group path:
Off - Add to ID token:
On - Add to access token:
On - Add to userinfo:
On - Save
- Name:
Step 3: Client에 Scope 추가
- Clients →
grafana→ Client scopes 탭 - Add client scope →
groups선택 - Add → Default 선택
6.4. 그룹 생성
Step 1: 그룹 생성
- Groups → Create group
- Name:
admin→ Create - Name:
editor→ Create - Name:
viewer→ Create
- Name:
Step 2: Default Group 설정
- Realm settings → User registration 탭
- Default groups → Add groups
viewer선택 → Add
새로 생성되는 사용자는 자동으로
viewer그룹에 포함됩니다.
6.5. 사용자 생성
Step 1: 사용자 추가
- Users → Create new user
- Username:
alice - Email:
alice@cnapcloud.com - Email verified:
On - Create
- Username:
Step 2: 그룹 할당
- Groups 탭
- Join Group →
editor선택 → Join
- Join Group →
Step 3: 비밀번호 설정
- Credentials 탭
- Set password
- Password:
your-password - Password confirmation:
your-password - Temporary:
Off - Save
7. OIDC API 활용
7.1. OpenID Configuration 확인
Keycloak의 OIDC 엔드포인트 정보를 확인합니다.
curl https://keycloak.cnapcloud.com/realms/cnap/.well-known/openid-configuration
7.2. 액세스 토큰 발급
사용자 인증 후 액세스 토큰을 발급받습니다.
curl -X POST \
-d "client_id=grafana" \
-d "client_secret=<client_secret>" \
-d "grant_type=password" \
-d "username=alice" \
-d "password=<password>" \
https://keycloak.cnapcloud.com/realms/cnap/protocol/openid-connect/token
응답 예시:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI...",
"refresh_expires_in": 1800
}
7.3. 사용자 정보 조회
발급받은 토큰으로 사용자 정보를 조회합니다.
curl -H "Authorization: Bearer <access_token>" \
https://keycloak.cnapcloud.com/realms/cnap/protocol/openid-connect/userinfo
응답 예시:
{
"sub": "f9a5b2c1-3d4e-5f6g-7h8i-9j0k1l2m3n4o",
"email_verified": true,
"preferred_username": "alice",
"email": "alice@cnapcloud.com",
"groups": ["editor", "viewer"]
}
8. 문제 해결 (Troubleshooting)
8.1. Pod가 Running 상태가 아닌 경우
확인 방법:
# Pod 상태 확인
kubectl get pods -n security
# Pod 상세 정보 확인
kubectl describe pod keycloak-0 -n security
# Pod 로그 확인
kubectl logs keycloak-0 -n security
주요 원인:
- PVC가 Bound 상태가 아님 → StorageClass 확인
- 리소스 부족 →
kubectl top nodes로 노드 상태 확인 - 데이터베이스 연결 실패 → PostgreSQL Pod 상태 확인
8.2. Ingress 접속 불가
확인 방법:
# Ingress 상태 확인
kubectl get ingress -n security
# TLS Secret 확인
kubectl get secret cnapcloud.com-tls -n security
# Service 확인
kubectl get svc keycloak -n security
주요 원인:
- TLS Secret이 없음 → cert-manager 설정 확인
- Ingress Controller 미설치 → nginx-ingress 확인
- DNS 설정 오류 → 도메인 A 레코드 확인
8.3. 관리자 로그인 실패
확인 방법:
# values.yaml의 관리자 계정 정보 확인
cat kustomize/overlays/dev/helm/values.yaml | grep -A2 "auth:"
비밀번호 재설정:
kubectl exec -it keycloak-0 -n security -- /opt/bitnami/keycloak/bin/kcadm.sh \
config credentials \
--server http://localhost:8080 \
--realm master \
--user admin \
--password <현재_비밀번호>
kubectl exec -it keycloak-0 -n security -- /opt/bitnami/keycloak/bin/kcadm.sh \
set-password \
--username admin \
--new-password <새_비밀번호>
9. 보안 강화 (프로덕션 권장)
9.1. Secret 관리
민감한 정보를 별도 Secret으로 관리합니다.
# 관리자 비밀번호 Secret 생성
kubectl create secret generic keycloak-admin \
--from-literal=admin-password='your-strong-password' \
-n security
# PostgreSQL 비밀번호 Secret 생성
kubectl create secret generic keycloak-postgresql \
--from-literal=password='your-strong-password' \
-n security
values.yaml에서 Secret 참조:
auth:
existingSecret: keycloak-admin
passwordKey: admin-password
postgresql:
auth:
existingSecret: keycloak-postgresql
secretKeys:
adminPasswordKey: password
userPasswordKey: password
9.2. Network Policy
Keycloak Pod 간 네트워크 통신을 제한합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak
namespace: security
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: keycloak
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: postgresql
ports:
- protocol: TCP
port: 5432
10. 배포 제거
10.1. Keycloak 삭제
make delete DEPLOY_ENV=dev
10.2. Namespace 삭제 (선택사항)
kubectl delete namespace security
주의: Namespace를 삭제하면 PVC를 포함한 모든 데이터가 영구 삭제됩니다.
부록: 최종 체크리스트
설치 전:
- Kubernetes 클러스터 준비 (v1.20+)
- kubectl, kustomize, helm 설치
- Nginx Ingress Controller 설치
- cert-manager 및 TLS Secret 준비
설치:
-
values.yaml커스터마이징 완료 -
kustomization.yaml구성 완료 -
make namespace실행 -
make preview실행 및 확인 -
make apply실행
검증:
- 모든 Pod이 Running 상태
- PostgreSQL PVC가 Bound 상태
- Ingress 리소스 생성 확인
- 관리자 콘솔 접속 가능
- 관리자 로그인 성공
구성:
- Realm 자동 생성 확인
- Client 등록
- Groups Mapper 추가
- 그룹 생성
- 사용자 생성 및 그룹 할당
- OIDC API 테스트
프로덕션:
- Secret 관리 적용
- Network Policy 구성
- 백업 전략 수립
- 모니터링 및 알림 설정