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.
- Replaces the Bitnami charts:
mysql - Generators: kubernetes
- Operators: none
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.