k8s mysql master-slave replication (with password) ubuntu arm kernel

Install NFS service

## Installation
sudo apt install nfs-kernel-server
## start up
sudo /etc/init.d/nfs-kernel-server start
## Create a directory
mkdir /data/nfs
sudo chmod -R 777 /data/nfs
## Share
nano /etc/exports
## Add to end of file
/var/nfs *(insecure,rw,sync,no_root_squash)
## Restart
sudo /etc/init.d/nfs-kernel-server restart

Install Helm

## Installation
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
## Add and modify the ip to the server's internal network ip
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=x.x.x.x \
    --set nfs.path=/data/nfs

kubectl apply -f mysql.yaml

# mysql secret key
apiVersion: v1
Kind: Secret
metadata:
  name: mysql-secret
  labels:
    app: mysql
type: Opaque
data:
  password: XXXXXXXXX # base64 encrypted password
---
# mysql configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
data:
  primary.cnf: |
    #Master
    [mysqld]
    log-bin=mysqllog
  replica.cnf: |
    #Slave
    [mysqld]
    super-read-only
---
#StatefulSet mysql pod
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  selector:
    matchLabels:
      app: mysql
      app.kubernetes.io/name: mysql
  serviceName: mysql
  replicas: 3 # Number of replicas, number in the cluster
  template:
    metadata:
      labels:
        app:mysql
        app.kubernetes.io/name: mysql
    spec:
      initContainers:
        - name: init-mysql
          image: freelizhun/mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          command:
            - bash
            - "-c"
            - |
              set-ex
              # Generate the ID of the MySQL server based on the Pod serial number.
              [[ $HOSTNAME =~ -([0-9] + )$ ]] || exit 1
              ordinal=${BASH_REMATCH[1]}
              echo [mysqld] > /mnt/conf.d/server-id.cnf
              # Add an offset to avoid using the reserved value of server-id=0.
              echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
              # Copy the appropriate conf.d file from config-map to emptyDir.
              if [[ $ordinal -eq 0 ]]; then
                cp /mnt/config-map/primary.cnf /mnt/conf.d/
              else
                cp /mnt/config-map/replica.cnf /mnt/conf.d/
              fi
          volumeMounts:
            - name: conf
              mountPath: /mnt/conf.d
            - name: config-map
              mountPath: /mnt/config-map
        - name: clone-mysql
          image: freelizhun/xtrabackup:v2
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          command:
            - bash
            - "-c"
            - |
              set-ex
              # If data already exists, skip cloning.
              [[ -d /var/lib/mysql/mysql ]] & amp; & amp; exit 0
              # Skip cloning of the master instance (serial index 0).
              [[ `hostname` =~ -([0-9] + )$ ]] || exit 1
              ordinal=${BASH_REMATCH[1]}
              [[ $ordinal -eq 0 ]] & amp; & amp; exit 0
              # Clone data from original peer.
              ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
              # Prepare backup.
              xtrabackup --prepare --target-dir=/var/lib/mysql
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
              subPath: mysql
            - name: conf
              mountPath: /etc/mysql/conf.d
      containers:
        - name: mysql
          image: freelizhun/mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          ports:
            - name: mysql
              containerPort: 3306
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
              subPath: mysql
            - name: conf
              mountPath: /etc/mysql/conf.d
          resources:
            requests:
              cpu: 500m
              memory: 1Gi
          livenessProbe:
            exec:
              command:
                - bash
                - "-c"
                - |
                  set-ex
                  mysqladmin ping -uroot -p${MYSQL_ROOT_PASSWORD}
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
          readinessProbe:
            exec:
              # Check if we can perform the query over TCP (skip-networking is off).
              command:
                - bash
                - "-c"
                - |
                  set-ex
                  mysqladmin ping -uroot -p${MYSQL_ROOT_PASSWORD}
            initialDelaySeconds: 5
            periodSeconds: 2
            timeoutSeconds: 1
        - name: xtrabackup
          image: freelizhun/xtrabackup:v2
          ports:
            - name: xtrabackup
              containerPort: 3307
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          command:
            - bash
            - "-c"
            - |
              set-ex
              cd /var/lib/mysql
              
              # Determine the binlog location of cloned data (if any).
              if [[ -f xtrabackup_slave_info & amp; & amp; "x$(<xtrabackup_slave_info)" != "x" ]]; then
                # XtraBackup has generated part of the "CHANGE MASTER TO" query
                # Because we are cloning from an existing copy. (Need to remove the semicolon at the end!)
                cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
                # Ignore xtrabackup_binlog_info here (it is useless).
                rm -f xtrabackup_slave_info xtrabackup_binlog_info
              elif [[ -f xtrabackup_binlog_info ]]; then
                # We clone directly from the master instance. Parse binlog location.
                [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]] + (.*?)$ ]] || exit 1
                rm -f xtrabackup_binlog_info xtrabackup_slave_info
                echo "CHANGE MASTER TO MASTER_LOG_FILE='${<!-- -->BASH_REMATCH[1]}',\
                      MASTER_LOG_POS=${<!-- -->BASH_REMATCH[2]}" > change_master_to.sql.in
              fi
              
              # Check if we need to complete cloning by starting replication.
              if [[ -f change_master_to.sql.in ]]; then
                echo "Waiting for mysqld to be ready (accepting connections)"
                until mysql -h 127.0.0.1 -uroot -p${<!-- -->MYSQL_ROOT_PASSWORD} -e "SELECT 1"; do sleep 1; done
              
                echo "Initializing replication from clone position"
                mysql -h 127.0.0.1 -uroot -p${<!-- -->MYSQL_ROOT_PASSWORD} \
                      -e "$(<change_master_to.sql.in), \
                              MASTER_HOST='mysql-0.mysql', \
                              MASTER_USER='root', \
                              MASTER_PASSWORD='${<!-- -->MYSQL_ROOT_PASSWORD}', \
                              MASTER_CONNECT_RETRY=10; \
                            START SLAVE;" || exit 1
                # If the container is restarted, try at most once.
                mv change_master_to.sql.in change_master_to.sql.orig
              fi
              
              # Start the server to send backups when requested by the peer.
              exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
                "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root --password=${<!-- -->MYSQL_ROOT_PASSWORD}"
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
              subPath: mysql
            - name: conf
              mountPath: /etc/mysql/conf.d
      volumes:
        - name: conf
          emptyDir: {<!-- --> }
        - name: config-map
          configMap:
            name: mysql
  volumeClaimTemplates:
    - metadata:
        name:data
      spec:
        accessModes:
          - "ReadWriteOnce"
        storageClassName: nfs-client
        resources:
          requests:
            Storage: 2Gi
---
#HeadlessService
apiVersion: v1
Kind: Service
metadata:
  name: mysql
  labels:
    app:mysql
    app.kubernetes.io/name: mysql
spec:
  ports:
    - name: mysql
      port: 3306
  clusterIP: None # Headless Service Headless Service
  selector:
    app:mysql
---
apiVersion: v1
Kind: Service
metadata:
  name: mysql-read
  labels:
    app:mysql
    app.kubernetes.io/name: mysql
    readonly: "true"
spec:
  ports:
    - name: mysql
      port: 3306
  selector:
    app: mysql
---
# NodePort Service
apiVersion: v1
Kind: Service
metadata:
  name: mysql-node-port
  labels:
    app:mysql
spec:
  ports:
    - nodePort: 30006
      port: 3306
      targetPort: 3306
      protocol:TCP
      name: mysql
  type: NodePort
  selector:
    app:mysql

Refer to the official documentation: https://kubernetes.io/zh-cn/docs/tasks/run-application/run-replicated-stateful-application/