一. 簡介
Kubernetes 里“最小”的 API 對象是 Pod。Pod 可以等價為一個應(yīng)用,所以,Pod 可以由多個緊密協(xié)作的容器組成。在 Kubernetes 中,我們經(jīng)常會看到它通過一種 API 對象來管理另一種 API 對象,比如 Deployment 和 Pod 之間的關(guān)系,而由于 Pod 是“最小”的對象,所以它往往都是被其他對象控制的。
這種組合方式,正是 Kubernetes 進行容器編排的重要模式。
二. 流程
關(guān)于如何演示kubectl apply 如何使用,下面講創(chuàng)造一個這樣的場景:
運行一個3個副本的nginx應(yīng)用,并且nginx指定版本為1.7.9,同時對外暴露80端口。
本篇文章相關(guān)代碼,已在GitHub此鏈接:demo-deployment.yaml
2.1 編寫YAML
Kubernetes 跟 Docker 等很多項目最大的不同,就在于它不推薦我們使用命令行的方式直接運行容器(雖然 Kubernetes 項目也支持這種方式,比如:kubectl run),而是希望用 YAML文件的方式,即:把容器的定義、參數(shù)、配置,統(tǒng)統(tǒng)記錄在一個YAML文件中。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
如上的一個 YAML 文件,對應(yīng)到 Kubernetes 中,就是一個 API Object(API 對象)。當(dāng)你為這個對象的各個字段填好值并提交給 Kubernetes 之后,Kubernetes 就會負責(zé)創(chuàng)建出這些對象所定義的容器或者其他類型的 API 資源。
2.2 分析YAML
針對如上的yaml,細節(jié)參數(shù)含義如下:
- apiVersion:Api版本
- kind:指定當(dāng)前Api type為Deployment
- spec.selector.mathcLabels:標(biāo)簽,作為過濾篩選使用
- spec.replicas:當(dāng)前定義的Pod的數(shù)量,也就是期望值,Kubernetes會一直保證期望值=實際值
- spec.containers.image:容器的具體鏡像名稱+版本號
- spec.containers:容器相關(guān)配置
Labels這個字段也是上面的重點。這樣的每一個 API 對象都有一個叫作 Metadata 的字段,這個字段就是 API 對象的“標(biāo)識”,即元數(shù)據(jù),它也是我們從 Kubernetes 里找到這個對象的主要依據(jù)。這其中最主要使用到的字段是 Labels。換句話說,Labels 就是一組 key-value 格式的標(biāo)簽。
而像 Deployment 這樣的控制器對象,就可以通過這個 Labels 字段從 Kubernetes 中過濾出它所關(guān)心的被控制對象。
比如,在上面這個YAML文件中,Deployment 會把所有正在運行的、攜帶“app: nginx”標(biāo)簽的 Pod 識別為被管理的對象,并確保這些 Pod 的總數(shù)嚴格等于三個。而這個過濾規(guī)則的定義,是在 Deployment 的“spec.selector.matchLabels”字段。我們一般稱之為:Label Selector。
一個 Kubernetes 的 API 對象的定義,大多可以分為 Metadata 和 Spec 兩個部分。
- Metadata
Metadata存放的是這個對象的元數(shù)據(jù),對所有 API 對象來說,這部分的字段和格式基本上是一樣的。 - Spec
Spec存放的是屬于這個對象獨有的定義,用來描述它所要表達的功能。
2.3 運行YAML
指令如下:
kubectl apply -f demo-deployment.yaml (推薦)
# kubectl create -f demo-deployment.yaml
成功后,如下圖:

2.4 檢查Pods
通過kubectl get命令檢查這個YAML運行的運行結(jié)果是否與期望一致:
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
demo-deployment-5d59d67564-6dxnv 1/1 Running 0 2m23s
demo-deployment-5d59d67564-lgxzx 1/1 Running 0 2m23s
demo-deployment-5d59d67564-pcdk5 1/1 Running 0 2m23s
看到如下的狀態(tài),我們可以看到現(xiàn)在有3個 Pod 處于 Running 狀態(tài),也就意味著我們這個 Deployment 所管理的 Pod 都處于預(yù)期的狀態(tài)。

kubectl get指令的作用,就是從 Kubernetes 里面獲?。℅ET)指定的 API 對象??梢钥吹?,在這里還加上了一個-l參數(shù),即獲取所有匹配 app: nginx標(biāo)簽的 Pod。
一個細節(jié)問題:在命令行中,所有 key-value格式的參數(shù),都使用“=”而非“:”表示。
2.5 kubectl describe
我們從上面的Pod Name中挑選一個Pod,使用kubectl describe進入查看。
kubectl describe pod demo-deployment-5d59d67564-6dxnv
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8m default-scheduler Successfully assigned default/demo-deployment-5d59d67564-6dxnv to docker-desktop
Normal Pulled 7m59s kubelet Container image "nginx:1.7.9" already present on machine
Normal Created 7m59s kubelet Created container nginx
Normal Started 7m58s kubelet Started container nginx
在kubectl describe命令返回的結(jié)果中,可以清楚地看到這個 Pod 的詳細信息,比如它的 IP 地址等等。
在整個pod內(nèi)容體里面,我們可以關(guān)注一下Events的內(nèi)容。在 Kubernetes 執(zhí)行的過程中,對 API 對象的所有重要操作,都會被記錄在這個對象的 Events 里,并且顯示在kubectl describe指令返回的結(jié)果中。
這個Events從上向下看即可,對于當(dāng)前這個 Pod,我們可以看到它被創(chuàng)建之后,被調(diào)度器調(diào)度(Successfully assigned)到了 docker-desktop,拉取了指定的鏡像(本地)(Container image),然后啟動了 Pod 里定義的容器(Started container)。
這個正是我們將來進行 Debug 的重要依據(jù)。如果有異常發(fā)生,我們就可以第一時間查看這些 Events,往往可以看到非常詳細的錯誤信息。
2.6 更新YAML(patch功能)
如果我們要對這個 Nginx 服務(wù)進行升級,把它的鏡像版本從 1.7.9 升級為 1.8,那么操作還是很簡單。
- 只需要變動YAML的nginx版本即可
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8.0 # 這里被從1.7.9修改為1.8
ports:
- containerPort: 80
- 執(zhí)行
kubectl apply指令
kubectl apply -f demo-deployment.yaml (推薦)
# kubectl replace -f demo-deployment.yaml(不推薦)
- 查看Pods狀態(tài)
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
demo-deployment-64c9d67564-9m64m 1/1 Running 0 39s
demo-deployment-64c9d67564-m7l8w 1/1 Running 0 37s
demo-deployment-64c9d67564-rv5vs 1/1 Running 0 82s
是否覺得同一條指令kubectl apply可以進行增改操作很神奇?這就是 Kubernetes“聲明式 API”所推薦的使用方法。
執(zhí)行的命令始終是 kubectl apply,而 Kubernetes 則會根據(jù) YAML 文件的內(nèi)容變化,自動進行具體的處理。而這個流程的好處是,它有助于幫助開發(fā)和運維人員,圍繞著可以版本化管理的 YAML 文件,而不是“行蹤不定”的命令行進行協(xié)作,從而大大降低開發(fā)人員和運維人員之間的溝通成本。
2.7 刪除demo-deployment
Kubernetes會自動刪除YAML所相關(guān)的所有程序,這也是自動化與自描述帶來的好處。
執(zhí)行如下指令即可:
kubectl delete -f demo-deployment.yaml
三. Volume
3.1 Volume基礎(chǔ)類型
這兒的Volume是Kubernetes里面,而非docker,但是本質(zhì)上出別不大。具體為如下倆種:
emptyDir
其實就等同于我們之前講過的 Docker 的隱式 Volume 參數(shù),即:不顯式聲明宿主機目錄的 Volume。所以,Kubernetes 也會在宿主機上創(chuàng)建一個臨時目錄,這個目錄將來就會被綁定掛載到容器所聲明的 Volume 目錄上。hostPath
與上面相反,就是顯式的 Volume 定義。
3.2 案例
- emptyDir類型
參考項目項目中的V2版本,使用的是emptyDir類型。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-volume
volumes:
- name: nginx-volume
emptyDir: {}
我們在 Deployment 的 Pod 模板部分添加了一個volumes字段,定義了這個 Pod 聲明的所有 Volume。它的名字叫作 nginx-volume,類型是 emptyDir。
Pod 中的容器,使用的是 volumeMounts字段來聲明自己要掛載哪個Volume,并通過 mountPath字段來定義容器內(nèi)的 Volume 目錄,比如:/usr/share/nginx/html。
- hostPath類型
參考項目項目中的V3版本,使用的是hostPath類型。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-volume
volumes:
- name: nginx-volume
hostPath:
path: "/var/tmp"
這個和上面類型原理同等,只是容器 Volume 掛載的宿主機目錄,就變成了 /var/tmp。
3.4 kubectl exec
可以使用 kubectl exec指令,進入到這個 Pod 當(dāng)中(即容器的 Namespace 中)查看這個 Volume 目錄:
kubectl exec -it demo-deployment-5846465d4d-vpkb2 -- /bin/bash
# ls /usr/share/nginx/html/
四. 總結(jié)
關(guān)于Kubernetes與Docker的組合,我們已經(jīng)可以按照如下步驟進行落地編寫了:
- 在本地通過 Docker 測試代碼,制作鏡像
- 選擇合適的 Kubernetes API 對象,編寫對應(yīng) YAML 文件(比如,Pod,Deployment)
- 在 Kubernetes 上部署這個 YAML 文件。
我們會發(fā)現(xiàn)Kubernetes與Docker很多指令很相同,但是以后我們還是少用Docker指令,畢竟Docker只是整個云原生中很小的一個實現(xiàn)類而已,Kubernetes才是中樞系統(tǒng)。
本篇文章,我講了一個最常見的Deployment落地的全生命周期操作流程,關(guān)于每個細節(jié)其實還有很多其他內(nèi)容,這也是后面細分方向文章的重點。
最后,歡迎關(guān)注我的博客:https://blog.wyatt.plus
Reference
https://kubernetes.io/docs/setup/
https://medium.com/google-cloud/kubernetes-101-pods-nodes-containers-and-clusters-c1509e409e16
https://time.geekbang.org/column/article/40008?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title