[incubator/keycloak] Add high availability and various improvements (#2756)

* [incubator/keycloak] Add high availability and various improvements

* Add various improvements, such as node selector, tolerations, and pod anti-affinity
* Switch to StatefulSet due to length restrictions for WildFly node identifier and
  for potential advantages for cluster discovery when names pod stable
* Add high availability and clustering using JDBC_PING discovery for JGroups
* Add customizability via WildFly CLI
* Improve ingress configuration

* Fix NOTES.txt

* Improve external database configuration

* Fix typos

* Add login test

* Fix Postgres conditionals

* Automatically enable HA when keycloak.replicas > 1
This commit is contained in:
Reinhard Nägele
2017-12-17 08:07:35 -08:00
committed by k8s-ci-robot
parent ed12196be2
commit 1bde9b4ac9
20 changed files with 799 additions and 237 deletions
+4 -3
View File
@@ -1,6 +1,6 @@
name: keycloak
version: 0.1.0
appVersion: 3.3.0.Final
version: 0.2.0
appVersion: 3.4.0.Final
description: Open Source Identity and Access Management For Modern Applications and Services
keywords:
- authentication
@@ -11,4 +11,5 @@ sources:
maintainers:
- name: nexeck
email: marcel@beck.im
engine: gotpl
- name: unguiculus
email: unguiculus@gmail.com
+132 -10
View File
@@ -11,18 +11,21 @@ $ helm install incubator/keycloak
## Introduction
This chart bootstraps a [Keycloak](http://www.keycloak.org/) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
This chart bootstraps a [Keycloak](http://www.keycloak.org/) StatefulSet on a [Kubernetes](https://kubernetes.io) cluster
using the [Helm](https://helm.sh) package manager. It provisions a fully featured Keycloak installation.
For more information on Keycloak and its capabilities, see it's [documentation](http://www.keycloak.org/documentation.html).
## Prerequisites Details
* PV support on underlying infrastructure (if persistence is required)
The chart has an optional dependency on the [PostgreSQL](https://github.com/kubernetes/charts/tree/master/stable/postgresql) chart.
By default, the PostgreSQL chart requires PV support on underlying infrastructure (may be disabled).
## Installing the Chart
To install the chart with the release name `keycloak` into the namespace keycloak-system:
To install the chart with the release name `keycloak`:
```console
$ helm install --name keycloak incubator/keycloak --namespace keycloak-system
$ helm install --name keycloak incubator/keycloak
```
## Uninstalling the Chart
@@ -33,13 +36,59 @@ To uninstall/delete the `keycloak` deployment:
$ helm delete keycloak
```
## Chart Details
This chart will provision a fully functional and fully featured Keycloak installation.
For more information on Keycloak and its capabilities, see it's [documentation](http://www.keycloak.org/documentation.html).
## Configuration
Configurable values are documented in the `values.yaml`.
The following tables lists the configurable parameters of the Keycloak chart and their default values.
Parameter | Description | Default
--- | --- | ---
`hyperkube.image.repository` | Hyperkube image repository | `quay.io/coreos/hyperkube`
`hyperkube.image.tag` | Hyperkube image tag | `v1.8.1_coreos.0`
`hyperkube.image.pullPolicy` | Hyperkube image pull policy | `IfNotPresent`
`keycloak.replicas` | The number of Keycloak replicas | `1`
`keycloak.image.repository` | The Keycloak image repository | `jboss/keycloak`
`keycloak.image.tag` | The Keycloak image tag | `3.4.0.Final`
`keycloak.image.pullPolicy` | The Keycloak image pull policy | `IfNotPresent`
`keycloak.username` | Username for the initial Keycloak admin user | `keycloak`
`keycloak.password` | Password for the initial Keycloak admin user. If not set, a random 10 characters password is created | `""`
`keycloak.additionalEnv` | Allows the specification of additional environment variables for Keycloak | `[]`
`keycloak.resources` | Pod resource requests and limits | `{}`
`keycloak.podAntiAffinity` | Pod anti-affinity (`soft` or `hard`) | `soft`
`keycloak.nodeSelector` | Node labels for pod assignment | `{}`
`keycloak.tolerations` | Node taints to tolerate | `[]`
`keycloak.cli.nodeIdentifier` | WildFly CLI script for setting the node identifier | See `values.yaml`
`keycloak.cli.logging` | WildFly CLI script for logging configuration | See `values.yaml`
`keycloak.cli.reverseProxy` | WildFly CLI script for reverse proxy configuration | See `values.yaml`
`keycloak.cli.discovery` | WildFly CLI script for cluster discovery | See `values.yaml`
`keycloak.cli.custom` | Additional custom WildFly CLI script | `""`
`keycloak.service.annotations` | Annotations for the Keycloak service | `{}`
`keycloak.service.labels` | Additional labels for ther Keycloak service | `{}`
`keycloak.service.type` | The service type | `ClusterIP`
`keycloak.service.port` | The service port | `80`
`keycloak.service.nodePort` | The node port used if the service is of type `NodePort` | `""`
`keycloak.ingress.enabled` | If true, an ingress is be created | `false`
`keycloak.ingress.path` | The ingress path | `/`
`keycloak.ingress.annotations` | Annotations for the ingress | `{}`
`keycloak.ingress.hosts` | A list of hosts for the ingress | `[keycloak.example.com]`
`keycloak.ingress.tls.enabled` | If true, tls is enabled for the ingress | `false`
`keycloak.ingress.tls.existingSecret` | If tls is enabled, uses an existing secret with this name; otherwise a secret is created | `false`
`keycloak.ingress.tls.secretContents` | Contents for the tls secret | `{}`
`keycloak.ingress.tls.secretAnnotations` | Annotations for the newly created tls secret | `{}`
`keycloak.persistence.deployPostgres` | If true, the PostgreSQL chart is installed | `true`
`keycloak.persistence.existingSecret` | Name of an existing secret to be used for the database password (if `keycloak.persistence.deployPostgres=false`). Otherwise a new secret is created | `""`
`keycloak.persistence.existingSecretKey` | The key for the database password in the existing secret (if `keycloak.persistence.deployPostgres=false`) | `password`
`keycloak.persistence.dbVendor` | One of `H2`, `POSTGRES`, or `MYSQL` (if `deployPostgres=false`) | `H2`
`keycloak.persistence.dbName` | The name of the database to connect to (if `deployPostgres=false`) | `keycloak`
`keycloak.persistence.dbHost` | The database host name (if `deployPostgres=false`) | `mykeycloak`
`keycloak.persistence.dbPort` | The database host port (if `deployPostgres=false`) | `5432`
`keycloak.persistence.dbUser` |The database user (if `deployPostgres=false`) | `keycloak`
`keycloak.persistence.dbPassword` |The database password (if `deployPostgres=false`) | `keycloak`
`postgresql.postgresUser` | The PostgreSQL user (if `keycloak.persistence.deployPostgres=true`) | `keycloak`
`postgresql.postgresPassword` | The PostgreSQL password (if `keycloak.persistence.deployPostgres=true`) | `""`
`postgresql.postgresDatabase` | The PostgreSQL database (if `keycloak.persistence.deployPostgres=true`) | `keycloak`
`test.image.repository` | Test image repository | `unguiculus/docker-python3-phantomjs-selenium`
`test.image.tag` | Test image tag | `v1`
`test.image.pullPolicy` | Test image pull policy | `IfNotPresent`
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
@@ -49,4 +98,77 @@ Alternatively, a YAML file that specifies the values for the parameters can be p
$ helm install --name keycloak -f values.yaml incubator/keycloak
```
> **Tip**: You can use the default [values.yaml](values.yaml)
### Database Setup
By default, the [PostgreSQL](https://github.com/kubernetes/charts/tree/master/stable/postgresql) chart is deployed and used as database.
Please refer to this chart for additional PostgreSQL configuration options. If PostgreSQL is disabled, Keycloak uses an embedded H2
database which is only suitable for testing with a single replica.
#### Using an External Database
The Keycloak Docker image supports PostgreSQL and MySQL. The password for the database user is read from a Kubernetes secret. It
is possible to specify an existing secret that is not managed with this chart. The key in the secret the password is read
from may be specified as well (defaults to `password`).
```yaml
keycloak:
persistence:
# Disable deployment of the PostgreSQL chart
deployPostgres: false
# Optionally specify an existing secret
existingSecret: "my-database-password-secret"
existingSecretKey: "password-key in-my-database-secret"
dbVendor: POSTGRES # for MySQL use "MYSQL"
dbName: keycloak
dbHost: mykeycloak
dbPort: 5432 # 5432 is PostgreSQL's default port. For MySQL it would be 3306
dbUser: keycloak
# Only used if no existing secret is specified. In this case a new secret is created
dbPassword: keycloak
```
See also:
* https://github.com/jboss-dockerfiles/keycloak/blob/master/server/cli/databases/postgres/change-database.cli
* https://github.com/jboss-dockerfiles/keycloak/blob/master/server/cli/databases/mysql/change-database.cli
### Configuring additional environment variables:
```yaml
keycloak:
additionalEnv:
- name: KEYCLOAK_LOGLEVEL
value: : DEBUG
- name: WILDFLY_LOGLEVEL
value: DEBUG
- name: CACHE_OWNERS:
value"3"
```
### WildFly Configuration
WildFly can be configured via its [command line interface (CLI)](https://docs.jboss.org/author/display/WFLY/Command+Line+Interface).
This chart uses the official Keycloak Docker image and customizes the installation running CLI scripts at server startup.
In order to make further customization easier, the CLI commands are separated by their concerns into smaller scripts.
Everything is in `values.yaml` and can be overridden. Additional CLI commands may be added via `keycloak.cli.custom`,
which is empty by default.
### High Availability and Clustering
For high availability, Keycloak should be run with multiple replicas (`keycloak.replicas > 1`). WildFly uses Infinispan
for caching. These caches can be replicated across all instances forming a cluster. If `keycloak.replicas > 1`, the
WildFly CLI script `keycloak.cli.discovery` adds JGroups' [JDBC_PING](http://www.jgroups.org/javadoc/org/jgroups/protocols/JDBC_PING.html)
for cluster discovery and Keycloak is started with `--server-config standalone-ha.xml`.
## Why StatefulSet?
The chart sets node identifiers to the system property `jboss.node.name` which is in fact the pod name. Node identifiers
must not be longer than 23 characters. This can be problematic because pod names are quite long. We would have to truncate
the chart's fullname to six characters because pods get a 17-character suffix (e. g. `-697f8b7655-mf5ht`). Using a
StatefulSet allows us to truncate to 20 characters leaving room for up to 99 replicas, which is much better.
Additionally, we get stable values for `jboss.node.name` which can be advantageous for cluster discovery.
+2 -2
View File
@@ -2,5 +2,5 @@ dependencies:
- name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/
version: 0.8.3
digest: sha256:b67c843d95aa0a2e7684abb787b22913e54baa92f45a450a3c3bcb7f3e068748
generated: 2017-10-28T10:01:56.578879+02:00
digest: sha256:cb2fd7e1ac6001bc8f963a39acb2af903e1e0f684535b9790631c9db59787ada
generated: 2017-11-29T09:01:49.898438236+01:00
+1
View File
@@ -2,3 +2,4 @@ dependencies:
- name: postgresql
version: 0.8.3
repository: https://kubernetes-charts.storage.googleapis.com/
condition: keycloak.persistence.deployPostgres
+37 -32
View File
@@ -1,44 +1,49 @@
1. Keycloak can be accessed:
* Within your cluster, at the following DNS name at port {{ .Values.keycloak.service.port }}:
* Within your cluster, at the following DNS name at port {{ .Values.keycloak.service.port }}:
{{ template "keycloak.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
{{ template "keycloak.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
{{- if .Values.keycloak.ingress.enabled }}
{{- if .Values.keycloak.ingress.enabled }}
* From outside the cluster, the URL is:
{{- if .Values.keycloak.ingress.tls }}
https://{{ .Values.keycloak.ingress.hostname }}
{{- else }}
http://{{ .Values.keycloak.ingress.hostname }}
{{- end }}
{{- else }}
* From outside the cluster:
* From outside the cluster, run these commands in the same shell:
{{- if contains "NodePort" .Values.keycloak.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "keycloak.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.keycloak.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "keycloak.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.keycloak.service.port }}
{{- else if contains "ClusterIP" .Values.keycloak.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "keycloak.name" . }},release={{ .Release.Name }} -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use Keycloak"
kubectl port-forward --namespace {{ .Release.Namespace }} $POD_NAME 8080:{{ .Values.keycloak.service.port }}
{{- range .Values.keycloak.ingress.hosts }}
- http{{ if $.Values.keycloak.ingress.tls.enabled }}s{{ end }}://{{ . }}
{{- end }}
{{- else }}
* From outside the cluster, run these commands in the same shell:
{{- if contains "NodePort" .Values.keycloak.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "keycloak.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.keycloak.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "keycloak.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.keycloak.service.port }}
{{- else if contains "ClusterIP" .Values.keycloak.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "keycloak.name" . }},release={{ .Release.Name }} -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use Keycloak"
kubectl port-forward --namespace {{ .Release.Namespace }} $POD_NAME 8080
{{- end }}
2. Login with the following credentials
{{- end }}
To get your user password run:
kubectl get secret --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }} -o jsonpath="{.data.keycloak-password}" | base64 --decode; echo
2. Login with the following credentials:
Username: {{ .Values.keycloak.username }}
Username: {{ .Values.keycloak.username }}
To retrieve the initial user password run:
kubectl get secret --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }}-http -o jsonpath="{.data.password}" | base64 --decode; echo
+74 -2
View File
@@ -8,11 +8,13 @@ Expand the name of the chart.
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
We truncate to 20 characters because this is used to set the node identifier in WildFly which is limited to
23 characters. This allows for a replica suffix for up to 99 replicas.
*/}}
{{- define "keycloak.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- $fullname := printf "%s-%s" $name .Release.Name -}}
{{- default $fullname .Values.fullnameOverride | trunc 20 | trimSuffix "-" -}}
{{- end -}}
{{/*
@@ -23,3 +25,73 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this
{{- $name := default "postgresql" .Values.postgresql.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name for the tls secret.
*/}}
{{- define "keycloak.tlsSecret" -}}
{{- if .Values.keycloak.ingress.tls.existingSecret -}}
{{- .Values.keycloak.ingress.tls.existingSecret -}}
{{- else -}}
{{- template "keycloak.fullname" . -}}-tls
{{- end -}}
{{- end -}}
{{/*
Create the name for the database secret.
*/}}
{{- define "keycloak.externalDbSecret" -}}
{{- if .Values.keycloak.persistence.existingSecret -}}
{{- .Values.keycloak.persistence.existingSecret -}}
{{- else -}}
{{- template "keycloak.fullname" . -}}-db
{{- end -}}
{{- end -}}
{{/*
Create the name for the password secret key.
*/}}
{{- define "keycloak.dbPasswordKey" -}}
{{- if .Values.keycloak.persistence.existingSecret -}}
{{- .Values.keycloak.persistence.existingSecretKey -}}
{{- else -}}
password
{{- end -}}
{{- end -}}
{{/*
Create environment variables for database configuration.
*/}}
{{- define "keycloak.externalDbConfig" -}}
- name: DB_VENDOR
value: {{ .Values.keycloak.persistence.dbVendor | quote }}
{{- if eq .Values.keycloak.persistence.dbVendor "POSTGRES" }}
- name: POSTGRES_PORT_5432_TCP_ADDR
value: {{ .Values.keycloak.persistence.dbHost | quote }}
- name: POSTGRES_PORT_5432_TCP_PORT
value: {{ .Values.keycloak.persistence.dbPort | quote }}
- name: POSTGRES_USER
value: {{ .Values.keycloak.persistence.dbUser | quote }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.externalDbSecret" . }}
key: {{ include "keycloak.dbPasswordKey" . | quote }}
- name: POSTGRES_DATABASE
value: {{ .Values.keycloak.persistence.dbName | quote }}
{{- else if eq .Values.keycloak.persistence.dbVendor "MYSQL" }}
- name: MYSQL_PORT_3306_TCP_ADDR
value: {{ .Values.keycloak.persistence.dbHost | quote }}
- name: MYSQL_PORT_3306_TCP_PORT
value: {{ .Values.keycloak.persistence.dbPort | quote }}
- name: MYSQL_USER
value: {{ .Values.keycloak.persistence.dbUser | quote }}
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.externalDbSecret" . }}
key: {{ include "keycloak.dbPasswordKey" . | quote }}
- name: MYSQL_DATABASE
value: {{ .Values.keycloak.persistence.dbName | quote }}
{{- end }}
{{- end -}}
@@ -0,0 +1,71 @@
{{- $highAvailability := gt (int .Values.keycloak.replicas) 1 -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "keycloak.fullname" . }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
data:
{{- if .Values.keycloak.persistence.deployPostgres }}
wait_for_postgresql.sh: |
#!/usr/bin/env bash
set -eu
pod_labels="${1?Please specify PostgreSQL labels}"
max_retries=30
retry_count=0
sleep_secs=2
ready_pods=
echo 'Polling for PostgreSQL readiness...'
while [ -z "$ready_pods" ] && [ "$retry_count" -lt "$max_retries" ]; do
ready_pods=$(kubectl get pods -l "$pod_labels" -o jsonpath="{.items[*].status.containerStatuses[?(@.ready==true)].name}")
retry_count=$((retry_count + 1))
sleep "$sleep_secs"
done
if [ -z "$ready_pods" ]; then
echo "ERROR: Timeout waiting for PostgreSQL to start up." >&2
exit 1
fi
echo "PostgreSQL ready!"
{{- end }}
keycloak.sh: |
#!/usr/bin/env bash
set -eu
/opt/jboss/keycloak/bin/jboss-cli.sh --file=/scripts/keycloak.cli
args=(-b 0.0.0.0{{ if $highAvailability }} --server-config standalone-ha.xml{{ end }})
exec /opt/jboss/docker-entrypoint.sh "${args[@]}"
exit "$?"
keycloak.cli: |
embed-server{{ if $highAvailability }} --server-config=standalone-ha.xml{{ end }} --std-out=echo
{{- $cli := .Values.keycloak.cli }}
{{ $cli.nodeIdentifier | indent 4 }}
{{ $cli.logging | indent 4 }}
{{ $cli.reverseProxy | indent 4 }}
{{- if $highAvailability }}
{{ $cli.discovery | indent 4 }}
{{- end }}
{{- if $cli.custom }}
{{ $cli.custom | indent 4 }}
{{- end }}
stop-embedded-server
@@ -0,0 +1,14 @@
{{- if and (not .Values.keycloak.persistence.deployPostgres) (not .Values.keycloak.persistence.existingSecret) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "keycloak.fullname" . }}-db
labels:
app: {{ template "keycloak.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
type: Opaque
data:
{{ template "keycloak.dbPasswordKey" . }}: {{ .Values.keycloak.persistence.dbPassword | b64enc | quote }}
{{- end -}}
@@ -1,63 +0,0 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "keycloak.fullname" . }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
replicas: 1
template:
metadata:
labels:
app: {{ template "keycloak.name" . }}
release: "{{ .Release.Name }}"
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: PROXY_ADDRESS_FORWARDING
value: "true"
- name: KEYCLOAK_LOGLEVEL
value: {{ .Values.keycloak.loglevel | quote }}
- name: KEYCLOAK_USER
value: {{ .Values.keycloak.username | quote }}
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.fullname" . }}
key: keycloak-password
- name: POSTGRES_PORT_5432_TCP_ADDR
value: {{ template "keycloak.postgresql.fullname" . }}
- name: POSTGRES_PORT_5432_TCP_PORT
value: "5432"
- name: POSTGRES_USER
value: {{ .Values.postgresql.postgresUser | quote }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.postgresql.fullname" . }}
key: postgres-password
- name: POSTGRES_DATABASE
value: {{ .Values.postgresql.postgresDatabase | quote }}
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 1
resources:
{{ toYaml .Values.keycloak.resources | indent 10 }}
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "keycloak.fullname" . }}-headless
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
type: ClusterIP
clusterIP: None
ports:
- name: http
port: {{ .Values.keycloak.service.port }}
targetPort: http
protocol: TCP
selector:
app: {{ template "keycloak.name" . }}
release: "{{ .Release.Name }}"
@@ -0,0 +1,30 @@
{{- $service := .Values.keycloak.service -}}
apiVersion: v1
kind: Service
metadata:
name: {{ template "keycloak.fullname" . }}-http
{{- if $service.annotations }}
annotations:
{{ toYaml $service.annotations | indent 4 }}
{{- end }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- if $service.labels }}
{{ toYaml $service.labels | indent 4 }}
{{- end }}
spec:
type: {{ $service.type }}
ports:
- name: http
port: {{ $service.port }}
targetPort: http
{{- if and (eq "NodePort" $service.type) $service.nodePort }}
nodePort: {{ $service.nodePort }}
{{- end }}
protocol: TCP
selector:
app: {{ template "keycloak.name" . }}
release: "{{ .Release.Name }}"
+26 -19
View File
@@ -1,28 +1,35 @@
{{- if .Values.keycloak.ingress.enabled -}}
{{- $ingress := .Values.keycloak.ingress -}}
{{- if $ingress.enabled -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "keycloak.fullname" . }}
{{- if $ingress.annotations }}
annotations:
{{ toYaml $ingress.annotations | indent 4 }}
{{- end }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- if .Values.keycloak.ingress.annotations }}
annotations:
{{ toYaml .Values.keycloak.ingress.annotations | indent 4 }}
{{- end }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
spec:
rules:
- host: {{ .Values.keycloak.ingress.hostname }}
http:
paths:
- path: /
backend:
serviceName: {{ template "keycloak.fullname" . }}
servicePort: {{ .Values.keycloak.service.port }}
{{- if .Values.keycloak.ingress.tls }}
{{- if $ingress.tls.enabled }}
tls:
{{ toYaml .Values.keycloak.ingress.tls | indent 4 }}
{{- end -}}
- hosts:
{{- range $ingress.hosts }}
- {{ . }}
{{- end }}
secretName: {{ template "keycloak.tlsSecret" . }}
{{- end }}
rules:
{{- range $ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: {{ $ingress.path }}
backend:
serviceName: {{ template "keycloak.fullname" $ }}-http
servicePort: http
{{- end }}
{{- end -}}
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "keycloak.fullname" . }}-http
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
type: Opaque
data:
{{- if .Values.keycloak.password }}
password: {{ .Values.keycloak.password | b64enc | quote }}
{{- else }}
password: {{ randAlphaNum 10 | b64enc | quote }}
{{- end }}
-16
View File
@@ -1,16 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "keycloak.fullname" . }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
type: Opaque
data:
{{ if .Values.keycloak.password }}
keycloak-password: {{ .Values.keycloak.password | b64enc | quote }}
{{ else }}
keycloak-password: {{ randAlphaNum 10 | b64enc | quote }}
{{ end }}
-29
View File
@@ -1,29 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "keycloak.fullname" . }}
{{- if .Values.keycloak.service.annotations }}
annotations:
{{ toYaml .Values.keycloak.service.annotations | indent 4 }}
{{- end }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- if .Values.keycloak.service.labels }}
{{ toYaml .Values.keycloak.service.labels | indent 4 }}
{{- end }}
spec:
type: {{ .Values.keycloak.service.type }}
ports:
- name: http
port: {{ .Values.keycloak.service.port }}
targetPort: 8080
{{ if and (eq "NodePort" .Values.keycloak.service.type) .Values.keycloak.service.nodePort }}
nodePort: {{ .Values.keycloak.service.nodePort}}
{{ end }}
protocol: TCP
selector:
app: {{ template "keycloak.name" . }}
release: "{{ .Release.Name }}"
@@ -0,0 +1,124 @@
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: {{ template "keycloak.fullname" . }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
replicas: {{ .Values.keycloak.replicas }}
serviceName: {{ template "keycloak.fullname" . }}-headless
podManagementPolicy: Parallel
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: {{ template "keycloak.name" . }}
release: "{{ .Release.Name }}"
spec:
{{- if .Values.keycloak.persistence.deployPostgres }}
initContainers:
- name: wait-for-postgresql
image: "{{ .Values.hyperkube.image.repository }}:{{ .Values.hyperkube.image.tag }}"
imagePullPolicy: "{{ .Values.hyperkube.image.pullPolicy }}"
command:
- "bash"
- "-c"
- "/scripts/wait_for_postgresql.sh app={{ template "keycloak.postgresql.fullname" . }}"
volumeMounts:
- name: scripts
mountPath: /scripts
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.keycloak.image.repository }}:{{ .Values.keycloak.image.tag }}"
imagePullPolicy: {{ .Values.keycloak.image.pullPolicy }}
command:
- /scripts/keycloak.sh
env:
- name: KEYCLOAK_USER
value: {{ .Values.keycloak.username }}
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.fullname" . }}-http
key: password
{{- if .Values.keycloak.persistence.deployPostgres }}
- name: POSTGRES_PORT_5432_TCP_ADDR
value: {{ template "keycloak.postgresql.fullname" . }}
- name: POSTGRES_USER
value: {{ .Values.postgresql.postgresUser | quote }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.postgresql.fullname" . }}
key: postgres-password
- name: POSTGRES_DATABASE
value: {{ .Values.postgresql.postgresDatabase | quote }}
{{- else }}
{{ include "keycloak.externalDbConfig" . | indent 12 }}
{{- end }}
{{- if .Values.keycloak.additionalEnv }}
{{ toYaml .Values.keycloak.additionalEnv | indent 12 }}
{{- end }}
volumeMounts:
- name: scripts
mountPath: /scripts
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /auth/
port: http
initialDelaySeconds: 120
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /auth/
port: http
initialDelaySeconds: 5
timeoutSeconds: 1
resources:
{{ toYaml .Values.keycloak.resources | indent 12 }}
{{- if eq .Values.keycloak.podAntiAffinity "hard" }}
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: "kubernetes.io/hostname"
labelSelector:
matchLabels:
app: "{{ template "keycloak.fullname" . }}"
release: "{{ .Release.Name }}"
{{- else if eq .Values.keycloak.podAntiAffinity "soft" }}
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: "{{ template "keycloak.fullname" . }}"
release: "{{ .Release.Name }}"
{{- end }}
{{- if .Values.keycloak.nodeSelector }}
nodeSelector:
{{ toYaml .Values.keycloak.nodeSelector | indent 8 }}
{{- end }}
{{- if .Values.keycloak.tolerations }}
tolerations:
{{ toYaml .Values.keycloak.tolerations | indent 8 }}
{{- end }}
terminationGracePeriodSeconds: 60
volumes:
- name: scripts
configMap:
name: {{ template "keycloak.fullname" . }}
defaultMode: 0755
@@ -0,0 +1,48 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "keycloak.fullname" . }}-test
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
data:
test.py: |
import os
from selenium import webdriver
from urllib.parse import urlparse
print('Creating PhantomJS river...')
driver = webdriver.PhantomJS()
base_url = 'http://{{ template "keycloak.fullname" . }}-http{{ if ne 80 (int .Values.keycloak.service.port) }}{{ .Values.keycloak.service.port }}{{ end }}'
print('Opening Keycloak...')
driver.get('{0}/auth/admin/'.format(base_url))
username = os.environ['KEYCLOAK_USER']
password = os.environ['KEYCLOAK_PASSWORD']
print('Entering username...')
driver.find_element_by_id('username').send_keys(username)
print('Entering password...')
driver.find_element_by_id('password').send_keys(password)
print('Clicking login button...')
driver.find_element_by_id('kc-login').click()
current_url = urlparse(driver.current_url)
expected_url = urlparse('{0}/auth/admin/master/console/'.format(base_url))
print('Current URL: {0}'.format(current_url))
print('Expected URL: {0}'.format(expected_url))
if current_url.path != expected_url.path:
print('Login failed. Current url is not expected url')
exit(1)
print('URLs match. Login successful.')
driver.quit()
@@ -0,0 +1,35 @@
apiVersion: v1
kind: Pod
metadata:
name: {{ template "keycloak.fullname" . }}-test
labels:
app: {{ template "keycloak.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: {{ .Chart.Name }}-test
image: "{{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}"
imagePullPolicy: {{ .Values.test.image.pullPolicy }}
command:
- python3
- /tests/test.py
env:
- name: KEYCLOAK_USER
value: {{ .Values.keycloak.username }}
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "keycloak.fullname" . }}-http
key: password
volumeMounts:
- name: tests
mountPath: /tests
volumes:
- name: tests
configMap:
name: {{ template "keycloak.fullname" . }}-test
restartPolicy: Never
@@ -0,0 +1,21 @@
{{- $tls := .Values.keycloak.ingress.tls -}}
{{- if and $tls.enabled (not $tls.existingSecret) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "keycloak.fullname" . }}-tls
{{- if $tls.secretAnnotations }}
annotations:
{{ toYaml $tls.secretAnnotations | indent 4 }}
{{- end }}
labels:
app: {{ template "keycloak.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
type: Opaque
data:
{{- range $key, $value := $tls.secretContents }}
{{ $key }}: {{ $value | b64enc | quote }}
{{- end }}
{{- end -}}
+144 -61
View File
@@ -1,35 +1,36 @@
image:
## Keycloak image.
##
repository: jboss/keycloak
## keycloak image version.
## ref: https://hub.docker.com/r/jboss/keycloak/tags/
##
tag: 3.3.0.Final
## Specify a imagePullPolicy: 'Always' if imageTag is 'latest', else set to 'IfNotPresent'.
## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-image
##
pullPolicy: IfNotPresent
## Used to run 'kubectl' in an init container in order to wait for Postgresql to start up.
hyperkube:
image:
repository: quay.io/coreos/hyperkube
tag: v1.8.1_coreos.0
pullPolicy: IfNotPresent
keycloak:
## First keycloak username
## This user has full admin access
replicas: 1
image:
repository: jboss/keycloak
tag: 3.4.0.Final
pullPolicy: IfNotPresent
## Username for the initial Keycloak admin user
username: keycloak
## Keycloak Password for the first user.
## If not set, a random 10 characters password will be used.
##
# password: keycloak
## Password for the initial Keycloak admin user
## If not set, a random 10 characters password will be used
password: ""
## Keycloak logging level
loglevel: INFO
## Allows the specification of additional environment variables for Keycloak
additionalEnv: {}
# KEYCLOAK_LOGLEVEL: DEBUG
# WILDFLY_LOGLEVEL: DEBUG
# CACHE_OWNERS: 2
## Configure resource requests and limits.
## ref: https://kubernetes.io/docs/user-guide/compute-resources/
##
resources:
podAntiAffinity: soft
nodeSelector: {}
tolerations: []
resources: {}
# limits:
# cpu: "100m"
# memory: "1024Mi"
@@ -37,53 +38,129 @@ keycloak:
# cpu: "100m"
# memory: "1024Mi"
## Ingress configuration.
## ref: https://kubernetes.io/docs/user-guide/ingress/
##
ingress:
## Enable Ingress.
##
enabled: false
## WildFly CLI configurations. They all end up in the file 'keycloak.cli' configured in the configmap whichn is
## executed on server startup.
cli:
## Sets the node identifier to the node name (= pod name). Node identifiers have to be unique. They can have a
## maximum length of 23 characters. Thus, the chart's fullname template truncates its length accordingly.
nodeIdentifier: |
# Makes node identifier unique getting rid of a warning in the logs
/subsystem=transactions:write-attribute(name=node-identifier, value=${jboss.node.name})
## Annotations to be added to the ingress.
##
# annotations:
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: 'true'
logging: |
# Allow log level to be configured via environment variable
/subsystem=logging/console-handler=CONSOLE:write-attribute(name=level, value=${env.WILDFLY_LOGLEVEL:INFO})
/subsystem=logging/root-logger=ROOT:write-attribute(name=level, value=${env.WILDFLY_LOGLEVEL:INFO})
# Log only to console
/subsystem=logging/root-logger=ROOT:write-attribute(name=handlers, value=[CONSOLE])
reverseProxy: |
/socket-binding-group=standard-sockets/socket-binding=proxy-https:add(port=443)
/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=redirect-socket, value=proxy-https)
/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding, value=true)
discovery: |
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=owners, value=${env.CACHE_OWNERS:2})
/subsystem=jgroups/stack=tcp:remove()
/subsystem=jgroups/stack=tcp:add()
/subsystem=jgroups/stack=tcp/transport=TCP:add(socket-binding="jgroups-tcp")
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING:add()
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING/property=datasource_jndi_name:add(value=java:jboss/datasources/KeycloakDS)
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING/property=clear_table_on_view_change:add(value=true)
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING/property=break_on_coord_rsp:add(value=true)
# Statements must be adapted for PostgreSQL. Additionally, we add a 'creation_timestamp' column.
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING/property=initialize_sql:add(value="CREATE TABLE IF NOT EXISTS JGROUPSPING (own_addr varchar(200) NOT NULL, creation_timestamp timestamp NOT NULL, cluster_name varchar(200) NOT NULL, ping_data bytea, constraint PK_JGROUPSPING PRIMARY KEY (own_addr, cluster_name))")
/subsystem=jgroups/stack=tcp/protocol=JDBC_PING/property=insert_single_sql:add(value="INSERT INTO JGROUPSPING (own_addr, creation_timestamp, cluster_name, ping_data) values (?, NOW(), ?, ?)")
/subsystem=jgroups/stack=tcp/protocol=MERGE3:add()
/subsystem=jgroups/stack=tcp/protocol=FD_SOCK:add(socket-binding="jgroups-tcp-fd")
/subsystem=jgroups/stack=tcp/protocol=FD:add()
/subsystem=jgroups/stack=tcp/protocol=VERIFY_SUSPECT:add()
/subsystem=jgroups/stack=tcp/protocol=pbcast.NAKACK2:add()
/subsystem=jgroups/stack=tcp/protocol=UNICAST3:add()
/subsystem=jgroups/stack=tcp/protocol=pbcast.STABLE:add()
/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS:add()
/subsystem=jgroups/stack=tcp/protocol=pbcast.GMS/property=max_join_attempts:add(value=5)
/subsystem=jgroups/stack=tcp/protocol=MFC:add()
/subsystem=jgroups/stack=tcp/protocol=FRAG2:add()
/subsystem=jgroups/channel=ee:write-attribute(name=stack, value=tcp)
/subsystem=jgroups/stack=udp:remove()
/socket-binding-group=standard-sockets/socket-binding=jgroups-mping:remove()
/interface=private:write-attribute(name=nic, value=eth0)
/interface=private:undefine-attribute(name=inet-address)
custom: ""
## Hostnames.
## Must be provided if Ingress is enabled.
##
hostname: keycloak.example.com
## TLS configuration.
## Secrets must be manually created in the namespace.
##
# tls:
# - secretName: keycloak.example.com-tls
# hosts:
# - keycloak.example.com
#
#
service:
# annotations:
# service.beta.kubernetes.io/aws-load-balancer-internal: "0.0.0.0/0"
# labels:
# key: value
annotations: {}
# service.beta.kubernetes.io/aws-load-balancer-internal: "0.0.0.0/0"
labels: {}
# key: value
## ServiceType
## ref: https://kubernetes.io/docs/user-guide/services/#publishing-services---service-types
##
type: ClusterIP
## nodePort port number
## Is not required, but allows for static port assignment with
## serviceType NodePort.
## Default: nil
## Optional static port assignment for service type NodePort.
# nodePort: 30000
port: 80
## Ingress configuration.
## ref: https://kubernetes.io/docs/user-guide/ingress/
ingress:
enabled: false
path: /
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
# ingress.kubernetes.io/affinity: cookie
## List of hosts for the ingress
hosts:
- keycloak.example.com
## TLS configuration
tls:
enabled: false
existingSecret: ""
secretContents: {}
secretAnnotations: {}
## Persistence configuration
persistence:
# If true, the Postgres chart is deployed
deployPostgres: true
## The following values only apply if "deployPostgres" is set to "false"
# Specifies an existing secret to be used for the database password
existingSecret: ""
# The key in the existing secret that stores the password
existingSecretKey: password
# The database vendor. Can be either "POSTGRES" or "MYSQL"
dbVendor: H2
dbName: keycloak
dbHost: mykeycloak
dbPort: 5432
dbUser: keycloak
# Only used if no existing secret is specified. In this case a new secret is created
dbPassword: keycloak
postgresql:
### PostgreSQL User to create.
##
@@ -92,7 +169,7 @@ postgresql:
## PostgreSQL Password for the new user.
## If not set, a random 10 characters password will be used.
##
# postgresPassword: keycloak
postgresPassword: ""
## PostgreSQL Database to create.
##
@@ -105,3 +182,9 @@ postgresql:
## Enable PostgreSQL persistence using Persistent Volume Claims.
##
enabled: true
test:
image:
repository: unguiculus/docker-python3-phantomjs-selenium
tag: v1
pullPolicy: IfNotPresent