HTMLTAG_67__HTMLTAG_68___1。 Keycloak Kubernetes 操作員概述___HTMLTAG_69__HTMLTAG_70
Keycloak Operator 是官方的 Kubernetes Operator,根據 聲明式方法 管理 Keycloak 部署。操作員使用自訂資源定義 (CRD) 來定義所需的狀態,然後自動協調以確保實際狀態匹配。
預編碼_0
HTMLTAG_75__HTMLTAG_76___1.1 CRD 概論___HTMLTAG_77__HTMLTAG_78
___HTMLTAG_81__HTMLTAG_82___CRD___HTMLTAG_83__HTMLTAG_84___API 群組___HTMLTAG_85__HTMLTAG_86___用途___HTMLTAG_87__HTMLTAG_88___ ___HTMLTAG_91__HTMLTAG_92__HTMLTAG_93___Keycloak___HTMLTAG_94__HTMLTAG_95__HTMLTAG_96__HTMLTAG_97___k8s.keycloak.org/v2alpha1___UMLG_98______HTTAG_910___部署(實例、資料庫、主機名稱、TLS、功能)___HTMLTAG_101__HTMLTAG_102___ ___HTMLTAG_103__HTMLTAG_104__HTMLTAG_105___KeycloakRealmImport___HTMLTAG_106__HTMLTAG_107__HTM LTAG_108__HTMLTAG_109___k8s.keycloak.org/v2alpha1___HTMLTAG_110__HTMLTAG_111__HTMLTAG_112___從下列位置匯入領域設定JSON___HTMLTAG_113__HTMLTAG_114___HTMLTAG_117__HTMLTAG_118___2。安裝 Keycloak Operator___HTMLTAG_119__HTMLTAG_120
___HTMLTAG_121__HTMLTAG_122___2.1 方法 1:kubectl apply(清單)HTMLTAG_123__HTMLTAG_124
預編碼_1
___HTMLTAG_125__HTMLTAG_126___2.2 方法 2:OLM(操作員生命週期管理器)HTMLTAG_127__HTMLTAG_128
預編碼_2
HTMLTAG_129__HTMLTAG_130___2.3 方法 3:Helm 圖表___HTMLTAG_131__HTMLTAG_132
預編碼_3
HTMLTAG_133__HTMLTAG_134___3。 Keycloak CR 設定___HTMLTAG_135__HTMLTAG_136
HTMLTAG_137__HTMLTAG_138___3.1 基本鑰匙斗篷 CR___HTMLTAG_139__HTMLTAG_140______預編碼_4
HTMLTAG_141__HTMLTAG_142___3.2 支援資源___HTMLTAG_143__HTMLTAG_144
預編碼_5
HTMLTAG_145__HTMLTAG_146___3.3 鑰匙斗篷 CR 狀態___HTMLTAG_147__HTMLTAG_148
預編碼_6
HTMLTAG_149__HTMLTAG_150___4。 KeycloakRealmImport CR___HTMLTAG_151__HTMLTAG_152
HTMLTAG_153__HTMLTAG_154___4.1 領域導入設定___HTMLTAG_155__HTMLTAG_156
預編碼_7
預編碼_8
HTMLTAG_157__HTMLTAG_158___4.2 從 JSON 檔案匯入領域___HTMLTAG_159__HTMLTAG_160
預編碼_9
HTMLTAG_161__HTMLTAG_162___5。運營商升級策略___HTMLTAG_163__HTMLTAG_164
預編碼_10
# Upgrade process
# 1. Update image version trong Keycloak CR
kubectl patch keycloak keycloak -n keycloak \
--type=merge \
-p '{"spec":{"image":"quay.io/keycloak/keycloak:26.1"}}'
# 2. Monitor upgrade
kubectl rollout status statefulset/keycloak -n keycloak --timeout=600s
# 3. Watch pods
kubectl get pods -n keycloak -w
# 4. Rollback nếu cần
kubectl patch keycloak keycloak -n keycloak \
--type=merge \
-p '{"spec":{"image":"quay.io/keycloak/keycloak:26.0"}}'
HTMLTAG_165__HTMLTAG_166___6。使用 Prometheus + Grafana 進行監控___HTMLTAG_167__HTMLTAG_168
HTMLTAG_169__HTMLTAG_170___6.1 啟用指標___HTMLTAG_171__HTMLTAG_172
# Keycloak CR - enable metrics
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: keycloak
namespace: keycloak
spec:
additionalOptions:
- name: metrics-enabled
value: "true"
- name: health-enabled
value: "true"
HTMLTAG_173__HTMLTAG_174___6.2 Prometheus 服務監視器___HTMLTAG_175__HTMLTAG_176
# servicemonitor.yaml - Prometheus scrape config
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: keycloak-metrics
namespace: keycloak
labels:
app: keycloak
release: prometheus # Match Prometheus operator label selector
spec:
selector:
matchLabels:
app: keycloak
app.kubernetes.io/managed-by: keycloak-operator
endpoints:
- port: https
path: /metrics
scheme: https
tlsConfig:
insecureSkipVerify: true # Hoặc cấu hình CA cert
interval: 30s
scrapeTimeout: 10s
honorLabels: true
namespaceSelector:
matchNames:
- keycloak
HTMLTAG_177__HTMLTAG_178___6.3 Keycloak 關鍵指標___HTMLTAG_179__HTMLTAG_180
___HTMLTAG_183__HTMLTAG_184___公制____HTMLTAG_185__HTMLTAG_186___類型___HTMLTAG_187__HTMLTAG_188___說明____HTMLTAG_189__HTMLTAG_190___ ___HTMLTAG_193__HTMLTAG_194__HTMLTAG_195___keycloak_logins_total___HTMLTAG_196__HTMLTAG_197__HTMLT AG_198___計數器____HTMLTAG_199__HTMLTAG_200___成功登入總數(按領域、提供者、client_id)___HTMLTAG_201__HTMLTAG_202___ ___HTMLTAG_203__HTMLTAG_204__HTMLTAG_205___keycloak_registrations_total___HTMLTAG_206__HTMLTAG_207__HTMLTAG_208___計數器___HTMLTAG_209__HTMLTAG_210_208___ML2121112________ ___HTMLTAG_213__HTMLTAG_214__HTMLTAG_215___keycloak_failed_login_attempts_total___HTMLTAG_216__HTMLTA G_217__HTMLTAG_218____計數器____HTMLTAG_219__HTMLTAG_220___失敗登入嘗試總數(以錯誤類型)___HTMLTAG_221__HTMLTAG_222___ ___HTMLTAG_223__HTMLTAG_224__HTMLTAG_225___keycloak_request_duration_seconds____HTMLTAG_226__HTMLTAG_227__HTMLTAG_228___直方圖___HTMLTAG_229__HTMLTAG_230113230___ML_UML_229__HT ___HTMLTAG_233__HTMLTAG_234__HTMLTAG_235___keycloak_refresh_tokens_total___HTMLTAG_236__HTMLTAG_237__HTMLTAG_238___計數器___HTMLTAG_239__HTMLTAG_240___MLG1420_____ ___HTMLTAG_243__HTMLTAG_244__HTMLTAG_245___keycloak_client_logins_total___HTMLTAG_246__HTMLTAG_247__HTMLTAG_248____logins_total___HT____249__HTMLTAG_250____MLU_250_MLHT ___HTMLTAG_253__HTMLTAG_254__HTMLTAG_255___vendor_memory_heap_usage_bytes___HTMLTAG_256__HTMLTAG_257__HTMLTAG_258____________HTMLTAG_259__HTMLTAG_26010260260259__ ___HTMLTAG_263__HTMLTAG_264__HTMLTAG_265___vendor_memory_non_heap_usage_bytes___HTMLTAG_266__HTML TAG_267__HTMLTAG_268____計量___HTMLTAG_269__HTMLTAG_270___JVM非堆記憶體(元空間)___HTMLTAG_271__HTMLTAG_272___ ___HTMLTAG_273__HTMLTAG_274__HTMLTAG_275___vendor_cpu_processCpuLoad___HTMLTAG_276__HTMLTAG_277__HTMLTAG_278___計量___HTMLTAG_2791HTMLTAGMLTAG_278___計量___HTMLTAG_2791HTMLTAG1801801802____ML ___HTMLTAG_283__HTMLTAG_284__HTMLTAG_285____vendor_gc_pause_seconds____HTMLTAG_286__HTMLTAG_287__HTMLTAG_288____摘要___HTMLTAG_289__HTMLTAG129070147076____ML ___HTMLTAG_293__HTMLTAG_294__HTMLTAG_295___vendor_thread_count___HTMLTAG_296__HTMLTAG_297__HTMLTAG_298_________HTMLTAG_299__HTMLTAG_300___活動 JVM___ 測量____HTMLTAG_299__HTMLTAG_300___活動 JVM___]130130_______ML ___HTMLTAG_303__HTMLTAG_304__HTMLTAG_305___vendor_db_pool_active_count___HTMLTAG_306__HTMLTAG_307__HTMLTAG_308___ML]___HTMLTAG_309__HTMLTAG_ML310___1308___ML1312________ ___HTMLTAG_313__HTMLTAG_314__HTMLTAG_315___vendor_db_pool_available_count___HTMLTAG_316__HTMLTAG_317__HTMLTAG_318___計量___HTMLTAG_319__HTMLTAGML_320____318120____MLTAG_319__HTMLTAGML_320___G1320____23__HTMLTAG_325__HTMLTAG_326___6.4 Grafana 儀表板___HTMLTAG_327__HTMLTAG_328
# grafana-dashboard-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: keycloak-grafana-dashboard
namespace: monitoring
labels:
grafana_dashboard: "1"
data:
keycloak-dashboard.json: |
{
"annotations": { "list": [] },
"title": "Keycloak Overview",
"uid": "keycloak-overview",
"panels": [
{
"title": "Login Rate (per minute)",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(keycloak_logins_total[5m])) by (realm) * 60",
"legendFormat": "{{realm}}"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
},
{
"title": "Failed Login Rate",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(keycloak_failed_login_attempts_total[5m])) by (realm, error) * 60",
"legendFormat": "{{realm}} - {{error}}"
}
],
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
},
{
"title": "Request Duration p95",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(keycloak_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95"
},
{
"expr": "histogram_quantile(0.99, sum(rate(keycloak_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p99"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }
},
{
"title": "JVM Heap Usage",
"type": "gauge",
"targets": [
{
"expr": "vendor_memory_heap_usage_bytes / vendor_memory_heap_max_bytes * 100",
"legendFormat": "Heap %"
}
],
"gridPos": { "h": 8, "w": 6, "x": 12, "y": 8 }
},
{
"title": "DB Connection Pool",
"type": "timeseries",
"targets": [
{
"expr": "vendor_db_pool_active_count",
"legendFormat": "Active"
},
{
"expr": "vendor_db_pool_available_count",
"legendFormat": "Available"
}
],
"gridPos": { "h": 8, "w": 6, "x": 18, "y": 8 }
}
]
}
HTMLTAG_329__HTMLTAG_330___6.5 警報規則___HTMLTAG_331__HTMLTAG_332
# prometheus-rules.yaml apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: keycloak-alerts namespace: keycloak labels: release: prometheus spec: groups: - name: keycloak.rules rules: # High failed login rate - alert: KeycloakHighFailedLoginRate expr: > sum(rate(keycloak_failed_login_attempts_total[5m])) by (realm) > 10 for: 5m labels: severity: warning annotations: summary: "High failed login rate on realm {{ $labels.realm }}" description: > Failed login rate is {{ $value | humanize }}/s on realm {{ $labels.realm }}. Possible brute force attack.# Keycloak pod not ready - alert: KeycloakPodNotReady expr: > kube_pod_status_ready{namespace="keycloak", pod=~"keycloak-.*"} == 0 for: 2m labels: severity: critical annotations: summary: "Keycloak pod {{ $labels.pod }} is not ready" description: "Pod {{ $labels.pod }} has been not ready for 2 minutes." # High response time - alert: KeycloakHighResponseTime expr: > histogram_quantile(0.95, sum(rate(keycloak_request_duration_seconds_bucket[5m])) by (le) ) > 1.0 for: 10m labels: severity: warning annotations: summary: "Keycloak p95 response time exceeds 1 second" description: "p95 latency is {{ $value | humanize }}s for the last 10 minutes." # High JVM heap usage - alert: KeycloakHighHeapUsage expr: > vendor_memory_heap_usage_bytes / vendor_memory_heap_max_bytes * 100 > 85 for: 10m labels: severity: warning annotations: summary: "Keycloak JVM heap usage > 85%" description: "Heap usage is {{ $value | humanize }}%. Consider scaling or increasing memory." # Database connection pool exhaustion - alert: KeycloakDBPoolExhausted expr: > vendor_db_pool_available_count < 5 for: 5m labels: severity: critical annotations: summary: "Keycloak database connection pool nearly exhausted" description: "Only {{ $value }} connections available. Increase pool size or investigate slow queries." # No successful logins (potential outage) - alert: KeycloakNoLogins expr: > sum(rate(keycloak_logins_total[10m])) == 0 for: 15m labels: severity: critical annotations: summary: "No successful logins in the last 15 minutes" description: "Keycloak may be experiencing an outage." # High error rate - alert: KeycloakHighErrorRate expr: > sum(rate(keycloak_request_duration_seconds_count{status=~"5.."}[5m])) / sum(rate(keycloak_request_duration_seconds_count[5m])) > 0.05 for: 5m labels: severity: critical annotations: summary: "Keycloak error rate exceeds 5%" description: "{{ $value | humanizePercentage }} of requests are returning 5xx errors."
___HTMLTAG_333__HTMLTAG_334___7。管理 CLI (kcadm.sh)HTMLTAG_335__HTMLTAG_336
HTMLTAG_337__HTMLTAG_338___7.1 驗證___HTMLTAG_339__HTMLTAG_340
# Login vào Keycloak Admin CLI
bin/kcadm.sh config credentials \
--server https://auth.example.com \
--realm master \
--user admin \
--password admin_password
# Login với client credentials (service account)
bin/kcadm.sh config credentials \
--server https://auth.example.com \
--realm master \
--client admin-cli \
--secret client_secret
# Trong container
kubectl exec -it keycloak-0 -n keycloak -- \
/opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user admin \
--password admin
HTMLTAG_341__HTMLTAG_342___7.2 領域操作___HTMLTAG_343__HTMLTAG_344
# ===== Realm CRUD =====
# List tất cả realms
bin/kcadm.sh get realms --fields realm,enabled
# Tạo realm mới
bin/kcadm.sh create realms \
-s realm=my-new-realm \
-s enabled=true \
-s displayName="My New Realm" \
-s registrationAllowed=false \
-s loginWithEmailAllowed=true \
-s resetPasswordAllowed=true \
-s bruteForceProtected=true \
-s permanentLockout=false \
-s maxFailureWaitSeconds=900 \
-s failureFactor=5
# Get realm details
bin/kcadm.sh get realms/my-new-realm
# Update realm
bin/kcadm.sh update realms/my-new-realm \
-s "ssoSessionIdleTimeout=1800" \
-s "ssoSessionMaxLifespan=36000" \
-s "accessTokenLifespan=300"
# Delete realm
bin/kcadm.sh delete realms/my-new-realm
HTMLTAG_345__HTMLTAG_346___7.3 使用者操作____HTMLTAG_347__HTMLTAG_348
# ===== User CRUD =====
# Tạo user mới
bin/kcadm.sh create users -r my-realm \
-s username=john.doe \
-s [email protected] \
-s firstName=John \
-s lastName=Doe \
-s enabled=true \
-s emailVerified=true
# Set password cho user
bin/kcadm.sh set-password -r my-realm \
--username john.doe \
--new-password "SecureP@ss123" \
--temporary=false
# List users
bin/kcadm.sh get users -r my-realm --fields id,username,email,enabled
# Search users
bin/kcadm.sh get users -r my-realm -q "username=john"
bin/kcadm.sh get users -r my-realm -q "[email protected]"
# Get user by ID
bin/kcadm.sh get users/USER_ID -r my-realm
# Update user
bin/kcadm.sh update users/USER_ID -r my-realm \
-s firstName="Jonathan" \
-s "attributes.department=[\"Engineering\"]"
# Disable user
bin/kcadm.sh update users/USER_ID -r my-realm \
-s enabled=false
# Delete user
bin/kcadm.sh delete users/USER_ID -r my-realm
# ===== User Roles =====
# Add realm role to user
bin/kcadm.sh add-roles -r my-realm \
--uusername john.doe \
--rolename ADMIN
# Add client role to user
bin/kcadm.sh add-roles -r my-realm \
--uusername john.doe \
--cclientid my-app \
--rolename app-admin
# Remove role from user
bin/kcadm.sh remove-roles -r my-realm \
--uusername john.doe \
--rolename ADMIN
# List user roles
bin/kcadm.sh get-roles -r my-realm --uusername john.doe
# ===== User Groups =====
# Add user to group
bin/kcadm.sh update users/USER_ID/groups/GROUP_ID -r my-realm \
-s realm=my-realm -s userId=USER_ID -s groupId=GROUP_ID -n
# Remove user from group
bin/kcadm.sh delete users/USER_ID/groups/GROUP_ID -r my-realm
# List user groups
bin/kcadm.sh get users/USER_ID/groups -r my-realm
HTMLTAG_349__HTMLTAG_350___7.4 用戶端操作____HTMLTAG_351__HTMLTAG_352
# ===== Client CRUD =====
# Tạo public client (SPA)
bin/kcadm.sh create clients -r my-realm \
-s clientId=my-spa \
-s name="My SPA Application" \
-s enabled=true \
-s publicClient=true \
-s standardFlowEnabled=true \
-s directAccessGrantsEnabled=false \
-s 'redirectUris=["https://app.example.com/*"]' \
-s 'webOrigins=["https://app.example.com"]' \
-s protocol=openid-connect \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
# Tạo confidential client (backend service)
bin/kcadm.sh create clients -r my-realm \
-s clientId=my-backend \
-s name="My Backend Service" \
-s enabled=true \
-s publicClient=false \
-s serviceAccountsEnabled=true \
-s standardFlowEnabled=false \
-s protocol=openid-connect
# Tạo bearer-only client (API)
bin/kcadm.sh create clients -r my-realm \
-s clientId=my-api \
-s name="My API" \
-s enabled=true \
-s publicClient=false \
-s bearerOnly=true \
-s protocol=openid-connect
# List clients
bin/kcadm.sh get clients -r my-realm --fields id,clientId,enabled
# Get client secret
bin/kcadm.sh get clients/CLIENT_ID/client-secret -r my-realm
# Regenerate client secret
bin/kcadm.sh create clients/CLIENT_ID/client-secret -r my-realm
# Update client
bin/kcadm.sh update clients/CLIENT_ID -r my-realm \
-s 'redirectUris=["https://new-app.example.com/*"]'
# Delete client
bin/kcadm.sh delete clients/CLIENT_ID -r my-realm
HTMLTAG_353__HTMLTAG_354___7.5 角色操作___HTMLTAG_355__HTMLTAG_356
預編碼_20
HTMLTAG_357__HTMLTAG_358___7.6 群組操作___HTMLTAG_359__HTMLTAG_360
# ===== Group CRUD =====
# Create group
bin/kcadm.sh create groups -r my-realm \
-s name="Engineering"
# Create sub-group
bin/kcadm.sh create groups/PARENT_GROUP_ID/children -r my-realm \
-s name="Backend Team"
# List groups
bin/kcadm.sh get groups -r my-realm
# Add role to group
bin/kcadm.sh add-roles -r my-realm \
--gname Engineering \
--rolename USER
# List group members
bin/kcadm.sh get groups/GROUP_ID/members -r my-realm
# Delete group
bin/kcadm.sh delete groups/GROUP_ID -r my-realm
HTMLTAG_361__HTMLTAG_362___7.7 身分識別提供者操作___HTMLTAG_363__HTMLTAG_364
# ===== Identity Provider CRUD =====
# Create Google Identity Provider
bin/kcadm.sh create identity-provider/instances -r my-realm \
-s alias=google \
-s providerId=google \
-s enabled=true \
-s 'config.clientId=GOOGLE_CLIENT_ID' \
-s 'config.clientSecret=GOOGLE_CLIENT_SECRET' \
-s 'config.defaultScope=openid email profile' \
-s trustEmail=true \
-s firstBrokerLoginFlowAlias="first broker login"
# Create OIDC Identity Provider
bin/kcadm.sh create identity-provider/instances -r my-realm \
-s alias=corporate-idp \
-s providerId=oidc \
-s enabled=true \
-s 'config.authorizationUrl=https://idp.example.com/authorize' \
-s 'config.tokenUrl=https://idp.example.com/token' \
-s 'config.clientId=keycloak-client' \
-s 'config.clientSecret=client_secret' \
-s 'config.userInfoUrl=https://idp.example.com/userinfo' \
-s 'config.defaultScope=openid email profile'
# Create SAML Identity Provider
bin/kcadm.sh create identity-provider/instances -r my-realm \
-s alias=saml-idp \
-s providerId=saml \
-s enabled=true \
-s 'config.singleSignOnServiceUrl=https://idp.example.com/sso' \
-s 'config.nameIDPolicyFormat=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' \
-s 'config.principalType=ATTRIBUTE' \
-s 'config.principalAttribute=email'
# List identity providers
bin/kcadm.sh get identity-provider/instances -r my-realm
# Update identity provider
bin/kcadm.sh update identity-provider/instances/google -r my-realm \
-s enabled=false
# Delete identity provider
bin/kcadm.sh delete identity-provider/instances/google -r my-realm
# ===== Identity Provider Mappers =====
# Create mapper
bin/kcadm.sh create identity-provider/instances/google/mappers -r my-realm \
-s name="email-mapper" \
-s identityProviderMapper=hardcoded-user-session-attribute-idp-mapper \
-s identityProviderAlias=google \
-s 'config={"syncMode":"INHERIT","attribute":"email","attribute.value":""}'
HTMLTAG_365__HTMLTAG_366___7.8 驗證流程管理___HTMLTAG_367__HTMLTAG_368
# ===== Authentication Flows =====
# List authentication flows
bin/kcadm.sh get authentication/flows -r my-realm --fields id,alias,builtIn
# Get flow executions
bin/kcadm.sh get authentication/flows/browser/executions -r my-realm
# Copy built-in flow (để customize)
bin/kcadm.sh create authentication/flows/browser/copy -r my-realm \
-s newName="Custom Browser Flow"
# Add execution to flow
bin/kcadm.sh create authentication/flows/Custom%20Browser%20Flow/executions/execution -r my-realm \
-s provider=auth-otp-form
# Update execution requirement
bin/kcadm.sh update authentication/flows/Custom%20Browser%20Flow/executions -r my-realm \
-b '[{"id":"EXECUTION_ID","requirement":"REQUIRED"}]'
# Set flow as realm browser flow
bin/kcadm.sh update realms/my-realm \
-s browserFlow="Custom Browser Flow"
HTMLTAG_369__HTMLTAG_370___7.9 元件管理___HTMLTAG_371__HTMLTAG_372
預編碼_24
HTMLTAG_373__HTMLTAG_374___7.10 匯出/匯入與腳本___HTMLTAG_375__HTMLTAG_376
預編碼_25
#!/bin/bash
# setup-realm.sh - Automated realm setup script
KEYCLOAK_URL="https://auth.example.com"
REALM="my-realm"
KCADM="bin/kcadm.sh"
# 1. Login
$KCADM config credentials \
--server "$KEYCLOAK_URL" \
--realm master \
--user admin \
--password "$KEYCLOAK_ADMIN_PASSWORD"
# 2. Create Realm
echo "=== Creating realm: $REALM ==="
$KCADM create realms \
-s realm="$REALM" \
-s enabled=true \
-s displayName="My Application" \
-s loginWithEmailAllowed=true \
-s resetPasswordAllowed=true \
-s bruteForceProtected=true \
-s failureFactor=5 \
-s ssoSessionIdleTimeout=1800 \
-s accessTokenLifespan=300
# 3. Create Roles
echo "=== Creating roles ==="
for ROLE in ADMIN USER MANAGER; do
$KCADM create roles -r "$REALM" -s name="$ROLE"
done
# 4. Create Clients
echo "=== Creating clients ==="
# SPA Client
$KCADM create clients -r "$REALM" \
-s clientId=my-spa \
-s publicClient=true \
-s standardFlowEnabled=true \
-s directAccessGrantsEnabled=false \
-s 'redirectUris=["https://app.example.com/*"]' \
-s 'webOrigins=["https://app.example.com"]' \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
# API Client
$KCADM create clients -r "$REALM" \
-s clientId=my-api \
-s publicClient=false \
-s bearerOnly=true
# Service Account
$KCADM create clients -r "$REALM" \
-s clientId=my-service \
-s publicClient=false \
-s serviceAccountsEnabled=true \
-s standardFlowEnabled=false
# 5. Create Test Users
echo "=== Creating test users ==="
$KCADM create users -r "$REALM" \
-s username=admin-user \
-s [email protected] \
-s firstName=Admin \
-s lastName=User \
-s enabled=true \
-s emailVerified=true
$KCADM set-password -r "$REALM" \
--username admin-user \
--new-password "Admin@123" \
--temporary=false
$KCADM add-roles -r "$REALM" \
--uusername admin-user \
--rolename ADMIN
echo "=== Realm setup completed ==="
HTMLTAG_377__HTMLTAG_378___8。 Kubernetes 的備援策略___HTMLTAG_379__HTMLTAG_380
HTMLTAG_381__HTMLTAG_382___8.1 資料庫備份___HTMLTAG_383__HTMLTAG_384
預編碼_27
HTMLTAG_385__HTMLTAG_386___8.2 領域匯出備份___HTMLTAG_387__HTMLTAG_388
預編碼_28
HTMLTAG_389__HTMLTAG_390___8.3 Kubernetes 的 Velero 備份___HTMLTAG_391__HTMLTAG_392
預編碼_29
HTMLTAG_393__HTMLTAG_394___9。完成 Kubernetes 部署___HTMLTAG_395__HTMLTAG_396
以下是在 Kubernetes 上完全部署 Keycloak 所需的所有清單:
# 01-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: keycloak
labels:
app.kubernetes.io/part-of: keycloak
---
# 02-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: keycloak-db-secret
namespace: keycloak
type: Opaque
stringData:
username: keycloak
password: "secure_db_password_here"
---
apiVersion: v1
kind: Secret
metadata:
name: keycloak-admin-secret
namespace: keycloak
type: Opaque
stringData:
username: admin
password: "secure_admin_password_here"
---
# 03-postgresql-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: keycloak
spec:
type: ClusterIP
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: keycloak
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: keycloak
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: keycloak-db-secret
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-db-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
readinessProbe:
exec:
command: ["pg_isready", "-U", "keycloak"]
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
exec:
command: ["pg_isready", "-U", "keycloak"]
initialDelaySeconds: 30
periodSeconds: 10
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
---
# 04-keycloak-cr.yaml
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: keycloak
namespace: keycloak
spec:
instances: 3
db:
vendor: postgres
url: jdbc:postgresql://postgres:5432/keycloak
usernameSecret:
name: keycloak-db-secret
key: username
passwordSecret:
name: keycloak-db-secret
key: password
poolInitialSize: 25
poolMinSize: 25
poolMaxSize: 100
hostname:
hostname: auth.example.com
strict: true
http:
tlsSecret: keycloak-tls-secret
httpEnabled: false
features:
enabled:
- token-exchange
additionalOptions:
- name: proxy-headers
value: xforwarded
- name: health-enabled
value: "true"
- name: metrics-enabled
value: "true"
- name: cache
value: ispn
- name: cache-stack
value: kubernetes
- name: log-console-output
value: json
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
unsupported:
podTemplate:
metadata:
labels:
app.kubernetes.io/name: keycloak
spec:
containers:
- env:
- name: JAVA_OPTS_KC_HEAP
value: "-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=70.0"
- name: JAVA_OPTS_APPEND
value: >-
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+UseContainerSupport -XX:+ExitOnOutOfMemoryError
-Djgroups.dns.query=keycloak-discovery.keycloak.svc.cluster.local
- name: KC_BOOTSTRAP_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: keycloak-admin-secret
key: username
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-admin-secret
key: password
---
# 05-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak
namespace: keycloak
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "KC_ROUTE"
nginx.ingress.kubernetes.io/session-cookie-expires: "3600"
nginx.ingress.kubernetes.io/session-cookie-secure: "true"
nginx.ingress.kubernetes.io/session-cookie-httponly: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- auth.example.com
secretName: keycloak-tls-secret
rules:
- host: auth.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak-service
port:
number: 8443
---
# 06-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: keycloak
namespace: keycloak
labels:
release: prometheus
spec:
selector:
matchLabels:
app: keycloak
endpoints:
- port: https
path: /metrics
scheme: https
tlsConfig:
insecureSkipVerify: true
interval: 30s
namespaceSelector:
matchNames:
- keycloak
---
# 07-prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: keycloak-alerts
namespace: keycloak
labels:
release: prometheus
spec:
groups:
- name: keycloak.rules
rules:
- alert: KeycloakPodNotReady
expr: kube_pod_status_ready{namespace="keycloak", pod=~"keycloak-.*"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Keycloak pod {{ $labels.pod }} not ready"
- alert: KeycloakHighFailedLogins
expr: sum(rate(keycloak_failed_login_attempts_total[5m])) by (realm) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High failed login rate on {{ $labels.realm }}"
- alert: KeycloakHighLatency
expr: histogram_quantile(0.95, sum(rate(keycloak_request_duration_seconds_bucket[5m])) by (le)) > 1
for: 10m
labels:
severity: warning
annotations:
summary: "Keycloak p95 latency exceeds 1s"
# Deploy tất cả
kubectl apply -f 01-namespace.yaml
kubectl apply -f 02-secrets.yaml
kubectl apply -f 03-postgresql-statefulset.yaml
# Chờ PostgreSQL ready
kubectl wait --for=condition=ready pod/postgres-0 -n keycloak --timeout=120s
# Deploy Keycloak (cần Operator đã installed)
kubectl apply -f 04-keycloak-cr.yaml
kubectl apply -f 05-ingress.yaml
kubectl apply -f 06-servicemonitor.yaml
kubectl apply -f 07-prometheus-rules.yaml
# Verify
kubectl get all -n keycloak
kubectl get keycloak -n keycloak
kubectl logs -f keycloak-0 -n keycloak