如何在K8s中复制MariaDB?

134 阅读3分钟

现在,我们将尝试创建一个复制的statefulset应用程序。作为创建这篇博客的良好参考,我想归功于Kubernetes文档以及阿里云的一个例子。

配置复制

为了复制MariaDB应用程序,我们将创建一个由一个初始容器和一个应用程序容器组成的statefulset。这两个容器都是基于MariaDB镜像的。

init容器将在应用容器之前运行,我们将使用该容器来处理与复制有关的所有设置,主要是为特定的容器类型(主容器或副本)在特定的目录中加载特定文件的卷。在这个过程中使用的文件将从配置图中获得(为此我们需要挂载一个额外的卷),该配置图将容纳必要的主卷和副本的配置文件,这些文件将被挂载在/etc/mysql/conf.d/ ,配置文件的全局目录中,以及带有SQL语句的SQL文件将被挂载在docker-entrypoint-initdb.d ,这些语句将在容器的首次启动中执行。

应用MariaDB容器要使用那些带有微调配置的卷来进行复制,以及一个用于数据目录的持久卷。

配置文件看起来像这样。

# ConfigMap holding information about configuration files for primary/secondary and dockerinit
apiVersion: v1
kind: ConfigMap
metadata:
  name: mariadb-configmap
data:

  primary.cnf: |
    [mariadb]
    log-bin                         # enable binary logging
    log-basename=my-mariadb         # used to be independent of hostname changes (otherwise is in datadir/mysql)

  replica.cnf: |
    [mariadb]
    log-basename=my-mariadb         # used to be independent of hostname changes (otherwise is in datadir/mysql)

  primary.sql: |
    CREATE USER 'repluser'@'%' IDENTIFIED BY 'replsecret';
    GRANT REPLICATION REPLICA ON *.* TO 'repluser'@'%';
    CREATE DATABASE primary_db;

  secondary.sql: |
    # We have to know name of sts (`mariadb-sts`) and 
    # service `mariadb-service` in advance as an FQDN.
    # No need to use master_port
    CHANGE MASTER TO 
    MASTER_HOST='mariadb-sts-0.mariadb-service.default.svc.cluster.local',
    MASTER_USER='repluser',
    MASTER_PASSWORD='replsecret',
    MASTER_CONNECT_RETRY=10;

# Secret holds information about root password
---
apiVersion: v1
kind: Secret
metadata:
    name: mariadb-secret
type: Opaque
data:
  mariadb-root-password: c2VjcmV0 # echo -n 'secret'|base64

# Headless service
---
apiVersion: v1
kind: Service
metadata:
  name: mariadb-service
  labels:
    app: mariadb
spec:
  ports:
  - port: 3306
    name: mariadb-port
  clusterIP: None
  selector:
    app: mariadb

# Statefulset
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb-sts
spec:
  serviceName: "mariadb-service"
  replicas: 3
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      initContainers:
      - name: init-mariadb
        image: mariadb
        imagePullPolicy: Always
        command:
        - bash
        - "-c"
        - |
          set -ex
          echo 'Starting init-mariadb';
          # Check config map to directory that already exists 
          # (but must be used as a volume for main container)
          ls /mnt/config-map
          # Statefulset has sticky identity, number should be last
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          # Copy appropriate conf.d files from config-map to 
          # mariadb-config volume (emptyDir) depending on pod number
          if [[ $ordinal -eq 0 ]]; then
            # This file holds SQL for connecting to primary
            cp /mnt/config-map/primary.cnf /etc/mysql/conf.d/server-id.cnf
            # Create the users needed for replication on primary on a volume
            # initdb (emptyDir)
            cp /mnt/config-map/primary.sql /docker-entrypoint-initdb.d
          else
            # This file holds SQL for connecting to secondary
            cp /mnt/config-map/replica.cnf /etc/mysql/conf.d/server-id.cnf
            # On replicas use secondary configuration on initdb volume
            cp /mnt/config-map/secondary.sql /docker-entrypoint-initdb.d
          fi
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((3000 + $ordinal)) >> etc/mysql/conf.d/server-id.cnf
          ls /etc/mysql/conf.d/
          cat /etc/mysql/conf.d/server-id.cnf
        volumeMounts:
          - name: mariadb-config-map
            mountPath: /mnt/config-map
          - name: mariadb-config
            mountPath: /etc/mysql/conf.d/
          - name: initdb
            mountPath: /docker-entrypoint-initdb.d
      restartPolicy: Always
      containers:
      - name: mariadb
        image: mariadb
        ports:
        - containerPort: 3306
          name: mariadb-port
        env:
        # Using Secrets
        - name: MARIADB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: mariadb-root-password
        - name: MYSQL_INITDB_SKIP_TZINFO
          value: "1"
        # Mount volume from persistent volume claim
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/mysql/
        - name: mariadb-config
          mountPath: /etc/mysql/conf.d/
        - name: initdb
          mountPath: /docker-entrypoint-initdb.d
      volumes:
      - name: mariadb-config-map
        configMap:
          name: mariadb-configmap
          #defaultMode: 0544
      - name: mariadb-config
        emptyDir: {}
      - name: initdb
        emptyDir: {}

  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 300M

测试复制

应用配置文件并观察pod的创建。

$ kubectl apply -f mariadb-sts-replication.yaml
configmap/mariadb-configmap created
secret/mariadb-secret created
service/mariadb-service created
statefulset.apps/mariadb-sts created

$ kubectl get pod -w
NAME            READY   STATUS     RESTARTS   AGE
mariadb-sts-0   1/1     Running    0          14s
mariadb-sts-1   1/1     Running    0          8s
mariadb-sts-2   0/1     Init:0/1   0          2s
mariadb-sts-2   0/1     PodInitializing   0          4s
mariadb-sts-2   1/1     Running           0          6s

要调试特定的pod/container,使用以下命令。

$ kubectl describe pod mariadb-sts-0
$ kubectl logs mariadb-sts-0 -c init-mariadb

在主目录上创建数据

$ kubectl exec -it mariadb-sts-0 -- mariadb -uroot -psecret
Defaulted container "mariadb" out of: mariadb, init-mariadb (init)
MariaDB [primary_db]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| primary_db         |
| sys                |
+--------------------+
5 rows in set (0.000 sec)
MariaDB [primary_db]> create table my_table (t int); insert into my_table values (5),(15),(25);
Query OK, 0 rows affected (0.031 sec)

Query OK, 3 rows affected (0.004 sec)
Records: 3  Duplicates: 0  Warnings: 0

检查复制体上的数据

$ kubectl exec -it mariadb-sts-2 -- mariadb -uroot -psecret
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| primary_db         |
| sys                |
+--------------------+
MariaDB [(none)]> use primary_db;
Database changed
MariaDB [primary_db]> show tables;
+----------------------+
| Tables_in_primary_db |
+----------------------+
| my_table             |
+----------------------+
1 row in set (0.000 sec)

MariaDB [primary_db]> select * from my_table;
+------+
| t    |
+------+
|    5 |
|   15 |
|   25 |
+------+
3 rows in set (0.000 sec)

扩大规模

$ kubectl scale sts mariadb-sts --replicas=4
statefulset.apps/mariadb-sts scaled

$ kubectl get pod -w
NAME            READY   STATUS    RESTARTS   AGE
mariadb-sts-0   1/1     Running   0          2m52s
mariadb-sts-1   1/1     Running   0          2m46s
mariadb-sts-2   1/1     Running   0          2m40s
mariadb-sts-3   0/1     Pending   0          0s
mariadb-sts-3   0/1     Pending   0          0s
mariadb-sts-3   0/1     Pending   0          2s
mariadb-sts-3   0/1     Init:0/1   0          2s
mariadb-sts-3   0/1     PodInitializing   0          5s
mariadb-sts-3   1/1     Running           0          7s

检查新创建的副本

$ kubectl exec -it mariadb-sts-3 -- mariadb -uroot -psecret
MariaDB [(none)]> use primary_db;
MariaDB [primary_db]> show tables;
+----------------------+
| Tables_in_primary_db |
+----------------------+
| my_table             |
+----------------------+
1 row in set (0.000 sec)
MariaDB [primary_db]> select * from my_table;
+------+
| t    |
+------+
|    5 |
|   15 |
|   25 |
+------+
3 rows in set (0.000 sec)

尝试在主服务器上插入新数据

MariaDB [primary_db]> insert into my_table values (40),(45);

检查副本mariadb-sts-3

MariaDB [primary_db]> select * from my_table;
+------+
| t    |
+------+
|    5 |
|   15 |
|   25 |
|   40 |
|   45 |
+------+
5 rows in set (0.000 sec)

我们也可以检查PVC

$ kubectl get pvc
NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
datadir-mariadb-sts-0   Bound    pvc-c1676027-6c75-473b-9b46-3d9a7d370fdc   300M       RWO            standard       31m
datadir-mariadb-sts-1   Bound    pvc-7f969265-3d8f-4677-950b-271ea670321e   300M       RWO            standard       30m
datadir-mariadb-sts-2   Bound    pvc-d116e494-5078-46ec-abcb-864fa8ae6b59   300M       RWO            standard       30m
datadir-mariadb-sts-3   Bound    pvc-00aca08a-7a5e-459a-8ae1-a854c4171a27   300M       RWO            standard       28m

缩小规模

$ kubectl scale sts mariadb-sts --replicas=2
statefulset.apps/mariadb-sts scaled

$ kubectl get pod -w
mariadb-sts-3   1/1     Terminating       0          78s
mariadb-sts-3   0/1     Terminating       0          79s
mariadb-sts-3   0/1     Terminating       0          79s
mariadb-sts-3   0/1     Terminating       0          79s
mariadb-sts-2   1/1     Terminating       0          4m4s
mariadb-sts-2   0/1     Terminating       0          4m5s
mariadb-sts-2   0/1     Terminating       0          4m5s
mariadb-sts-2   0/1     Terminating       0          4m5s

请注意,缩减规模不会删除PVC。

总结

该博客展示了如何在K8s中执行MariaDB复制。欢迎大家在Zulip上讨论这个问题。