All Products
Search
Document Center

Container Service for Kubernetes:Deploy a stateful and high-availability MySQL cluster on ACK Serverless

Last Updated:Dec 25, 2025

High availability is essential for distributed applications. In an ACK serverless cluster, you can use native Kubernetes scheduling semantics to spread distributed applications across multiple zones to achieve a high-availability deployment. This document describes how to deploy a stateful, high-availability MySQL cluster on ACK Serverless.

Prerequisites

Step 1: Pull YAML files from GitHub to create Kubernetes resources

  1. Run the following command to clone the code repository.

    git clone https://github.com/AliyunContainerService/serverless-k8s-examples.git
  2. Run the following command to navigate to the working directory and view the list of YAML files.

    cd serverless-k8s-examples/stateful-mysql
    ls -al

    Expected output:

    README.md		secret.yaml		stateful-mysql.yaml	 storageclass.yaml

    The secret.yaml file is used to create a Secret, the storageclass.yaml file is used to create a StorageClass, and the stateful-mysql.yaml file is used to create a MySQL StatefulSet.

Step 2: Create a namespace, Secret, and StorageClass

  1. Run the following command to create the mysql namespace.

    kubectl create namespace mysql

    Expected output:

    namespace/mysql created
  2. Run the following command to create a Secret to store the MySQL password.

    kubectl apply -n mysql -f secret.yaml

    Expected output:

    secret/mysql-secret created

    The following is the YAML file for the Secret.

    Click to view the YAML file

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysql-secret
    type: Opaque
    data:
      password: VGVzdFBhc3N3b3JkMTIz            # Enter the Base64-encoded password.
      admin-password: QWRtaW5QYXNzd29yZDQ1Ng==  # Enter the Base64-encoded admin password.
    # Generate the Base64 encoding of the password.
    echo -n "your-password" | base64
    
    # Generate the Base64 encoding of the admin password.
    echo -n "your-admin-password" | base64
  3. Run the following command to create a StorageClass.

    kubectl apply -f storageclass.yaml

    Expected output:

    storageclass.storage.k8s.io/fast-storageclass created

    The following is the YAML file for the StorageClass.

    Click to view the YAML file

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast-storageclass
    provisioner: diskplugin.csi.alibabacloud.com
    parameters:
      type: cloud_auto,cloud_essd,cloud_ssd # This configuration adaptively selects a disk type based on priority. The final disk type depends on factors such as the node instance and the disk types supported in the zone.
      fstype: ext4
      diskTags: "a:b,b:c"
      encrypted: "false"
      performanceLevel: PL1
      volumeExpandAutoSnapshot: "forced" # This setting takes effect only when the created disk type is cloud_essd.
      provisionedIops: "40000"
      burstingEnabled: "false"
    volumeBindingMode: WaitForFirstConsumer # Postpones disk creation until a pod is created. This ensures that the disk is created in the same zone as the pod.
    reclaimPolicy: Retain
    allowVolumeExpansion: true

Step 3: Configure pod topology spread and create a MySQL StatefulSet

  1. Run the following command to create a MySQL StatefulSet. This example creates a StatefulSet that is evenly distributed across multiple zones by configuring pod topology spread constraints for virtual nodes. For more information about how to spread pods across zones on virtual nodes, see Spread pods on virtual nodes across zones and configure affinity.

    kubectl apply -n mysql -f stateful-mysql.yaml

    Expected output:

    statefulset.apps/dbc1 created

    The following is the YAML file for the StatefulSet.

    Click to view the YAML file

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: dbc1
      labels:
        app: mysql
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: mysql
      serviceName: mysql
      template:
        metadata:
          labels:
            app: mysql
        spec:
          topologySpreadConstraints:
            - maxSkew: 1
              topologyKey: "topology.kubernetes.io/zone"
              whenUnsatisfiable: DoNotSchedule
              labelSelector:
                matchLabels:
                  app: mysql
          affinity:
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                - labelSelector:
                    matchExpressions:
                      - key: app
                        operator: In
                        values:
                          - mysql
                  topologyKey: "kubernetes.io/hostname"
          containers:
            - name: mysql
              image: registry.cn-hangzhou.aliyuncs.com/kubeway/hub:mysql-server_8.0.28
              command:
                - /bin/bash
              args:
                - -c
                - >-
                  /entrypoint.sh
                  --server-id=$((20 +  $(echo $HOSTNAME | grep -o '[^-]*$') + 1))
                  --report-host=${HOSTNAME}.mysql.mysql.svc.cluster.local
                  --binlog-checksum=NONE
                  --enforce-gtid-consistency=ON
                  --gtid-mode=ON
                  --default-authentication-plugin=mysql_native_password
              env:
                - name: MYSQL_ROOT_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-secret
                      key: password
                - name: MYSQL_ADMIN_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-secret
                      key: admin-password
                - name: MYSQL_ROOT_HOST
                  value: "%"
              ports:
                - name: mysql
                  containerPort: 3306
                - name: mysqlx
                  containerPort: 33060
                - name: xcom
                  containerPort: 33061
              resources:
                limits:
                  cpu: "500m"
                  ephemeral-storage: "1Gi"
                  memory: "1Gi"
                requests:
                  cpu: "500m"
                  ephemeral-storage: "1Gi"
                  memory: "1Gi"
              volumeMounts:
                - name: mysql
                  mountPath: /var/lib/mysql
                  subPath: mysql
              readinessProbe:
                exec:
                  command:
                    - bash
                    - "-c"
                    - |
                      mysql -h127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD -e'SELECT 1'
                initialDelaySeconds: 5
                periodSeconds: 2
                timeoutSeconds: 1
              livenessProbe:
                exec:
                  command:
                    - bash
                    - "-c"
                    - |
                      mysqladmin -uroot -p$MYSQL_ROOT_PASSWORD ping
                initialDelaySeconds: 30
                periodSeconds: 10
                timeoutSeconds: 5
      updateStrategy:
        rollingUpdate:
          partition: 0
        type: RollingUpdate
      volumeClaimTemplates:
        - metadata:
            name: mysql
            labels:
              app: mysql
          spec:
            storageClassName: fast-storageclass
            volumeMode: Filesystem
            accessModes:
              - ReadWriteOnce
            resources:
              requests:
                storage: 25Gi

Step 4: Verify the deployment of pods in the MySQL cluster

  1. Run the following command to verify that the StatefulSet is created.

    kubectl get statefulset -n mysql

    Expected output:

    NAME   READY   AGE
    dbc1   3/3     7m21s
  2. Run the following command to view the details of the pods.

    kubectl -n mysql get po -o wide

    Expected output:

    NAME     READY   STATUS    RESTARTS   AGE     IP             NODE                            NOMINATED NODE   READINESS GATES
    dbc1-0   1/1     Running   0          2m28s   10.1.214.213   virtual-kubelet-cn-hangzhou-j   <none>           <none>
    dbc1-1   1/1     Running   0          106s    10.3.146.113   virtual-kubelet-cn-hangzhou-h   <none>           <none>
    dbc1-2   1/1     Running   0          64s     10.4.232.78    virtual-kubelet-cn-hangzhou-g   <none>           <none>

    The `NODE` column shows the node where each pod is deployed. The node name contains the zone information. The node names indicate that the pods are spread across different zones.

References