在之前的兩篇文章中, 先後介紹瞭如何從原始碼開始構建一個Node.js應用和Spring Boot 應用, 並且部署到Kubernetes 中(這個Kubernetes 環境主要是之前建好的#minikube#) , 現在我們開始進一步深入的學習Kubernetes, 用一個個可以實際執行的例子的形式, 深入理解Kubernetes的概念以及原理.
本篇是動手動腦學Kubernetes系列的第一篇, 動手動腦學Kubernetes, 就是先動手體驗, 然後再理論學習, 讓你從例子中體會Kubernetes 的具體用法, 然後再去學習Kubernetes的理論知識, 達到實際操作的昇華!
首先來從Pod 開始!
先從映象開始我們之前的文章裡面, 都是先從原始碼開始, 然後製作映象, 這次我們省略原始碼的步驟, 直接從映象註冊中心拉取一個映象.
本著先動手, 在動腦的方法, 我們先來下載一個Docker 映象, 這次我們需要使用DockerHub 之外的另外一個映象註冊中心: Quay.io.
Quay.io是什麼呢? 這就得先從映象註冊中心(Container Registry)開始談起了.
什麼是容器登錄檔(Container Registry)?容器登錄檔的用途非常簡單明瞭:它們託管您建立的容器和/或其他人建立並要使用的容器的映像。它們還允許您上載容器映像的新版本,並跟蹤多次迭代中的版本歷史。
從這個意義上講,容器登錄檔類似於GitHub(或GitLab,或者國內的Gitee)儲存庫,不同之處在於容器登錄檔託管容器映像而不是原始碼。
確實,與容器登錄檔相關的一些關鍵術語也與Git相似。例如,用於從儲存庫下載容器映像的術語是pull,它或多或少與Git中的pull命令等效。同樣,push是Docker CLI和Git中的命令,用於將容器映象或原始碼上傳到登錄檔或儲存庫。
實際上,我們認為,如果我們將容器登錄檔稱為容器儲存庫,那麼會減少混亂。
集裝箱登記處的型別
那裡有很多容器登錄檔。它們可以分為幾個不同的類別:
基於雲的登錄檔:由第三方提供商(例如Docker Hub)託管在公共雲中的登錄檔。本地登錄檔:您可以在自己的基礎結構上安裝的登錄檔。您可以使用Docker,透過Docker Registry(不同於Docker Hub)或使用第三方登錄檔(如Quay)手動建立本地登錄檔。公共登錄檔:任何人都可以訪問的登錄檔。 Docker Hub就是一個例子。 Quay.io也是如此,它是使用Quay構建的託管公共登錄檔。 (要清楚,您可以將Quay本身安裝在基礎結構上; Quay.io是Quay的託管實現,旨在供公眾訪問。)私人登錄檔:需要許可權才能上載或下載容器映像的登錄檔。即使可以公開訪問許多Docker Hub登錄檔,也可以使用Docker Hub之類的服務來建立私有登錄檔。這次我們不採用DockerHub的映象, 而是使用另外一個映象, Quay.io, 一個Redhat OpenShift 支援的註冊中心, 實際上用法差不多.
Quay.io是一個容器登錄檔,用於儲存容器,Helm圖表和其他與容器相關的內容。 對於想要建立自己的公共儲存庫的人來說,該服務是免費的;如果您要建立私人儲存庫,則需要付費。 Quay.io還包括用於構建和掃描映象的功能。
不過你首先需要登入到Quay.io 頁面上去建立賬號:
然後需要在命令列裡登入一下:
vis
輸入配置好的賬號密碼就可以了, 然後可以嘗試下載一個映象試一下:
docker pull quay.io/openshiftlabs/simpleservice:0.5.0
在Kubernetes中執行映象, 建立Pod
沒有問題之後, 就可以在minikube 上面直接執行這個映象:
kubectl run sise --image=quay.io/openshiftlabs/simpleservice:0.5.0 --port=9876
這裡面需要注意的一個點就是, 老版本的kubectl版本將產生一個Deployment資源,而較新的版本將產生單個pod資源。
我們來驗證一下執行的結果:
kubectl get podskubectl get deployments
執行結果:
從結果中我們可以看出, sise 這個pod 是建立成功了而且處於執行狀態; 而並沒有產生Deployment 資源.而且, 這就與我們之前的練習結果有所不同, 之前都是先建立一個Deployment 檔案, 透過這個檔案來指明需要建立的Deployment 資源, Kubernetes 根據這個Deployment 描述來建立Pod, 而這次直接使用kubectl run --image, 也可以產生pod 資源.
來看看kubectl run的作用, 學習各種命令的最好的方法, 就是看幫助文件, 因此我們來看這個kubectl run --help:
[vagrant@control-plane ~]$ kubectl run --help
Create and run a particular image in a pod.
Examples:
# Start a nginx pod.
kubectl run nginx --image=nginx
# Start a hazelcast pod and let the container expose port 5701.
kubectl run hazelcast --image=hazelcast/hazelcast --port=5701
# Start a hazelcast pod and set environment variables "DNS_DOMAIN=cluster" and
"POD_NAMESPACE=default" in the container.
kubectl run hazelcast --image=hazelcast/hazelcast --env="DNS_DOMAIN=cluster"
--env="POD_NAMESPACE=default"
# Start a hazelcast pod and set labels "app=hazelcast" and "env=prod" in the container.
kubectl run hazelcast --image=hazelcast/hazelcast --labels="app=hazelcast,env=prod"
# Dry run. Print the corresponding API objects without creating them.
kubectl run nginx --image=nginx --dry-run=client
# Start a nginx pod, but overload the spec with a partial set of values parsed from JSON.
kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'
# Start a busybox pod and keep it in the foreground, don't restart it if it exits.
kubectl run -i -t busybox --image=busybox --restart=Never
# Start the nginx pod using the default command, but use custom arguments (arg1 .. argN) for that
command.
kubectl run nginx --image=nginx -- <arg1> <arg2> ... <argN>
# Start the nginx pod using a different command and custom arguments.
kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
可以看到, kubectl 可以根據容器映象直接生成一個Pod 資源, 可以指定這個Pod 資源的埠, 設定Pod 資源裡面執行容器的變數, 還可以指定dry run(也就是乾燒, 只冒煙不實際執行), 相當於模擬執行.
之前我們使用kubectl 建立Deployment 部署檔案的時候, 其實也藉助了這個kubectl create --dry-run選項, 這個是kubectl 一個比較強大的功能!
kubectl create deployment hello-springwebflux --image=hintcnuie/hello-springwebflux --dry-run=client -o=yaml > deployment.yaml
回到我們這個例子, 此容器映象包含curl的命令,因此我們可以用另一種方式來驗證主web服務程序是否正在響應(至少在本地網路上):
kubectl exec sise -t -- curl -s localhost:9876/info
結果:
$ kubectl exec sise -t -- curl -s localhost:9876/info
{"host": "localhost:9876", "version": "0.5.0", "from": "127.0.0.1"}
可以看到, 我們直接呼叫在Pod 資源裡面執行的容器的命令, 成功呼叫了容器裡面的本機埠, 來看看kubectl exec的用法:
Execute a command in a container.Examples: # Get output from running 'date' command from pod mypod, using the firstcontainer by default kubectl exec mypod -- date # Get output from running 'date' command in ruby-container from pod mypod kubectl exec mypod -c ruby-container -- date # Switch to raw terminal mode, sends stdin to 'bash' in ruby-container frompod mypod # and sends stdout/stderr from 'bash' back to the client kubectl exec mypod -c ruby-container -i -t -- bash -il # List contents of /usr from the first container of pod mypod and sort bymodification time. # If the command you want to execute in the pod has any flags in common (e.g.-i), # you must use two dashes (--) to separate your command's flags/arguments. # Also note, do not surround your command and its flags/arguments with quotes # unless that is how you would execute it normally (i.e., do ls -t /usr, not"ls -t /usr"). kubectl exec mypod -i -t -- ls -t /usr # Get output from running 'date' command from the first pod of the deploymentmydeployment, using the first container by default kubectl exec deploy/mydeployment -- date # Get output from running 'date' command from the first pod of the servicemyservice, using the first container by default kubectl exec svc/myservice -- date
可以看到, kubectl exec 可以直接在Pod裡面的容器裡面執行命令, 所要執行的命令透過-- 後面的字串指定, 透過-c 來指定是Pod 中的哪個容器, 要知道Pod 裡面的容器, 有可能是一個, 也有可能會是多個(這稱之為sidecar模式), 所以可以用-c 來進一步過濾.
kubectl exec可以線上檢查Pod 容器情況, 相當於開啟在Pod 裡面的容器裡面打開了一個命令列, 因而對於故障排查非常有幫助.
kubectl delete pod,deployment sise
Pod 究竟是什麼
來看看Pod 的正式定義:
Pod是Kubernetes中可以建立和管理的最小的可部署計算單元。
Pod是一組一個或多個容器,具有共享的儲存和網路資源,以及有關如何執行這些容器的規範。 Pod的內容始終位於同一位置,並在同一時間安排,並在共享上下文中執行。
Pod為特定於應用程式的“邏輯主機”建模:它包含一個或多個相對緊密耦合的應用程式容器。 在非雲環境中,在同一物理或虛擬機器上執行的應用程式類似於在同一邏輯主機上執行的雲應用程式。
除應用程式容器外,Pod還可包含在Pod啟動期間執行的init容器。 如果叢集提供此功能,則還可以注入臨時容器進行除錯。
這一段的定義很是簡單明瞭, 就是說Pod 是Kubernetes建立和管理的最小單元, 裡面包含一個或者多個容器, 而且共享上下文(Context), 在這裡有一個比喻, 我看很多國內的文件裡面並沒有講到, 可以擴充套件一下.
首先要講一下Pod 這個詞, Pod, 原意是一個幾何詞語。球形或方形的小物件,
Pod這個詞我們其實是不陌生的, 我第一反應就是,iPod, 當年喬布斯推廣的音樂播放器, 現在來看也是很漂亮的一個裝置.
Apple 公司似乎對於Pod 這個名字是情有獨鍾, 現在流行的Air Pods, 仍然是以Pod 結尾:
Air Pods
現在有這麼幾個含義:
豆莢, 扁豆類植物的長而狹窄的扁平部分,例如豆類和豌豆,含有種子,通常皮厚. 這個是植物方面的一個名詞, 比如豌豆莢,我們玩的植物大戰殭屍的豌豆戰士...看看這個豌豆戰士, 是不是很形象地表達出豆莢的含義?
細長容器(例如安裝在飛機上的),用於運輸發動機、武器、以及額外的燃料等, 比如飛機的吊艙, 航空艙等。其實本質意思可以用"匣" 這個漢字來表達. 匣子, 就是特指這類細長類物體的容器.
這個是一個現代的用語, 同時有一個引申的用法, 就是是特指小型的簡單建築物,或建築物中的小型簡單結構,通常是圓形的. 對於這類物體, 這裡我們中文一般用xx艙來標識,
這方面,我們有很多例子, 比如返回艙, 其實就是space pod:
神舟五號返回艙
3.一些海洋哺乳動物組成的社交型的群體, 例如鯨魚或者海豚組成的"隊伍", 這裡面我用的是"隊伍", 而不僅僅是'群', 因為這是一個社交型的團隊, 其中母親和下一代的關係是最親密的, 而且團隊成員之間會相互保護, 所以不能稱之為"群", 而是團隊, 隊伍.
Whale Pod
鯨魚團隊中的成員規模, 按照鯨魚的類別不同, 其成員數量也不一樣:
可以看出, 對於齒鯨來說, 一般在6個以上, 多的在20個左右.
對於團隊管理而言, <<天下無賊>>裡面, 黎叔有句名言: 人心散了, 隊伍不好帶了. 放在Pod的語境中, 其實就是當一個Pod 裡面包含的容器很多時, 整個Pod就會陷入混亂! 如果沒有高明的管理手段,
人心散了
隊伍不好帶了
透過對上面對Pod 這個英語單詞的三種釋義, 我們能更好的理解Kubernetes Pod 的含義.
首先, Pod 在國內大多數是以第一類為主, 也就是豌豆莢的含義, 這個從容器的角度是很好解釋的, Pod 裡面可以包含一個或者多個容器, 就像一個豆莢裡面會包含幾顆豆子一樣, 這裡豆子類似於容器, 豆莢會給予豆子以保護和營養(豆莢的管理功能).
其次, 從Pod中容器的相互關係來看, 把Pod 類比於鯨魚群的社交單位也是可以的, Pod 裡面的容器並不是孤立的, 他們是相互合作的, 這裡面可以這麼細分:
Pod 裡面只有一個容器,"one-container-per-Pod"----一個Pod 一個容器, 這種屬於"獨生兒女", 不需要合作.“每個容器一個容器”模型反而是最常見的Kubernetes的用法。 在這種情況下,類似於豆莢,可以將Pod視為單個容器的包裝盒(匣); Kubernetes管理Pod而不是直接管理容器。Pod 裡面有多個相互合作的容器,這屬於"一個Pod多個容器"模型. Pod可以封裝一個應用程式,這個應用程式由緊密關聯且需要共享資源的多個位於同一地點的容器組成。 這些位於同一地點的容器形成了一個單一的服務單元.例如,一個容器向公眾提供儲存在共享卷中的資料,而一個單獨的sidecar容器則重新整理或更新這些檔案。 Pod將這些容器,儲存資源和臨時網路標識包裝在一起,成為一個單元。
在這裡Kubernetes 有一個注意提示, 提示這種"單Pod多容器"模型是高階用法, 謹慎使用:
在單個Pod中將多個位於同一地點並受共同管理的容器分組是一個相對高階的用例。 您僅應在容器緊密耦合的特定例項中使用此模式。
儘管Pod 有上面這兩種模型, 但是Pod 的用法是一致的, 每個Pod 都是一個給定應用程式的一個例項(instance), 如果需要水平擴充套件一個應用程式的話(水平擴充套件, 意味著提供更多的資源, 執行更多的例項), 那麼就需要擴充套件更多的Pod, 每個Pod 代表一個例項. 這種水平擴充套件, 在Kubernetes術語中稱之為"複製(replication)", 複製的多個Pod會被作為一個組來建立和管理, 而管理這些組的則是工作負荷(workload)資源以及相應的控制器(controller).
Pods 和 控制器(controllers)資源的控制器在Pod失敗的情況下處理複製和退回(rollout)以及自動修復。 例如,如果某個節點發生故障,則控制器會注意到該節點上的Pod已停止工作,並建立了一個替換Pod。 排程程式將替換的Pod放置在健康的節點上。
下面幾個控制器是管理一個或多個Pod的工作負載資源的一些示例:
DeploymentStatefulSetDaemonSet我們最先接觸的就是Deployment資源, 我們在之前的用法中就已經瞭解到, 一個Deployment 檔案就是可以描述和定義一個Pod, 而這個Deployment 檔案, 實際上就相當於一個PodTemplate,也就是Pod 模板.
PodTemplate是建立Pod 的規範, 每個工作負荷資源(workload resource, 比如Deployment, Job, 以及DaemonSet) 的控制器(controller), 都會使用PodTemplate來建立實際的Pod, PodTemplate就是一個目標狀態的定義, 而背後的工作資源物件以及控制器會在背後去實現這個目標狀態, 這也是為什麼稱Kubernetes的資源管理為"宣告式", 就是在這裡.
下面是一個Job 的模板的宣告
apiVersion: batch/v1kind: Jobmetadata: name: hellospec: template: # This is the pod template spec: containers: - name: hello image: busybox command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600'] restartPolicy: OnFailure # The pod template ends here
PodTemplate的修改和生效對於Pod template模板的修改,或者切換到新的pod template, 對於已經存在的Pod 來說並沒有直接的效果; 如果更改了工作負荷資源的Pod template 模板, 這些資源需要建立"替代版"的Pod, 這些替代版Pod使用的是更新過的新"版本"template.
例如, StatefulSet Controller能夠確保, 對於每個StatefulSet物件, 其正在執行的Pod都會和當前的Pod template模板保持一致; 如果你編輯StatefulSet物件並更新了裡面的pod 模板, 那麼StatefulSet就會基於新的Pod template來建立新的Pod, 最終所有的舊的Pod會被新的Pod所取代, 這時更新才算結束.
每個工作負荷資源的實現都有其單獨的規則(rule)去處理Pod template的更新, 對於StatefulSet以及其更新策略, 會在後續的文章中給予介紹.
Pod的更新和替換
Kubenetes並不會阻止使用者直接管理Pod, 更新一個正在執行的Pod的某些值是允許的, 但是, Pod的更新操作,例如patch 和replace, 有一些限制:
關於Pod的大多數元資料都是不可變的。 例如,您不能更改namespace,name,uid或creationTimestamp欄位。 generation是獨一無二的。 它僅接受會增加欄位當前值的更新。如果設定了metadata.deletionTimestamp,則不能將新條目新增到metas.finalizers列表中。Pod更新可能不會更改spec.containers [*].image,spec.initContainers [*].image,spec.activeDeadlineSeconds、spec.tolerations以外的其他欄位。 對於spec.tolerations,您只能新增新條目。更新spec.activeDeadlineSeconds欄位時,允許兩種型別的更新:將未分配欄位設定為正數;將欄位從正數更新為較小的非負數。Pod中的資源共享和通訊Pod 允許其中的容器之間資料共享和相互通訊
Pod的儲存空間(Storage)
Pod可以指定一組共享儲存卷(volume)。 Pod中的所有容器都可以訪問共享卷,從而使這些容器可以共享資料。 卷還允許Pod中的持久資料保留下來,以防其中的容器之一需要重新啟動。 有關Kubernetes如何實現共享儲存並將其提供給Pods的更多資訊,請參見儲存。
Pod的網路通訊
針對每個網路系列,每個Pod都會被分配一個唯一的IP地址。
Pod中的每個容器都共享網路名稱空間,包括IP地址和網路埠。在Pod內部(並且只有那時),屬於Pod的容器可以使用localhost相互通訊。
當Pod中的容器與Pod外部的實體進行通訊時,它們必須協調它們如何使用共享的網路資源(例如埠)。在Pod中,容器共享一個IP地址和埠空間,並且可以透過localhost找到彼此。
Pod中的容器還可以使用標準的程序間通訊(例如System V訊號量或POSIX共享記憶體)相互通訊。不同Pod中的容器具有不同的IP地址,並且沒有特殊配置就無法透過IPC進行通訊。想要與在其他Pod中執行的容器進行互動的容器可以使用IP網路進行通訊。
Pod中的容器將系統主機名視為與Pod的配置名稱相同。
Kubernetes 網路結構圖
Pod網路還是比較複雜的, 這個需要在以後的文章中用更多的篇幅來介紹, 這裡只需要暫時理解這些基礎就可以了.
容器的特權模式
Pod中的任何容器都可以使用容器規範的安全上下文上的特權標誌來啟用特權模式。這對於想要使用作業系統管理功能(如操縱網路堆疊或訪問硬體裝置)的容器很有用。特權容器中的程序幾乎獲得與容器外部程序相同的特權。
注意:容器執行時必須支援特權容器的概念才能使此設定相關。
回顧在這篇文章中, 我們先學習了容器註冊中心, 並介紹了#Quay.io#, 一個和DockerHub 不同的註冊中心, 然後我們透過kubectl run來建立一個執行的Pod, 並透過kubectl exec來驗證Pod 中執行的容器功能;
docker login quay.io |
登入容器註冊中心 |
kubectl run --image= |
執行指定的映象, 生成Pod |
kubectl exec <pod> -- <cmd> |
在Pod特定容器上執行指令 |
kubectl delete | |
在第二部分,我們從原理部分學習Pod知識, 先從字面意思上了解Pod的三個含義, 然後結合這三方面的含義, 來學習Pod的兩種模型, 以及PodTemplate和修改情況; 最後, 瞭解Pod 的資料儲存和網路模型情況.
Whales Pod