前言
上一節(jié)發(fā)布了一個(gè)容器到 K8S 中,但其實(shí)實(shí)際操作的是 Pod 芯杀,那么為什么是 Pod,而不是容器。
更新歷史
- 20200531 - 初稿 - 左程立
- 原文地址 - https://blog.zuolinux.com/2020/05/22/about-pod.html
Pod 的本質(zhì)
Pod 包裝了一個(gè)或多個(gè)容器边篮。
Pod 是 K8S 的最小執(zhí)行單元。
Pod 是 K8S 中的一個(gè)進(jìn)程奏甫。
Pod 可包裝 Docker戈轿,也支持包裝其他類型容器。
Pod 包含封裝的容器阵子、存儲(chǔ)資源思杯、網(wǎng)絡(luò)資源、以及指導(dǎo)容器如何運(yùn)行的配置挠进。
可以把容器理解為一個(gè)無(wú)掛鉤的光禿禿的集裝箱色乾,K8S 這艘大船無(wú)法直接掛載它,通過(guò)給集裝箱(容器)加裝掛鉤(IP地址)等形成一個(gè) Pod领突,方便 K8S 來(lái)操作暖璧。
也可以把 Pod 理解為傳統(tǒng)的虛擬機(jī),而容器是傳統(tǒng)虛擬機(jī)中運(yùn)行的程序君旦,只不過(guò)虛擬機(jī)是一個(gè)實(shí)體澎办,而 Pod 是一個(gè)邏輯概念。
K8S 通過(guò)編排 Pod 來(lái)調(diào)度容器金砍,而不是直接操作容器局蚀,K8S 無(wú)法直接操縱容器。
Pod 中的共享資源
Pod 為其中運(yùn)行的多容器提供共享的網(wǎng)絡(luò)恕稠、存儲(chǔ)資源琅绅、命名空間。
網(wǎng)絡(luò)
Pod 具有唯一 IP 地址谱俭,Pod 中的多個(gè)容器共享一個(gè) IP 地址和網(wǎng)絡(luò)端口等網(wǎng)絡(luò)資源
Pod 中多容器可使用 localhost 通信
Pod 中容器和外部通信時(shí)候奉件,多容器需要協(xié)調(diào)網(wǎng)絡(luò)端口
Pod 中的容器獲取的系統(tǒng)主機(jī)名與為 Pod 配置的 name 相同
存儲(chǔ)
Pod 可指定一組存儲(chǔ)卷
Pod 中多容器均可以訪問(wèn)該存儲(chǔ)卷宵蛀,以便互相共享數(shù)據(jù)
Pod 中的共享卷可以持久保存,防止容器重啟丟失數(shù)據(jù)
Pod 的特點(diǎn)
如果使用 kind: Pod 的 yaml 文件來(lái)創(chuàng)建 Pod县貌,當(dāng)前節(jié)點(diǎn)服務(wù)器出現(xiàn)問(wèn)題后术陶,Pod 不能被自動(dòng)調(diào)度到其他可用服務(wù)器。
一般使用 kind: Deployment 的 yaml 來(lái)創(chuàng)建 Pod煤痕。
Deployment 是一種控制器梧宫,可以用來(lái)創(chuàng)建、管理 Pod摆碉。如創(chuàng)建多副本 Pod塘匣,滾動(dòng)更新 Pod。
當(dāng) Pod 所在節(jié)點(diǎn)出現(xiàn)問(wèn)題巷帝,Deployment 控制器可以在集群中其他節(jié)點(diǎn)啟動(dòng)新 Pod忌卤。
Pod 模板
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
鏡像拉取策略
不具體指定的情況下,imagePullPolicy 是 Always楞泼,即 kubelet 會(huì)嘗試從指定的倉(cāng)庫(kù)拉取每個(gè)鏡像驰徊。
如果容器屬性 imagePullPolicy 設(shè)置為 IfNotPresent , 則會(huì)優(yōu)先使用本地鏡像堕阔。
如果容器屬性 imagePullPolicy 設(shè)置為 IfNotPresent Never棍厂, 則會(huì)一定使用本地鏡像。
apiVersion: v1
kind: Pod
metadata:
name: private-image-test-1
spec:
containers:
- name: uses-private-image
image: nginx
imagePullPolicy: Always
command: [ "echo", "SUCCESS" ]
Pod 常用參數(shù)
NodeSelector
功能:將 Pod 與 Node 綁定
apiVersion: v1
kind: Pod
...
spec:
nodeSelector:
disktype: ssd
該 Pod 只能運(yùn)行在攜帶了“disktype:ssd”標(biāo)簽(Label)的節(jié)點(diǎn)上超陆,如果沒(méi)有這種標(biāo)簽的節(jié)點(diǎn)牺弹,調(diào)度將失敗。
NodeName
該字段一般由調(diào)度器設(shè)置时呀,但我們測(cè)試時(shí)候可以手工設(shè)置該字段张漂,讓調(diào)度器認(rèn)為該 Pod 已經(jīng)被調(diào)度過(guò)了。
HostAliase
給 Pod 里各容器的 /etc/hosts 文件設(shè)置內(nèi)容
apiVersion: v1
kind: Pod
......
spec:
hostAliases:
- ip: "10.20.20.20"
hostnames:
- "test1.com"
- "test2.com"
進(jìn)容器檢查一下
[root@master01 ~]# kubectl exec -it nginx -- bash
root@nginx:/# cat /etc/hosts
......
# Entries added by HostAliases.
10.20.20.20 test1.com test2.com
shareProcessNamespace
定義參數(shù) shareProcessNamespace=true退唠,那么該 Pod 中所有容器將共享 PID Namespace
創(chuàng)建一個(gè)包含兩個(gè)容器的 Pod
[root@master01 ~]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
shareProcessNamespace: true
containers:
- name: nginx
image: nginx
- name: shell
image: busybox
stdin: true
tty: true
查看運(yùn)行情況
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 2/2 Running 0 117s 192.10.137.131 work03 <none> <none>
進(jìn)入到 Pod nginx 中的名為 shell 的容器中
[root@master01 ~]# kubectl attach -it nginx -c shell
If you don't see a command prompt, try pressing enter.
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 nginx: master process nginx -g daemon off;
33 101 0:00 nginx: worker process
34 root 0:00 sh
39 root 0:00 ps aux
可以看到鹃锈,shell 容器中可以看到 nginx 容器的進(jìn)程
Pod 的預(yù)設(shè)參數(shù) PodPreset
我們可以預(yù)先設(shè)置好一些通用的配置,當(dāng)用戶提交自己的個(gè)性化 Pod 配置時(shí)瞧预,PodPreset 就可以自動(dòng)附加通用配置到對(duì)應(yīng)的 Pod 上屎债。
PodPreset 里定義的內(nèi)容,只會(huì)在 Pod API 對(duì)象被創(chuàng)建之前追加在這個(gè)對(duì)象本身 上垢油,而不會(huì)影響任何 Pod 的控制器的定義盆驹。
比如,我們現(xiàn)在提交的是一個(gè) nginx-deployment滩愁,那么這個(gè) Deployment 對(duì)象本身是永遠(yuǎn)不會(huì)被 PodPreset 改變的躯喇,被修改的只是這個(gè) Deployment 創(chuàng)建出來(lái)的所有 Pod。
未啟用 PodPreset 特性時(shí)
# kubectl get podpresets
error: the server doesn't have a resource type "podpresets"
啟用 PodPreset 特性
修改
[/etc/kubernetes/manifests/kube-apiserver.yaml]
中的
spec.containers.command:
修改原
- --runtime-config=api/all=true
為
- --runtime-config=api/all=true,settings.k8s.io/v1alpha1=true
新加一行
- --enable-admission-plugins=PodPreset
3臺(tái)MASTER均執(zhí)行重啟 kubelet
systemctl restart kubelet
預(yù)設(shè)值配置 preset.yaml
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: allow-tz-env
spec:
selector:
matchLabels:
env:
- name: TZ
value: Asia/Shanghai
Pod配置 nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
創(chuàng)建 PodPreset
# kubectl get podpreset
No resources found in default namespace.
# kubectl apply -f podpreset.yaml
podpreset.settings.k8s.io/allow-tz-env created
# kubectl get podpreset
NAME CREATED AT
allow-tz-env
創(chuàng)建 Pod
[root@master01 ~]# kubectl apply -f nginx.yaml
pod/nginx created
查看該 Pod 是否被注入
# kubectl get pod nginx -o yaml
...省略
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
image: nginx
imagePullPolicy: Always
name: nginx
resources: {}
...省略
可以看到 Pod 被注入了名為 TZ 的 env
Init 容器
特點(diǎn)
- Init 容器在 Pod 內(nèi)應(yīng)用容器啟動(dòng)之前運(yùn)行。
- 一個(gè) Pod 可以有一個(gè)或多個(gè) Init 容器廉丽。
- 每個(gè) Init 容器運(yùn)行必須完成倦微。
- 如果 Init 容器運(yùn)行失敗,K8S 會(huì)不斷重啟該 Pod正压,直到 Init 容器運(yùn)行成功欣福。
- 但如果 Pod 對(duì)應(yīng)的 restartPolicy 值為 Never,它不會(huì)重新啟動(dòng)焦履。
- 如果一個(gè) Pod 有多個(gè) Init 容器拓劝,這些容器會(huì)按順序逐個(gè)運(yùn)行。每個(gè) Init 容器必須運(yùn)行成功嘉裤,下一個(gè)才能夠運(yùn)行郑临。
- Init 容器可以包含一些安裝過(guò)程中應(yīng)用容器中不存在的實(shí)用工具或個(gè)性化代碼。例如屑宠,沒(méi)有必要僅為了在安裝過(guò)程中使用類似 sed厢洞、 awk、 python 或 dig 這樣的工具而去FROM 一個(gè)鏡像來(lái)生成一個(gè)新的鏡像典奉。
- Init 容器可以安全地運(yùn)行這些工具犀变,避免這些工具導(dǎo)致應(yīng)用鏡像的安全性降低。
- 應(yīng)用鏡像的創(chuàng)建者和部署者可以各自獨(dú)立工作秋柄,而沒(méi)有必要聯(lián)合構(gòu)建一個(gè)單獨(dú)的應(yīng)用鏡像。
- Init 容器能以不同于 Pod 內(nèi)應(yīng)用容器的文件系統(tǒng)視圖運(yùn)行蠢正。因此骇笔,Init容器可具有訪問(wèn) Secrets 的權(quán)限,而應(yīng)用容器不能夠訪問(wèn)嚣崭。
- 由于 Init 容器必須在應(yīng)用容器啟動(dòng)之前運(yùn)行完成笨触,因此 Init 容器提供了一種機(jī)制來(lái)阻塞或延遲應(yīng)用容器的啟動(dòng),直到滿足了一組先決條件雹舀。一旦前置條件滿足芦劣,Pod 內(nèi)的所有的應(yīng)用容器會(huì)并行啟動(dòng)。
- 在所有的 Init 容器沒(méi)有成功之前说榆,Pod 將不會(huì)變成 Ready 狀態(tài)虚吟。
- Init 容器鏡像的變更會(huì)引起 Pod 重啟, 應(yīng)用容器鏡像的變更僅會(huì)重啟應(yīng)用容器签财。
- Pod 中每一個(gè)容器副本啟動(dòng)之前串慰,都會(huì)執(zhí)行一遍 Init 容器。
如何使用
- 定義一個(gè)具有 2 個(gè) Init 容器的 Pod唱蒸。
- init 容器為 myservice 和 mydb邦鲫。 這兩個(gè) Init 容器都啟動(dòng)完成,Pod 才能啟動(dòng) spec 區(qū)域中的應(yīng)用容器 myapp-container
創(chuàng)建 Pod 的 YAML 文件:
cat myapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
創(chuàng)建 mydb 和 myservice 兩個(gè) Service 的 YAML 文件:
cat myservice.yaml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
要啟動(dòng)這個(gè) Pod神汹,可以執(zhí)行如下命令:
kubectl apply -f myapp.yaml
pod/myapp-pod created
檢查其狀態(tài):
kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
使用下面命令查看更詳細(xì)的信息:
kubectl describe -f myapp.yaml
Name: myapp-pod
Namespace: default
[...]
Labels: app=myapp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
查看Pod內(nèi) Init 容器的日志
$ kubectl logs myapp-pod -c init-myservice
$ kubectl logs myapp-pod -c init-mydb
此時(shí)庆捺,Init 容器將會(huì)等待直到發(fā)現(xiàn)名稱為mydb和myservice的 Service古今。
創(chuàng)建mydb和myservice的 service:
$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created
可以看到這些 Init 容器執(zhí)行完畢,隨后my-app的Pod轉(zhuǎn)移進(jìn)入 Running 狀態(tài):
$ kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
只有我們啟動(dòng)了 mydb 和 myservice 這兩個(gè) Service滔以,Init 容器完成捉腥,myapp-pod 才能被創(chuàng)建。
Debug Pod
如果 Pod 被終止醉者,可通過(guò)如下命令查看原因
kubectl describe pod pod名稱
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' pod名稱
結(jié)束語(yǔ)
Pod 包裝了容器但狭,K8S 通過(guò)操作 Pod 來(lái)操作容器。
Pod 可以包含多個(gè)應(yīng)用容器和多個(gè) Init 容器撬即。
人工直接創(chuàng)建的 Pod 在服務(wù)器節(jié)點(diǎn)故障時(shí)立磁,沒(méi)有自愈能力,需要使用控制器來(lái)解決這個(gè)問(wèn)題剥槐。