14 | 深入解析Pod对象(一):基本概念
生活随笔
收集整理的這篇文章主要介紹了
14 | 深入解析Pod对象(一):基本概念
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
今天我和你分享的主題是:深入解析 Pod 對象之基本概念。在上一篇文章中,我詳細介紹了 Pod 這個 Kubernetes 項目中最重要的概念。而在今天這篇文章中,我會和你分享 Pod 對象的更多細節。現在,你已經非常清楚:Pod,而不是容器,才是 Kubernetes 項目中的最小編排單位。將這個設計落實到 API 對象上,容器(Container)就成了 Pod 屬性里的一個普通的字段。那么,一個很自然的問題就是:到底哪些屬性屬于 Pod 對象,而又有哪些屬性屬于 Container 呢?要徹底理解這個問題,你就一定要牢記我在上一篇文章中提到的一個結論:Pod 扮演的是傳統部署環境里“虛擬機”的角色。這樣的設計,是為了使用戶從傳統環境(虛擬機環境)向 Kubernetes(容器環境)的遷移,更加平滑。而如果你能把 Pod 看成傳統環境里的“機器”、把容器看作是運行在這個“機器”里的“用戶程序”,那么很多關于 Pod 對象的設計就非常容易理解了。比如,凡是調度、網絡、存儲,以及安全相關的屬性,基本上是 Pod 級別的。這些屬性的共同特征是,它們描述的是“機器”這個整體,而不是里面運行的“程序”。比如,配置這個“機器”的網卡(即:Pod 的網絡定義),配置這個“機器”的磁盤(即:Pod 的存儲定義),配置這個“機器”的防火墻(即:Pod 的安全定義)。更不用說,這臺“機器”運行在哪個服務器之上(即:Pod 的調度)。
Pod 重要字段的含義和用法
接下來,我就先為你介紹 Pod 中幾個重要字段的含義和用法。NodeSelector:是一個供用戶將 Pod 與 Node 進行綁定的字段,用法如下所示:apiVersion: v1 kind: Pod ... spec:nodeSelector:disktype: ssd這樣的一個配置,意味著這個 Pod 永遠只能運行在攜帶了“disktype: ssd”標簽(Label)的節點上;否則,它將調度失敗。NodeName:一旦 Pod 的這個字段被賦值,Kubernetes 項目就會被認為這個 Pod 已經經過了調度,調度的結果就是賦值的節點名字。所以,這個字段一般由調度器負責設置,但用戶也可以設置它來“騙過”調度器,當然這個做法一般是在測試或者調試的時候才會用到。HostAliases:定義了 Pod 的 hosts 文件(比如 /etc/hosts)里的內容,用法如下:apiVersion: v1kind: Pod...spec:hostAliases:- ip: "10.1.2.3"hostnames:- "foo.remote"- "bar.remote"...在這個 Pod 的 YAML 文件中,我設置了一組 IP 和 hostname 的數據。這樣,這個 Pod 啟動后,/etc/hosts 文件的內容將如下所示:cat /etc/hosts# Kubernetes-managed hosts file.127.0.0.1 localhost...10.244.135.10 hostaliases-pod10.1.2.3 foo.remote10.1.2.3 bar.remote其中,最下面兩行記錄,就是我通過 HostAliases 字段為 Pod 設置的。需要指出的是,在 Kubernetes 項目中,如果要設置 hosts 文件里的內容,一定要通過這種方法。否則,如果直接修改了 hosts 文件的話,在 Pod 被刪除重建之后,kubelet 會自動覆蓋掉被修改的內容。除了上述跟“機器”相關的配置外,你可能也會發現,凡是跟容器的 Linux Namespace 相關的屬性,也一定是 Pod 級別的。這個原因也很容易理解:Pod 的設計,就是要讓它里面的容器盡可能多地共享 Linux Namespace,僅保留必要的隔離和限制能力。這樣,Pod 模擬出的效果,就跟虛擬機里程序間的關系非常類似了。舉個例子,在下面這個 Pod 的 YAML 文件中,我定義了 shareProcessNamespace=true:apiVersion: v1kind: Podmetadata:name: nginxspec:shareProcessNamespace: truecontainers:- name: nginx? ? image: nginx? - name: shellimage: busyboxstdin: truetty: true這就意味著這個 Pod 里的容器要共享 PID Namespace。而在這個 YAML 文件中,我還定義了兩個容器:一個是 nginx 容器,一個是開啟了 tty 和 stdin 的 shell 容器。我在前面介紹容器基礎時,曾經講解過什么是 tty 和 stdin。而在 Pod 的 YAML 文件里聲明開啟它們倆,其實等同于設置了 docker run 里的 -it(-i 即 stdin,-t 即 tty)參數。如果你還是不太理解它們倆的作用的話,可以直接認為 tty 就是 Linux 給用戶提供的一個常駐小程序,用于接收用戶的標準輸入,返回操作系統的標準輸出。當然,為了能夠在 tty 中輸入信息,你還需要同時開啟 stdin(標準輸入流)。于是,這個 Pod 被創建后,你就可以使用 shell 容器的 tty 跟這個容器進行交互了。我們一起實踐一下:$ kubectl create -f nginx.yaml接下來,我們使用 kubectl attach 命令,連接到 shell 容器的 tty 上:$ kubectl attach -it nginx -c shell這樣,我們就可以在 shell 容器里執行 ps 指令,查看所有正在運行的進程:$ kubectl attach -it nginx -c shell/ # ps axPID?? USER? ?? TIME? COMMAND1 root? ? ? 0:00 /pause8 root? ? ? 0:00 nginx: master process nginx -g daemon off;14 101? ? ?? 0:00 nginx: worker process15 root? ? ? 0:00 sh21 root? ? ? 0:00 ps ax可以看到,在這個容器里,我們不僅可以看到它本身的 ps ax 指令,還可以看到 nginx 容器的進程,以及 Infra 容器的 /pause 進程。這就意味著,整個 Pod 里的每個容器的進程,對于所有容器來說都是可見的:它們共享了同一個 PID Namespace。類似地,凡是 Pod 中的容器要共享宿主機的 Namespace,也一定是 Pod 級別的定義,比如:apiVersion: v1kind: Podmetadata:name: nginxspec:hostNetwork: truehostIPC: truehostPID: truecontainers:- name: nginximage: nginx- name: shellimage: busyboxstdin: truetty: true在這個 Pod 中,我定義了共享宿主機的 Network、IPC 和 PID Namespace。這就意味著,這個 Pod 里的所有容器,會直接使用宿主機的網絡、直接與宿主機進行 IPC 通信、看到宿主機里正在運行的所有進程。當然,除了這些屬性,Pod 里最重要的字段當屬“Containers”了。而在上一篇文章中,我還介紹過“Init Containers”。其實,這兩個字段都屬于 Pod 對容器的定義,內容也完全相同,只是 Init Containers 的生命周期,會先于所有的 Containers,并且嚴格按照定義的順序執行。Kubernetes 項目中對 Container 的定義,和 Docker 相比并沒有什么太大區別。我在前面的容器技術概念入門系列文章中,和你分享的 Image(鏡像)、Command(啟動命令)、workingDir(容器的工作目錄)、Ports(容器要開發的端口),以及 volumeMounts(容器要掛載的 Volume)都是構成 Kubernetes 項目中 Container 的主要字段。不過在這里,還有這么幾個屬性值得你額外關注。首先,是 ImagePullPolicy 字段。它定義了鏡像拉取的策略。而它之所以是一個 Container 級別的屬性,是因為容器鏡像本來就是 Container 定義中的一部分。ImagePullPolicy 的值默認是 Always,即每次創建 Pod 都重新拉取一次鏡像。另外,當容器的鏡像是類似于 nginx 或者 nginx:latest 這樣的名字時,ImagePullPolicy 也會被認為 Always。而如果它的值被定義為 Never 或者 IfNotPresent,則意味著 Pod 永遠不會主動拉取這個鏡像,或者只在宿主機上不存在這個鏡像時才拉取。其次,是 Lifecycle 字段。它定義的是 Container Lifecycle Hooks。顧名思義,Container Lifecycle Hooks 的作用,是在容器狀態發生變化時觸發一系列“鉤子”。我們來看這樣一個例子:apiVersion: v1kind: Podmetadata:name: lifecycle-demospec:containers:- name: lifecycle-demo-containerimage: nginxlifecycle:postStart:exec:command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]preStop:exec:command: ["/usr/sbin/nginx","-s","quit"]這是一個來自 Kubernetes 官方文檔的 Pod 的 YAML 文件。它其實非常簡單,只是定義了一個 nginx 鏡像的容器。不過,在這個 YAML 文件的容器(Containers)部分,你會看到這個容器分別設置了一個 postStart 和 preStop 參數。這是什么意思呢?先說 postStart 吧。它指的是,在容器啟動后,立刻執行一個指定的操作。需要明確的是,postStart 定義的操作,雖然是在 Docker 容器 ENTRYPOINT 執行之后,但它并不嚴格保證順序。也就是說,在 postStart 啟動時,ENTRYPOINT 有可能還沒有結束。當然,如果 postStart 執行超時或者錯誤,Kubernetes 會在該 Pod 的 Events 中報出該容器啟動失敗的錯誤信息,導致 Pod 也處于失敗的狀態。而類似地,preStop 發生的時機,則是容器被殺死之前(比如,收到了 SIGKILL 信號)。而需要明確的是,preStop 操作的執行,是同步的。所以,它會阻塞當前的容器殺死流程,直到這個 Hook 定義操作完成之后,才允許容器被殺死,這跟 postStart 不一樣。所以,在這個例子中,我們在容器成功啟動之后,在 /usr/share/message 里寫入了一句“歡迎信息”(即 postStart 定義的操作)。而在這個容器被刪除之前,我們則先調用了 nginx 的退出指令(即 preStop 定義的操作),從而實現了容器的“優雅退出”。在熟悉了 Pod 以及它的 Container 部分的主要字段之后,我再和你分享一下這樣一個的 Pod 對象在 Kubernetes 中的生命周期。Pod 生命周期的變化,主要體現在 Pod API 對象的Status 部分,這是它除了 Metadata 和 Spec 之外的第三個重要字段。其中,pod.status.phase,就是 Pod 的當前狀態,它有如下幾種可能的情況:Pending。這個狀態意味著,Pod 的 YAML 文件已經提交給了 Kubernetes,API 對象已經被創建并保存在 Etcd 當中。但是,這個 Pod 里有些容器因為某種原因而不能被順利創建。比如,調度不成功。Running。這個狀態下,Pod 已經調度成功,跟一個具體的節點綁定。它包含的容器都已經創建成功,并且至少有一個正在運行中。Succeeded。這個狀態意味著,Pod 里的所有容器都正常運行完畢,并且已經退出了。這種情況在運行一次性任務時最為常見。Failed。這個狀態下,Pod 里至少有一個容器以不正常的狀態(非 0 的返回碼)退出。這個狀態的出現,意味著你得想辦法 Debug 這個容器的應用,比如查看 Pod 的 Events 和日志。Unknown。這是一個異常狀態,意味著 Pod 的狀態不能持續地被 kubelet 匯報給 kube-apiserver,這很有可能是主從節點(Master 和 Kubelet)間的通信出現了問題。更進一步地,Pod 對象的 Status 字段,還可以再細分出一組 Conditions。這些細分狀態的值包括:PodScheduled、Ready、Initialized,以及 Unschedulable。它們主要用于描述造成當前 Status 的具體原因是什么。比如,Pod 當前的 Status 是 Pending,對應的 Condition 是 Unschedulable,這就意味著它的調度出現了問題。而其中,Ready 這個細分狀態非常值得我們關注:它意味著 Pod 不僅已經正常啟動(Running 狀態),而且已經可以對外提供服務了。這兩者之間(Running 和 Ready)是有區別的,你不妨仔細思考一下。Pod 的這些狀態信息,是我們判斷應用運行情況的重要標準,尤其是 Pod 進入了非“Running”狀態后,你一定要能迅速做出反應,根據它所代表的異常情況開始跟蹤和定位,而不是去手忙腳亂地查閱文檔。總結在今天這篇文章中,我詳細講解了 Pod API 對象,介紹了 Pod 的核心使用方法,并分析了 Pod 和 Container 在字段上的異同。希望這些講解能夠幫你更好地理解和記憶 Pod YAML 中的核心字段,以及這些字段的準確含義。實際上,Pod API 對象是整個 Kubernetes 體系中最核心的一個概念,也是后面我講解各種控制器時都要用到的。在學習完這篇文章后,我希望你能仔細閱讀 $GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/api/core/v1/types.go 里,type Pod struct ,尤其是 PodSpec 部分的內容。爭取做到下次看到一個 Pod 的 YAML 文件時,不再需要查閱文檔,就能做到把常用字段及其作用信手拈來。而在下一篇文章中,我會通過大量的實踐,幫助你鞏固和進階關于 Pod API 對象核心字段的使用方法,敬請期待吧。思考題你能否舉出一些 Pod(即容器)的狀態是 Running,但是應用其實已經停止服務的例子?相信 Java Web 開發者的親身體會會比較多吧。感謝你的收聽,歡迎你給我留言,也歡迎分享給更多的朋友一起閱讀。總結
以上是生活随笔為你收集整理的14 | 深入解析Pod对象(一):基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第3章:Kubernetes监控与日志管
- 下一篇: 32 | 浅谈容器网络