Pod
資源對象是一種集合了一到多個應用容器、存儲資源、專用IP
及支撐容器運行的其他選項的邏輯組件,如圖所示。Pod
代表着Kubernetes
的部署單元及原子運行單元,即一個應用程序的單一運行實例,它通常由共享資源且關系緊密的一個或多個應用容器組成。
Kubernetes
的網絡模型要求其各Pod
對象的IP
地址位於同一網絡平面內(同一IP
網段),各Pod
之間可使用其IP
地址直接進行通信,無論它們運行於集群內的哪個工作節點上,這些Pod
對象都像運行於同一局域網中的多個主機。不過,
Pod
對象中的各進程均運行於彼此隔離的容器中,並於容器間共享兩種關鍵資源:網絡和存儲卷。
網絡:每個
Pod
對象都會被分配一個集群內專用的IP
地址,也稱為Pod IP
,同一Pod
內部的所有容器共享Pod
對象的Network
和UTS
名稱空間,其中包括主機名、IP
地址和端口等。因此,這些容器間的通信可以基於本地回環接口lo
進行,而與Pod
外的其他組件的通信則需要使用Service
資源對象的ClusterIP
及相應的端口完成。存儲卷:用戶可以為
Pod
對象配置一組“存儲卷”資源,這些資源可以共享給其內部的所有容器使用,從而完成容器間數據的共享。存儲卷還可以確保在容器終止后重啟,甚至是被刪除后也能確保數據不會丟失,從而保證了生命周期內的Pod
對象數據的持久化存儲。
一個
Pod
對象代表某個應用程序的一個特定實例,如果需要擴展應用程序,則意味着為此應用程序同時創建多個Pod
實例,每個實例均代表應用程序的一個運行的“副本”(replica
)。這些副本化的Pod
對象的創建和管理通常由另一組稱為“控制器”(Controller
)的對象實現,例如,Deployment
控制器對象。創建
Pod
時,還可以使用Pod Preset
對象為Pod
注入特定的信息,如ConfigMap
、Secret
、存儲卷、掛載卷和環境變量等。有了Pod Preset
對象,Pod
模板的創建者就無須為每個模板顯示提供所有信息,因此,也就無須事先了解需要配置的每個應用的細節即可完成模板定義。基於期望的目標狀態和各節點的資源可用性,
Master
會將Pod
對象調度至某選定的工作節點運行,工作節點於指向的鏡像倉庫(image register
)下載鏡像,並於本地的容器運行時環境中啟動容器。Master
會將整個集群的狀態保存於etcd
中,並通過API Server
共享給集群的各組件及客戶端。
但
Pod
對象本身並不具有“自愈”功能,若是因為工作節點甚至是調度器自身導致了運行失敗,那么它將會被刪除;同樣,資源耗盡或節點故障導致的回收操作也會刪除相關的Pod
對象。在設計上,Kubernetes
使用”控制器“實現對一次性的(用后即棄)Pod
對象的管理操作,例如,要確保部署的應用程序的Pod副本數量嚴格反映用戶期望的數目,以及基於Pod
模板來創建Pod
對象等,從而實現Pod
對象的擴縮容、滾動更新和自愈能力等。例如,某節點發生故障時,相關的控制器會將此節點上運行的Pod
對象重新調度到其他節點進行重建。控制器本身也是一種資源類型,它有着多種實現,其中與工作負載相關的實現如
Replication Controller
、Deployment
、StatefulSet
、DaemonSet
和Jobs
等,也可統稱它們為Pod
控制器。
Pod
控制器的定義通常由期望的副本數量、Pod
模板和標簽選擇器(Label Selector
)組成。Pod
控制器會根據標簽選擇器對Pod
對象的標簽進行匹配檢查,所有滿足選擇條件的Pod
對象都將受控於當前控制器並計入其副本總數,並確保此數目能夠精確反映期望的副本數。
換言之,
Service
是“微服務”的一種實現,事實上它是一種抽象:通過規則定義出由多個Pod
對象組合而成的邏輯集合,並附帶訪問這組Pod
對象的策略。Service
對象挑選、關聯Pod
對象的方式同Pod
控制器一樣,都是要基於Label Selector
進行定義,其示意圖如下
集群內的
Pod
對象可直接請求此類的Cluster IP
,例如,圖中來自Pod client
的訪問請求即可以Service
的Cluster IP
作為目標地址,但集群網絡屬於私有網絡地址,它們僅在集群內部可達。將集群外部的訪問流量引入集群內部的常用方法是通過節點網絡進行,實現方法是通過工作節點的IP
地址和某端口(NodePort
)接入請求並將其代理至相應的Service
對象的Cluster IP
上的服務端口,而后由Service
對象將請求代理至后端的Pod
對象的Pod IP
及應用程序監聽的端口。因此,圖中的External Clients
這種來自集群外部的客戶端無法直接請求此Service
提供的服務,而是需要事先經由某一個工作節點(如NodeY
)的IP
地址進行,這類請求需要兩次轉發才能到達目標Pod
對象,因此在通信效率上必然存在負面影響。事實上,
NodePort
會部署於集群中的每一個節點,這就意味着,集群外部的客戶端通過任何一個工作節點的IP
地址來訪問定義好的NodePort
都可以到達相應的Service
對象。此種場景下,如果存在集群外部的一個負載均衡器,即可將用戶請求負載均衡至集群中的部分或者所有節點。這是一種稱為“LoadBalancer”
類型的Service
,它通常是由Cloud Provider
自動創建並提供的軟件負載均衡器,不過,也可以是有管理員手工配置的諸如F5
一類的硬件設備。簡單來說,
Service
主要有三種常用類型:第一種是僅用於集群內部通信的ClusterIP
類型;第二種是接入集群外部請求的NodePort
類型,它工作與每個節點的主機IP
之上;第三種是LoadBalancer
類型,它可以把外部請求負載均衡至多個Node
的主機IP
的NodePort
之上。此三種類型中,每一種都以其前一種為基礎才能實現,而且第三種類型中的LoadBalancer
需要協同集群外部的組件才能實現,並且此外部組件並不接受Kubernetes
的管理。
在
Kubernetes
集群上自主運行的Pod
對象在非計划內終止后,其生命周期即告結束,用戶需要再次手動創建類似的Pod
對象才能確保其容器中的依然可得。對於Pod
數量眾多的場景,尤其是對微服務業務來說,用戶必將疲於應付此類需求。Kubernetes
的工作負載(workload
)類型的控制器能夠自動確保由其管控的Pod
對象按用戶期望的方式運行,因此,Pod的創建和管理大多會通過這種類型的控制器來進行,包括Deployment
、ReplicasSet
、ReplicationController
等。
1)創建Deployment控制器對象
kubectl run
命令可用於命令行直接創建Deploymen
t控制器,並以--image
選項指定的鏡像運行Pod
中的容器,--dry-run
選項可以用於命令的測試,但並不真正執行資源對象的創建過程。
# 創建一個名字叫做nginx的deployment控制器,並指定pod鏡像使用nginx:1.12版本,並暴露容器內的80端口,並指定副本數量為1個,並先通過--dry-run測試命令是否錯誤。 [root@k8s-master ~]# kubectl run nginx --image=nginx:1.12 --port=80 --replicas=1 --dry-run=true [root@k8s-master ~]# kubectl run nginx --image=nginx:1.12 --port=80 --replicas=1 deployment.apps/nginx created [root@k8s-master ~]# kubectl get pods #查看所有pod對象 NAME READY STATUS RESTARTS AGE nginx-685cc95cd4-9z4f4 1/1 Running 0 89s ###參數說明: --image 指定需要使用到的鏡像。 --port 指定容器需要暴露的端口。 --replicas 指定目標控制器對象要自動創建Pod對象的副本數量。
2)打印資源對象的相關信息
kubectl get
命令可用來獲取各種資源對象的相關信息,它既能顯示對象類型特有格式的簡要信息,也能按照指定格式為YAML
或JSON
的詳細信息,或者使用Go
模板自定義要顯示的屬性及信息等。
[root@k8s-master ~]# kubectl get deployment #查看所有deployment控制器對象 NAME READY UP-TO-DATE AVAILABLE AGE nginx 1/1 1 1 66s ###字段說明: NAME 資源對象名稱 READY 期望由當前控制器管理的Pod對象副本數及當前已有的Pod對象副本數 UP-TO-DATE 更新到最新版本定義的Pod對象的副本數量,在控制器的滾動更新模式下,表示已經完成版本更新的Pod對象的副本數量 AVAILABLE 當前處於可用狀態的Pod對象的副本數量,即可正常提供服務的副本數。 AGE Pod的存在時長 說明:Deployment資源對象通過ReplicaSet控制器實例完成對Pod對象的控制,而非直接控制。另外,通過控制器創建的Pod對象都會被自動附加一個標簽。格式為“run=<Controller_Name>”。 [root@k8s-master ~]# kubectl get deployment -o wide #查看deployment控制器對象的詳細信息 NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR nginx 1/1 1 1 69m nginx nginx:1.12 run=nginx [root@k8s-master ~]# kubectl get pods #查看pod資源 NAME READY STATUS RESTARTS AGE nginx-685cc95cd4-9z4f4 1/1 Running 0 72m [root@k8s-master ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-685cc95cd4-9z4f4 1/1 Running 0 73m 10.244.1.12 k8s-node1 <none> <none> ###字段說明: NAME pode資源對象名稱 READY pod中容器進程初始化完成並能夠正常提供服務時即為就緒狀態,此字段用於記錄處於就緒狀態的容器數量 STATUS pod的當前狀態,其值有Pending、Running、Succeeded、Failed和Unknown等其中之一 RESTARTS Pod重啟的次數 IP pod的IP地址,通常由網絡插件自動分配 NODE pod被分配的節點。
3)訪問Pod對象
這里部署的是
pod
是運行的為nginx
程序,所以我們可以訪問是否ok
,在kubernetes
集群中的任意一個節點上都可以直接訪問Pod
的IP
地址。
[root@k8s-master ~]# kubectl get pods -o wide #查看pod詳細信息 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-685cc95cd4-9z4f4 1/1 Running 0 88m 10.244.1.12 k8s-node1 <none> <none> [root@k8s-master ~]# curl 10.244.1.12 #kubernetes集群的master節點上訪問 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> [root@k8s-node2 ~]# curl 10.244.1.12 #kubernetes集群的node節點上訪問 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
[root@k8s-master ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-685cc95cd4-9z4f4 1/1 Running 0 99m 10.244.1.12 k8s-node1 <none> <none> [root@k8s-master ~]# kubectl delete pods nginx-685cc95cd4-9z4f4 #刪除上面的pod pod "nginx-685cc95cd4-9z4f4" deleted [root@k8s-master ~]# kubectl get pods -o wide #可以看出,當上面pod剛刪除,接着deployment控制器又馬上創建了一個新的pod,且這次分配在k8s-node2節點上了。 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-685cc95cd4-z5z9p 1/1 Running 0 89s 10.244.2.14 k8s-node2 <none> <none> [root@k8s-master ~]# curl 10.244.1.12 #訪問之前的pod,可以看到已經不能訪問 curl: (7) Failed connect to 10.244.1.12:80; 沒有到主機的路由 [root@k8s-master ~]# [root@k8s-master ~]# curl 10.244.2.14 #訪問新的pod,可以正常訪問 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
簡單來說,一個
Service
對象可視作通過其標簽選擇器過濾出的一組Pod
對象,並能夠為此組Pod
對象監聽的套接字提供端口代理及調度服務。就好比上面做的測試,如果沒有Service
,那么每次都得去訪問pod
對象自己的地址等。且那還只是創建了一個pod
對象,如果是多個。那么該如何是好?故使用Service
解決此問題。
1)創建Service對象(將Service端口代理至Pod端口示例)
"kubectl expose"
命令可用於創建Service
對象以將應用程序“暴露”(expose
)於網絡中。
#方法一 [root@k8s-master ~]# kubectl expose deployment nginx --name=nginx-svc --port=80 --target-port=80 --protocol=TCP #為deployment的nginx創建service,取名叫nginx-svc,並通過service的80端口轉發至容器的80端口上。 service/nginx-svc exposed #方法二 [root@k8s-master ~]# kubectl expose deployment/nginx --name=nginx-svc --port=80 --target-port=80 --protocol=TCP service/nginx-svc exposed ###參數說明: --name 指定service對象的名稱 --port 指定service對象的端口 --target-port 指定pod對象容器的端口 --protocol 指定協議 [root@k8s-master ~]# kubectl get svc #查看service對象。或者kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25h nginx-svc ClusterIP 10.109.54.136 <none> 80/TCP 41s
# master節點上通過ServiceIP進行訪問 [root@k8s-master ~]# curl 10.109.54.136 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> #新建一個客戶端pod進行訪問,這里這個客戶端使用busybox鏡像,且pod副本數量為1個,-it表示進入終端模式。--restart=Never,表示從不重啟。 [root@k8s-master ~]# kubectl run client --image=busybox --replicas=1 -it --restart=Never If you don't see a command prompt, try pressing enter. / # wget -O - -q 10.109.54.136 #訪問上面創建的(service)nginx-svc的IP <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...... / # / # wget -O - -q nginx-svc #訪問上面創建的(service)名稱nginx-svc <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title>
2)創建Service對象(將創建的Pod對象使用“NodePort”類型的服務暴露到集群外部)
[root@k8s-master ~]# kubectl run mynginx --image=nginx:1.12 --port=80 --replicas=2 #創建一個deployments控制器並使用nginx鏡像作為容器運行的應用。 [root@k8s-master ~]# kubectl get pods #查看創建的pod NAME READY STATUS RESTARTS AGE client 1/1 Running 0 15h mynginx-68676f64-28fm7 1/1 Running 0 24s mynginx-68676f64-9q8dj 1/1 Running 0 24s nginx-685cc95cd4-z5z9p 1/1 Running 0 16h [root@k8s-master ~]# [root@k8s-master ~]# kubectl expose deployments/mynginx --type="NodePort" --port=80 --name=mynginx-svc #創建一個service對象,並將mynginx創建的pod對象使用NodePort類型暴露到集群外部。 service/mynginx-svc exposed [root@k8s-master ~]# [root@k8s-master ~]# kubectl get svc #查看service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41h mynginx-svc NodePort 10.111.89.58 <none> 80:30884/TCP 10s nginx-svc ClusterIP 10.109.54.136 <none> 80/TCP 15h ###字段說明: PORT(S) 這里的mynginx-svc對象可以看出,集群中各工作節點會捕獲發往本地的目標端口為30884的流量,並將其代理至當前service對象的80端口。於是集群外部的用戶可以使用當前集群中任一節點的此端口來請求Service對象上的服務。 [root@k8s-master ~]# [root@k8s-master ~]# netstat -nlutp |grep 30884 #查看master節點上是否有監聽上面的30884端口 tcp6 0 0 :::30884 :::* LISTEN 7340/kube-proxy [root@k8s-node1 ~]# [root@k8s-node1 ~]# netstat -nlutp |grep 30884 #查看node節點是否有監聽上面的30884端口 tcp6 0 0 :::30884 :::* LISTEN 2537/kube-proxy
3)Service資源對象的描述
[root@k8s-master ~]# kubectl describe service mynginx-svc Name: mynginx-svc Namespace: default Labels: run=mynginx Annotations: <none> Selector: run=mynginx Type: NodePort IP: 10.111.89.58 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30884/TCP Endpoints: 10.244.1.14:80,10.244.2.15:80 Session Affinity: None External Traffic Policy: Cluster Events: <none> ###字段說明: Selector 當前Service對象使用的標簽選擇器,用於選擇關聯的Pod對象 Type 即Service的類型,其值可以是ClusterIP、NodePort和LoadBalancer等其中之一 IP 當前Service對象的ClusterIP Port 暴露的端口,即當前Service用於接收並響應的端口 TargetPort 容器中的用於暴露的目標端口,由Service Port路由請求至此端口 NodePort 當前Service的NodePort,它是否存在有效值與Type字段中的類型相關 Endpoints 后端端點,即被當前Service的Selector挑中的所有Pod的IP及其端口 Session Affinity 是否啟用會話粘性 External Traffic Policy 外部流量的調度策略
Service
對象內建的負載均衡機制可在其后端副本數量不止一個時自動進行流量分發,它還會自動監控關聯到的Pod
的健康狀態,以確保將請求流量分發至可用的后端Pod
對象。若某Deployment
控制器管理包含多個Pod
實例,則必要時用戶還可以為其使用“滾動更新”機制將其容器鏡像升級到新的版本或變更那些支持動態修改的Pod
屬性。使用
kubect run
命令創建Deployment
對象時,“--replicas=”
選項能夠指定由該對象創建或管理的Pod
對象副本的數量,且其數量支持運行時進行修改,並立即生效。“kubectl scale”
命令就是專用於變動控制器應用規模的命令,它支持對Deployment
資源對象的擴容和縮容操作。
[root@k8s-master ~]# kubectl get pods -l run=nginx #查看標簽run=nginx的pod NAME READY STATUS RESTARTS AGE nginx-685cc95cd4-z5z9p 1/1 Running 0 17h [root@k8s-master ~]# [root@k8s-master ~]# kubectl scale deployments/nginx --replicas=3 #將其擴容到3個 deployment.extensions/nginx scaled [root@k8s-master ~]# [root@k8s-master ~]# kubectl get pods -l run=nginx #再次查看 NAME READY STATUS RESTARTS AGE nginx-685cc95cd4-f2cwb 1/1 Running 0 5s nginx-685cc95cd4-pz9dk 1/1 Running 0 5s nginx-685cc95cd4-z5z9p 1/1 Running 0 17h [root@k8s-master ~]# [root@k8s-master ~]# kubectl describe deployments/nginx #查看Deployment對象nginx詳細信息 Name: nginx Namespace: default CreationTimestamp: Thu, 29 Aug 2019 15:29:31 +0800 Labels: run=nginx Annotations: deployment.kubernetes.io/revision: 1 Selector: run=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate ... #由nginx自動創建的pod資源全部擁有同一個標簽選擇器“run=nginx”,因此,前面創建的Service資源對象nginx-svc的后端端點也已經通過標簽選擇器自動擴展到了這3個Pod對象相關的端點 [root@k8s-master ~]# kubectl describe service/nginx-svc Name: nginx-svc Namespace: default Labels: run=nginx Annotations: <none> Selector: run=nginx Type: ClusterIP IP: 10.109.54.136 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.15:80,10.244.2.14:80,10.244.2.16:80 Session Affinity: None Events: <none>
縮容的方式和擴容相似,只不過是將
Pod
副本的數量調至比原來小的數字即可。例如將nginx
的pod
副本縮減至2個
[root@k8s-master ~]# kubectl scale deployments/nginx --replicas=2 deployment.extensions/nginx scaled [root@k8s-master ~]# [root@k8s-master ~]# kubectl get pods -l run=nginx NAME READY STATUS RESTARTS AGE nginx-685cc95cd4-pz9dk 1/1 Running 0 10m nginx-685cc95cd4-z5z9p 1/1 Running 0 17h
[root@k8s-master ~]# kubectl get services #查看當前所有的service對象 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 43h mynginx-svc NodePort 10.111.89.58 <none> 80:30884/TCP 96m nginx-svc ClusterIP 10.109.54.136 <none> 80/TCP 17h [root@k8s-master ~]# kubectl delete service nginx-svc #刪除service對象nginx-svc
[root@k8s-master ~]# kubectl delete deployment --all deployment.extensions "mynginx" deleted
注意:受控於控制器的Pod
對象在刪除后會被重建,刪除此類對象需要直接刪除其控制器對象。不過,刪除控制器時若不想刪除其Pod
對象,可在刪除命令上使用“--cascade=false“
選項。
雖然直接命令式管理的相關功能強大且適合用於操縱
Kubernetes
資源對象,但其明顯的缺點是缺乏操作行為以及待運行對象的可信源。另外,直接命令式管理資源對象存在較大的局限性,它們在設置資源對象屬性方面提供的配置能力相當有限,而且還有不少資源並不支持命令操作進行創建,例如,用戶無法創建帶有多個容器的Pod
對象,也無法為Pod
對象創建存儲卷。因此,管理資源對象更有效的方式是基於保存有對象配置信息的配置清單來進行。