Redis 叢集

Posted by Adam on August 24, 2022
# Redis 叢集 這份會使用 **StatefulSet + Headless Service** 方式部署 6 個節點(3 master + 3 replica),是 Redis Cluster 的標準結構。 --- ## 📘 一、檔案名稱建議 建議你放在一個檔案裡,例如: ``` redis-cluster.yaml ``` 然後用: ```bash kubectl apply -f redis-cluster.yaml ``` 就能部署。 --- ## 🧱 二、完整 YAML + 詳細註解 ```yaml # ========================= # Redis Cluster 部署文件(含密碼) # 部署 6 個節點(3 主 + 3 從),啟用 requirepass/masterauth # ========================= # ------------------------- # Step 0: 密碼 Secret # ------------------------- apiVersion: v1 # Kubernetes API 版本 kind: Secret # 宣告建立 Secret 物件 metadata: name: redis-auth # Secret 名稱,之後會被 StatefulSet/Job 參照 type: Opaque # 一般鍵值型 Secret stringData: # 用文字直接放入(K8s 會自動轉 base64) password: "YourStrongPasswordHere" # 教學用:叢集對外與節點互連共用的密碼(示範用相同一組) --- # ------------------------- # Step 1: Headless Service # ------------------------- apiVersion: v1 # Kubernetes API 版本 kind: Service # 宣告建立 Service metadata: name: redis # Service 名稱,將做為 StatefulSet 的 serviceName labels: app: redis # 統一的標籤,供 selector 使用 spec: clusterIP: None # 設為 Headless Service,提供穩定 DNS(不做負載平衡) ports: - port: 6379 # 對外公布的 Service 連接埠(Pod 也用 6379) name: redis # 連接埠命名(可讀性用途) selector: app: redis # 只選到帶有 app=redis 的 Pod --- # ------------------------- # Step 2: StatefulSet(6 個節點) # ------------------------- apiVersion: apps/v1 # Kubernetes API 版本 kind: StatefulSet # 宣告建立 StatefulSet(提供穩定網域與 Volume 序號) metadata: name: redis # StatefulSet 名稱(Pod 會變成 redis-0, redis-1...) spec: serviceName: redis # 綁定上面的 Headless Service,產生穩定 DNS replicas: 6 # 一共 6 個 Pod(3 主 + 3 從) selector: matchLabels: app: redis # 管理帶有 app=redis 的 Pod template: metadata: labels: app: redis # Pod 標籤 spec: containers: - name: redis # 容器名稱 image: redis:7.2 # 使用官方 Redis 7.2 映像 imagePullPolicy: IfNotPresent # 本地有就用,沒有才拉 ports: - containerPort: 6379 # 容器內 Redis 服務埠 name: redis # 連接埠命名 env: # 從 Secret 注入密碼到環境變數 - name: REDIS_PASSWORD # 容器內環境變數名稱 valueFrom: secretKeyRef: name: redis-auth # 參照上方 Secret 名稱 key: password # 取用鍵:password # 主要啟動命令:開啟 cluster 模式 + 設定密碼 command: - "redis-server" # 執行 redis-server 主程式 - "--cluster-enabled" # 啟用叢集模式 - "yes" - "--cluster-config-file" # 叢集的節點配置檔,會寫在 /data 下 - "nodes.conf" - "--cluster-node-timeout" # 叢集節點逾時(毫秒),影響故障切換速度 - "5000" - "--appendonly" # 啟用 AOF 持久化(較安全) - "yes" - "--appendfilename" # AOF 檔名 - "appendonly.aof" - "--appendfsync" # AOF fsync 策略(everysec:每秒) - "everysec" - "--protected-mode" # 關閉保護模式以便於叢集內外部連線(搭配密碼) - "no" - "--requirepass" # 設定客戶端連線認證密碼(default user) - "$(REDIS_PASSWORD)" # 從環境變數注入(上面從 Secret 來) - "--masterauth" # 設定節點間複寫/叢集內互連所用的密碼 - "$(REDIS_PASSWORD)" # 這裡示範與 requirepass 同一組 # ---- 若你使用 ACL 想更細權限,可改用: # - "--aclfile" "users.acl" 或 "--user" "default on >$(REDIS_PASSWORD) ~* &* +@all" # 但為了學習簡單,我們此處用 requirepass/masterauth。 volumeMounts: - name: data # 掛載持久化 Volume mountPath: /data # Redis 預設會把 RDB/AOF/nodes.conf 放在這 resources: requests: # 最低資源需求(避免被擠兌) cpu: 100m memory: 128Mi limits: # 上限(避免暴衝) cpu: 500m memory: 512Mi volumeClaimTemplates: # 為每個 Pod 建立一個獨立 PVC(data-redis-0,1,...) - metadata: name: data # 對應到上面的 volumeMounts.name spec: accessModes: ["ReadWriteOnce"] # 單節點掛載 resources: requests: storage: 1Gi # 每個節點要求 1Gi 空間(可依需要調整) --- # ------------------------- # Step 3: Cluster Init Job(自動建立叢集,會帶密碼) # ------------------------- apiVersion: batch/v1 # Kubernetes API 版本 kind: Job # 宣告建立 Job(一次性工作) metadata: name: redis-cluster-init # Job 名稱 spec: template: spec: restartPolicy: OnFailure # 失敗才重試;成功就不會重跑 containers: - name: redis-cluster-init # 容器名稱 image: redis:7.2 # 使用 redis-cli 來建立叢集 env: - name: REDISCLI_AUTH # redis-cli 讀到此變數會自動帶密碼 valueFrom: secretKeyRef: name: redis-auth # 參照同一個 Secret key: password command: - "sh" # 用 shell 跑多行腳本 - "-c" - | echo "等待 Redis Pod 啟動中..." # 逐一等待 6 個節點能回應 PING(會自動用 REDISCLI_AUTH 帶密碼) for i in 0 1 2 3 4 5; do until redis-cli -h redis-$i.redis ping; do echo "redis-$i 還沒好,稍等一下..." sleep 3 done done echo "全部節點已啟動,建立叢集中..." # 建立 Redis Cluster(3 主 3 從),redis-cli 會自動帶 REDISCLI_AUTH 密碼 # 也可改成:redis-cli -a "$REDISCLI_AUTH" --cluster create ... redis-cli --cluster create \ redis-0.redis:6379 \ redis-1.redis:6379 \ redis-2.redis:6379 \ redis-3.redis:6379 \ redis-4.redis:6379 \ redis-5.redis:6379 \ --cluster-replicas 1 \ --cluster-yes echo "✅ Redis Cluster 建立完成!" ``` --- ## 📖 三、部署教學步驟 1️⃣ 建立 Redis Cluster ```bash kubectl apply -f redis-cluster.yaml ``` 2️⃣ 查看 Pod 狀態 ```bash kubectl get pods -l app=redis ``` 等所有 Pod 都是 `Running` 後,Job 會自動執行初始化。 3️⃣ 驗證叢集狀態 ```bash kubectl exec -it redis-0 -- redis-cli --cluster info redis-0.redis:6379 ``` 4️⃣ 連線測試 ```bash kubectl exec -it redis-0 -- redis-cli -c 127.0.0.1:6379> set user:100 "Alice" 127.0.0.1:6379> get user:100 ``` --- ## 🧠 四、延伸學習重點 | 主題 | 說明 | | -------------------- | ------------------------------------------------------ | | **Headless Service** | 給 StatefulSet 提供固定 DNS(redis-0.redis、redis-1.redis...) | | **StatefulSet** | 確保 Pod 和 Volume 對應固定,不會亂掉 | | **Cluster 模式** | Redis 自動分 slot(0–16383),分配到不同 master | | **Job** | 自動執行 `redis-cli --cluster create` 指令組成叢集 | | **PVC** | 確保重啟後資料不遺失 | --- ### 查詢資料量 ```bash for i in {0..5}; do echo "=== redis-cluster-$i ===" kubectl exec -it redis-cluster-$i -- redis-cli dbsize kubectl exec -it redis-cluster-$i -- redis-cli info memory | grep used_memory_human done ``` --- ## `redis-cli cluster info` 詳細解析 ```bash kubectl exec -it redis-cluster-0 -- redis-cli cluster info ``` 這個指令用來查看**整個集群的健康狀態與總體概況**。 執行的輸出範例通常長這樣: ```text cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:1 cluster_stats_messages_ping_sent:10482 cluster_stats_messages_pong_received:10482 cluster_stats_messages_sent:20964 cluster_stats_messages_received:20964 ``` ### 關鍵欄位對照表 | 欄位名稱 | 說明與重點 | | --- | --- | | **`cluster_state`** | **集群狀態**。`ok` 代表正常運作;若為 `fail` 表示有過半數 Master 故障或有 Slot 遺失,無法讀寫。 | | **`cluster_slots_assigned`** | **已指派的 Slot 數量**。Redis Cluster 共有 16384 個 Slot,這裡必須是 `16384` 才算完整分配。 | | **`cluster_slots_ok`** | **正常運作中的 Slot 數量**。正常情況下應等於 `16384`。 | | **`cluster_slots_pfail`** | **疑似故障 (Possible Fail) 的 Slot 數**。節點失去回應但尚未被集群判定死亡。 | | **`cluster_slots_fail`** | **已確認故障 (Fail) 的 Slot 數**。若大於 0 表示部分資料區塊無法服務。 | | **`cluster_known_nodes`** | **集群知道的總節點數**。包含 Master 與 Slave(3 主 3 從這裡會顯示 `6`)。 | | **`cluster_size`** | **正在服務 Slot 的 Master 數量**。3 主 3 從的架構下,這裡會顯示 `3`。 | | **`cluster_current_epoch`** | **集群當前的紀元 (Epoch)**。用於故障轉移 (Failover) 時的版本控制,數字會隨節點變更而遞增。 | | **`cluster_my_epoch`** | **當前連線的這個節點自身的 Epoch**。 | | **`cluster_stats_messages_*`** | **心跳包 (Gossip Protocol) 統計**。顯示節點間互相發送 `ping`/`pong` 訊息的次數。 | --- ## `redis-cli cluster nodes` 詳細解析 ```bash kubectl exec -it redis-cluster-0 -- redis-cli cluster nodes ``` 這個指令會列出**集群內「每一個節點」的詳細身分、角色與主從連線狀態**。 執行的輸出範例通常長這樣: ```text d81a9f83... 10.244.0.5:6379@16379 myself,master - 0 1689230000 1 connected 0-5460 a28b7e1c... 10.244.0.8:6379@16379 slave d81a9f83... 0 1689230001 4 connected 5f83b1a4... 10.244.0.6:6379@16379 master - 0 1689230002 2 connected 5461-10922 e12f451b... 10.244.0.9:6379@16379 slave 5f83b1a4... 0 1689230000 5 connected 8c39a2d9... 10.244.0.7:6379@16379 master - 0 1689230001 3 connected 10923-16383 b983c21a... 10.244.0.10:6379@16379 slave 8c39a2d9... 0 1689230002 6 connected ``` 每一行代表一個節點,包含 **8 個欄位**(如果有指派 Slot 則為 9 個): ### 欄位解析(由左至右) ```text [1. Node ID] [2. IP:Port@BusPort] [3. Flags] [4. Master ID] [5. Ping] [6. Pong] [7. Epoch] [8. Link State] [9. Slots] ``` 1. **Node ID** (例如 `d81a9f83...`): * 每個節點隨機產生的 40 位元 Hash 唯一標籤,即便 IP 改變,Node ID 也不會變。 2. **IP:Port@ClusterBusPort** (例如 `10.244.0.5:6379@16379`): * 節點的 IP、服務埠號 (`6379`),以及集群內部溝通用(Gossip 協定)的 Bus 埠號 (`16379`)。 3. **Flags (節點標籤/狀態)**: * `myself`:代表你**當前執行指令連到的那台節點**。 * `master`:該節點是 **Master**。 * `slave`:該節點是 **Slave**。 * `fail?` / `fail`:代表該節點處於疑似離線 (`pfail`) 或已被確認離線 (`fail`)。 4. **Master ID** (例如 `d81a9f83...` 或 `-`): * 如果該列是 **Slave**,這裡會寫它**跟隨的 Master Node ID**(可藉此判斷誰是誰的從屬)。 * 如果該列本身是 **Master**,這裡會顯示 `-`。 5. **Ping sent**: * 離上一次向該節點發送 Ping 心跳包經過的毫秒數(`0` 表示目前正常)。 6. **Pong received**: * 離上一次收到該節點回應 Pong 經過的時間戳(Unix Epoch milliseconds)。 7. **Config Epoch**: * 該節點的配置版本號。 8. **Link State (連線狀態)**: * `connected`:網路連線正常。 * `disconnected`:節點斷開連線。 9. **Slots (哈希槽範圍)** *(僅 Master 節點會有此欄位)*: * 顯示該 Master 負責處理解析的 Slot 區間(例如 `0-5460`)。三主架構通常會均分這 16384 個 Slot。 ## 直接用 Pod 的 DNS 名稱初始化集群! 改用 StatefulSet + Headless Service 提供給 Pod 的固定 DNS 域名(格式為 `<pod-name>.<service-name>`): ```bash kubectl exec -it redis-cluster-0 -- redis-cli --cluster create \ redis-cluster-0.redis-cluster-service:6379 \ redis-cluster-1.redis-cluster-service:6379 \ redis-cluster-2.redis-cluster-service:6379 \ redis-cluster-3.redis-cluster-service:6379 \ redis-cluster-4.redis-cluster-service:6379 \ redis-cluster-5.redis-cluster-service:6379 \ --cluster-replicas 1 ``` ## 新 Pod 加入為指定 Master 的 Slave ```bash kubectl exec -it redis-cluster-0 -- redis-cli --cluster add-node redis8-cluster-0.redis-cluster-service:6379 redis-cluster-0.redis-cluster-service:6379 --cluster-slave --cluster-master-id <MASTER_NODE_ID> ``` ### 集群中剔除舊的 Slave ```bash kubectl exec -it redis-cluster-0 -- redis-cli --cluster del-node redis-cluster-0.redis-cluster-service:6379 <OLD_SLAVE_NODE_ID> ```