Todos os produtos
Search
Central de documentação

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

Última atualização: Jun 27, 2026

Implante um cluster MySQL com estado e alta disponibilidade no ACK Serverless

O ACK Serverless permite usar a semântica nativa de agendamento do Kubernetes para distribuir pods por várias zonas de disponibilidade sem gerenciar nós. Este guia orienta a implantação de um StatefulSet MySQL com três réplicas no ACK Serverless, de modo que cada réplica seja executada em uma zona diferente, oferecendo tolerância a falhas no nível de zona para cargas de trabalho com estado.

Importante

Os limites de recursos e a configuração do MySQL neste guia são dimensionados para fins de demonstração. Revise e ajuste a CPU, a memória e as flags do MySQL antes de implantar em produção.

Ao final deste guia, você terá:

  • Um namespace mysql com um Secret e uma StorageClass

  • Um StatefulSet MySQL (dbc1) com três réplicas distribuídas por três zonas

  • Verificado que cada pod está agendado em um nó virtual diferente, em zonas distintas

Pré-requisitos

Antes de começar, certifique-se de ter:

Como funciona

O StatefulSet usa duas restrições de agendamento complementares para alcançar alta disponibilidade no nível de zona:

  • Restrições de distribuição de topologia (topologySpreadConstraints): distribuem os pods uniformemente entre as zonas. Com maxSkew: 1 e topologyKey: topology.kubernetes.io/zone, o agendador garante que nenhuma zona hospede mais de uma réplica extra em comparação a qualquer outra zona.

  • Anti-afinidade de pod (podAntiAffinity): impõe separação no nível de nó. Mesmo dentro da mesma zona, cada pod deve ser executado em um nó virtual diferente. Isso evita que duas réplicas compartilhem um único nó.

Ambas as restrições são necessárias porque a distribuição de topologia sozinha controla a alocação por zona, mas não impede que dois pods sejam colocados no mesmo nó dentro de uma zona. A anti-afinidade de pod preenche essa lacuna ao impor uma separação estrita no nível de nó.

A StorageClass fast-storageclass provisiona o armazenamento usando a vinculação WaitForFirstConsumer. Cada PersistentVolumeClaim (PVC) só é criado quando um pod é agendado, o que garante que o disco em nuvem seja provisionado na mesma zona do pod — um requisito para armazenamento com reconhecimento de zona.

Importante

reclaimPolicy: Retain

significa que os PVCs não são excluídos automaticamente quando você reduz a escala do StatefulSet. Para liberar o armazenamento, exclua os PVCs manualmente após a redução da escala.

Etapa 1: Clonar o repositório de exemplo

  1. Clone o repositório.

    git clone https://github.com/AliyunContainerService/serverless-k8s-examples.git
  2. Acesse o diretório de trabalho e liste os arquivos YAML.

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

    Saída esperada:

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

    Os três arquivos YAML a serem aplicados:

    Arquivo

    Cria

    secret.yaml

    Secret para senhas do MySQL

    storageclass.yaml

    StorageClass para provisionamento de discos em nuvem por zona

    stateful-mysql.yaml

    StatefulSet MySQL com agendamento distribuído por zonas

Etapa 2: Criar o namespace, Secret e StorageClass

  1. Crie o namespace mysql.

    kubectl create namespace mysql

    Saída esperada:

    namespace/mysql created
  2. Crie o Secret que armazena as senhas do MySQL.

    kubectl apply -n mysql -f secret.yaml

    Saída esperada:

    secret/mysql-secret created

    Visualize abaixo o arquivo YAML do Secret.

    Clique em para visualizar o arquivo YAML

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysql-secret
    type: Opaque
    data:
      password: VGVzdFBhc3N3b3JkMTIz            # Base64-encoded MySQL root password
      admin-password: QWRtaW5QYXNzd29yZDQ1Ng==  # Base64-encoded MySQL admin password

    Para gerar valores codificados em Base64 para suas próprias senhas:

    # Encode the root password
    echo -n "your-password" | base64
    
    # Encode the admin password
    echo -n "your-admin-password" | base64
  3. Crie a StorageClass.

    kubectl apply -f storageclass.yaml

    Saída esperada:

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

    Visualize abaixo o arquivo YAML da StorageClass.

    Clique em para visualizar o arquivo YAML

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast-storageclass
    provisioner: diskplugin.csi.alibabacloud.com
    parameters:
      type: cloud_auto,cloud_essd,cloud_ssd # Selects disk type by priority; actual type depends on node instance type and zone availability
      fstype: ext4
      diskTags: "a:b,b:c"
      encrypted: "false"
      performanceLevel: PL1
      volumeExpandAutoSnapshot: "forced" # Takes effect only when the disk type is cloud_essd
      provisionedIops: "40000"
      burstingEnabled: "false"
    volumeBindingMode: WaitForFirstConsumer # Delays disk creation until a pod is scheduled, ensuring the disk and pod are in the same zone
    reclaimPolicy: Retain
    allowVolumeExpansion: true

Etapa 3: Implantar o StatefulSet MySQL

  1. Aplique o manifesto do StatefulSet. Esse comando cria três réplicas MySQL distribuídas entre zonas usando restrições de distribuição de topologia e anti-afinidade de pod.

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

    Saída esperada:

    statefulset.apps/dbc1 created

    Visualize abaixo o arquivo YAML do StatefulSet.

    Clique em para visualizar o arquivo YAML

    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

Etapa 4: Verificar a distribuição por zonas

  1. Confirme se o StatefulSet está pronto.

    kubectl get statefulset -n mysql

    Saída esperada:

    NAME   READY   AGE
    dbc1   3/3     7m21s
  2. Verifique a alocação dos pods entre as zonas.

    kubectl -n mysql get po -o wide

    Saída esperada:

    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>

    A coluna NODE contém o identificador da zona. No exemplo acima, os três pods estão agendados em cn-hangzhou-j, cn-hangzhou-h e cn-hangzhou-g, confirmando que o cluster está distribuído por três zonas separadas.

Próximos passos