Home > Software design >  MySQL Kubernetes statefulset with defined env variables
MySQL Kubernetes statefulset with defined env variables

Time:10-28

I'm following Kubernetes's MySQL as a StatefulSet from here. What the tutorial did not cover is how do I specify other environmental variables, like MYSQL_ROOT_PASSWORD, MYSQL_USER and such. I tried doing it myself, but didn't work. Here's the code:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-root-password
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ `hostname` =~ -([0-9] )$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100   $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files 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: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Skip the clone if data already exists.
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # Skip the clone on primary (ordinal index 0).
          [[ `hostname` =~ -([0-9] )$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # Clone data from previous peer.
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # Prepare the 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: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-root-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: 100m
            memory: 500Mi
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-root-password
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # Determine binlog position of cloned data, if any.
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup already generated a partial "CHANGE MASTER TO" query
            # because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # Ignore xtrabackup_binlog_info in this case (it's useless).
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # We're cloning directly from primary. Parse binlog position.
            [[ `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 a clone 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 \
                  -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 USER='root' PASSWORD=$MYSQL_ROOT_PASSWORD;" || exit 1
            # In case of container restart, attempt this at-most-once.
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # Start a server to send backups when requested by peers.
          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
        resources:
          requests:
            cpu: 50m
            memory: 50Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

and here is my kubectl get all,secret,configmap's result:

NAME                              READY   STATUS                  RESTARTS   AGE
pod/mysql-0                       2/2     Running                 0          11m
pod/mysql-1                       1/2     Error                   7          11m
pod/mysql-2                       0/2     Init:CrashLoopBackOff   6          10m

NAME                         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)          AGE
service/kubernetes           ClusterIP      10.245.0.1       <none>           443/TCP          2d23h
service/mysql                ClusterIP      None             <none>           3306/TCP         43h
service/mysql-read           ClusterIP      10.245.81.173    <none>           3306/TCP         43h

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/phpmyadmin   1/1     1            1           2d18h

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/phpmyadmin-6c57fd98bf   1         1         1       2d18h

NAME                     READY   AGE
statefulset.apps/mysql   1/3     11m

NAME                         TYPE                                  DATA   AGE
secret/mysql-pass            Opaque                                1      46h
secret/mysql-secret          Opaque                                3      2d18h

NAME                         DATA   AGE
configmap/mysql              4      2d18h

When I tried to describe the mysql-1 pod, it said that:

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

But when I tried logging in using my defined root password on mysql-0 pod, it works. I have also tried looking a duplicated question here, but the past's tutorial code has changed (FYI, I also tried it too despite the change), so it did not really help. Any help will do! Thanks in advance!

UPDATE I tried adding this:

  - name: MYSQL_ROOT_HOST
    value: "%"

on my mysql and xtrabackup containers, also didn't work, here's the get pods --watch return:

NAME                          READY   STATUS     RESTARTS   AGE
mysql-0                       0/2     Init:0/2   0          7s
mysql-0                       0/2     Init:1/2   0          18s
mysql-0                       0/2     PodInitializing   0          19s
mysql-0                       2/2     Running           0          20s
mysql-1                       0/2     Pending           0          0s
mysql-1                       0/2     Pending           0          0s
mysql-1                       0/2     Pending           0          8s
mysql-1                       0/2     Init:0/2          0          8s
mysql-1                       0/2     Init:1/2          0          26s
mysql-1                       0/2     Init:1/2          0          27s
mysql-1                       0/2     PodInitializing   0          34s
mysql-1                       2/2     Running           0          35s
mysql-2                       0/2     Pending           0          0s
mysql-2                       0/2     Pending           0          0s
mysql-1                       1/2     Error             0          37s
mysql-1                       1/2     Error             1          38s
mysql-2                       0/2     Pending           0          3s
mysql-2                       0/2     Init:0/2          0          3s
mysql-1                       1/2     CrashLoopBackOff   1          53s
mysql-1                       1/2     Error              2          54s
mysql-2                       0/2     Init:1/2           0          21s
mysql-2                       0/2     Init:Error         0          22s
mysql-2                       0/2     Init:Error         1          23s
mysql-2                       0/2     Init:CrashLoopBackOff   1          24s
mysql-1                       1/2     CrashLoopBackOff        2          64s
mysql-2                       0/2     Init:Error              2          38s
mysql-1                       1/2     Error                   3          79s
mysql-2                       0/2     Init:CrashLoopBackOff   2          50s
mysql-1                       1/2     CrashLoopBackOff        3          92s
mysql-2                       0/2     Init:Error              3          62s
mysql-2                       0/2     Init:CrashLoopBackOff   3          74s
mysql-1                       1/2     Error                   4          2m1s
mysql-1                       1/2     CrashLoopBackOff        4          2m11s
mysql-2                       0/2     Init:Error              4          113s
mysql-2                       0/2     Init:CrashLoopBackOff   4          2m6s
mysql-1                       1/2     Error                   5          3m29s
mysql-1                       1/2     CrashLoopBackOff        5          3m41s
mysql-2                       0/2     Init:Error              5          3m23s
mysql-2                       0/2     Init:CrashLoopBackOff   5          3m34s

For some reason mysql-1 ran for a while, then stopped.

Logs from mysql-1 pod :

2021-10-25 03:16:36 00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.36-1debian10 started.
2021-10-25 03:16:36 00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2021-10-25 03:16:36 00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.36-1debian10 started.
2021-10-25T03:16:37.231906Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2021-10-25T03:16:37.234015Z 0 [Note] mysqld (mysqld 5.7.36) starting as process 1 ...
2021-10-25T03:16:37.237044Z 0 [Note] InnoDB: PUNCH HOLE support available
2021-10-25T03:16:37.237196Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2021-10-25T03:16:37.237263Z 0 [Note] InnoDB: Uses event mutexes
2021-10-25T03:16:37.237345Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2021-10-25T03:16:37.237416Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2021-10-25T03:16:37.237489Z 0 [Note] InnoDB: Using Linux native AIO
2021-10-25T03:16:37.237792Z 0 [Note] InnoDB: Number of pools: 1
2021-10-25T03:16:37.238102Z 0 [Note] InnoDB: Using CPU crc32 instructions
2021-10-25T03:16:37.240179Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M, instances = 1, chunk size = 128M
2021-10-25T03:16:37.248111Z 0 [Note] InnoDB: Completed initialization of buffer pool
2021-10-25T03:16:37.251017Z 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
2021-10-25T03:16:37.263519Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
2021-10-25T03:16:37.307064Z 0 [Note] InnoDB: Removed temporary tablespace data file: "ibtmp1"
2021-10-25T03:16:37.307319Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2021-10-25T03:16:37.307466Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2021-10-25T03:16:37.353707Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2021-10-25T03:16:37.354244Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
2021-10-25T03:16:37.354256Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
2021-10-25T03:16:37.355048Z 0 [Note] InnoDB: Waiting for purge to start
2021-10-25T03:16:37.406335Z 0 [Note] InnoDB: 5.7.36 started; log sequence number 12660776
2021-10-25T03:16:37.406850Z 0 [Note] Plugin 'FEDERATED' is disabled.
2021-10-25T03:16:37.413880Z 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2021-10-25T03:16:37.426491Z 0 [Note] InnoDB: Buffer pool(s) load completed at 211025  3:16:37
2021-10-25T03:16:37.427210Z 0 [Note] Salting uuid generator variables, current_pid: 1, server_start_time: 1635131797, bytes_sent: 0, 
2021-10-25T03:16:37.427438Z 0 [Note] Generated uuid: 'f6f89137-3541-11ec-84bc-f29710c93c86', server_start_time: 281476611842454, bytes_sent: 94355589700272
2021-10-25T03:16:37.427554Z 0 [Warning] No existing UUID has been found, so we assume that this is the first time that this server has been started. Generating a new UUID: f6f89137-3541-11ec-84bc-f29710c93c86.
2021-10-25T03:16:38.109098Z 0 [Note] Auto generated SSL certificates are placed in data directory.
2021-10-25T03:16:38.109327Z 0 [Warning] A deprecated TLS version TLSv1 is enabled. Please use TLSv1.2 or higher.
2021-10-25T03:16:38.109395Z 0 [Warning] A deprecated TLS version TLSv1.1 is enabled. Please use TLSv1.2 or higher.
2021-10-25T03:16:38.109907Z 0 [Warning] CA certificate ca.pem is self signed.
2021-10-25T03:16:38.354105Z 0 [Note] Auto generated RSA key files are placed in data directory.
2021-10-25T03:16:38.354795Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
2021-10-25T03:16:38.354968Z 0 [Note] IPv6 is available.
2021-10-25T03:16:38.355039Z 0 [Note]   - '::' resolves to '::';
2021-10-25T03:16:38.355125Z 0 [Note] Server socket created on IP: '::'.
2021-10-25T03:16:38.357907Z 0 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2021-10-25T03:16:38.361720Z 0 [Note] Failed to start slave threads for channel ''
2021-10-25T03:16:38.366691Z 0 [Note] Event Scheduler: Loaded 0 events
2021-10-25T03:16:38.367135Z 0 [Note] mysqld: ready for connections.
Version: '5.7.36'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2021-10-25T03:16:39.011694Z 3 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:16:39.759020Z 5 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:16:56.006754Z 7 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:17:21.016988Z 9 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:18:03.010443Z 11 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:19:30.984929Z 13 [Note] Access denied for user 'root'@'localhost' (using password: NO)
2021-10-25T03:22:25.011291Z 15 [Note] Access denied for user 'root'@'localhost' (using password: NO)

UPDATE

What I have tried again is stripping down a lot of optional code(I think...), and here is the result:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: admin123
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ `hostname` =~ -([0-9] )$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100   $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files 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
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: admin123
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 500Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

From the yaml above, the master pod is running without a problem, but for the slave pods, the process is pretty normal until it ran for a while, then they encountered an error. After that, the state went back to running again. I checked the error from the logs and turned out that they could not authenticate mysql using the user root and the root password that I've set, so they tried authenticating mysql using the user root with no password specified and succeeded. That's weird because I've set the env variables MYSQL_ROOT_PASSWORD already inside the initContainers and the containers themselves, so I tried to exec each of the slave pods. I ran echo $MYSQL_ROOT_PASSWORD and the result is the same as I have specified. So the problem now from the yaml above is the env variable that I've set on the yaml file did not penetrate through the mysql application, but only through the container's environmental system.

CodePudding user response:

I have solved the problem. Turns out I tweaked some of my first uploaded code because there was a syntax error and typos:

$MYSQL_ROOT_PASSWORD ==> $(MYSQL_ROOT_PASSWORD) 
#when calling env var inside command, you have to add the brackets
-uroot -p$MYQSL_ROOT_PASSWORD ==> -u root --password=$(MYSQL_ROOT_PASSWORD)
START SLAVE USER='root' PASSWORD=$(MYSQL_ROOT_PASSWORD);" || exit 1 ==> START SLAVE;" || exit 1

and

MASTER_PASSWORD=$(MYSQL_ROOT_PASSWORD), \ ==> MASTER_PASSWORD='$(MYSQL_ROOT_PASSWORD)', \
# have to put ' before and after getting the env value

Also, I noticed that the code from mine only works if you removed the livenessProbe and readinessProbe. Somehow, both of them is called before the env var gets loaded, that's why when readinessProbe executes this command:

command: ["mysql", "-h", "127.0.0.1", "-u", "root", "--password=$(MYSQL_ROOT_PASSWORD)", "-e", "SELECT 1"]

it failed because $(MYSQL_ROOT_PASSWORD)'s value was not found, therefore, resulting this error:

2021-10-25T03:16:39.011694Z 3 [Note] Access denied for user 'root'@'localhost' (using password: NO)

If someone has any additional info between livenessProbe, readinessProbe, and env var, please let me know, because my theory might be wrong. CMIIW. :)

  • Related