Galera behind NAT (mixed environment in k8s and docker)
Database plays an important role in the application development. To have a redundant and high available database, we can use Galera for mysql/mariadb. With Galera, the database can have replication across different server and the load can be divided into some servers. By that capability, we can have a reliable backend database for our application.
In this tutorial, we will deploy galera cluster inside kubernetes and docker. In normal circumstance, when we install galera inside kubernetes, the replication communication is using internal network. So we need to set galera to use external network so that galera cluster can communicate with mariadb outside the kubernetes. With this setup, we can combine galera node located in kubernetes, docker, private or public cloud.
In kubernetes, we need to bind the pod with the worker node so that the incoming and outgoing traffic delivered from the same node. Below is the illustration for the networking diagram.
Deployment in Kubernetes
First we need to create service.yaml, secret.yaml, configmap.yaml and statefulsets.yaml. Below is the service.yaml, this service will be used in statefulsets configuration.
apiVersion: v1 kind: Service metadata: name: db-headless namespace: database spec: clusterIP: None clusterIPs: - None internalTrafficPolicy: Cluster ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - name: mysql port: 3306 protocol: TCP targetPort: mysql selector: app.kubernetes.io/name: mix-mariadb-galera sessionAffinity: None type: ClusterIP status: loadBalancer: {}
apiVersion: v1 data: my.cnf: | [client] port=3306 socket=/opt/bitnami/mariadb/tmp/mysql.sock plugin_dir=/opt/bitnami/mariadb/plugin [mysqld] default_storage_engine=InnoDB basedir=/opt/bitnami/mariadb datadir=/bitnami/mariadb/data plugin_dir=/opt/bitnami/mariadb/plugin tmpdir=/opt/bitnami/mariadb/tmp socket=/opt/bitnami/mariadb/tmp/mysql.sock pid_file=/opt/bitnami/mariadb/tmp/mysqld.pid bind_address=0.0.0.0 ## Character set ## collation_server=utf8_unicode_ci init_connect='SET NAMES utf8' character_set_server=utf8 ## MyISAM ## key_buffer_size=32M myisam_recover_options=FORCE,BACKUP ## Safety ## skip_host_cache skip_name_resolve max_allowed_packet=16M max_connect_errors=1000000 sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY sysdate_is_now=1 ## Binary Logging ## log_bin=mysql-bin expire_logs_days=14 # Disabling for performance per http://severalnines.com/blog/9-tips-going-production-galera-cluster-mysql sync_binlog=0 # Required for Galera binlog_format=row ## Caches and Limits ## tmp_table_size=32M max_heap_table_size=32M # Re-enabling as now works with Maria 10.1.2 query_cache_type=1 query_cache_limit=4M query_cache_size=256M max_connections=500 thread_cache_size=50 open_files_limit=65535 table_definition_cache=4096 table_open_cache=4096 ## InnoDB ## innodb=FORCE innodb_strict_mode=1 # Mandatory per https://github.com/codership/documentation/issues/25 innodb_autoinc_lock_mode=2 # Per https://www.percona.com/blog/2006/08/04/innodb-double-write/ innodb_doublewrite=1 innodb_flush_method=O_DIRECT innodb_log_files_in_group=2 innodb_log_file_size=128M innodb_flush_log_at_trx_commit=1 innodb_file_per_table=1 # 80% Memory is default reco. # Need to re-evaluate when DB size grows innodb_buffer_pool_size=2G innodb_file_format=Barracuda ## Logging ## log_error=/opt/bitnami/mariadb/logs/mysqld.log slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log log_queries_not_using_indexes=1 slow_query_log=1 ## SSL ## Use extraVolumes and extraVolumeMounts to mount /certs filesystem # ssl_ca=/certs/ca.pem # ssl_cert=/certs/server-cert.pem # ssl_key=/certs/server-key.pem [galera] wsrep_on=ON wsrep_provider=/opt/bitnami/mariadb/lib/libgalera_smm.so wsrep_sst_method=mariabackup wsrep_slave_threads=4 wsrep_cluster_address=gcomm:// wsrep_cluster_name=galera wsrep_sst_auth="root:" # Enabled for performance per https://mariadb.com/kb/en/innodb-system-variables/#innodb_flush_log_at_trx_commit innodb_flush_log_at_trx_commit=2 # MYISAM REPLICATION SUPPORT # wsrep_replicate_myisam=ON [mariadb] plugin_load_add=auth_pam kind: ConfigMap metadata: labels: app.kubernetes.io/instance: galera-db app.kubernetes.io/name: mariadb-galera name: mariadb-galera-configuration namespace: database
apiVersion: v1 data: mariadb-galera-mariabackup-password: cGFzc1dPUkQK mariadb-password: cGFzc1dPUkQK mariadb-root-password: cGFzc1dPUkQK kind: Secret metadata: labels: app.kubernetes.io/instance: galera-db app.kubernetes.io/name: mariadb-galera name: secret-mariadb-galera namespace: database type: Opaque
apiVersion: apps/v1 kind: StatefulSet metadata: labels: app.kubernetes.io/instance: INS_NAME app.kubernetes.io/name: mix-mariadb-galera name: APP_NAME namespace: database spec: podManagementPolicy: OrderedReady replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app.kubernetes.io/instance: INS_NAME app.kubernetes.io/name: mix-mariadb-galera template: metadata: creationTimestamp: null labels: app.kubernetes.io/instance: INS_NAME app.kubernetes.io/name: mix-mariadb-galera spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchLabels: app.kubernetes.io/instance: INS_NAME app.kubernetes.io/name: mix-mariadb-galera namespaces: - database topologyKey: kubernetes.io/hostname weight: 1 containers: - command: - bash - -ec - | exec /opt/bitnami/scripts/mariadb-galera/entrypoint.sh /opt/bitnami/scripts/mariadb-galera/run.sh env: - name: MY_POD_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.name - name: BITNAMI_DEBUG value: "false" - name: MARIADB_DEFAULT_PORT_NUMBER value: "3306" - name: MARIADB_GALERA_CLUSTER_NAME value: galera - name: MARIADB_GALERA_CLUSTER_ADDRESS value: gcomm://192.168.1.1:30002,192.168.1.2:30002,192.168.2.1:30002 - name: MARIADB_ROOT_USER value: root - name: MARIADB_ROOT_PASSWORD valueFrom: secretKeyRef: key: mariadb-root-password name: secret-mariadb-galera - name: MARIADB_DATABASE value: my_database - name: MARIADB_GALERA_MARIABACKUP_USER value: mariabackup - name: MARIADB_GALERA_MARIABACKUP_PASSWORD valueFrom: secretKeyRef: key: mariadb-galera-mariabackup-password name: secret-mariadb-galera - name: MARIADB_ENABLE_LDAP value: "no" - name: MARIADB_EXTRA_FLAGS value: --wsrep_provider_options=ist.recv_addr=NODE_IP:IST_PORT;ist.recv_bind=0.0.0.0:IST_PORT --wsrep_sst_receive_address=NODE_IP:SST_PORT --wsrep_node_incoming_address=NODE_IP - name: MARIADB_ENABLE_TLS value: "no" - name: MARIADB_GALERA_NODE_ADDRESS value: NODE_IP:GALERA_PORT image: docker.io/bitnami/mariadb-galera:10.6.7-debian-10-r17 imagePullPolicy: IfNotPresent name: mariadb-galera ports: - containerPort: 3306 hostIP: NODE_IP hostPort: MYSQL_PORT name: mysql protocol: TCP - containerPort: GALERA_PORT hostIP: NODE_IP hostPort: GALERA_PORT name: galera protocol: TCP - containerPort: IST_PORT hostIP: NODE_IP hostPort: IST_PORT name: ist protocol: TCP - containerPort: SST_PORT hostIP: NODE_IP hostPort: SST_PORT name: sst protocol: TCP - containerPort: GALERA_PORT hostIP: NODE_IP hostPort: GALERA_PORT name: galera-udp protocol: UDP readinessProbe: exec: command: - bash - -ec - | password_aux="${MARIADB_ROOT_PASSWORD:-}" if [[ -f "${MARIADB_ROOT_PASSWORD_FILE:-}" ]]; then password_aux=$(cat "$MARIADB_ROOT_PASSWORD_FILE") fi exec mysqladmin status -u"${MARIADB_ROOT_USER}" -p"${password_aux}" failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 livenessProbe: exec: command: - bash - '-ec' - > password_aux="${MARIADB_ROOT_PASSWORD:-}" if [[ -f "${MARIADB_ROOT_PASSWORD_FILE:-}" ]]; then password_aux=$(cat "$MARIADB_ROOT_PASSWORD_FILE") fi exec mysql -u"${MARIADB_ROOT_USER}" -p"${password_aux}" -e "select * from mysql.wsrep_cluster_members;" failureThreshold: 30 initialDelaySeconds: 320 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 3 resources: {} securityContext: runAsNonRoot: true runAsUser: 1001 terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /opt/bitnami/mariadb/.bootstrap name: previous-boot - mountPath: /bitnami/mariadb name: data - mountPath: /bitnami/conf/my.cnf name: mariadb-galera-config subPath: my.cnf dnsPolicy: ClusterFirst nodeName: NODE_NAME restartPolicy: Always schedulerName: default-scheduler securityContext: fsGroup: 1001 runAsUser: 1001 serviceAccount: default serviceAccountName: default terminationGracePeriodSeconds: 30 volumes: - emptyDir: {} name: previous-boot - configMap: defaultMode: 420 name: mariadb-galera-configuration name: mariadb-galera-config serviceName: db-headless updateStrategy: type: RollingUpdate volumeClaimTemplates: - apiVersion: v1 kind: PersistentVolumeClaim metadata: creationTimestamp: null labels: app.kubernetes.io/instance: INS_NAME app.kubernetes.io/name: mix-mariadb-galera name: data spec: accessModes: - ReadWriteOnce resources: requests: storage: 200Gi volumeMode: Filesystem status: phase: Pending
kubectl apply -f service.yaml kubectl apply -f secret.yaml kubectl apply -f configmap.yaml
#!/bin/bash INS_NAME=$1 APP_NAME=${INS_NAME}-mariadb-galera NODE_IP=$2 MYSQL_PORT=30001 GALERA_PORT=30002 IST_PORT=30003 SST_PORT=30004 NODE_NAME=$3 cat statefulsets.yaml | sed "s/APP_NAME/${APP_NAME}/g" | sed "s/NODE_IP/${NODE_IP}/g" | sed "s/GALERA_PORT/${GALERA_PORT}/g" | sed "s/IST_PORT/${IST_PORT}/g" | sed "s/SST_PORT/${SST_PORT}/g" | sed "s/NODE_NAME/${NODE_NAME}/g" | sed "s/INS_NAME/${INS_NAME}/g" | sed "s/MYSQL_PORT/${MYSQL_PORT}/g" | kubectl apply --validate=false -f -
./deploy.sh db1 192.168.1.1 k8s_node_1
./deploy.sh db2 192.168.1.2 k8s_node_2
Deployment using Docker
docker run -d --name db3-mariadb-galera-0 \ -e MARIADB_GALERA_CLUSTER_NAME=galera \ -e MARIADB_GALERA_CLUSTER_ADDRESS=gcomm://192.168.1.1:30002,192.168.1.2:30002,192.168.2.1:30002 \
-e MARIADB_GALERA_MARIABACKUP_PASSWORD=passWORD \ -e MARIADB_ROOT_PASSWORD=passWORD \ -e MARIADB_GALERA_NODE_NAME=db3-mariadb-galera-0 \ -e MARIADB_REPLICATION_PASSWORD=passWORD \ -e MARIADB_GALERA_NODE_ADDRESS=192.168.2.1:30002 \
-e MARIADB_EXTRA_FLAGS="--wsrep_provider_options=ist.recv_addr=192.168.2.1:30003;ist.recv_bind=0.0.0.0:30003 --wsrep_node_incoming_address=192.168.2.1 --wsrep_sst_receive_address=192.168.2.1:30004" \
-p 30001:3306 -p 30004:30004 -p 30002:30002/tcp -p 30002:30002/udp -p 30003:30003 \
-v /var/lib/mysql:/bitnami/mariadb \ --health-cmd='mysql -uroot -ppassWORD -e "select * from mysql.wsrep_cluster_members;" || exit 1' \
--health-timeout=10s \ --health-retries=30 \ --health-interval=10s \ --restart always \ bitnami/mariadb-galera:latest
Finishing touch
Access the Database
mysql -uroot -ppassWORD -h 192.168.1.1 -P 30001 -e "select * from mysql.wsrep_cluster_members;"
Comments