書名: Kubernetes進階實戰作者名: 馬永亮本章字數: 6790字更新時間: 2019-03-13 14:20:37
2.4 命令式容器應用編排
本節將使用示例鏡像“ikubernetes/myapp: v1”來演示容器應用編排的基礎操作:應用部署、訪問、查看、服務暴露和擴縮容等。一般說來,Kubernetes之上應用程序的基礎管理操作由如下幾個部分組成。
1)通過合用的Controller類的資源(如Deployment或ReplicationController)創建并管控Pod對象以運行特定的應用程序,如Nginx或tomcat等。無狀態(stateless)應用的部署和控制通常使用Deployment控制器進行,而有狀態應用則需要使用StatefulSet控制器。
2)為Pod對象創建Service對象,以便向客戶端提供固定的訪問路徑,并借助于CoreDNS進行服務發現。
3)隨時按需獲取各資源對象的簡要或詳細信息,以了解其運行狀態。
4)如有需要,則手動對支持擴縮容的Controller組件進行擴容或縮容;或者,為支持HPA的Controller組件(如Deployment或ReplicationController)創建HPA資源對象以實現Pod副本數目的自動伸縮。
5)滾動更新:當應用程序的鏡像出現新版本時,對其執行更新操作;必要時,為Pod對象中的容器更新其鏡像版本;并可根據需要執行回滾操作。
本節中的操作示例僅演示了前三個部分的功能,即應用的部署、服務暴露及相關信息的查看。應用的擴縮容、升級及回滾等操作會在后面的章節中進行詳細介紹。
提示
以下操作命令在任何部署了kubectl并能正常訪問到Kubernetes集群的主機上均可執行,包括集群外的主機。復制master主機上的/etc/kubernetes/admin.conf至相關用戶主目錄下的.kube/config文件即可正常執行,具體方法請參考kubeadm init命令結果中的提示。
2.4.1 部署應用(Pod)
在Kubernetes集群上自主運行的Pod對象在非計劃內終止后,其生命周期即告結束,用戶需要再次手動創建類似的Pod對象才能確保其容器中的應用依然可得。對于Pod數量眾多的場景,尤其是對微服務業務來說,用戶必將疲于應付此類需求。Kubernetes的工作負載(workload)類型的控制器能夠自動確保由其管控的Pod對象按用戶期望的方式運行,因此,Pod的創建和管理大多都會通過這種類型的控制器來進行,包括Deployment、ReplicaSet、ReplicationController等。
1.創建Deployment控制器對象
“kubectl run”命令可于命令行直接創建Deployment控制器,并以--image選項指定的鏡像運行Pod中的容器,--dry-run選項可用于命令的測試運行,但并未真正執行資源對象的創建過程。例如,下面的命令要創建一個名為myapp的Deployment控制器對象,它使用鏡像ikubernetes/myapp: v1創建Pod對象,但僅在測試運行后即退出:
~]$ kubectl run myapp --image=ikubernetes/myapp:v1 --port=80 --replicas=1 --dry-run NAME AGE myapp <unknown>
鏡像ikubernetes/myapp: v1中定義的容器主進程為默認監聽于80端口的Web服務程序Nginx,因此,如下命令使用“--port=80”來指明容器要暴露的端口。而“--replicas=1”選項則指定了目標控制器對象要自動創建的Pod對象的副本數量。確認測試命令無誤后,可移除“--dry-run”選項后再次執行命令以完成資源對象的創建:
~]$ kubectl run myapp --image=ikubernetes/myapp:v1--port=80--replicas=1 deployment.apps/myapp created
創建完成后,其運行效果示意圖如圖2-10所示,它在default名稱空間中創建了一個名為myapp的Deployment控制器對象,并由它基于指定的鏡像文件創建了一個Pod對象。

圖2-10 Deployment對象myapp及其創建的Pod對象
kubectl run命令其他常用的選項還有如下幾個,它們支持用戶在創建資源對象時實現更多的控制,具體如下。
?□-l, --labels:為Pod對象設定自定義標簽。
?□--record:是否將當前的對象創建命令保存至對象的Annotation中,布爾型數據,其值可為true或false。
?□--save-config:是否將當前對象的配置信息保存至Annotation中,布爾型數據,其值可為true或false。
?□--restart=Never:創建不受控制器管控的自主式Pod對象。
其他可用選項及使用方式可通過“kubectl run --help”命令獲取。資源對象創建完成后,通常需要了解其當前狀態是否正常,以及是否能夠吻合于用戶期望的目標狀態,相關的操作一般使用kubectl get、kubectl describe等命令進行。
2.打印資源對象的相關信息
kubectl get命令可用于獲取各種資源對象的相關信息,它既能夠顯示對象類型特有格式的簡要信息,也能夠指定出格式為YAML或JSON的詳細信息,或者使用Go模板自定義要顯示的屬性及信息等。例如,下面是查看前面創建的Deployment對象的相關運行狀態的命令及其輸出結果:
~]$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE myapp 1 1 1 1 1m
上面命令的執行結果中,各字段的說明具體如下。
1)NAME:資源對象的名稱。
2)DESIRED:用戶期望由當前控制器管理的Pod對象副本的精確數量。
3)CURRENT:當前控制器已有的Pod對象的副本數量。
4)UP-TO-DATE:更新到最新版本定義的Pod對象的副本數量,在控制器的滾動更新模式下,它表示已經完成版本更新的Pod對象的副本數量。
5)AVAILABLE:當前處于可用狀態的Pod對象的副本數量,即可正常提供服務的副本數。
6)AGE:Pod的存在時長。
提示
Deployment資源對象通過ReplicaSet控制器實例完成對Pod對象的控制,而非直接控制。另外,通過控制器創建的Pod對象都會被自動附加一個標簽,其格式為“run=<Controller_Name>”,例如,上面的命令所創建的Pod,會擁有“run=myapp”標簽。后面的章節對此會有詳細描述。
而此Deployment控制器創建的唯一Pod對象運行正常與否,其被調度至哪個節點運行,當前是否就緒等也是用戶在創建完成后應該重點關注的信息。由控制器創建的Pod對象的名稱通常是以控制器名稱為前綴,以隨機字符為后綴,例如,下面命令輸出結果中的myapp-6865459dff-5nsjc:
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE myapp-6865459dff-5nsjc 1/1 Running 0 3m 10.244.3.2 node03. ilinux.io
上面命令的執行結果中,每一個字段均代表著Pod資源對象一個方面的屬性,除了NAME之外的其他字段及功用說明如下。
①READY:Pod中的容器進程初始化完成并能夠正常提供服務時即為就緒狀態,此字段用于記錄處于就緒狀態的容器數量。
②STATUS:Pod的當前狀態,其值可能是Pending、Running、Succeeded、Failed和Unknown等其中之一。
③RESTARTS:Pod對象可能會因容器進程崩潰、超出資源限額等原因發生故障問題而被重啟,此字段記錄了它重啟的次數。
④IP:Pod的IP地址,其通常由網絡插件自動分配。
⑤NODE:創建時,Pod對象會由調度器調度至集群中的某節點運行,此字段即為節點的相關標識信息。
提示
如果指定名稱空間中存在大量的Pod對象而使得類似如上命令的輸出結果存在太多的不相關信息時,則可通過指定選項“-l run=myapp”進行Pod對象過濾,其僅顯示符合此標簽選擇器的Pod對象。
確認Pod對象已轉為“Running”狀態之后,即可于集群中的任一節點(或其他Pod對象)直接訪問其容器化應用中的服務,如圖2-11中節點NodeX上的客戶端程序Client,或者集群上運行于Pod中的客戶端程序。

圖2-11 訪問Pod中容器化應用服務程序
例如,在集群中任一節點上使用curl命令對地址為10.244.3.2的Pod對象myapp-6865459dff-5nsjc的80端口發起服務請求,命令及結果如下所示:
[ik8s@node03~]$ curl http://10.244.3.2:80/ Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
2.4.2 探查Pod及應用詳情
資源創建或運行過程中偶爾會因故出現異常,此時用戶需要充分獲取相關的狀態及配置信息以便確定問題的所在。另外,在對資源對象進行創建或修改完成之后,也需要通過其詳細的狀態來了解操作成功與否。kubectl有多個子命令可用于從不同的角度顯示對象的狀態信息,這些信息有助于用戶了解對象的運行狀態、屬性詳情等信息。
1)kubectl describe:顯示資源的詳情,包括運行狀態、事件等信息,但不同的資源類型其輸出內容不盡相同。
2)kubectl logs:查看Pod對象中容器輸出在控制臺的日志信息。在Pod中運行有多個容器時,需要使用選項“-c”指定容器名稱。
3)kubectl exec:在Pod對象某容器內運行指定的程序,其功能類似于“docker exec”命令,可用于了解容器各方面的相關信息或執行必需的設定操作等,其具體功能取決于容器內可用的程序。
1.查看Pod對象的詳細描述
下面給出的命令打印了此前由myapp創建的Pod對象的詳細狀態信息,為了便于后續的多次引用,這里先將其名稱保存于變量POD_NAME中。命令的執行結果中省略了部分輸出:
~]$ POD_NAME=myapp-6865459dff-5nsjc ~]$ kubectl describe pods $POD_NAME Name: myapp-6865459dff-5nsjc Namespace: default Priority: 0 PriorityClassName: <none> Node: node03.ilinux.io/172.16.0.68 …… Status: Running IP: 10.244.3.2 Controlled By: ReplicaSet/myapp-6865459dff Containers: myapp: …… …… Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 55m default-scheduler Successfully assigned default/myapp-6865459dff-5nsjc to node03.ilinux.io Normal Pulling 55m kubelet, node03.ilinux.io pulling image "ikubernetes/ myapp:v1" Normal Pulled 54m kubelet, node03.ilinux.io Successfully pulled image "ikubernetes/myapp:v1" Normal Created 54m kubelet, node03.ilinux.io Created container Normal Started 54m kubelet, node03.ilinux.io Started container
不同的需求場景中,用戶需要關注不同緯度的輸出,但一般來說,Events和Status字段會是重點關注的對象,它們分別代表了Pod對象運行過程中的重要信息及當前狀態。上面命令執行結果中的不少字段都可以見名而知義,而且部分字段在前面介紹其他命令輸出時已經給出,還有一部分會在本書后面的篇幅中給予介紹。
2.查看容器日志
Docker容器一般僅運行單個應用程序,其日志信息將通過標準錯誤輸出等方式直接打印至控制臺,“kubectl logs”命令即用于查看這些日志。例如,查看由Deployment控制器myapp創建的Pod對象的控制臺日志,命令如下:
~]$ kubectl logs $POD_NAME 10.244.3.1- - [……] "GET / HTTP/1.1" 200 65 "-" "curl/7.29.0" "-" ……
如果Pod中運行有多個容器,則需要在查看日志時為其使用“-c”選項指定容器名稱。例如,當讀者所部署的是KubeDNS附件而非CoreDNS時,kube-system名稱空間內的kube-dns相關的Pod中同時運行著kubedns、dnsmasq和sidecar三個容器,如果要查看kubedns容器的日志,需要使用類似如下的命令:
~]$ DNS_POD=$(kubectl get pods -o name -n kube-system | grep kube-dns) ~]$ kubectl logs $DNS_POD -c kubedns -n kube-system
需要注意的是,日志查看命令僅能用于打印存在于Kubernetes系統上的Pod中容器的日志,對于已經刪除的Pod對象,其容器日志信息將無從獲取。日志信息是用于輔助用戶獲取容器中應用程序運行狀態的最有效的途徑之一,也是非常重要的排錯手段,因此通常需要使用集中式的日志服務器統一收集存儲于各Pod對象中容器的日志信息。
3.在容器中運行額外的程序
運行著非交互式進程的容器中,默認運行的唯一進程及其子進程啟動后,容器即進入獨立、隔離的運行狀態。對容器內各種詳情的了解需要穿透容器邊界進入其中運行其他的應用程序來進行,“kubectl exec”可以讓用戶在Pod的某容器中運行用戶所需要的任何存在于容器中的程序。在“kubectl logs”獲取的信息不夠全面時,此命令可以通過在Pod中運行其他指定的命令(前提是容器中存在此程序)來輔助用戶獲取更多的信息。一個更便捷的使用接口的方式是直接交互式運行容器中的某個Shell程序。例如,直接查看Pod中的容器運行的進程:
~]$ kubectl exec $POD_NAME ps aux PID USER TIME COMMAND 1 root 0:00 nginx: master process nginx -g daemon off; 8 nginx 0:00 nginx: worker process 9 root 0:00 ps aux
注意
如果Pod對象中運行了多個容器,那么在程序運行時還需要使用“-c <container_name>”選項指定要于其內部運行程序的容器名稱。
若要進入容器的交互式Shell接口,可使用類似如下的命令,斜體部分表示在容器的交互式接口中執行的命令:
~]$ kubectl -it exec $POD_NAME /bin/sh / # hostname myapp-6865459dff-5nsjc / # netstat -tnl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN / #
2.4.3 部署Service對象
簡單來說,一個Service對象可視作通過其標簽選擇器過濾出的一組Pod對象,并能夠為此組Pod對象監聽的套接字提供端口代理及調度服務。
1.創建Service對象
“kubectl expose”命令可用于創建Service對象以將應用程序“暴露”(expose)于網絡中。例如,下面的命令即可將myapp創建的Pod對象使用“NodePort”類型的服務暴露到集群外部:
~]$ kubectl expose deployments/myapp --type="NodePort" --port=80--name=myapp service "myapp" exposed
上面的命令中,--type選項用于指定Service的類型,而--port則用于指定要暴露的容器端口,目標Service對象的名稱為myapp。創建完成后,default名稱空間中的對象及其通信示意圖如圖2-12所示。

圖2-12 Service對象在Pod對象前端添加了一個固定訪問層
下面通過運行于同一集群中的Pod對象中的客戶端程序發起訪問測試,來模擬圖2-12中的源自myapp Client Pod對象的訪問請求。首先,使用kubectl run命令創建一個Pod對象,并直接接入其交互式接口,如下命令的-it組合選項即用于交互式打開并保持其shell命令行接口;而后通過wget命令對此前創建的Service對象的名稱發起訪問請求,如下命令中的myapp即Service對象名稱,default即其所屬的Namespace對象的名稱:
$ kubectl run client --image=busybox --restart=Never -it -- /bin/sh If you don't see a command prompt, try pressing enter. / # wget -O - -q http://myapp.default:80 Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
創建時,Service對象名稱及其ClusterIP會由CoreDNS附件動態添加至名稱解析庫當中,因此,名稱解析服務在對象創建后即可直接使用。
類似于列出Deployment控制器及Pod對象的方式,“kubectl get services”命令能夠列出Service對象的相關信息,例如下面的命令顯示了Service對象myapp的簡要狀態信息:
~]$ kubectl get svc/myapp NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE myapp NodePort 10.109.39.145 <none> 80:31715/TCP 5m
其中,“PORT(s)”字段表明,集群中各工作節點會捕獲發往本地的目標端口為31715的流量,并將其代理至當前Service對象的80端口,于是,集群外部的用戶可以使用當前集群中任一節點的此端口來請求Service對象上的服務。CLUSTER-IP字段為當前Service的IP地址,它是一個虛擬IP,并沒有配置于集群中的任何主機的任何接口之上,但每個node之上的kube-proxy都會為CLUSTER-IP所在的網絡創建用于轉發的iptables或ipvs規則。此時,用戶可于集群外部任一瀏覽器請求集群任一節點的相關端口來進行訪問測試。
創建Service對象的另一種方式是使用“kubectl create service”命令,對應于每個類型,它分別有一個專用的子命令,例如“kubectl create service clusterip”和“kubectl create service nodeport”等,各命令在使用方式上也略有區別。
2.查看Service資源對象的描述
“kubectl describe services”命令用于打印Service對象的詳細信息,它通常包括Service對象的Cluster IP,關聯Pod對象時使用的標簽選擇器及關聯到的Pod資源的端點等,示例如下:
~]$ kubectl describe services myapp-svc Name: myapp Namespace: default Labels: run=myapp Annotations: <none> Selector: run=myapp Type: NodePort IP: 10.109.39.145 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31715/TCP Endpoints: 10.244.3.2:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
上面命令的執行結果輸出基本上可以做到見名而知義,此處需要特別說明的幾個字段具體如下:
1)Selector:當前Service對象使用的標簽選擇器,用于選擇關聯的Pod對象。
2)Type:即Service的類型,其值可以是ClusterIP、NodePort和LoadBalancer等其中之一。
3)IP:當前Service對象的ClusterIP。
4)Port:暴露的端口,即當前Service用于接收并響應請求的端口。
5)TargetPort:容器中的用于暴露的目標端口,由Service Port路由請求至此端口。
6)NodePort:當前Service的NodePort,它是否存在有效值與Type字段中的類型相關。
7)EndPoints:后端端點,即被當前Service的Selector挑中的所有Pod的IP及其端口。
8)Session Affinity:是否啟用會話粘性。
9)External Traffic Policy:外部流量的調度策略。
2.4.4 擴容和縮容
前面示例中創建的Deployment對象myapp僅創建了一個Pod對象,其所能夠承載的訪問請求數量即受限于這單個Pod對象的服務容量。請求流量上升到接近或超出其容量之前,用戶可以通過Kubernetes的“擴容機制”來擴展Pod的副本數量,從而提升其服務容量。
簡單來說,所謂的“伸縮”(Scaling)就是指改變特定控制器上Pod副本數量的操作,“擴容”(scaling up)即為增加副本數量,而“縮容”(scaling down)則意指縮減副本數量。不過,無論是擴容還是縮容,其數量都需要由用戶明確給出。
Service對象內建的負載均衡機制可在其后端副本數量不止一個時自動進行流量分發,它還會自動監控關聯到的Pod的健康狀態,以確保僅將請求流量分發至可用的后端Pod對象。若某Deployment控制器管理包含多個Pod實例,則必要時用戶還可以為其使用“滾動更新”機制將其容器鏡像升級到新的版本或變更那些支持動態修改的Pod屬性。
使用kubectl run命令創建Deployment對象時,“--replicas=”選項能夠指定由該對象創建或管理的Pod對象副本的數量,且其數量支持運行時進行修改,并立即生效。“kubectl scale”命令就是專用于變動控制器應用規模的命令,它支持對Deployment資源對象的擴容和縮容操作。例如,如果要將myapp的Pod副本數量擴展為3個,則可以使用如下命令來完成:
~]$ kubectl scale deployments/myapp --replicas=3 deployment.extensions "myapp" scaled
而后列出由myapp創建的Pod副本,確認其擴展操作的完成狀態。如下命令顯示出其Pod副本數量已經擴增至3個,其中包括此前的myapp-6865459dff-5nsjc:
~]$ kubectl get pods -l run=myapp NAME READY STATUS RESTARTS AGE myapp-6865459dff-52j7t 0/1 ContainerCreating 0 53s myapp-6865459dff-5nsjc 1/1 Running 0 2h myapp-6865459dff-jz7t6 0/1 ContainerCreating 0 53s
Deployment對象myapp規模擴展完成之后,default名稱空間中的資源對象及其關聯關系如圖2-13所示。

圖2-13 Deployment對象規模擴增完成
而后由“kubectl describe deployment”命令打印Deployment對象myapp的詳細信息,了解其應用規模的變動及當前Pod副本的狀態等相關信息。從下面的命令結果可以看出,其Pod副本數量的各項指標都已經轉換到了新的目標數量,而其事件信息中也有相應的事件顯示其擴增操作已成功完成:
~]$ kubectl describe deployments/myapp Name: myapp …… Selector: run=myapp Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable …… Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set myapp-6865459dff to 3
由myapp自動創建的Pod資源全都擁有同一個標簽選擇器“run=myapp”,因此,前面創建的Service資源對象myapp的后端端點也已經通過標簽選擇器自動擴展到了這3個Pod對象相關的端點,如下面的命令結果及圖2-13所示:
~]$ kubectl describe services/myapp Name: myapp …… Endpoints: 10.244.1.3:80,10.244.2.2:80,10.244.3.2:80 ……
回到此前創建的客戶端Pod對象client的交互式接口,對Service對象myapp反復發起測試請求,即可驗正其負載均衡的效果。由如下命令及其結果可以看出,它會將請求調度至后端的各Pod對象進行處理:
~]$ / # while true; do wget -O - -q http://myapp.default:80/hostname.html; sleep 1; done myapp-6865459dff-jz7t6 myapp-6865459dff-52j7t myapp-6865459dff-5nsjc ……
應用規模縮容的方式與擴容相似,只不過是將Pod副本的數量調至比原來小的數字即可。例如,將myapp的Pod副本縮減至2個,可以使用如下命令進行:
~]$ kubectl scale deployments/myapp --replicas=2 deployment.extensions "myapp" scaled
至此,功能基本完整的容器化應用已在Kubernetes上部署完成,即便是一個略復雜的分層應用也只需要通過合適的鏡像以類似的方式就能部署完成。
2.4.5 修改及刪除對象
成功創建于Kubernetes之上的對象也稱為活動對象(live object),其配置信息(live object configuration)由API Server保存于集群狀態存儲系統etcd中,“kubectl get TYPE NAME -o yaml”命令可獲取到相關的完整信息,而運行“kubectl edit”命令可調用默認編輯器對活動對象的可配置屬性進行編輯。例如,修改此前創建的Service對象myapp的類型為ClusterIP,使用“kubectl edit service myapp”命令打開編輯界面后修改type屬性的值為ClusterIP,并刪除NodePort屬性,然后保存即可。對活動對象的修改將實時生效,但資源對象的有些屬性并不支持運行時修改,此種情況下,編輯器將不允許保存退出。
有些命令是kubectl edit命令某一部分功能的二次封裝,例如,kubectl scale命令不過是專用于修改資源對象的replicas屬性值而已,它也同樣直接作用于活動對象。
不再有價值的活動對象可使用“kubectl delete”命令予以刪除,需要刪除Service對象myapp時,使用如下命令即可完成:
~]$ kubectl delete service myapp service "myapp" deleted
有時候需要清空某一類型下的所有對象,只需要將上面命令對象的名稱換成“--all”選項便能實現。例如,刪除默認名稱空間中所有的Deployment控制器的命令如下:
~]$ kubectl delete deployment --all deployment.extensions "myapp" deleted
需要注意的是,受控于控制器的Pod對象在刪除后會被重建,刪除此類對象需要直接刪除其控制器對象。不過,刪除控制器時若不想刪除其Pod對象,可在刪除命令上使用“--cascade=false”選項。
雖然直接命令式管理的相關功能強大且適合用于操縱Kubernetes資源對象,但其明顯的缺點是缺乏操作行為以及待運行對象的可信源。另外,直接命令式管理資源對象存在較大的局限性,它們在設置資源對象屬性方面提供的配置能力相當有限,而且還有不少資源并不支持命令操作進行創建,例如,用戶無法創建帶有多個容器的Pod對象,也無法為Pod對象創建存儲卷。因此,管理資源對象更有效的方式是基于保存有對象配置信息的配置清單來進行。