0
votes

I'm trying to get Helm Keycloak chart to work with Postgres database. I would like my data to persist during the uninstalls. I've created a /mnt/disks/vol1/postgresql mount. When I run this command: helm install keycloak -f keycloak.yaml codecentric/keycloak

Postgres database is created in the mount directory. The problem is, it doesn't save any of the my updated data in Keycloak like adding a new realm, etc. Next time I come back after doing "helm delete keycloak" and installing the chart again, all the data is gone. What am I doing wrong? Here are my files:

keycloak.yaml

init:
  image:
    repository: busybox
    tag: 1.31
    pullPolicy: IfNotPresent
  resources: {}
    # limits:
    #   cpu: "10m"
    #   memory: "32Mi"
    # requests:
    #   cpu: "10m"
  #   memory: "32Mi"

clusterDomain: cluster.local

## Optionally override the fully qualified name
# fullnameOverride: keycloak

## Optionally override the name
# nameOverride: keycloak

keycloak:
  replicas: 1
  image:
    repository: jboss/keycloak
    tag: 8.0.1
    pullPolicy: IfNotPresent

    ## Optionally specify an array of imagePullSecrets.
    ## Secrets must be manually created in the namespace.
    ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
    ##
    pullSecrets: []
    # - myRegistrKeySecretName

  hostAliases: []
  #  - ip: "1.2.3.4"
  #    hostnames:
  #      - "my.host.com"

  proxyAddressForwarding: true

  enableServiceLinks: false

  podManagementPolicy: Parallel

  restartPolicy: Always

  serviceAccount:
    # Specifies whether a service account should be created
    create: false
    # The name of the service account to use.
    # If not set and create is true, a name is generated using the fullname template
    name:

  securityContext:
    fsGroup: 1000

  containerSecurityContext:
    runAsUser: 1000
    runAsNonRoot: true

  ## The path keycloak will be served from. To serve keycloak from the root path, use two quotes (e.g. "").
  basepath: "auth"


  ## Additional sidecar containers, e. g. for a database proxy, such as Google's cloudsql-proxy
  extraContainers: |

  ## lifecycleHooks defines the container lifecycle hooks
  lifecycleHooks: |
    # postStart:
    #   exec:
    #     command: ["/bin/sh", "-c", "ls"]

  ## Override the default for the Keycloak container, e.g. for clusters with large cache that requires rebalancing.
  terminationGracePeriodSeconds: 60

  ## Additional arguments to start command e.g. -Dkeycloak.import= to load a realm
  extraArgs: ""

  ## Username for the initial Keycloak admin user
  username: admin

  ## Password for the initial Keycloak admin user. Applicable only if existingSecret is not set.
  ## If not set, a random 10 characters password will be used
  password: "aaaa"

  # Specifies an existing secret to be used for the admin password
  existingSecret: ""

  # The key in the existing secret that stores the password
  existingSecretKey: password

  ## jGroups configuration (only for HA deployment)
  jgroups:
    discoveryProtocol: dns.DNS_PING
    discoveryProperties: >
      "dns_query={{ template "keycloak.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}"

  javaToolOptions: >-
    -XX:+UseContainerSupport
    -XX:MaxRAMPercentage=50.0

  ## Allows the specification of additional environment variables for Keycloak
  extraEnv: |
    - name: KEYCLOAK_LOGLEVEL
      value: DEBUG
    # - name: WILDFLY_LOGLEVEL
    #   value: DEBUG
    # - name: CACHE_OWNERS
    #   value: "2"
    # - name: DB_QUERY_TIMEOUT
    #   value: "60"
    # - name: DB_VALIDATE_ON_MATCH
    #   value: true
    # - name: DB_USE_CAST_FAIL
    #   value: false

  affinity: |
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              {{- include "keycloak.selectorLabels" . | nindent 10 }}
            matchExpressions:
              - key: role
                operator: NotIn
                values:
                  - test
          topologyKey: kubernetes.io/hostname
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchLabels:
                {{- include "keycloak.selectorLabels" . | nindent 12 }}
              matchExpressions:
                - key: role
                  operator: NotIn
                  values:
                    - test
            topologyKey: failure-domain.beta.kubernetes.io/zone

  nodeSelector: {}
  priorityClassName: ""
  tolerations: []

  ## Additional pod labels
  ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
  podLabels: {}

  ## Extra Annotations to be added to pod
  podAnnotations: {}

  livenessProbe: |
    httpGet:
      path: {{ if ne .Values.keycloak.basepath "" }}/{{ .Values.keycloak.basepath }}{{ end }}/
      port: http
    initialDelaySeconds: 300
    timeoutSeconds: 5
  readinessProbe: |
    httpGet:
      path: {{ if ne .Values.keycloak.basepath "" }}/{{ .Values.keycloak.basepath }}{{ end }}/realms/master
      port: http
    initialDelaySeconds: 30
    timeoutSeconds: 1

  resources: {}
    # limits:
    #   cpu: "100m"
    #   memory: "1024Mi"
    # requests:
    #   cpu: "100m"
  #   memory: "1024Mi"

  ## WildFly CLI configurations. They all end up in the file 'keycloak.cli' configured in the configmap which is
  ## executed on server startup.
  cli:
    enabled: true
    nodeIdentifier: |
      {{ .Files.Get "scripts/node-identifier.cli" }}

    logging: |
      {{ .Files.Get "scripts/logging.cli" }}

    ha: |
      {{ .Files.Get "scripts/ha.cli" }}

    datasource: |
      {{ .Files.Get "scripts/datasource.cli" }}

    # Custom CLI script
    custom: |

  ## Custom startup scripts to run before Keycloak starts up
  startupScripts: {}
    # mystartup.sh: |
    #   #!/bin/sh
    #
  #   echo 'Hello from my custom startup script!'

  ## Additional init containers, e. g. for providing custom themes
  extraInitContainers: |

  ## Add additional volumes and mounts, e. g. for custom themes
  extraVolumeMounts: |
   - name: theme
     mountPath: /opt/jboss/keycloak/themes/
     subPath: kcthemes
  extraVolumes: |
   - name: theme
     persistentVolumeClaim:
      claimName: keycloak-pv-claim

  ## Add additional ports, eg. for custom admin console
  extraPorts: |

  podDisruptionBudget: {}
    # maxUnavailable: 1
  # minAvailable: 1

  service:
    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

    ## Optional static port assignment for service type NodePort.
    # nodePort: 30000

    httpPort: 8080
    httpNodePort: ""

    httpsPort: 8443
    httpsNodePort: ""

    # Optional: jGroups port for high availability clustering
    jgroupsPort: 7600

  ## Ingress configuration.
  ## ref: https://kubernetes.io/docs/user-guide/ingress/
  ingress:
    enabled: true
    path: /

    annotations: {}
      # kubernetes.io/ingress.class: nginx
      # kubernetes.io/tls-acme: "true"
    # ingress.kubernetes.io/affinity: cookie

    labels: {}
    # key: value

    ## List of hosts for the ingress
    hosts:
      - localhost

    ## TLS configuration
    tls:
     - hosts:
         - localhost
       secretName: keycloak-secret

  ## OpenShift route configuration.
  ## ref: https://docs.openshift.com/container-platform/3.11/architecture/networking/routes.html
  route:
    enabled: false
    path: /

    annotations: {}
      # kubernetes.io/tls-acme: "true"
      # haproxy.router.openshift.io/disable_cookies: "true"
    # haproxy.router.openshift.io/balance: roundrobin

    labels: {}
    # key: value

    # Host name for the route
    host:

    # TLS configuration
    tls:
      enabled: true
      insecureEdgeTerminationPolicy: Redirect
      termination: edge

  ## Persistence configuration
  persistence:
    # If true, the Postgres chart is deployed
    deployPostgres: true

    # The database vendor. Can be either "postgres", "mysql", "mariadb", or "h2"
    dbVendor: postgres

    ## The following values only apply if "deployPostgres" is set to "false"
    dbName: keycloak
    dbHost: keycloak-postgresql
    dbPort: 5432

    ## Database Credentials are loaded from a Secret residing in the same Namespace as keycloak.
    ## The Chart can read credentials from an existing Secret OR it can provision its own Secret.

    ## Specify existing Secret
    # If set, specifies the Name of an existing Secret to read db credentials from.
    existingSecret: ""
    existingSecretPasswordKey: ""  # read keycloak db password from existingSecret under this Key
    existingSecretUsernameKey: ""  # read keycloak db user from existingSecret under this Key

    ## Provision new Secret
    # Only used if existingSecret is not specified. In this case a new secret is created
    # populated by the variables below.
    dbUser: KcSuperUser
    dbPassword: KcSuperPass

postgresql:
  image:
    tag: 12
  ### PostgreSQL User to create.
  ##
  postgresqlUsername: "KcSuperUser"

  ## PostgreSQL Password for the new user.
  ## If not set, a random 10 characters password will be used.
  ##
  postgresqlPassword: "KcSuperPassword"

  ## PostgreSQL Database to create.
  ##
  postgresqlDatabase: keycloak

  ## Persistent Volume Storage configuration.
  ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes
  ##
  persistence:
    ## Enable PostgreSQL persistence using Persistent Volume Claims.
    ##
    enabled: true
    exitingClaim: "pdata-keycloak-postgresql-0"
    mountPath: /var/lib/postgresql/data

classes:
  - name: postgres # Defines name of storage class.
    # Path on the host where local volumes of this storage class are mounted
    # under.
    #hostDir: /mnt/fast-disks
    # Optionally specify mount path of local volumes. By default, we use same
    # path as hostDir in container.
    mountDir: /mnt/disks/vol1/postgresdata
    # The volume mode of created PersistentVolume object. Default to Filesystem
    # if not specified.
    volumeMode: Filesystem
    # Filesystem type to mount.
    # It applies only when the source path is a block device,
    # and desire volume mode is Filesystem.
    # Must be a filesystem type supported by the host operating system.
    fsType: ext4
    blockCleanerCommand:
      #  Do a quick reset of the block device during its cleanup.
      #  - "/scripts/quick_reset.sh"
      #  or use dd to zero out block dev in two iterations by uncommenting these lines
      #  - "/scripts/dd_zero.sh"
      #  - "2"
      # or run shred utility for 2 iteration.s
      - "/scripts/shred.sh"
      - "2"
    # or blkdiscard utility by uncommenting the line below.
    #  - "/scripts/blkdiscard.sh"
    # Uncomment to create storage class object with default configuration.
    # storageClass: true
    # Uncomment to create storage class object and configure it.
    # storageClass:

test:
  enabled: true
  image:
    repository: unguiculus/docker-python3-phantomjs-selenium
    tag: v1
    pullPolicy: IfNotPresent
  securityContext:
    fsGroup: 1000
  containerSecurityContext:
    runAsUser: 1000
    runAsNonRoot: true

prometheus:
  operator:
    ## Are you using Prometheus Operator?
    enabled: false

    serviceMonitor:
      ## Optionally set a target namespace in which to deploy serviceMonitor
      namespace: ""

      ## Additional labels to add to the ServiceMonitor so it is picked up by the operator.
      ## If using the [Helm Chart](https://github.com/helm/charts/tree/master/stable/prometheus-operator) this is the name of the Helm release.
      selector:
        release: prometheus

      ## Interval at which Prometheus scrapes metrics
      interval: 10s

      ## Timeout at which Prometheus timeouts scrape run
      scrapeTimeout: 10s

      ## The path to scrape
      path: /auth/realms/master/metrics

    prometheusRules:
      ## Add Prometheus Rules?
      enabled: false

      ## Additional labels to add to the PrometheusRule so it is picked up by the operator.
      ## If using the [Helm Chart](https://github.com/helm/charts/tree/master/stable/prometheus-operator) this is the name of the Helm release and 'app: prometheus-operator'
      selector:
        app: prometheus-operator
        release: prometheus

      ## Some example rules.
      rules: {}
      #  - alert: keycloak-IngressHigh5xxRate
      #    annotations:
      #      message: The percentage of 5xx errors for keycloak over the last 5 minutes is over 1%.
      #    expr: (sum(rate(nginx_ingress_controller_response_duration_seconds_count{exported_namespace="mynamespace",ingress="mynamespace-keycloak",status=~"5[0-9]{2}"}[1m]))/sum(rate(nginx_ingress_controller_response_duration_seconds_count{exported_namespace="mynamespace",ingress="mynamespace-keycloak"}[1m])))*100 > 1
      #    for: 5m
      #    labels:
      #      severity: warning
      #  - alert: keycloak-IngressHigh5xxRate
      #    annotations:
      #      message: The percentage of 5xx errors for keycloak over the last 5 minutes is over 5%.
      #    expr: (sum(rate(nginx_ingress_controller_response_duration_seconds_count{exported_namespace="mynamespace",ingress="mynamespace-keycloak",status=~"5[0-9]{2}"}[1m]))/sum(rate(nginx_ingress_controller_response_duration_seconds_count{exported_namespace="mynamespace",ingress="mynamespace-keycloak"}[1m])))*100 > 5
      #    for: 5m
      #    labels:
      #      severity: critical

Volume

kind: PersistentVolume
apiVersion: v1
metadata:
  name: keycloak-postgres-volume
  labels:
    app: postgres
    type: local
spec:
  storageClassName: postgres
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/disks/vol1/postgresdata"

Claim:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: data-keycloak-postgresql-0
  labels:
    app: postgres
spec:
  storageClassName: postgres
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Storage

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: postgres
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
1
I am using the latest Helm with Postgres 12 with this chart: github.com/codecentric/helm-charts/tree/master/charts/keycloakgetITdone

1 Answers

0
votes

I got it to work with the following in my values.yaml persistence: ## Enable PostgreSQL persistence using Persistent Volume Claims. ## enabled: true exitingClaim: "pdata-keycloak-postgresql-0" mountPath: /bitnami/postgresql

Somehow, mounthPath of /var/lib/postgresql wasn't working.