DEV Community

Cover image for Install Keycloak with the Operator
Austin Cunningham
Austin Cunningham

Posted on

Install Keycloak with the Operator

Install the Keycloak via the operator

Create a project

oc new-project keycloak 
Enter fullscreen mode Exit fullscreen mode

I am going to install the operator the OperatorHub in Openshift which uses OLM(Operator Lifecyecle Manager). Select Operator/OperatorHub from the side bar and search for keycloak and install it in the keycloak namespace

Install the keycloak operator via operatorhub

The install should finish
Keycloak operator install is finished

I will go through the requirements now. This will not be a production setup.

Install an DB

Going to install an Ephemeral PostgreSQL DB as this is just a demo, create a yaml file called example-postgres.yaml with the following content.

apiVersion: apps/v1 kind: StatefulSet metadata: name: postgresql-db spec: serviceName: postgresql-db-service selector: matchLabels: app: postgresql-db replicas: 1 template: metadata: labels: app: postgresql-db spec: containers: - name: postgresql-db image: postgres:latest env: - name: POSTGRES_PASSWORD value: postgres #<-- change this - name: POSTGRES_USER value: postgres #<-- change this - name: PGDATA value: /data/pgdata - name: POSTGRES_DB value: keycloak --- apiVersion: v1 kind: Service metadata: name: postgres-db spec: selector: app: postgresql-db ports: - port: 5432 targetPort: 5432 
Enter fullscreen mode Exit fullscreen mode

And then apply the file to the cluster

oc apply -f example-postgres.yaml 
Enter fullscreen mode Exit fullscreen mode

The result

statefulset.apps/postgresql-db created service/postgres-db created 
Enter fullscreen mode Exit fullscreen mode

After checking the postgres pod logs I see the following error

mkdir: cannot create directory ‘/data’: Permission denied 
Enter fullscreen mode Exit fullscreen mode

Nothing is ever easy. This is because Openshift blocks containers being run as root. To get this to work we need to add a service account with the correct permissions and add that service account to the statefulset. This is a good reference blog for more details. Here are the commands I used

# create the service account ~ oc create sa postgres-sa serviceaccount/postgres-sa created # add policy ~ oc adm policy add-scc-to-user anyuid -z postgres-sa clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "postgres-sa" # set the policy on the namespace ~ oc adm policy add-scc-to-user anyuid -z postgres-sa -n keycloak clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "postgres-sa" # add the service account to the statefulset ~ oc set sa statefulset postgresql-db postgres-sa statefulset.apps/postgresql-db serviceaccount updated # delete the failing existing pod oc delete po postgresql-db-0 # check the pods  ~ oc get po NAME READY STATUS RESTARTS AGE keycloak-operator-5659f58f4b-vtrm9 1/1 Running 0 29m postgresql-db-0 1/1 Running 0 8m55s 
Enter fullscreen mode Exit fullscreen mode

Ok that's working

Hostname

We need a resolvable domain name. You can add any domain to your openshift cluster using a customDomain. As I couldn't be bothered setting up an actual domain name here or a valid cert this a quick hack for creating a resolvable domain name on Openshift.

# create a self signed cert  openssl req -subj '/CN=apps.austin.me/O=Test Keycloak./C=US' -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem # create cert project oc new-project certs # create a secret from that cert in the cert namespace oc create secret tls austin-me-tls --cert=austin.me.crt --key=austin.me.key -n certs # create a custom domain based on the cert oc apply -f - <<EOF --- apiVersion: managed.openshift.io/v1alpha1 kind: CustomDomain metadata: name: cunningham spec: domain: apps.austin.me scope: External certificate: name: austin-me-tls namespace: certs EOF # Wait for the custom domain to become ready  oc get customdomains NAME ENDPOINT DOMAIN STATUS cunningham oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org apps.austin.me Ready 
Enter fullscreen mode Exit fullscreen mode

All pretty standard a this point but the domain apps.austin.me doesn't exist. So here is the hack edit the custom domain oc edit customdomain cunningham and replace the spec.host with the endpoint.

apiVersion: managed.openshift.io/v1alpha1 kind: CustomDomain metadata: name: cunningham spec: certificate: name: austin-me-tls namespace: certs domain: oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org #<---- was apps.austin.me now points at the endpoint loadBalancerType: Classic scope: External 
Enter fullscreen mode Exit fullscreen mode

So our domain going forward is the endpoint oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org

Create a DB secret

oc project keycloak # change these to match the secret used in your statefulset kubectl create secret generic keycloak-db-secret \ --from-literal=username=postgres \ --from-literal=password=postgres 
Enter fullscreen mode Exit fullscreen mode

Create the Keycloak CR

The CR(Custom Resource) creates a instance of the Keycloak UI. Create a yaml file called example-kc.yaml

apiVersion: k8s.keycloak.org/v2alpha1 kind: Keycloak metadata: name: example-kc spec: instances: 1 db: vendor: postgres host: postgres-db usernameSecret: name: keycloak-db-secret key: username passwordSecret: name: keycloak-db-secret key: password http: tlsSecret: austin-me-tls hostname: hostname: oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org 
Enter fullscreen mode Exit fullscreen mode

And then apply the file

kubectl apply -f example-kc.yaml 
Enter fullscreen mode Exit fullscreen mode

We can check the CR status of keycloak, it will look like this when it is type: Ready status: "True"

# using https://mikefarah.gitbook.io/yq/ ~ oc get keycloak example-kc -oyaml | yq '.status' conditions: - lastTransitionTime: "2023-09-15T08:08:56.665783288Z" message: "" observedGeneration: 1 status: "True" #<------ This is what we are looking for I hate these condition to difficult to read a first glance type: Ready - lastTransitionTime: "2023-09-15T08:08:26.147242224Z" message: "" observedGeneration: 1 status: "False" type: HasErrors - lastTransitionTime: "2023-09-15T08:08:26.147242224Z" message: "" observedGeneration: 1 status: "False" type: RollingUpdate instances: 1 observedGeneration: 1 selector: app=keycloak,app.kubernetes.io/managed-by=keycloak-operator,app.kubernetes.io/instance=example-kc 
Enter fullscreen mode Exit fullscreen mode

That looks to have completed and if we port-forward the service it looks to be working

~ oc port-forward service/example-kc-service 8443:8443 Forwarding from 127.0.0.1:8443 -> 8443 Forwarding from [::1]:8443 -> 8443 Handling connection for 8443 Handling connection for 8443 Handling connection for 8443 Handling connection for 8443 Handling connection for 8443 
Enter fullscreen mode Exit fullscreen mode

Keycloak landing page after port forward

And if we check the route we can open the keycloak landing page.

oc get routes --namespace keycloak NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD example-kc-ingress-76p8j oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org ... 1 more example-kc-service https passthrough/Redirect None 
Enter fullscreen mode Exit fullscreen mode

Keycloak landing page using route

Top comments (0)