apiVersion: v1 kind: Service metadata: name: wrestlingdev-mariadb labels: app: wrestlingdev spec: ports: - port: 3306 selector: app: wrestlingdev tier: mariadb clusterIP: None --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wrestlingdev-mariadb-pv-claim labels: app: wrestlingdev spec: # storageClassName: standard accessModes: - ReadWriteOnce resources: requests: storage: 20Gi --- apiVersion: apps/v1 kind: StatefulSet metadata: name: wrestlingdev-mariadb labels: app: wrestlingdev spec: replicas: 1 serviceName: wrestlingdev-mariadb selector: matchLabels: app: wrestlingdev updateStrategy: type: RollingUpdate template: metadata: labels: app: wrestlingdev tier: mariadb annotations: prometheus.io/port: "9125" prometheus.io/scrape: "true" spec: initContainers: - name: bootstrap image: mariadb:10.3 env: - name: MARIADB_ROOT_PASSWORD valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbpassword - name: MASTER_HOST valueFrom: secretKeyRef: name: wrestlingdev-secrets key: replication_host command: - bash - -c - | if [ -d /var/lib/mysql/mysql ]; then echo "Data directory already initialized, skipping bootstrap" exit 0 fi echo "Fresh data directory — bootstrapping replica from ${MASTER_HOST}" DBS=$(mysql --protocol=TCP -h "$MASTER_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" \ -e "SHOW DATABASES;" --skip-column-names \ | grep -Ev '^(information_schema|performance_schema|mysql|sys)$' \ | tr '\n' ' ') echo "Dumping databases: ${DBS}" { echo "SET SESSION sql_log_bin=0;" mysqldump --protocol=TCP -h "$MASTER_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" \ --single-transaction --master-data=2 --gtid --databases $DBS } > /docker-entrypoint-initdb.d/dump.sql echo "Bootstrap dump complete" volumeMounts: - name: wrestlingdev-mariadb-persistent-storage mountPath: /var/lib/mysql - name: init-scripts mountPath: /docker-entrypoint-initdb.d containers: - image: mariadb:10.3 name: mariadb env: - name: MARIADB_ROOT_PASSWORD valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbpassword - name: MASTER_HOST valueFrom: secretKeyRef: name: wrestlingdev-secrets key: replication_host - name: MYSQL_REPLICATION_USER valueFrom: secretKeyRef: name: wrestlingdev-secrets key: replication_user - name: MYSQL_REPLICATION_PASSWORD valueFrom: secretKeyRef: name: wrestlingdev-secrets key: replication_password lifecycle: postStart: exec: command: - bash - -c - | for i in $(seq 1 60); do mysqladmin ping -uroot -p"$MARIADB_ROOT_PASSWORD" --protocol=TCP -h 127.0.0.1 --silent && break sleep 2 done SLAVE_STATUS=$(mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "SHOW SLAVE STATUS\G" 2>/dev/null) SLAVE_IO=$(echo "$SLAVE_STATUS" | grep -m1 "Slave_IO_Running" | awk '{print $2}') SLAVE_SQL=$(echo "$SLAVE_STATUS" | grep -m1 "Slave_SQL_Running" | awk '{print $2}') if [ "${SLAVE_IO}" = "Yes" ] && [ "${SLAVE_SQL}" = "Yes" ]; then echo "Replication is already running" exit 0 fi mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "STOP SLAVE; RESET SLAVE ALL;" if [ -f /docker-entrypoint-initdb.d/dump.sql ]; then GTID_POS=$(grep -m1 "SET GLOBAL gtid_slave_pos" /docker-entrypoint-initdb.d/dump.sql | sed "s/.*gtid_slave_pos='\([^']*\)'.*/\1/") echo "Setting gtid_slave_pos from dump: '${GTID_POS}'" mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "SET GLOBAL gtid_slave_pos='${GTID_POS}';" fi mysql -uroot -p"$MARIADB_ROOT_PASSWORD" \ -e "CHANGE MASTER TO MASTER_HOST='${MASTER_HOST}', MASTER_USER='${MYSQL_REPLICATION_USER}', MASTER_PASSWORD='${MYSQL_REPLICATION_PASSWORD}', MASTER_USE_GTID=slave_pos;" \ -e "START SLAVE;" ports: - containerPort: 3306 name: mariadb volumeMounts: - name: wrestlingdev-mariadb-persistent-storage mountPath: /var/lib/mysql - name: mysettings-config-volume mountPath: /etc/mysql/mariadb.conf.d - name: init-scripts mountPath: /docker-entrypoint-initdb.d # resources: # limits: # memory: "512Mi" # requests: # memory: "256Mi" # cpu: "0.2" - image: jcwimer/mariadb-rclone-backup-docker:10.3 name: mariadb-backup env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbpassword - name: CRON_SCHEDULE value: "*/5 * * * *" # every 5 minutes - name: DB_USERNAME valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbusername - name: DB_HOST value: "127.0.0.1" - name: DAYS_TO_KEEP value: "7" - name: RCLONE_TYPE valueFrom: secretKeyRef: name: wrestlingdev-secrets key: rclone_type - name: S3_ACCESS_ID valueFrom: secretKeyRef: name: wrestlingdev-secrets key: s3_access_id - name: S3_ACCESS_KEY valueFrom: secretKeyRef: name: wrestlingdev-secrets key: s3_access_key - name: S3_ENDPOINT valueFrom: secretKeyRef: name: wrestlingdev-secrets key: s3_endpoint - name: S3_REGION valueFrom: secretKeyRef: name: wrestlingdev-secrets key: s3_region - name: RCLONE_EXTRA_ARGS valueFrom: secretKeyRef: name: wrestlingdev-secrets key: rclone_extra_args - name: RCLONE_PATH valueFrom: secretKeyRef: name: wrestlingdev-secrets key: rclone_path volumeMounts: - name: wrestlingdev-mariadb-persistent-storage mountPath: /var/lib/mysql # resources: # limits: # memory: "100Mi" # requests: # memory: "50Mi" # cpu: "0.1" - image: prom/mysqld-exporter:v0.11.0 name: mariadb-exporter ports: - containerPort: 9125 name: "http" args: - --web.listen-address=0.0.0.0:9125 - --web.telemetry-path=/metrics - --collect.heartbeat - --collect.heartbeat.database=sys_operator env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbpassword - name: DB_USERNAME valueFrom: secretKeyRef: name: wrestlingdev-secrets key: dbusername - name: DB_HOST value: "127.0.0.1:3306" - name: DATA_SOURCE_NAME value: $(DB_USERNAME):$(DB_PASSWORD)@($(DB_HOST))/ - name: DAYS_TO_KEEP value: "7" # resources: # limits: # memory: "128Mi" # requests: # memory: "32Mi" # cpu: "10m" livenessProbe: httpGet: path: /metrics port: 9125 initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 30 volumes: - name: wrestlingdev-mariadb-persistent-storage persistentVolumeClaim: claimName: wrestlingdev-mariadb-pv-claim - name: mysettings-config-volume configMap: name: mariadb-mysettings - name: init-scripts emptyDir: {} --- apiVersion: v1 kind: ConfigMap metadata: name: mariadb-mysettings labels: kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: EnsureExists data: 70-mysettings.cnf: | [mariadb] # Slow query log — records queries taking longer than long_query_time seconds slow_query_log=1 #slow_query_log_file=/var/log/mariadb/slow.log slow_query_log_file=/var/lib/mysql/slow.log long_query_time=0.2 # mysqltunner recommendations # Max size for in-memory temp tables before spilling to disk tmp_table_size=32M max_heap_table_size=32M # Collect detailed query/table statistics (required by some monitoring tools) performance_schema=ON # Size of each InnoDB redo log file; increase for write-heavy workloads innodb_log_file_size=32M # Number of open table handles to cache; reduces overhead of reopening tables table_open_cache=4000 # Replication (replica) # Must be unique and different from the master's server_id server_id=2 # Keep replica-local binlogged writes in a separate GTID domain from the master gtid_domain_id=2 # Enable binary logging on the replica (required for log_slave_updates) log_bin=mysql-bin # ROW format is safest: records exact row changes rather than SQL statements binlog_format=ROW # Write replicated events into this replica's own binlog (needed for chained replicas) log_slave_updates=ON # Enforce GTID consistency — rejects transactions that would break GTID sequences gtid_strict_mode=ON # Flush binlog to disk on every commit; prevents binlog loss on crash sync_binlog=1 # Prevent accidental writes directly to the replica read_only=1 # How many days to retain binary logs before automatic purge expire_logs_days=7 # Only replicate the application database — rails-specific: excludes the solid_queue DB so # background job workers can run independently on the replica cluster replicate-do-db=wrestlingdev # replicate-ignore-db=wrestlingtourney-queue # /etc/mysql/mariadb.conf.d/70-mysettings.cnf