[應用] redis 部署與測試教學(含注意事項)
===
###### tags: `K8s / app`
###### tags: `Kubernetes`, `k8s`, `app`, `redis`, `使用教學`
<br>
[TOC]
<br>
:::info
## 最常用的教學流程
```bash
# 建立 Namespace
kubectl create namespace demo
# 部署 Redis Pod & Service
kubectl -n demo run redis --image=redis --restart=Always
kubectl -n demo expose pod/redis --name=redis --port=6379
# 測試 redis 連線 & 操作 redis
kubectl -n demo run -it --rm --image=redis --restart=Never redis-shell -- bash
# 進 shell 再下
redis-cli -h redis
# 操作名為 "hello" 的 queue
# redis:6379> LRANGE hello 0 -1 # [] (不用事先建立 "hello")
# redis:6379> LPUSH hello apple # ["apple"]
# redis:6379> LPUSH hello banana # ["banana", "apple"]
# redis:6379> RPUSH hello cherry # ["banana", "apple", "cherry"]
# redis:6379> LRANGE hello 0 -1 # ["banana", "apple", "cherry"]
# redis:6379> LLEN hello # 3
# redis:6379> LPOP hello # ["apple", "cherry"]
# redis:6379> RPOP hello # ["apple"]
# redis:6379> LRANGE hello 0 -1 # ["apple"]
# redis:6379> LLEN hello # 1
# redis:6379> DEL hello # []
```
:::
<br>
> by gpt4.1 (2025/08/01)
## 步驟 1:建立 Namespace(可選)
建議所有測試資源放進一個專屬 namespace,方便管理和清理。
```bash
kubectl create namespace demo
```
<br>
## 步驟 2:部署 Redis Pod & Service
### 建立 Redis Pod
```bash
kubectl -n demo run redis --image=redis --restart=Always
```
> 注意:這會建一個名為 `redis` 的 Pod
### 暴露 Redis Service
```bash
kubectl -n demo expose pod/redis --name=redis --port=6379
```
> 這樣會有一個名為 `redis`、type=ClusterIP 的 service,K8s 內部可用
> Service 名稱將決定你後續 redis-cli 連線時要用的 host 名稱
<br>
## **步驟 3:確認 Redis 運作**
```bash
kubectl -n demo get pods
kubectl -n demo get svc
```
或是:
```bash
kubectl -n demo get all
```
> 應該看到一個 Running 的 redis pod 與 redis 服務,port 6379。
<br>
## **步驟 4:用 redis-cli 測試連線與操作**
### **正確安全的「互動式」進入方法**
> 由於 K8s TTY attach 行為,**建議永遠用「先進 shell,再下 redis-cli」**,保證互動正常。
```bash
kubectl -n demo run -it --rm --image=redis --restart=Never redis-shell -- bash
```
進到 shell 後執行:
```bash
redis-cli -h redis
```
你應該看到:
```
redis:6379>
```
這時可自由互動!
<br>
---
### **常用 Redis 指令教學**
> 指令:不區分大小寫
| 動作 | 指令 | 說明 |
| ------- | ------------------- | -------------------- |
| 存資料 | `SET foo bar` | 設 foo=bar |
| 取資料 | `GET foo` | 查 foo |
| 發送訊息 | `LPUSH hello msg1` | 將 msg1 推入 hello list |
| 取出訊息 | `LPOP hello` | 從 hello 取出一個 |
| 看所有訊息 | `LRANGE hello 0 -1` | 查 hello 目前全部內容 |
| 查數量 | `LLEN hello` | 查 hello 目前有幾筆 |
| 清空 list | `DEL hello` | 刪除 hello 這個 list |
<br>
---
### 一次性(非互動)指令測試法
如果只想查詢而不需進互動模式:
```bash
kubectl -n demo run -it --rm --image=redis --restart=Never redis-cli-tmp -- redis-cli -h redis LRANGE hello 0 -1
```
這會直接查詢 hello list 的所有內容,然後退出。
<br>
## 補充:多種錯誤與注意事項
### 1. Pod 名稱/Service 名稱/Namespace 必須對齊!
- 如果你 expose 的 service 叫 `myredis`,你連線要寫 `redis-cli -h myredis`
- 指令中的 `-n demo` 要和你的 pod/service 所在 namespace 一致
### 2. 直接用 `kubectl run ... redis-cli ...` 容易卡住或沒 prompt
- 這是 K8s attach 與互動命令的機制問題,跟你的指令無關
- 保險作法是「先進 shell,再下 redis-cli」
### 3. redis 官方 image 有時沒 bash,只能用 sh
- 安全起見一律用 `sh`
- 若你確定 image 有 bash,當然也可用 bash
### 4. Service/Pod 沒 ready 會導致 redis-cli 卡住
- 若 service 或 pod 沒起來,redis-cli 會等不到主機,畫面一直卡在連線
### 5. 不要忘了 `--restart=Never`
- 若不用 `--restart=Never`,Pod 可能會以 Deployment 形式啟動,造成 Pod 一結束又自動重建,不利測試和管理
### 6. 善用 --rm 自動清除測試 Pod
- `--rm` 保證 Pod 結束即自動刪除,避免殘留
### 7. **清理所有資源**
最後測完可以一鍵刪除 namespace,回到乾淨狀態:
```bash
kubectl delete namespace demo
```
### 8. 遇到問題怎麼辦?
1. 先確認 pod 和 service 都在 Running
2. 檢查 namespace/名稱有沒有打錯
3. 用 busybox 測 service DNS
```bash
kubectl -n demo run -it --rm --image=busybox --restart=Never test -- nslookup redis
```
<br>
## 討論
### redis-cli 指令,為何要進到 shell 後,才執行 `redis-cli -h redis`?
```
kubectl -n demo run -it --rm --image=redis --restart=Never redis-shell -- redis-cli -h redis
```
1. 會卡住沒有反應...
2. 因為本地的 terminal 還沒連接上 Pod 內的標準輸入(stdin)、標準輸出(stdout)、標準錯誤(stderr)。
3. `redis-cli` 判斷自己不是互動終端機(isatty() 為 false),就進入「非互動模式」或直接結束、什麼都不顯示
- ### 核心說明
> gpt-4.1 (2025/08/01)
K8s 的 `kubectl run` 在「直接執行互動式指令」時,attach/tty 行為有時間差、stdin 問題,甚至各版本 Kubernetes、kubectl、底層 containerd/Docker 行為都可能不同。
### 1. **為何「直接 run redis-cli」可能卡住/沒 prompt?**
* `kubectl run ... -- sh -c "redis-cli -h redis"`
這個用法,**你會預期應該進 redis-cli prompt**,但現實上 K8s/Tty attach 時機不對,導致 redis-cli 可能沒法偵測到 tty,或 stdin 沒 attach 成功,最後就沒有互動 prompt(或你打什麼都沒反應)。
* **這現象有很高機率出現在不同 kubectl/k8s 版本,或你 cluster 的 runtime 實作跟 CLI 工具本身互動偵測方式不合。**
* 這也是為什麼大部分 K8s 高手都**強烈建議:真的要做互動,一律直接 exec 到一個已 attach 的 shell,再下互動命令**!
### 2. **為什麼進 shell 再下 redis-cli 就正常?**
* 你在已經「attach 完整、stdin/stdout 都建立」的 shell 裡啟動 redis-cli,這時不會 race,stdin/out/tty 都正常,redis-cli 自然偵測得到。
### 3. **這不是只有你會遇到**
* 很多 user、甚至老手都遇過這種「直接執行互動命令沒反應、進 shell 再執行就正常」的狀況。
* 甚至**同一個指令在不一樣的 k8s/dockerd/containerd 環境會有不同結果**。
---
### 你可以親自實驗:
1. **進 shell 再下**:
```bash
kubectl -n demo run -it --rm --image=redis \
--restart=Never redis-shell -- sh
# 在 shell 內下 redis-cli -h redis
```
幾乎百分百能進 prompt。
2. **直接下 redis-cli(sh -c 也一樣)**:
```bash
kubectl -n demo run -it --rm --image=redis \
--restart=Never redis-client -- sh -c "redis-cli -h redis"
```
很容易「沒 prompt」、「沒互動」,甚至打什麼都沒回應!
3. **[補充] 可以 work 的版本**:延遲執行 `redis-cli -h redis`
```bash
$ kubectl -n demo run -it --rm --image=redis \
--restart=Never redis-client -- sh -c "sleep 5; redis-cli -h redis"
If you don't see a command prompt, try pressing enter.
redis:6379>
```
---
### 實用建議
1. **想要「100%互動」→ 永遠用進 shell 再執行互動命令!**
2. **如果只是要跑一次性 command,可直接下,但不要預期有 prompt。**
3. **你遇到的就是業界實際會遇到的坑,所以大家都推薦用 shell 包起來做互動。**
<br>
### [接續] 解釋 Kubernetes 為何「直接執行互動式命令」時常常會卡住沒 prompt,而「先進到 shell 再手動執行互動命令」就沒事?
- ### gpt-4.1 (2025/08/01)
## **什麼叫 shell attach?它 attach 到哪裡?**
### 1. **Attach 是什麼?**
* 「attach」意思是你的終端機(本地 terminal)**連接上 Pod 內的標準輸入(stdin)、標準輸出(stdout)、標準錯誤(stderr)**。
* 這樣你在本機輸入、Pod 內程式會收到;Pod 輸出會出現在你螢幕上。
<br>
### 2. **Kubernetes Pod 啟動時序**(以 kubectl run 為例)
1. 你下 `kubectl run ... -- sh -c "redis-cli -h redis"`
2. k8s 先建一個 Pod、啟動 Container
3. Container 啟動後,立刻開始執行 command(這時通常是 sh -c "redis-cli -h redis")
4. **同時 kubectl 這邊再嘗試將你的本地 terminal attach 上 container 的 stdin/stdout/tty**
<br>
### 3. **Race Condition(賽跑現象)**
* **重點:attach 不一定比 command 執行還快!**
* 有時候 container 一啟動,command(redis-cli)就已經在跑
* 這時 attach 還沒來得及連上去,redis-cli 啟動時看不到標準輸入/tty
* redis-cli 判斷自己不是互動終端機(isatty() 為 false),就進入「非互動模式」或直接結束、什麼都不顯示
* 如果是進 shell(sh、bash),它本身很擅長等 stdin/stdout attach 好再進入互動
* 你這時下 redis-cli,已經 attach 完了,所以一切正常
<br>
### 4. **底層原理:isatty() 判斷**
* 幾乎所有 CLI 工具(redis-cli、mysql、psql、python REPL...)都會判斷 stdin 是否是「tty」(互動終端機)
* 如果不是,進入非互動模式(沒 prompt,執行一行就退出,或卡住)
<br>
---
## **流程圖(邏輯順序)**
```mermaid
graph TD
A[你下 kubectl run ... -- sh -c redis-cli -h redis]
B[Pod 建立,Container 啟動]
C[redis-cli 開始跑]
D[kubectl 對接 stdin/stdout/tty]
E[redis-cli 檢查 isatty 狀態]
F[若非互動模式,沒 prompt]
A --> B --> C
B --> D
C --> E
D --> E
E --> F
```
---
## 用 shell 的差別
* shell(sh、bash)設計來等待 stdin/stdout/tty 都 attach 完畢才進互動模式,所以永遠不會出這問題。
* redis-cli、mysql 等啟動就判斷是否 tty,判斷到非互動就不理你。
---
## **Docker 本地和 K8s 的差異**
* 本地 docker run 幾乎總是 attach 很快,幾乎不會遇到這問題。
* K8s 必須先排程 pod,建立 container,然後 kubelet 才能幫你 attach,會有短暫時間差。
---
## **總結:為什麼「先進 shell」就安全?**
* shell 已經 attach 好,之後所有子程序(redis-cli)都能正確得到 tty,永遠能互動。
* 直接 run 互動命令有機率來不及 attach(race),tty 判斷失敗,導致卡住或沒 prompt。
---
## **專業建議**
* 任何需要「人機互動 prompt」的命令,在 K8s 永遠「先進 shell,再執行互動命令」!
* 若只是一次性查詢(不互動),直接 run 即可。
<br>
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}