前言
上一節發布了一個容器到 K8S 中,但其實實際操作的是 Pod ,那么為什么是 Pod,而不是容器,
更新歷史
- 20200531 - 初稿 - 左程立
- 原文地址 - https://blog.zuolinux.com/2020/05/22/about-pod.html
Pod 的本質
Pod 包裝了一個或多個容器,
Pod 是 K8S 的最小執行單元,
Pod 是 K8S 中的一個行程,
Pod 可包裝 Docker,也支持包裝其他型別容器,
Pod 包含封裝的容器、存盤資源、網路資源、以及指導容器如何運行的配置,
可以把容器理解為一個無掛鉤的光禿禿的集裝箱,K8S 這艘大船無法直接掛載它,通過給集裝箱(容器)加裝掛鉤(IP地址)等形成一個 Pod,方便 K8S 來操作,
也可以把 Pod 理解為傳統的虛擬機,而容器是傳統虛擬機中運行的程式,只不過虛擬機是一個物體,而 Pod 是一個邏輯概念,
K8S 通過編排 Pod 來調度容器,而不是直接操作容器,K8S 無法直接操縱容器,
Pod 中的共享資源
Pod 為其中運行的多容器提供共享的網路、存盤資源、命名空間,
網路
Pod 具有唯一 IP 地址,Pod 中的多個容器共享一個 IP 地址和網路埠等網路資源
Pod 中多容器可使用 localhost 通信
Pod 中容器和外部通信時候,多容器需要協調網路埠
Pod 中的容器獲取的系統主機名與為 Pod 配置的 name 相同
存盤
Pod 可指定一組存盤卷
Pod 中多容器均可以訪問該存盤卷,以便互相共享資料
Pod 中的共享卷可以持久保存,防止容器重啟丟失資料
Pod 的特點
如果使用 kind: Pod 的 yaml 檔案來創建 Pod,當前節點服務器出現問題后,Pod 不能被自動調度到其他可用服務器,
一般使用 kind: Deployment 的 yaml 來創建 Pod,
Deployment 是一種控制器,可以用來創建、管理 Pod,如創建多副本 Pod,滾動更新 Pod,
當 Pod 所在節點出現問題,Deployment 控制器可以在集群中其他節點啟動新 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 會嘗試從指定的倉庫拉取每個鏡像,
如果容器屬性 imagePullPolicy 設定為 IfNotPresent , 則會優先使用本地鏡像,
如果容器屬性 imagePullPolicy 設定為 IfNotPresent Never, 則會一定使用本地鏡像,
apiVersion: v1
kind: Pod
metadata:
name: private-image-test-1
spec:
containers:
- name: uses-private-image
image: nginx
imagePullPolicy: Always
command: [ "echo", "SUCCESS" ]
Pod 常用引數
NodeSelector
功能:將 Pod 與 Node 系結
apiVersion: v1
kind: Pod
...
spec:
nodeSelector:
disktype: ssd
該 Pod 只能運行在攜帶了“disktype:ssd”標簽(Label)的節點上,如果沒有這種標簽的節點,調度將失敗,
NodeName
該欄位一般由調度器設定,但我們測驗時候可以手工設定該欄位,讓調度器認為該 Pod 已經被調度過了,
HostAliase
給 Pod 里各容器的 /etc/hosts 檔案設定內容
apiVersion: v1
kind: Pod
......
spec:
hostAliases:
- ip: "10.20.20.20"
hostnames:
- "test1.com"
- "test2.com"
進容器檢查一下
[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
定義引數 shareProcessNamespace=true,那么該 Pod 中所有容器將共享 PID Namespace
創建一個包含兩個容器的 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
查看運行情況
[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>
進入到 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 容器的行程
Pod 的預設引數 PodPreset
我們可以預先設定好一些通用的配置,當用戶提交自己的個性化 Pod 配置時,PodPreset 就可以自動附加通用配置到對應的 Pod 上,
PodPreset 里定義的內容,只會在 Pod API 物件被創建之前追加在這個物件本身 上,而不會影響任何 Pod 的控制器的定義,
比如,我們現在提交的是一個 nginx-deployment,那么這個 Deployment 物件本身是永遠不會被 PodPreset 改變的,被修改的只是這個 Deployment 創建出來的所有 Pod,
未啟用 PodPreset 特性時
# 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臺MASTER均執行重啟 kubelet
systemctl restart kubelet
預設值配置 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
創建 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
創建 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 容器
特點
- Init 容器在 Pod 內應用容器啟動之前運行,
- 一個 Pod 可以有一個或多個 Init 容器,
- 每個 Init 容器運行必須完成,
- 如果 Init 容器運行失敗,K8S 會不斷重啟該 Pod,直到 Init 容器運行成功,
- 但如果 Pod 對應的 restartPolicy 值為 Never,它不會重新啟動,
- 如果一個 Pod 有多個 Init 容器,這些容器會按順序逐個運行,每個 Init 容器必須運行成功,下一個才能夠運行,
- Init 容器可以包含一些安裝程序中應用容器中不存在的實用工具或個性化代碼,例如,沒有必要僅為了在安裝程序中使用類似 sed、 awk、 python 或 dig 這樣的工具而去FROM 一個鏡像來生成一個新的鏡像,
- Init 容器可以安全地運行這些工具,避免這些工具導致應用鏡像的安全性降低,
- 應用鏡像的創建者和部署者可以各自獨立作業,而沒有必要聯合構建一個單獨的應用鏡像,
- Init 容器能以不同于 Pod 內應用容器的檔案系統視圖運行,因此,Init容器可具有訪問 Secrets 的權限,而應用容器不能夠訪問,
- 由于 Init 容器必須在應用容器啟動之前運行完成,因此 Init 容器提供了一種機制來阻塞或延遲應用容器的啟動,直到滿足了一組先決條件,一旦前置條件滿足,Pod 內的所有的應用容器會并行啟動,
- 在所有的 Init 容器沒有成功之前,Pod 將不會變成 Ready 狀態,
- Init 容器鏡像的變更會引起 Pod 重啟, 應用容器鏡像的變更僅會重啟應用容器,
- Pod 中每一個容器副本啟動之前,都會執行一遍 Init 容器,
如何使用
- 定義一個具有 2 個 Init 容器的 Pod,
- init 容器為 myservice 和 mydb, 這兩個 Init 容器都啟動完成,Pod 才能啟動 spec 區域中的應用容器 myapp-container
創建 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"]
創建 mydb 和 myservice 兩個 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
要啟動這個 Pod,可以執行如下命令:
kubectl apply -f myapp.yaml
pod/myapp-pod created
檢查其狀態:
kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
使用下面命令查看更詳細的資訊:
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內 Init 容器的日志
$ kubectl logs myapp-pod -c init-myservice
$ kubectl logs myapp-pod -c init-mydb
此時,Init 容器將會等待直到發現名稱為mydb和myservice的 Service,
創建mydb和myservice的 service:
$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created
可以看到這些 Init 容器執行完畢,隨后my-app的Pod轉移進入 Running 狀態:
$ kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
只有我們啟動了 mydb 和 myservice 這兩個 Service,Init 容器完成,myapp-pod 才能被創建,
Debug Pod
如果 Pod 被終止,可通過如下命令查看原因
kubectl describe pod pod名稱
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' pod名稱
結束語
Pod 包裝了容器,K8S 通過操作 Pod 來操作容器,
Pod 可以包含多個應用容器和多個 Init 容器,
人工直接創建的 Pod 在服務器節點故障時,沒有自愈能力,需要使用控制器來解決這個問題,
聯系我
微信公眾號:zuolinux_com

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/180092.html
標籤:其他
