Before you begin, ensure you have the following:
Create multiple Secrets to securely store passwords and certificates required for database access, Keycloak admin console, and TLS certificates for Ingress.
apiVersion: v1
kind: Secret
metadata:
name: postgres-secrets
namespace: keycloak
type: Opaque
stringData:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: keycloak
---
apiVersion: v1
kind: Secret
metadata:
name: keycloak-secrets
namespace: keycloak
type: Opaque
stringData:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
---
apiVersion: v1
kind: Secret
metadata:
name: keycloak-tls
type: kubernetes.io/tls
data:
tls.crt:
tls.key:
kubectl apply -f secrets.yaml
Create a Persistent Volume and Persistent Volume Claim for PostgreSQL:
kind: PersistentVolume
apiVersion: v1
metadata:
name: postgres-pv
namespace: keycloak
labels:
type: local
app: postgres
spec:
storageClassName: "standard"
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
hostPath:
path: "/opt/postgres"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: keycloak
labels:
app: postgres
spec:
storageClassName: "standard"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
kubectl apply -f storage.yaml
Create a Deployment and Service for PostgreSQL, ensuring it uses the PVC for data storage.
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: keycloak
labels:
app: postgres
spec:
ports:
- port: 5432
name: postgres
selector:
app: postgres
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: keycloak
spec:
selector:
matchLabels:
app: postgres
strategy:
type: Recreate
template:
metadata:
labels:
app: postgres
spec:
containers:
- image: postgres:latest
name: postgres
envFrom:
- secretRef:
name: postgres-secrets
ports:
- containerPort: 5432
name: postgres
securityContext:
privileged: false
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
kubectl apply -f postgres.yaml
This ensures that your Postgres DB data will persist across pod restarts.
Next, deploy Keycloak with its own Persistent Volume Claim to maintain the data within Keycloak, such as users, clients, and configuration settings.
apiVersion: v1
kind: PersistentVolume
metadata:
name: keycloak-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: storage
hostPath:
path: /opt/keycloak
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: keycloak-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: storage
kubectl apply -f keycloak-storage.yaml
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
labels:
app: keycloak
spec:
ports:
- name: https
port: 443
targetPort: 8080
selector:
app: keycloak
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:21.0.2
args: ["start-dev"]
env:
- name: KEYCLOAK_ADMIN
valueFrom:
secretKeyRef:
key: KEYCLOAK_ADMIN
name: keycloak-secrets
- name: KEYCLOAK_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
key: KEYCLOAK_ADMIN_PASSWORD
name: keycloak-secrets
- name: KC_PROXY
value: "edge"
- name: KC_HEALTH_ENABLED
value: "true"
- name: KC_METRICS_ENABLED
value: "true"
- name: KC_HOSTNAME_STRICT_HTTPS
value: "true"
- name: KC_LOG_LEVEL
value: INFO
- name: KC_DB
value: postgres
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: postgres-credentials
key: POSTGRES_DB
- name: KC_DB_URL
value: jdbc:postgresql://postgres/$(POSTGRES_DB)
- name: KC_DB_USERNAME
valueFrom:
secretKeyRef:
name: postgres-credentials
key: POSTGRES_USER
- name: KC_DB_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: POSTGRES_PASSWORD
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 250
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 500
periodSeconds: 30
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
kubectl apply -f keycloak.yaml
With both PostgreSQL and Keycloak deployed, you can now access Keycloak to begin managing identities.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
nginx.ingress.kubernetes.io/forwarded-for-header: "X-Forwarded-For"
spec:
ingressClassName: nginx
rules:
- host:
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 8080
tls:
- hosts:
-
secretName: keycloak-tls
kubectl apply -f ingress.yaml
By following the steps outlined in this blog, you’ve set up Keycloak with a PostgreSQL database in a Kubernetes environment, ensuring that both Keycloak’s data and PostgreSQL’s data remain persistent. This setup provides resilience and data integrity, which are crucial in any production environment. Now, you can confidently manage your identities with the assurance that your data is safe, even in the event of pod failures or restarts.