寫在前面
本文是K8S系列第二篇,主要面向對K8S新手同學,閱讀本文需要讀者對K8S的基本概念,比如Pod、Deployment、Service、Namespace等基礎概念有所了解。尚且不熟悉的同學推薦先閱讀本系列的第一篇文章:《K8S系列一:概念入門》
本文旨在講述如何通過kubectl(kubernetes命令行工具)操作K8S集群、以及其集群上的服務等資源。本文的組織架構如下:
- 如何配置kubectl?
- 如何部署自己的服務?包括創建Pod、Deployment和Service。
- 如何查看、更新/編輯和刪除服務?包括查看、更新/編輯和刪除Pod、Deployment和Service。
- 如何排查自己部署的服務的問題?
I. 配置kubectl
1.1 什么是kubectl?
官方文檔中介紹kubectl是:
Kubectl 是一個命令行接口,用于對 Kubernetes
集群運行命令。Kubectl的配置文件在$HOME/.kube目錄。我們可以通過設置KUBECONFIG環境變量或設置命令參數–kubeconfig來指定其他位置的kubeconfig文件。
也就是說,可以通過kubectl來操作K8S集群,官方文檔中介紹其基本語法:
就如何使用kubectl而言,官方文檔已經說得非常清楚。不過對于新手而言,還是需要解釋幾句:kubectl是K8S的命令行工具,并不需要kubectl安裝在K8S集群的任何Node上,但是,需要確保安裝kubectl的機器和K8S的集群能夠進行網絡互通。
接下來,一起看看怎么使用kubectl吧,切身感受下kubectl的使用。
請注意,如何安裝kubectl的辦法有許多非常明確的教程,比如《安裝并配置 kubectl》,本文不再贅述。
1.2 怎么配置kubectl?
第一步,必須準備好要連接/使用的K8S的配置文件,筆者給出一份杜撰的配置:
apiVersion: v1
clusters:- cluster:certificate-authority-data: thisisfakecertifcateauthoritydata00000000000server: https://1.2.3.4:1234name: cls-dev
contexts:- context:cluster: cls-devuser: kubernetes-adminname: kubernetes-admin@test
current-context: kubernetes-admin@test
kind: Config
preferences: {}
users:- name: kubernetes-adminuser:token: thisisfaketoken00000
解讀如下:
- clusters記錄了clusters(一個或多個K8S集群)信息:
- name是這個cluster(K8S集群)的名稱代號
- server是這個cluster(K8S集群)的訪問方式,一般為IP+PORT
- certificate-authority-data是證書數據,只有當cluster(K8S集群)的連接方式是https時,為了安全起見需要證書數據
- users記錄了訪問cluster(K8S集群)的賬號信息:
- name是用戶賬號的名稱代號
- user/token是用戶的token認證方式,token不是用戶認證的唯一方式,其他還有賬號+密碼等。
- contexts是上下文信息,包括了cluster(K8S集群)和訪問cluster(K8S集群)的用戶賬號等信息:
- name是這個上下文的名稱代號
- cluster是cluster(K8S集群)的名稱代號
- user是訪問cluster(K8S集群)的用戶賬號代號
- current-context記錄當前kubectl默認使用的上下文信息
- kind和apiVersion都是固定值,用戶不需要關心
- preferences則是配置文件的其他設置信息,筆者沒有使用過,暫時不提。
第二步,給kubectl配置上配置文件。
請注意,上述操作的優先級分別是1>2>3,也就是說,kubectl會優先檢查–kubeconfig,若無則檢查KUBECONFIG,若無則最后檢查$HOME/.kube/config,如果還是沒有,報錯。但凡某一步找到了有效的cluster,就中斷檢查,去連接K8S集群了。
第三步:配置正確的上下文
按照第二步的做法,如果配置文件只有一個cluster是沒有任何問題的,但是對于有多個cluster怎么辦呢?到這里,有幾個關于配置的必須掌握的命令:
- kubectl config get-contexts。列出所有上下文信息。
- kubectl config current-context。查看當前的上下文信息。其實,命令1線束出來的*所指示的就是當前的上下文信息。
- kubectl config use-context ${CONTEXT_NAME}。更改上下文信息。
- kubectl config set-context{CONTEXT_NAME}|–current --{KEY}={VALUE}。
修改上下文的元素。比如可以修改用戶賬號、集群信息、連接到K8S后所在的namespace。
關于該命令,還有幾點要啰嗦的:
- config set-context可以修改任何在配置文件中的上下文信息,只需要在命令中指定上下文名稱就可以。而**–current則指代當前上下文**。
- 上下文信息所包括的內容有:cluster集群(名稱)、用戶賬號(名稱)、連接到K8S后所在的namespace,因此有config
set-context嚴格意義上的用法: kubectl config set-context [NAME|–current]
[–cluster=cluster_nickname] [–user=user_nickname]
[–namespace=namespace] [options]
(備注:[options]可以通過kubectl options查看)
綜上,如何操作kubectl配置都已交代。
II. kubectl部署服務
K8S核心功能就是部署運維容器化服務,因此最重要的就是如何又快又好地部署自己的服務了。本章會介紹如何部署Pod和Deployment。
2.1 如何部署Pod?
通過kubectl部署Pod的辦法分為兩步:1). 準備Pod的yaml文件;2). 執行kubectl命令部署
第一步:準備Pod的yaml文件。關于Pod的yaml文件初步解釋,本系列上一篇文章《K8S系列一:概念入門》已經有了初步介紹,這里再復習下:
apiVersion: v1
kind: Pod
metadata:name: memory-demonamespace: mem-example
spec:containers:- name: memory-demo-ctrimage: polinux/stressresources:limits:memory: "200Mi"requests:memory: "100Mi"command: ["stress"]args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]volumeMounts:- name: redis-storagemountPath: /data/redisvolumes:- name: redis-storageemptyDir: {}
繼續解讀:
- metadata,對于新入門的同學來說,需要重點掌握的兩個字段:
- name。這個Pod的名稱,后面到K8S集群中查找Pod的關鍵字段。
- namespace。命名空間,即該Pod隸屬于哪個namespace下,關于Pod和namespace的關系,上一篇文章已經交代了。
- spec記錄了Pod內部所有的資源的詳細信息,這里我們重點查看containers下的幾個重要字段:
- name。Pod下該容器名稱,后面查找Pod下的容器的關鍵字段。
- image。容器的鏡像地址,K8S會根據這個字段去拉取鏡像。
- resources。容器化服務涉及到的CPU、內存、GPU等資源要求。可以看到有limits和requests兩個子項,那么這兩者有什么區別嗎,該怎么使用?在What’s
the difference between Pod resources.limits and resources.requests in
Kubernetes?回答了:
limits是K8S為該容器至多分配的資源配額;而requests則是K8S為該容器至少分配的資源配額。打個比方,配置中要求了memory的requests為100M,而此時如果K8S集群中所有的Node的可用內存都不足100M,那么部署服務會失敗;又如果有一個Node的內存有16G充裕,可以部署該Pod,而在運行中,該容器服務發生了內存泄露,那么一旦超過200M就會因為OOM被kill,盡管此時該機器上還有15G+的內存。
- command。容器的入口命令。對于這個筆者還存在很多困惑不解的地方,暫時挖個坑,有清楚的同學歡迎留言。
- args。容器的入口參數。同上,有清楚的同學歡迎留言。
- volumeMounts。容器要掛載的Pod數據卷等。請務必記住:Pod的數據卷只有被容器掛載后才能使用!
第二步:執行kubectl命令部署。有了Pod的yaml文件之后,就可以用kubectl部署了,命令非常簡單:kubectl create -f ${POD_YAML}。
隨后,會提示該命令是否執行成功,比如yaml內容不符合要求,則會提示哪一行有問題:
修正后,再次部署:
2.2 如何部署Deployment?
第一步:準備Deployment的yaml文件。首先來看Deployment的yaml文件內容:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:name: rss-sitenamespace: mem-examplespec:replicas: 2template:metadata:labels:app: webspec:containers:- name: memory-demo-ctrimage: polinux/stressresources:limits:emory: "200Mi"requests:memory: "100Mi"command: ["stress"]args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]volumeMounts:- name: redis-storagemountPath: /data/redisvolumes:- name: redis-storageemptyDir: {}
繼續來看幾個重要的字段:
- metadata同Pod的yaml,這里提一點:如果沒有指明namespace,那么就是用kubectl默認的namespace(如果kubectl配置文件中沒有指明namespace,那么就是default空間)。
- spec,可以看到Deployment的spec字段是在Pod的spec內容外“包了一層”,那就來看Deployment有哪些需要注意的:
- replicas。副本個數。也就是該Deployment需要起多少個相同的Pod,如果用戶成功在K8S中配置了n(n>1)個,那么Deployment會確保在集群中始終有n個服務在運行。
- template。
- metadata,新手同學先不管這邊的信息。
- spec,會發現這完完全全是上文提到的Pod的spec內容,在這里寫明了Deployment下屬管理的每個Pod的具體內容。
第二步:執行kubectl命令部署。Deployment的部署辦法同Pod:kubectl create -f ${DEPLOYMENT_YAML}。由此可見,K8S會根據配置文件中的kind字段來判斷具體要創建的是什么資源。
這里插一句題外話:部署完deployment之后,可以查看到自動創建了ReplicaSet和Pod,如下圖所示:
還有一個有趣的事情:通過Deployment部署的服務,其下屬的RS和Pod命名是有規則的。讀者朋友們自己總結發現哦。
綜上,如何部署一個Pod或者Deployment就結束了。
III. kubectl查看、更新/編輯、刪除服務
作為K8S使用者而言,更關心的問題應該是本章所要討論的話題:如何通過kubectl查看、更新/編輯、刪除在K8S上部署著的服務。
3.1 如何查看服務?
請務必記得一個事情:在K8S中,一個獨立的服務即對應一個Pod。即,當我們說要xxx一個服務的就是,也就是操作一個Pod。而與Pod服務相關的且需要用戶關心的,有Deployment。
通過kubectl查看服務的基本命令是:
$ kubectl get|describe ${RESOURCE} [-o ${FORMAT}] -n=${NAMESPACE}
# ${RESOURCE}有: pod、deployment、replicaset(rs)
在此之前,還有一個需要回憶的事情是:Deployment、ReplicaSet和Pod之間的關系 - 層層隸屬;以及這些資源和namespace的關系是 - 隸屬。如下圖所示。
因此,**要查看一個服務,也就是一個Pod,必須首先指定namespace!**那么,如何查看集群中所有的namespace呢?kubectl get ns:
于是,只需要通過-n=${NAMESPACE}就可以指定自己要操作的資源所在的namespace。比如查看Pod:kubectl get pod -n=oona-test,同理,查看Deployment:kubectl get deployment -n=oona-test。
問題又來了:如果已經忘記自己所部屬的服務所在的namespace怎么辦?這么多namespace,一個一個查看過來嗎?
kubectl get pod --all-namespaces
這樣子就可以看到所有namespace下面部署的Pod了!同理,要查找所有的命名空間下的Deployment的命令是:kubectl get deployment --all-namespaces。
于是,就可以開心地查看Pod:kubectl get pod [-o wide] -n=oona-test,或者查看Deployment:kubectl get deployment [-o wide] -n=oona-test。
哎,這里是否加-o wide有什么區別嗎?實際操作下就明白了,其他資源亦然:
哎,那我們看到之前部署的Pod服務memory-demo顯示的“ImagePullBackOff”是怎么回事呢?先不著急,我們慢慢看下去。
3.2 如何更新/編輯服務?
兩種辦法:1). 修改yaml文件后通過kubectl更新;2). 通過kubectl直接編輯K8S上的服務。
**方法一:修改yaml文件后通過kubectl更新。**我們看到,創建一個Pod或者Deployment的命令是kubectl create -f ${YAML}。但是,如果K8S集群當前的namespace下已經有該服務的話,會提示資源已經存在:
通過kubectl更新的命令是kubectl apply -f ${YAML},我們再來試一試:
(備注:命令kubectl apply -f ${YAML}也可以用于首次創建一個服務哦)
方法二:通過kubectl直接編輯K8S上的服務。命令為kubectl edit ${RESOURCE} ${NAME},比如修改剛剛的Pod的命令為kubectl edit pod memory-demo,然后直接編輯自己要修改的內容即可。
但是請注意,無論方法一還是方法二,能修改的內容還是有限的,從筆者實戰下來的結論是:只能修改/更新鏡像的地址和個別幾個字段。如果修改其他字段,會報錯:
The Pod “memory-demo” is invalid: spec: Forbidden: pod updates may not
change fields other than spec.containers[].image,
spec.initContainers[].image, spec.activeDeadlineSeconds or
spec.tolerations (only additions to existing tolerations)
如果真的要修改其他字段怎么辦呢?恐怕只能刪除服務后重新部署了。
3.3 如何刪除服務?
在K8S上刪除服務的操作非常簡單,命令為kubectl delete ${RESOURCE} ${NAME}。比如刪除一個Pod是:kubectl delete pod memory-demo,再比如刪除一個Deployment的命令是:kubectl delete deployment ${DEPLOYMENT_NAME}。但是,請注意:
- 如果只部署了一個Pod,那么直接刪除該Pod即可;
- 如果是通過Deployment部署的服務,那么僅僅刪除Pod是不行的,正確的刪除方式應該是:先刪除Deployment,再刪除Pod。
關于第二點應該不難想象:僅僅刪除了Pod但是Deployment還在的話,Deployment定時會檢查其下屬的所有Pod,如果發現失敗了則會再拉起。因此,會發現過一會兒,新的Pod又被拉起來了。
另外,還有一個事情:有時候會發現一個Pod總也刪除不了,這個時候很有可能要實施強制刪除措施,命令為kubectl delete pod --force --grace-period=0 ${POD_NAME}。
IV. kubectl排查服務問題
上文說道:部署的服務memory-demo失敗了,是怎么回事呢?本章就會帶大家一起來看看常見的K8S中服務部署失敗、服務起來了但是不正常運行都怎么排查呢?
首先,祭出筆者最愛的一張K8S排查手冊,來自博客《Kubernetes Deployment故障排除圖解指南》:
哈哈哈,對于新手同學來說,上圖還是不夠友好,下面我們簡單來看兩個例子:
4.1 K8S上部署服務失敗了怎么排查?
請一定記住這個命令:kubectl describe ${RESOURCE} ${NAME}。比如剛剛的Pod服務memory-demo,我們來看:
拉到最后看到Events部分,會顯示出K8S在部署這個服務過程的關鍵日志。這里我們可以看到是拉取鏡像失敗了,好吧,大家可以換一個可用的鏡像再試試。
一般來說,通過kubectl describe pod ${POD_NAME}已經能定位絕大部分部署失敗的問題了,當然,具體問題還是得具體分析。大家如果遇到具體的報錯,歡迎分享交流。
4.2 K8S上部署的服務不正常怎么排查?
如果服務部署成功了,且狀態為running,那么就需要進入Pod內部的容器去查看自己的服務日志了:
- 查看Pod內部某個container打印的日志:kubectl log ${POD_NAME} -c ${CONTAINER_NAME}。
- 進入Pod內部某個container:kubectl exec -it [options] ${POD_NAME} -c
${CONTAINER_NAME} [args],嗯,這個命令的作用是通過kubectl執行了docker exec
xxx進入到容器實例內部。之后,就是用戶檢查自己服務的日志來定位問題。
顯然,線上可能會遇到更復雜的問題,需要借助更多更強大的命令和工具。
寫在后面
本文是K8S系列文章第二篇,旨在第一篇基礎上加深對K8S的理解,且鼓勵大家一起動手使用K8S。如果文章中有紕漏,非常歡迎留言或者私信指出;有理解錯誤的地方,更是歡迎留言或者私信告知。
因為是實戰入門,因此提到的命令相對是比較基礎、常見的。也十分歡迎大家留言或者私信交流更多K8S的問題。