---
# System prepended metadata

title: '[應用] redis 部署與測試教學（含注意事項）'
tags: [k8s, K8s / app, 使用教學, app, redis, Kubernetes]

---

[應用] 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 %}