Kubernetes 運(yùn)行一個(gè)有狀態(tài)的應(yīng)用程序

2022-06-14 10:21 更新

運(yùn)行一個(gè)有狀態(tài)的應(yīng)用程序

本頁(yè)展示如何使用 ?StatefulSet ?控制器運(yùn)行一個(gè)有狀態(tài)的應(yīng)用程序。此例是多副本的 MySQL 數(shù)據(jù)庫(kù)。 示例應(yīng)用的拓?fù)浣Y(jié)構(gòu)有一個(gè)主服務(wù)器和多個(gè)副本,使用異步的基于行(Row-Based) 的數(shù)據(jù)復(fù)制。

說明: 這不是生產(chǎn)環(huán)境下配置。 尤其注意,MySQL 設(shè)置都使用的是不安全的默認(rèn)值,這是因?yàn)槲覀兿氚阎攸c(diǎn)放在 Kubernetes 中運(yùn)行有狀態(tài)應(yīng)用程序的一般模式上。

在開始之前

  • 你必須擁有一個(gè) Kubernetes 的集群,同時(shí)你的 Kubernetes 集群必須帶有 kubectl 命令行工具。 建議在至少有兩個(gè)節(jié)點(diǎn)的集群上運(yùn)行本教程,且這些節(jié)點(diǎn)不作為控制平面主機(jī)。 如果你還沒有集群,你可以通過 Minikube 構(gòu)建一個(gè)你自己的集群,或者你可以使用下面任意一個(gè) Kubernetes 工具構(gòu)建:
  • 要獲知版本信息,請(qǐng)輸入 ?kubectl version?。

  • 你需要有一個(gè)帶有默認(rèn) ?StorageClass?的 動(dòng)態(tài) ?PersistentVolume ?供應(yīng)程序, 或者自己靜態(tài)的提供 ?PersistentVolume ?來滿足這里使用的 ?PersistentVolumeClaim?。
  • 本教程假定你熟悉 ?PersistentVolumes ?與 ?StatefulSet?, 以及其他核心概念,例如 ?Pod?、 服務(wù) 與 ?ConfigMap?.
  • 熟悉 MySQL 會(huì)有所幫助,但是本教程旨在介紹對(duì)其他系統(tǒng)應(yīng)該有用的常規(guī)模式。
  • 你正在使用默認(rèn)命名空間或不包含任何沖突對(duì)象的另一個(gè)命名空間。

教程目標(biāo)

  • 使用 StatefulSet 控制器部署多副本 MySQL 拓?fù)浼軜?gòu)。
  • 發(fā)送 MySQL 客戶端請(qǐng)求
  • 觀察對(duì)宕機(jī)的抵抗力
  • 擴(kuò)縮 StatefulSet 的規(guī)模

部署 MySQL 

MySQL 示例部署包含一個(gè) ConfigMap、兩個(gè) Service 與一個(gè) StatefulSet。

ConfigMap 

使用以下的 YAML 配置文件創(chuàng)建 ConfigMap :

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
data:
  primary.cnf: |
    # Apply this config only on the primary.
    [mysqld]
    log-bin
    datadir=/var/lib/mysql/mysql    
  replica.cnf: |
    # Apply this config only on replicas.
    [mysqld]
    super-read-only
    datadir=/var/lib/mysql/mysql    
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml

這個(gè) ConfigMap 提供 ?my.cnf? 覆蓋設(shè)置,使你可以獨(dú)立控制 MySQL 主服務(wù)器和從服務(wù)器的配置。 在這里,你希望主服務(wù)器能夠?qū)?fù)制日志提供給副本服務(wù)器,并且希望副本服務(wù)器拒絕任何不是通過 復(fù)制進(jìn)行的寫操作。

ConfigMap 本身沒有什么特別之處,因而也不會(huì)出現(xiàn)不同部分應(yīng)用于不同的 Pod 的情況。 每個(gè) Pod 都會(huì)在初始化時(shí)基于 StatefulSet 控制器提供的信息決定要查看的部分。

服務(wù) 

使用以下 YAML 配置文件創(chuàng)建服務(wù):

# Headless service for stable DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# Client service for connecting to any MySQL instance for reads.
# For writes, you must instead connect to the primary: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml

這個(gè)無頭服務(wù)給 StatefulSet 控制器為集合中每個(gè) Pod 創(chuàng)建的 DNS 條目提供了一個(gè)宿主。 因?yàn)闊o頭服務(wù)名為 ?mysql?,所以可以通過在同一 Kubernetes 集群和命名空間中的任何其他 Pod 內(nèi)解析 ?<Pod 名稱>.mysql? 來訪問 Pod。

客戶端服務(wù)稱為 ?mysql-read?,是一種常規(guī)服務(wù),具有其自己的集群 IP。 該集群 IP 在報(bào)告就緒的所有MySQL Pod 之間分配連接。 可能的端點(diǎn)集合包括 MySQL 主節(jié)點(diǎn)和所有副本節(jié)點(diǎn)。

請(qǐng)注意,只有讀查詢才能使用負(fù)載平衡的客戶端服務(wù)。 因?yàn)橹挥幸粋€(gè) MySQL 主服務(wù)器,所以客戶端應(yīng)直接連接到 MySQL 主服務(wù)器 Pod (通過其在無頭服務(wù)中的 DNS 條目)以執(zhí)行寫入操作。

StatefulSet

最后,使用以下 YAML 配置文件創(chuàng)建 StatefulSet:

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
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 基于 Pod 序號(hào)生成 MySQL 服務(wù)器的 ID。
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # 添加偏移量以避免使用 server-id=0 這一保留值。
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files from config-map to emptyDir.
          # 將合適的 conf.d 文件從 config-map 復(fù)制到 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
          # 如果已有數(shù)據(jù),則跳過克隆。
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # 跳過主實(shí)例(序號(hào)索引 0)的克隆。
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # 從原來的對(duì)等節(jié)點(diǎn)克隆數(shù)據(jù)。
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # 準(zhǔn)備備份。
          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_ALLOW_EMPTY_PASSWORD
          value: "1"
        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: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # 檢查我們是否可以通過 TCP 執(zhí)行查詢(skip-networking 是關(guān)閉的)。
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # 確定克隆數(shù)據(jù)的 binlog 位置(如果有的話)。
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup 已經(jīng)生成了部分的 “CHANGE MASTER TO” 查詢
            # 因?yàn)槲覀儚囊粋€(gè)現(xiàn)有副本進(jìn)行克隆。(需要?jiǎng)h除末尾的分號(hào)!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # 在這里要忽略 xtrabackup_binlog_info (它是沒用的)。
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # 我們直接從主實(shí)例進(jìn)行克隆。解析 binlog 位置。
            [[ `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

          # 檢查我們是否需要通過啟動(dòng)復(fù)制來完成克隆。
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -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='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # 如果容器重新啟動(dòng),最多嘗試一次。
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # 當(dāng)對(duì)等點(diǎn)請(qǐng)求時(shí),啟動(dòng)服務(wù)器發(fā)送備份。
          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"          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml

你可以通過運(yùn)行以下命令查看啟動(dòng)進(jìn)度:

kubectl get pods -l app=mysql --watch

一段時(shí)間后,你應(yīng)該看到所有 3 個(gè) Pod 進(jìn)入 Running 狀態(tài):

NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m

輸入 Ctrl+C 結(jié)束 watch 操作。 如果你看不到任何進(jìn)度,確保已啟用前提條件 中提到的動(dòng)態(tài) PersistentVolume 預(yù)配器。

此清單使用多種技術(shù)來管理作為 StatefulSet 的一部分的有狀態(tài) Pod。 下一節(jié)重點(diǎn)介紹其中的一些技巧,以解釋 StatefulSet 創(chuàng)建 Pod 時(shí)發(fā)生的狀況。

了解有狀態(tài)的 Pod 初始化

StatefulSet 控制器按序數(shù)索引順序地每次啟動(dòng)一個(gè) Pod。 它一直等到每個(gè) Pod 報(bào)告就緒才再啟動(dòng)下一個(gè) Pod。

此外,控制器為每個(gè) Pod 分配一個(gè)唯一、穩(wěn)定的名稱,形如 ?<statefulset 名稱>-<序數(shù)索引>?, 其結(jié)果是 Pods 名為 ?mysql-0?、?mysql-1? 和 ?mysql-2?。

上述 StatefulSet 清單中的 Pod 模板利用這些屬性來執(zhí)行 MySQL 副本的有序啟動(dòng)。

生成配置

在啟動(dòng) Pod 規(guī)約中的任何容器之前,Pod 首先按順序運(yùn)行所有的 Init 容器。

第一個(gè)名為 ?init-mysql? 的 Init 容器根據(jù)序號(hào)索引生成特殊的 MySQL 配置文件。

該腳本通過從 Pod 名稱的末尾提取索引來確定自己的序號(hào)索引,而 Pod 名稱由 ?hostname ?命令返回。 然后將序數(shù)(帶有數(shù)字偏移量以避免保留值)保存到 MySQL ?conf.d? 目錄中的文件 ?server-id.cnf?。 這一操作將 StatefulSet 所提供的唯一、穩(wěn)定的標(biāo)識(shí)轉(zhuǎn)換為 MySQL 服務(wù)器的 ID, 而這些 ID 也是需要唯一性、穩(wěn)定性保證的。

通過將內(nèi)容復(fù)制到 ?conf.d? 中,?init-mysql? 容器中的腳本也可以應(yīng)用 ConfigMap 中的 ?primary.cnf? 或 ?replica.cnf?。 由于示例部署結(jié)構(gòu)由單個(gè) MySQL 主節(jié)點(diǎn)和任意數(shù)量的副本節(jié)點(diǎn)組成, 因此腳本僅將序數(shù) ?0? 指定為主節(jié)點(diǎn),而將其他所有節(jié)點(diǎn)指定為副本節(jié)點(diǎn)。

與 StatefulSet 控制器的 部署順序保證 相結(jié)合, 可以確保 MySQL 主服務(wù)器在創(chuàng)建副本服務(wù)器之前已準(zhǔn)備就緒,以便它們可以開始復(fù)制。

克隆現(xiàn)有數(shù)據(jù)

通常,當(dāng)新 Pod 作為副本節(jié)點(diǎn)加入集合時(shí),必須假定 MySQL 主節(jié)點(diǎn)可能已經(jīng)有數(shù)據(jù)。 還必須假設(shè)復(fù)制日志可能不會(huì)一直追溯到時(shí)間的開始。

這些保守的假設(shè)是允許正在運(yùn)行的 StatefulSet 隨時(shí)間擴(kuò)大和縮小而不是固定在其初始大小的關(guān)鍵。

第二個(gè)名為 ?clone-mysql? 的 Init 容器,第一次在帶有空 PersistentVolume 的副本 Pod 上啟動(dòng)時(shí),會(huì)在從屬 Pod 上執(zhí)行克隆操作。 這意味著它將從另一個(gè)運(yùn)行中的 Pod 復(fù)制所有現(xiàn)有數(shù)據(jù),使此其本地狀態(tài)足夠一致, 從而可以開始從主服務(wù)器復(fù)制。

MySQL 本身不提供執(zhí)行此操作的機(jī)制,因此本示例使用了一種流行的開源工具 Percona XtraBackup。 在克隆期間,源 MySQL 服務(wù)器性能可能會(huì)受到影響。 為了最大程度地減少對(duì) MySQL 主服務(wù)器的影響,該腳本指示每個(gè) Pod 從序號(hào)較低的 Pod 中克隆。 可以這樣做的原因是 StatefulSet 控制器始終確保在啟動(dòng) Pod ?N + 1? 之前 Pod ?N? 已準(zhǔn)備就緒。

開始復(fù)制

Init 容器成功完成后,應(yīng)用容器將運(yùn)行。 MySQL Pod 由運(yùn)行實(shí)際 ?mysqld ?服務(wù)的 ?mysql ?容器和充當(dāng) 輔助工具 的 xtrabackup 容器組成。

?xtrabackup ?sidecar 容器查看克隆的數(shù)據(jù)文件,并確定是否有必要在副本服務(wù)器上初始化 MySQL 復(fù)制。 如果是這樣,它將等待 ?mysqld ?準(zhǔn)備就緒,然后使用從 XtraBackup 克隆文件中提取的復(fù)制參數(shù) 執(zhí)行 ?CHANGE MASTER TO? 和 ?START SLAVE? 命令。

一旦副本服務(wù)器開始復(fù)制后,它會(huì)記住其 MySQL 主服務(wù)器,并且如果服務(wù)器重新啟動(dòng)或 連接中斷也會(huì)自動(dòng)重新連接。 另外,因?yàn)楦北痉?wù)器會(huì)以其穩(wěn)定的 DNS 名稱查找主服務(wù)器(?mysql-0.mysql?), 即使由于重新調(diào)度而獲得新的 Pod IP,它們也會(huì)自動(dòng)找到主服務(wù)器。

最后,開始復(fù)制后,?xtrabackup ?容器監(jiān)聽來自其他 Pod 的連接,處理其數(shù)據(jù)克隆請(qǐng)求。 如果 StatefulSet 擴(kuò)大規(guī)模,或者下一個(gè) Pod 失去其 PersistentVolumeClaim 并需要重新克隆, 則此服務(wù)器將無限期保持運(yùn)行。

發(fā)送客戶端請(qǐng)求

你可以通過運(yùn)行帶有 ?mysql:5.7? 鏡像的臨時(shí)容器并運(yùn)行 ?mysql ?客戶端二進(jìn)制文件, 將測(cè)試查詢發(fā)送到 MySQL 主服務(wù)器(主機(jī)名 ?mysql-0.mysql?)。

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

使用主機(jī)名 ?mysql-read? 將測(cè)試查詢發(fā)送到任何報(bào)告為就緒的服務(wù)器:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"

你應(yīng)該獲得如下輸出:

Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

為了演示 ?mysql-read? 服務(wù)在服務(wù)器之間分配連接,你可以在循環(huán)中運(yùn)行 ?SELECT @@server_id?:

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

你應(yīng)該看到報(bào)告的 ?@@server_id? 發(fā)生隨機(jī)變化,因?yàn)槊看螄L試連接時(shí)都可能選擇了不同的端點(diǎn):

+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+

要停止循環(huán)時(shí)可以按 Ctrl+C ,但是讓它在另一個(gè)窗口中運(yùn)行非常有用, 這樣你就可以看到以下步驟的效果。

模擬 Pod 和 Node 的宕機(jī)時(shí)間 

為了證明從副本節(jié)點(diǎn)緩存而不是單個(gè)服務(wù)器讀取數(shù)據(jù)的可用性提高,請(qǐng)?jiān)谑?nbsp;Pod 退出 Ready 狀態(tài)時(shí),保持上述 ?SELECT @@server_id? 循環(huán)一直運(yùn)行。

破壞就緒態(tài)探測(cè)

?mysql ?容器的 就緒態(tài)探測(cè) 運(yùn)行命令 ?mysql -h 127.0.0.1 -e 'SELECT 1'?,以確保服務(wù)器已啟動(dòng)并能夠執(zhí)行查詢。

迫使就緒態(tài)探測(cè)失敗的一種方法就是中止該命令:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

此命令會(huì)進(jìn)入 Pod ?mysql-2? 的實(shí)際容器文件系統(tǒng),重命名 ?mysql ?命令,導(dǎo)致就緒態(tài)探測(cè)無法找到它。 幾秒鐘后, Pod 會(huì)報(bào)告其中一個(gè)容器未就緒。你可以通過運(yùn)行以下命令進(jìn)行檢查:

kubectl get pod mysql-2

在 ?READY ?列中查找 ?1/2?:

NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m

此時(shí),你應(yīng)該會(huì)看到 ?SELECT @@server_id? 循環(huán)繼續(xù)運(yùn)行,盡管它不再報(bào)告 ?102?。 回想一下,?init-mysql? 腳本將 ?server-id? 定義為 ?100 + $ordinal?, 因此服務(wù)器 ID ?102 ?對(duì)應(yīng)于 Pod ?mysql-2?。

現(xiàn)在修復(fù) Pod,幾秒鐘后它應(yīng)該重新出現(xiàn)在循環(huán)輸出中:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql

刪除 Pods 

如果刪除了 Pod,則 StatefulSet 還會(huì)重新創(chuàng)建 Pod,類似于 ReplicaSet 對(duì)無狀態(tài) Pod 所做的操作。

kubectl delete pod mysql-2

StatefulSet 控制器注意到不再存在 ?mysql-2? Pod,于是創(chuàng)建一個(gè)具有相同名稱并鏈接到相同 PersistentVolumeClaim 的新 Pod。 你應(yīng)該看到服務(wù)器 ID ?102 ?從循環(huán)輸出中消失了一段時(shí)間,然后又自行出現(xiàn)。

騰空節(jié)點(diǎn) 

如果你的 Kubernetes 集群具有多個(gè)節(jié)點(diǎn),則可以通過發(fā)出以下 drain 命令來模擬節(jié)點(diǎn)停機(jī)(就好像節(jié)點(diǎn)在被升級(jí))。

首先確定 MySQL Pod 之一在哪個(gè)節(jié)點(diǎn)上:

kubectl get pod mysql-2 -o wide

節(jié)點(diǎn)名稱應(yīng)顯示在最后一列中:

NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-node-9l2t

然后通過運(yùn)行以下命令騰空節(jié)點(diǎn),該命令將其保護(hù)起來,以使新的 Pod 不能調(diào)度到該節(jié)點(diǎn), 然后逐出所有現(xiàn)有的 Pod。將 ?<節(jié)點(diǎn)名稱>? 替換為在上一步中找到的節(jié)點(diǎn)名稱。

這可能會(huì)影響節(jié)點(diǎn)上的其他應(yīng)用程序,因此最好 僅在測(cè)試集群中執(zhí)行此操作。

kubectl drain <節(jié)點(diǎn)名稱> --force --delete-local-data --ignore-daemonsets

現(xiàn)在,你可以看到 Pod 被重新調(diào)度到其他節(jié)點(diǎn)上:

kubectl get pod mysql-2 -o wide --watch

它看起來應(yīng)該像這樣:

NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-node-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-node-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-node-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-node-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-node-fjlm

再次,你應(yīng)該看到服務(wù)器 ID ?102 ?從 ?SELECT @@server_id? 循環(huán)輸出 中消失一段時(shí)間,然后自行出現(xiàn)。

現(xiàn)在去掉節(jié)點(diǎn)保護(hù)(Uncordon),使其恢復(fù)為正常模式:

kubectl uncordon <節(jié)點(diǎn)名稱>

擴(kuò)展副本節(jié)點(diǎn)數(shù)量

使用 MySQL 復(fù)制,你可以通過添加副本節(jié)點(diǎn)來擴(kuò)展讀取查詢的能力。 使用 StatefulSet,你可以使用單個(gè)命令執(zhí)行此操作:

kubectl scale statefulset mysql --replicas=5

查看新的 Pod 的運(yùn)行情況:

kubectl get pods -l app=mysql --watch

一旦 Pod 啟動(dòng),你應(yīng)該看到服務(wù)器 IDs ?103 ?和 ?104 ?開始出現(xiàn)在 ?SELECT @@server_id? 循環(huán)輸出中。

你還可以驗(yàn)證這些新服務(wù)器在存在之前已添加了數(shù)據(jù):

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

向下縮容操作也是很平滑的:

kubectl scale statefulset mysql --replicas=3

但是請(qǐng)注意,按比例擴(kuò)大會(huì)自動(dòng)創(chuàng)建新的 PersistentVolumeClaims,而按比例縮小不會(huì)自動(dòng)刪除這些 PVC。 這使你可以選擇保留那些初始化的 PVC,以更快地進(jìn)行縮放,或者在刪除它們之前提取數(shù)據(jù)。

你可以通過運(yùn)行以下命令查看此信息:

kubectl get pvc -l app=mysql

這表明,盡管將 StatefulSet 縮小為3,所有5個(gè) PVC 仍然存在:

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m

如果你不打算重復(fù)使用多余的 PVC,則可以刪除它們:

kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4

清理現(xiàn)場(chǎng)

  1. 通過在終端上按 Ctrl+C 取消 ?SELECT @@server_id? 循環(huán),或從另一個(gè)終端運(yùn)行以下命令:
  2. kubectl delete pod mysql-client-loop --now
    
  3. 刪除 StatefulSet。這也會(huì)開始終止 Pod。
    kubectl delete statefulset mysql
    
  4. 驗(yàn)證 Pod 消失。他們可能需要一些時(shí)間才能完成終止。
    kubectl get pods -l app=mysql
    

    當(dāng)上述命令返回如下內(nèi)容時(shí),你就知道 Pod 已終止:

    No resources found.
    
  5. 刪除 ConfigMap、Services 和 PersistentVolumeClaims。
    kubectl delete configmap,service,pvc -l app=mysql
    
  6. 如果你手動(dòng)供應(yīng) PersistentVolume,則還需要手動(dòng)刪除它們,并釋放下層資源。 如果你使用了動(dòng)態(tài)預(yù)配器,當(dāng)?shù)弥銊h除 PersistentVolumeClaims 時(shí),它將自動(dòng)刪除 PersistentVolumes。 一些動(dòng)態(tài)預(yù)配器(例如用于 EBS 和 PD 的預(yù)配器)也會(huì)在刪除 PersistentVolumes 時(shí)釋放下層資源。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)