kadet-hub logokadet-hub

MySQL on Kubernetes without Bitnami, with Kapitan

MySQL Community Server, in place of Bitnami's mysql chart (standalone architecture): one StatefulSet with the database "app", owned by the user "app". MySQL Community Server is GPL-2.0; the image is Oracle's, from the MySQL team. The root and app passwords are Kapitan refs, generated on the first compile into refs/mysql/; compile again with --reveal, or apply through Tesoro, to get the values into the Secret. The base64 backend keeps the recipe self-contained and encrypts nothing: in a real project use gpg or a KMS backend. The data sits in a subdirectory of the volume because mysqld --initialize refuses a directory with lost+found in it. For InnoDB Cluster replication, Oracle's MySQL Operator for Kubernetes (UPL-1.0) has no kadet-hub generator yet.

Inventory class

inventory/classes/recipes/mysql.yml, as on the main branch of kadet-hub/examples. A target includes it next to the pins below.

# MySQL Community Server, in place of Bitnami's mysql chart (standalone
# architecture): one StatefulSet with the database "app", owned by the user
# "app". MySQL Community Server is GPL-2.0; the image is Oracle's, from the
# MySQL team. The root and app passwords are Kapitan refs, generated on the
# first compile into refs/mysql/; compile again with --reveal, or apply
# through Tesoro, to get the values into the Secret. The base64 backend
# keeps the recipe self-contained and encrypts nothing: in a real project
# use gpg or a KMS backend. The data sits in a subdirectory of the volume
# because mysqld --initialize refuses a directory with lost+found in it.
# For InnoDB Cluster replication, Oracle's MySQL Operator for Kubernetes
# (UPL-1.0) has no kadet-hub generator yet.
classes:
  - recipes.lib.kubernetes

parameters:
  generators:
    kubernetes:
      namespaces:
        mysql: {}
  components:
    mysql:
      namespace: mysql
      type: statefulset
      image: container-registry.oracle.com/mysql/community-server:9.7.3
      security_profile: restricted
      workload_security_context:
        runAsUser: 27
        runAsGroup: 27
        fsGroup: 27
      env:
        MYSQL_DATABASE: app
        MYSQL_USER: app
        MYSQL_PASSWORD:
          secretKeyRef:
            key: password
        MYSQL_ROOT_PASSWORD:
          secretKeyRef:
            key: root-password
      secrets:
        auth:
          string_data:
            password:
              value: ?{base64:mysql/password||random:str:32}
            root-password:
              value: ?{base64:mysql/root-password||random:str:32}
      ports:
        mysql:
          container_port: 3306
          service_port: 3306
      # The entrypoint initializes the data directory with networking off,
      # so the port opens only once the server is ready.
      healthcheck:
        startup:
          type: tcp
          port: mysql
          failure_threshold: 30
        readiness:
          type: tcp
          port: mysql
        liveness:
          type: tcp
          port: mysql
      resources:
        requests: {cpu: 100m, memory: 512Mi}
        limits: {memory: 1Gi}
      volume_mounts:
        data:
          mount_path: /var/lib/mysql
          sub_path: mysql
      volume_claims:
        data:
          spec:
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 8Gi
      service:
        type: ClusterIP

Compile classes

The class includes one per generator; each compiles its generator once, so a target can combine several recipes.

inventory/classes/recipes/lib/kubernetes.yml

# Compiles the kubernetes generator; recipes include this class, so a target that
# combines several recipes compiles it once.
parameters:
  kapitan:
    compile:
      - {input_type: kadet, output_type: yml, input_paths: [lib/kubernetes], output_path: kubernetes/}

Generator pins

The entries of inventory/classes/generators.yml for these generators, pinned by version and digest.

parameters:
  kapitan:
    dependencies:
    - output_path: lib/kubernetes
      source: ghcr.io/kadet-hub/kubernetes:1.2.0@sha256:52017626e77dd5bf7293ce465ff0c3dc7a61dae1ae8353a56a53ffebe1bfb67c
      type: oci

How it is checked

The examples CI verifies the signed index and the attestation of every pinned generator, compiles the target, validates the output against the generators' schemas, deploys it to a new kind cluster with the operators above and fails unless the StatefulSet is ready and select 1 runs as the app user in its own database. The Chainsaw test holds the assertions.

All recipes · Bitnami chart alternatives