跳至主要內容
技術

kubectl 長得像 docker 是故意的嗎?PR 原文就告訴你「We want to mimic docker run」

kubectl 長得像 docker 是故意的嗎?PR 原文就告訴你「We want to mimic docker run」

讀一份官方對照表時冒出的問題:這麼像,是故意的嗎?

Kubernetes 官方文件裡有一頁叫〈kubectl for Docker Users〉,開頭第一段是這樣寫的:

Using kubectl is straightforward if you are familiar with the Docker command line tool. However, there are a few differences between the Docker commands and the kubectl commands.

接著就是一整頁的 docker 指令對 kubectl 指令。我一路往下讀,越讀越覺得眼熟:exec、logs、attach,連參數都差不多。

於是我腦中冒出一個問題:kubectl 的指令長得這麼像 docker,是設計者故意的,還是剛好?

對照表攤開來看:一半一比一,另一半完全對不上

先把官方那頁整理成一張表:

Dockerkubectl像不像
docker exec -it c shkubectl exec -it pod -- sh幾乎一樣(kubectl 要多一個 --)
docker logs -f ckubectl logs -f pod一樣
docker attach ckubectl attach pod一樣
docker cpkubectl cp一樣
docker versionkubectl version一樣
docker pskubectl get po不一樣
docker stop + docker rmkubectl delete不一樣
docker infokubectl cluster-info不一樣
docker login沒有官方原話:“There is no direct analog of docker login in kubectl.”

分成兩堆之後,規律就浮出來了:

  • 跟「一個正在跑的東西」互動的動作(進去下指令、看 log、接上輸出、複製檔案),kubectl 幾乎照抄 docker。
  • 管理「東西本身」的動作(列出、刪除、看狀態),kubectl 完全不用 docker 的詞,而是換成 get、delete、describe 這組通用動詞。
flowchart TD
    A[kubectl 指令] --> B{操作對象是什麼?}
    B -->|一個正在跑的 Pod/容器| C[沿用 docker 的動詞]
    B -->|叢集裡的任何資源| D[動詞 + 資源類型 + 名稱]
    C --> C1[exec / logs / attach / cp]
    D --> D1[get / delete / describe / apply]
    D1 --> D2[pod、service、deployment、CRD 通通適用]

第二堆為什麼不照抄?因為 kubectl 背後的心智模型跟 docker 不一樣。docker CLI 是命令式的,對象永遠是「一個容器」,所以有 ps、stop、rm、images 這些專屬動詞。kubectl 則是一個 HTTP client,打的是 API Server 上的 REST 資源(這點在〈我送出去的 YAML 只有 8 行,存回來的多了一倍〉用 -v=6 實際看過,就是一行 HTTP 請求)。REST 資源只需要一組動詞,換什麼資源都一樣:

kubectl get pods
kubectl get services
kubectl delete deployment web
kubectl describe node worker-1

如果照 docker 的做法,每種資源都要一組自己的動詞,Kubernetes 光內建資源就有幾十種,再加上使用者自己定義的 CRD,根本不可能這樣設計。這也是為什麼不會有 kubectl ps。

從 kubectl run 就可以發現兩者的關聯

講到這裡,我的結論是:「動詞刻意借用 docker,但只要跟 K8s 的模型衝突,官方就會犧牲相似度。」

我還準備了一個自認很漂亮的例子:kubectl run。

早期的 kubectl run 名字像 docker run,實際上會偷偷幫你建一個 Deployment(背後再長出 ReplicaSet 跟 Pod)。到了 v1.18,它被砍到只剩建立單一 Pod,要建 Deployment 得改用 kubectl create deployment。我當時的解讀是:看吧,模仿 docker 走不下去,官方就回到 K8s 自己的模型了。

這個說法聽起來很合理,但我手上沒有任何一手來源。所以我去翻了當年的 PR。

v1.12 開始 deprecate 那批 generator 的 PR #68132,作者 soltysh 在描述裡寫得很清楚:

The direction is that we want to move away from kubectl run because it’s over bloated and complicated for both users and developers. We want to mimic docker run with kubectl run so that it only creates a pod, and if you’re interested in other resources kubectl create is the intended replacement.

兩年後在 v1.18 真正把 generator 全部移除的 PR #87077,release note 只有一句:

Remove all the generators from kubectl run. It will now only create pods.

我的推論整個反過來了。v1.18 之前的 kubectl run 只是名字像 docker run,行為跟它根本不一樣:docker run 跑一個容器,kubectl run 卻會建出三層物件。v1.18 做的事情是讓它連語意也對齊 docker run,一個指令只跑一個東西,其他資源交給 kubectl create。

所以「故意的嗎?」這個問題,現在可以用 maintainer 自己的話回答:至少 kubectl run 這一個指令,是白紙黑字寫著要模仿 docker run。

反思

技術面

kubectl 裡有兩套設計邏輯同時存在,而且分工很清楚。

跟單一工作負載互動的指令(run、exec、logs、attach、cp)刻意貼近 docker,讓已經會 docker 的人第一天就能上手。管理資源的指令(get、delete、describe、apply)則完全照 REST 資源模型設計,一組動詞通吃所有資源類型。

kubectl run 的故事也說明了這兩套邏輯之間的界線:一個指令如果名字借了 docker,行為就應該跟 docker 一樣,只做一件事。想做更多事的,就該去 create、apply 那一邊。

心態面

我最在意的不是一開始猜錯,而是那個錯誤的推論聽起來非常合理。它有時間軸、有前因後果,還剛好支持我前面的論點。如果沒去查,我會很有自信地把它寫出來,讀者也很可能照單全收。

推論出來的「反證」比單純說「我不知道」更危險,因為它長得像結論。而把它翻過來的成本,只是打開一個 PR、讀三行描述。從此以後,只要我要寫「官方為什麼這樣設計」,就會先去找當年的 PR 或 KEP,而不是自己補一個看起來合理的故事。

有趣發現

v1.18 那次改動讓不少既有教學一夕失效。Alex Ellis 直接寫了一篇〈Kubernetes 1.18 broke “kubectl run”, here’s what to do about it〉;《Kubernetes Up & Running》的配套 repo 也有人開 issue 回報,書裡 Job 章節用 kubectl run 建 Job 的指令,在 1.18 之後只會建出一個 Pod。「為了更像 docker」而做的改動,反而讓一批寫給 kubectl 使用者的教學壞掉,這個副作用我覺得有點諷刺。

kubectl 長得像 docker,有一部分是真的故意的,而且故意到連 PR 都寫明了;只是這份相似只停留在「跟一個正在跑的東西打交道」的那幾個動詞,其他部分走的是 Kubernetes 自己的路。

留言討論

esc
輸入關鍵字搜尋文章...
查看收藏 →