# 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>
```