linux12k8s --> 06Pod詳解
文章目錄
Pod詳解
一、Pod介紹
1、pod的結構

每個Pod中都可以包含一個或者多個容器,這些容器可以分為兩類:
# 主容器 pause
# 業務容器 user container1 user container2
-
用戶程序所在的容器,數量可多可少
-
Pause容器,這是每個Pod都會有的一個根容器,它的作用有兩個:
- 可以以它為依據,評估整個Pod的健康狀態
-
可以在根容器上設置Ip地址,其它容器都此Ip(Pod IP),以實現Pod內部的網路通信
這里是Pod內部的通訊,Pod的之間的通訊采用虛擬二層網絡技術來實現,我們當前環境用的是Flannel。
2、Pod定義
1、k8s集群中部署的最小單元
2、Pod最主要的功能管理是將一個業務或者一個調用鏈的所有服務(容器)
3、包含多個容器(一組容器的集合)
4、一個Pod中容器共享網絡命名空間,Pod是短暫的
Pod是在集群中運行部署應用或服務的最小單元,他是可以支持很多容器的。Pod的設計理念是支持多個容器在一個Pod中共享網絡地址和文件系統,可以通過進程間通信和文件共享這種簡單高效的方式組合完成服務。
比如:你運行一個操作系統發行版的軟件倉庫,一個nginx容器用來發布軟件,另一個容器專門用來從源倉庫做同步,這兩個容器的鏡像不太可能是一個團隊開發的,但是他們一塊工作才能提供一個微服務,這種情況下,不同的團隊各自開發構建自己的容器鏡像,在部署的時候組合成一個微服務對外提供服務。這就是k8s中的Pod。
目前k8s中業務主要可以分為長期伺服型(long-running)、批處理型(batch)、節點后臺支撐型(node-daemon)和有狀態應用型(stateful application);分別對應的小機器人控制器為Deployment、Job、DaemonSet 和 StatefulSet。
# Pod是k8s中最小部署單元,用來管理一個調用鏈的容器,它之中的主容器(pause)為整個調用鏈的容器提供基礎網絡,共享存儲,監控業務容器的運行狀態
1、k8s中的命名規范
1、必須小寫
2、必須以字母開頭
3、名稱當中只能夠包含字母、數字和中劃線(-)
1、下面是Pod的資源清單:
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,資源類型,例如 Pod
metadata: #必選,元數據
name: string #必選,Pod名稱
annotations: #選做,描述信息
nginx: nginx
namespace: string #Pod所屬的命名空間,默認為"default"
labels: #自定義標簽列表
- name: string
spec: #必選,Pod中容器的詳細定義
containers: #必選,Pod中容器列表
- name: string #必選,容器名稱
image: string #必選,容器的鏡像名稱
imagePullPolicy: [ Always|Never|IfNotPresent ] #獲取鏡像的策略
command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令
args: [string] #容器的啟動命令參數列表
workingDir: string #容器的工作目錄
volumeMounts: #掛載到容器內部的存儲卷配置
- name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名
mountPath: string #存儲卷在容器內mount的絕對路徑,應少于512字符
readOnly: boolean #是否為只讀模式
ports: #需要暴露的端口庫號列表
- name: string #端口的名稱
containerPort: 80 #容器需要監聽的端口號
hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同
protocol: string #端口協議,支持TCP和UDP,默認TCP
env: #容器運行前需設置的環境變量列表
- name: string #環境變量名稱
value: string #環境變量的值
resources: #資源限制和請求的設置
limits: #資源限制的設置
cpu: string #Cpu的限制,單位為core數,將用于docker run --cpu-shares參數
memory: string #內存限制,單位可以為Mib/Gib,將用于docker run --memory參數
requests: #資源請求的設置
cpu: string #Cpu請求,容器啟動的初始可用數量
memory: string #內存請求,容器啟動的初始可用數量
lifecycle: #生命周期鉤子
postStart: #容器啟動后立即執行此鉤子,如果執行失敗,會根據重啟策略進行重啟
preStop: #容器終止前執行此鉤子,無論結果如何,容器都會終止
livenessProbe: #對Pod內各容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器
exec: #對Pod容器內檢查方式設置為exec方式
command: [string] #exec方式需要制定的命令或腳本
httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式
port: number
initialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒
timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒
periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure] #Pod的重啟策略
nodeName: <string> #設置NodeName表示將該Pod調度到指定到名稱的node節點上
nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上
imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定
- name: string
hostNetwork: false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡
volumes: #在該pod上定義共享存儲卷列表
- name: string #共享存儲卷名稱 (volumes類型有很多種)
emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值
hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄
path: string #Pod所在宿主機的目錄,將被用于同期中mount的目錄
secret: #類型為secret的存儲卷,掛載集群與定義的secret對象到容器內部
scretname: string
items:
- key: string
path: string
configMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部
name: string
items:
- key: string
path: string
2、查看每種資源的可配置項
# 在這里,可通過一個命令來查看每種資源的可配置項
# kubectl explain 資源類型 查看某種資源可以配置的一級屬性
# kubectl explain 資源類型.屬性 查看屬性的子屬性
[root@k8s-m-01 ~]# kubectl explain pod
KIND: Pod
VERSION: v1
FIELDS:
apiVersion <string>
kind <string>
metadata <Object>
spec <Object>
status <Object>
[root@k8s-m-01 ~]# kubectl explain pod.metadata
KIND: Pod
VERSION: v1
RESOURCE: metadata <Object>
FIELDS:
annotations <map[string]string>
clusterName <string>
creationTimestamp <string>
deletionGracePeriodSeconds <integer>
deletionTimestamp <string>
finalizers <[]string>
generateName <string>
generation <integer>
labels <map[string]string>
managedFields <[]Object>
name <string>
namespace <string>
ownerReferences <[]Object>
resourceVersion <string>
selfLink <string>
uid <string>
3、案例
1、案例1 – 部署nginx、tomcat
# kubectl explain Pod #查apiVersion版本號 v1
[root@k8s-m-01 ~]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
- name: tomcat
image: tomcat
2、案例2 – wordpress
# 1、nginx PHP MySQL
# 2、 制作鏡像
# 3、創建nginx配置文件,然后構建鏡像
# 4、編寫配置清單,部署
[root@k8s-m-01 ~]# vim wordpress.yaml
apiVersion: v1
kind: Pod
metadata:
name: wordpress
spec:
containers:
- name: nginx
image: nginx
- name: php
image: alvinos/php:v2-fpm-mysql
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123"
# k8s部署一個yaml的應用:kubectl apply -f [配置清單]
[root@k8s-m-01 ~]# kubectl apply -f pod.yaml
pod/test-pod create
ImgPullErr : # 鏡像拉取失敗
ContainerCreating : # 容器創建中
# 擴展 1、進入不是默認空間的容器
[root@k8s-m-01 ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-5c767764f5-fq47c 1/1 Running 2 78m
[root@k8s-m-01 ~]# kubectl exec -it pc-deployment-5c767764f5-fq47c -n dev -c nginx -- bash
root@pc-deployment-5c767764f5-fq47c:/# curl localhost
<h1>Welcome to nginx!</h1>
# 2、權限問題
[root@k8s-m-01 ~]# ll -a
drwxr-xr-x 3 root root 33 Jul 30 14:43 .kube
# 必須要有這個文件,否則無法創建刪除pod
在kubernetes中基本所有資源的一級屬性都是一樣的,主要包含5部分:
-
apiVersion <string> 版本,由kubernetes內部定義,版本號必須可以用 kubectl api-versions 查詢到
-
kind <string> 類型,由kubernetes內部定義,版本號必須可以用 kubectl api-resources 查詢到
-
metadata <Object> 元數據,主要是資源標識和說明,常用的有name、namespace、labels等
-
spec <Object> 描述,這是配置中最重要的一部分,里面是對各種資源配置的詳細描述
-
status <Object> 狀態信息,里面的內容不需要定義,由kubernetes自動生成
在上面的屬性中,spec是接下來研究的重點,繼續看下它的常見子屬性:
- containers <[]Object> 容器列表,用于定義容器的詳細信息
- nodeName <String> 根據nodeName的值將pod調度到指定的Node節點上
- nodeSelector <map[]> 根據NodeSelector中定義的信息選擇將該Pod調度到包含這些label的Node 上
- hostNetwork <boolean> 是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡
- volumes <[]Object> 存儲卷,用于定義Pod上面掛在的存儲信息
- restartPolicy <string> 重啟策略,表示Pod在遇到故障的時候的處理策略
4、kubernetes沒有提供單獨運行Pod的命令,都是通過Pod控制器來實現的
# 命令格式: kubectl run (pod控制器名稱) [參數]
# --image 指定Pod的鏡像
# --port 指定端口
# --namespace 指定namespace
[root@k8s-m-01 ~]# kubectl run nginx --image=nginx:latest --port=80 --namespace dev
deployment.apps/nginx created
1、 查看pod信息
# 1、查看Pod基本信息
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 43s
# 2、查看Pod的詳細信息
[root@k8s-m-01 ~]# kubectl describe pod nginx -n dev
Name: nginx
Namespace: dev
Priority: 0
Node: node1/192.168.5.4
Start Time: Wed, 08 May 2021 09:29:24 +0800
Labels: pod-template-hash=5ff7956ff6
run=nginx
Annotations: <none>
Status: Running
IP: 10.244.1.23
IPs:
IP: 10.244.1.23
Controlled By: ReplicaSet/nginx
Containers:
nginx:
Container ID: docker://4c62b8c0648d2512380f4ffa5da2c99d16e05634979973449c98e9b829f6253c
Image: nginx:latest
Image ID: docker-pullable://nginx@sha256:485b610fefec7ff6c463ced9623314a04ed67e3945b9c08d7e53a47f6d108dc7
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Wed, 08 May 2021 09:30:01 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-hwvvw (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-hwvvw:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-hwvvw
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned dev/nginx-5ff7956ff6-fg2db to node1
Normal Pulling 4m11s kubelet, node1 Pulling image "nginx:latest"
Normal Pulled 3m36s kubelet, node1 Successfully pulled image "nginx:latest"
Normal Created 3m36s kubelet, node1 Created container nginx
Normal Started 3m36s kubelet, node1 Started container nginx
2、訪問Pod
# 1、獲取pod IP
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
nginx 1/1 Running 0 190s 10.244.1.23 node1 ...
# 2、訪問POD
[root@k8s-m-01 ~]# curl http://10.244.1.23:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
3、刪除指定Pod
# 1、刪除指定Pod
[root@k8s-m-01 ~]# kubectl delete pod nginx -n dev
pod "nginx" deleted
# 2、此時,顯示刪除Pod成功,但是再查詢,發現又新產生了一個
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 21s
# 這是因為當前Pod是由Pod控制器創建的,控制器會監控Pod狀況,一旦發現Pod死亡,會立即重建
# 此時要想刪除Pod,必須刪除Pod控制器
# 3、先來查詢一下當前namespace下的Pod控制器
[root@k8s-m-01 ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 9m7s
# 4、接下來,刪除此PodPod控制器
[root@k8s-m-01 ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted
# 5、稍等片刻,再查詢Pod,發現Pod被刪除了
[root@k8s-m-01 ~]# kubectl get pods -n dev
No resources found in dev namespace.
3、配置操作
創建一個pod-nginx.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
然后就可以執行對應的創建和刪除命令了:
# 創建:kubectl create -f pod-nginx.yaml
# 刪除:kubectl delete -f pod-nginx.yaml
3、Pod存在的意義
# 1、創建容器使用docker,一個docker對應一個容器,一個容器有進程,一個容器運行一個應用程序
# 2、pod是多進程,運行多個應用程序
# 3、一個Pod有多個容器,每個容器里面運行一個應用程序
# 4、Pod存在未來親密性應用
1、兩個應用之間進行交互
2、網絡之間調用
3、兩個應用需要頻繁調用
4、Pod實現機制
1、共享網絡 === 》 容器本身之間相互隔離的
2、共享存儲
# 1、共享網絡
通過Pause容器,把其他業務容器加入Pause容器里面,讓所有業務中在同一個名稱空間中,可以實現網絡共享
# 2、共享存儲
引入數據卷概念volumes,用數據卷進行持久化數據存儲
二、Pod配置
主要來研究pod.spec.containers屬性,這也是pod配置中最為關鍵的一項配置。
[root@k8s-m-01 ~]# kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 數組,代表可以有多個容器
FIELDS:
name <string> # 容器名稱
image <string> # 容器需要的鏡像地址
imagePullPolicy <string> # 鏡像拉取策略
command <[]string> # 容器的啟動命令列表,如不指定,使用打包時使用的啟動命令
args <[]string> # 容器的啟動命令需要的參數列表
env <[]Object> # 容器環境變量的配置
ports <[]Object> # 容器需要暴露的端口號列表
resources <Object> # 資源限制和資源請求的設置
1、基本配置
創建pod-base.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-base
namespace: dev
labels:
user: heima
spec:
containers:
- name: nginx
image: nginx
- name: busybox
image: busybox:1.30
上面定義了一個比較簡單Pod的配置,里面有兩個容器:
- nginx:用nginx版本的nginx鏡像創建, (nginx是一個輕量級web容器)
- busybox:用1.30版本的busybox鏡像創建,(busybox是一個小巧的linux命令集合)
# 1、創建Pod
[root@master pod]# kubectl apply -f pod-base.yaml
pod/pod-base created
# 2、查看Pod狀況
# READY 1/2 : 表示當前Pod中有2個容器,其中1個準備就緒,1個未就緒
# RESTARTS : 重啟次數,因為有1個容器故障了,Pod一直在重啟試圖恢復它
[root@master pod]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod-base 1/2 Running 4 95s
# 3、可以通過describe查看內部的詳情
# 此時已經運行起來了一個基本的Pod,雖然它暫時有問題
[root@master pod]# kubectl describe pod pod-base -n dev
2、鏡像拉取
創建pod-imagepullpolicy.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: Always # 用于設置鏡像拉取策略
- name: busybox
image: busybox:1.30
1、三種拉取策略(重啟策略)
imagePullPolicy,用于設置鏡像拉取策略,kubernetes支持配置三種拉取策略:
# 1、IfNotPresent:(默認值)
本地有則使用本地鏡像,本地沒有則從遠程倉庫拉取鏡像(本地有就本地,本地沒遠程下載)
# 2. Always:
創建Pod都會重新從遠程倉庫拉取一次鏡像(一直遠程下載)
# 3. Never:
只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯 (一直使用本地)
# 1、20版本后默認的是Onfailure :當容器終止且退出碼部位0,則kubetle則重新啟動容器
默認值說明:
? 如果鏡像tag為具體版本號, 默認策略是:IfNotPresent
? 如果鏡像tag為:latest(最終版本) ,默認策略是always
# 1、創建Pod
[root@master pod]# kubectl create -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created
# 2、查看Pod詳情
# 此時明顯可以看到nginx鏡像有一步Pulling image "nginx"的過程
[root@master pod]# kubectl describe pod pod-imagepullpolicy -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned dev/pod-imagePullPolicy to k8s-n-01
Normal Pulling 32s kubelet, k8s-n-01 Pulling image "nginx"
Normal Pulled 26s kubelet, k8s-n-01 Successfully pulled image "nginx"
Normal Created 26s kubelet, k8s-n-01 Created container nginx
Normal Started 25s kubelet, k8s-n-01 Started container nginx
Normal Pulled 7s (x3 over 25s) kubelet, k8s-n-01 Container image "busybox:1.30" already present on machine
Normal Created 7s (x3 over 25s) kubelet, k8s-n-01 Created container busybox
Normal Started 7s (x3 over 25s) kubelet, k8s-n-01 Started container busybox
3、啟動命令
# 在前面的案例中,一直有一個問題沒有解決,就是的busybox容器一直沒有成功運行,那么到底是什么原因導致這個容器的故障呢?
原來busybox并不是一個程序,而是類似于一個工具類的集合,kubernetes集群啟動管理后,它會自動關閉。
# 解決方法就是讓其一直在運行,這就用到了command配置。
1、創建pod-command.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
spec:
containers:
- name: nginx
image: nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
# 注:
"/bin/sh","-c", 使用sh執行命令
touch /tmp/hello.txt; 創建一個/tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中寫入當前時間
2、command,用于在pod中的容器初始化完畢之后運行一個命令。
# 1、創建Pod
[root@master pod]# kubectl create -f pod-command.yaml
pod/pod-command created
# 2、查看Pod狀態
# 此時發現兩個pod都正常運行了
[root@master pod]# kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Runing 0 2s
# 3、進入pod中的busybox容器,查看文件內容
# 補充一個命令: kubectl exec pod名稱 -n 命名空間 -it -c 容器名稱 /bin/sh 在容器內部執行命令
# 使用這個命令就可以進入某個容器的內部,然后進行相關操作了
# 可以查看txt文件的內容
[root@master pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
13:35:35
13:35:38
13:35:41
3、總結說明
通過上面發現command已經可以完成啟動命令和傳遞參數的功能,為什么這里還要提供一個args選項,用于傳遞參數呢?這其實跟docker有點關系,kubernetes中的command、args兩項其實是實現覆蓋Dockerfile中ENTRYPOINT的功能。
# 1 如果command和args均沒有寫,那么用Dockerfile的配置。
# 2 如果command寫了,但args沒有寫,那么Dockerfile默認的配置會被忽略,執行輸入的command
# 3 如果command沒寫,但args寫了,那么Dockerfile中配置的ENTRYPOINT的命令會被執行,使用當前args的參數
# 4 如果command和args都寫了,那么Dockerfile的配置被忽略,執行command并追加上args參數
4、環境變量
1、創建pod-env.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 設置環境變量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
2、env,環境變量,用于在pod中的容器設置環境變量。
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-env.yaml
pod/pod-env created
# 2、進入容器,輸出環境變量
[root@k8s-m-01 ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ # echo $password
123456
這種方式不是很推薦,推薦將這些配置單獨存儲在配置文件中,這種方式將在后面介紹。
5、端口設置
端口設置就是containers的ports選項
1、首先看下ports支持的子選項:
[root@k8s-m-01 ~]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名稱,如果指定,必須保證name在pod中是唯一的
containerPort<integer> # 容器要監聽的端口(0<x<65536)
hostPort <integer> # 容器要在主機上公開的端口,如果設置,主機上只能運行容器的一個副本(一般省略)
hostIP <string> # 要將外部端口綁定到的主機IP(一般省略)
protocol <string> # 端口協議。必須是UDP、TCP或SCTP。默認為“TCP”。
2、編寫一個測試案例,創建pod-ports.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx
ports: # 設置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-ports.yaml
pod/pod-ports created
# 2、查看pod
# 在下面可以明顯看到配置信息
[root@k8s-m-01 ~]# kubectl get pod pod-ports -n dev -o yaml
......
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: nginx-port
protocol: TCP
......
訪問容器中的程序需要使用的是podIp:containerPort
6、資源配額
容器中的程序要運行,肯定是要占用一定資源的,比如cpu和內存等,如果不對某個容器的資源做限制,那么它就可能吃掉大量資源,導致其它容器無法運行。
針對這種情況,kubernetes提供了對內存和cpu的資源進行配額的機制,這種機制主要通過resources選項實現,他有兩個子選項:
-
limits:用于限制運行時容器的最大占用資源,當容器占用資源超過limits時會被終止,并進行重啟
-
requests :用于設置容器需要的最小資源,如果環境資源不夠,容器將無法啟動
可以通過上面兩個選項設置資源的上下限。
1、編寫一個測試案例,創建pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx
resources: # 資源配額
limits: # 限制資源(上限)
cpu: "2" # CPU限制,單位是core數
memory: "10Gi" # 內存限制
requests: # 請求資源(下限)
cpu: "1" # CPU限制,單位是core數
memory: "10Mi" # 內存限制
2、在這對cpu和memory的單位做一個說明:
cpu:core數,可以為整數或小數
memory: 內存大小,可以使用Gi、Mi、G、M等形式
# 1、運行Pod
[root@k8s-m-01 ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created
# 2、查看發現pod運行正常
[root@k8s-m-01 ~]# kubectl get pod pod-resources -n dev
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 39s
# 3、停止(刪除)Pod
[root@k8s-m-01 ~]# kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted
# 4、編輯pod,修改resources.requests.memory的值為10Gi
[root@k8s-m-01 ~]# vim pod-resources.yaml
# 5、再次啟動pod
[root@k8s-m-01 ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created
# 6、查看Pod狀態,發現Pod啟動失敗
[root@k8s-m-01 ~]# kubectl get pod pod-resources -n dev -o wide
NAME READY STATUS RESTARTS AGE
pod-resources 0/2 Pending 0 20s
# 7、查看pod詳情會發現,如下提示
[root@k8s-m-01 ~]# kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling <unknown> default-scheduler 0/2 nodes are available: 2 Insufficient memory.(內存不足)
三、Pod生命周期
1、pod的生命周期
我們一般將pod對象從創建至終的這段時間范圍稱為pod的生命周期,它主要包含下面的過程:
-
pod創建過程
-
運行初始化容器(init container)過程
-
運行主容器(main container)
-
容器啟動后鉤子(post start)、容器終止前鉤子(pre stop)
-
容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
-
-
pod終止過程


1、創建pod,并調度到合適的節點上
2、創建pause基礎主容器,提供共享名稱空間 # (info容器) == 根容器 == 主容器
3、從上到下依次創建業務容器
4、啟動業務容器,啟動那一刻會同時運行主容器上定義的Poststart鉤子事件
5、持續存活狀態監測、就緒狀態監測
6、結束時,執行prestop鉤子事件
7、終止業務容器,在終止主容器
8、銷毀Pod
2、在整個生命周期中,Pod會出現5種狀態(相位),
# 1、掛起(Pending):
API Server 創建了 pod 資源對象已存入 etcd 中,但它尚未被調度完成,或者仍處于從倉庫下載鏡像的過程中。
# 2、運行中(Running):
Pod 已經被調度至某節點,并且所有容器都已經被 kubelet 創建完成
# 3、成功(Succeeded):
Pod 中的所有容器都已經成功終止并且不會被重啟。
# 4、失敗(Failed):
Pod 中的所有容器都已終止了,并且至少有一個容器是因為失敗終止。即容器以非 0 狀態退出或者被系統禁止。
# 5、未知(Unknown):
Api Server 無法正常獲取到 Pod 對象的狀態信息,通常是由于無法與所在工作節點的kubelet 通信所致。
===================================================================================================
# 6、ImgPullErr : (不常用)
鏡像拉取失敗
# 7、ContainerCreating : (不常用)
容器創建中
kubernetes在集群啟動之后,集群中的各個組件也都是以Pod方式運行的。可以通過下面命令查看:
[root@k8s-m-01 ~]# kubectl get pod -n kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6955765f44-68g6v 1/1 Running 0 2d1h
kube-system coredns-6955765f44-cs5r8 1/1 Running 0 2d1h
kube-system etcd-master 1/1 Running 0 2d1h
kube-system kube-apiserver-master 1/1 Running 0 2d1h
kube-system kube-controller-manager-master 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-47r25 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-ls5lh 1/1 Running 0 2d1h
kube-system kube-proxy-685tk 1/1 Running 0 2d1h
kube-system kube-proxy-87spt 1/1 Running 0 2d1h
kube-system kube-scheduler-master 1/1 Running 0 2d1h
3、創建和終止
1、pod的創建過程
1. 用戶通過kubectl或其他api客戶端提交需要創建的Pod信息給apiServer
2. apiServer開始生成Pod對象的信息,并將信息存入etcd,然后返回確認信息至客戶端
3. apiServer開始反映etcd中的Pod對象的變化,其它組件使用watch機制來跟蹤檢查apiServer上的變動
4. scheduler發現有新的Pod對象要創建,開始為Pod分配主機并將結果信息更新至apiServer
5. node節點上的kubelet發現有pod調度過來,嘗試調用docker啟動容器,并將結果回送至apiServer
6. apiServer將接收到的pod狀態信息存入etcd中

2、pod的終止過程
1. 用戶向apiServer發送刪除pod對象的命令
2. apiServcer中的pod對象信息會隨著時間的推移而更新,在寬限期內(默認30s),pod被視為dead
3. 將pod標記為terminating狀態(正在刪除狀態)
4. kubelet在監控到pod對象轉為terminating狀態的同時啟動pod關閉過程
5. 端點控制器監控到pod對象的關閉行為時將其從所有匹配到此端點的service資源的端點列表中移除
6. 如果當前pod對象定義了preStop鉤子處理器,則在其標記為terminating后即會以同步的方式啟動執行
7. pod對象中的容器進程收到停止信號
8. 寬限期結束后,若pod中還存在仍在運行的進程,那么pod對象會收到立即終止的信號
9. kubelet請求apiServer將此pod資源的寬限期設置為0從而完成刪除操作,此時pod對于用戶已不可見
3、初始化容器
初始化容器是在pod的主容器啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有兩大特征:
1. 初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么kubernetes需要重啟它直到成功完成
2. 初始化容器必須按照定義的順序執行,當且僅當前一個成功之后,后面的一個才能運行
初始化容器有很多的應用場景,下面列出的是最常見的幾個:
1、提供主容器鏡像中不具備的工具程序或自定義代碼
2、初始化容器要先于應用容器串行啟動并運行完成,因此可用于延后應用容器的啟動直至其依賴的條件得到滿足
案例,模擬下面這個需求:
? 假設要以主容器來運行nginx,但是要求在運行nginx之前先要能夠連接上mysql和redis所在服務器
? 為了簡化測試,事先規定好mysql(192.168.15.201)和redis(192.168.15.202)服務器的地址
1、創建pod-initcontainer.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
initContainers:
- name: test-mysql
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.15.201 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.15.202 -c 1 ; do echo waiting for reids...; sleep 2; done;']
# 1、創建pod
[root@k8s-m-01 ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
# 2、查看pod狀態
# 發現pod卡在啟動第一個初始化容器過程中,后面的容器不會運行
root@master ~]# kubectl describe pod pod-initcontainer -n dev
........
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 49s default-scheduler Successfully assigned dev/pod-initcontainer to k8s-n-01
Normal Pulled 48s kubelet, k8s-n-01 Container image "busybox:1.30" already present on machine
Normal Created 48s kubelet, k8s-n-01 Created container test-mysql
Normal Started 48s kubelet, k8s-n-01 Started container test-mysql
# 3、動態查看pod
[root@k8s-m-01 ~]# kubectl get pods pod-initcontainer -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
pod-initcontainer 0/1 Init:1/2 0 52s
pod-initcontainer 0/1 Init:1/2 0 53s
pod-initcontainer 0/1 PodInitializing 0 89s
pod-initcontainer 1/1 Running 0 90s
# 4、接下來新開一個shell,為當前服務器新增兩個ip,觀察pod的變化
[root@k8s-m-01 ~]# ifconfig eth0:1 192.168.15.201 netmask 255.255.255.0 up
[root@k8s-m-01 ~]# ifconfig eth0:2 192.168.15.202 netmask 255.255.255.0 up
4、鉤子函數
鉤子函數能夠感知自身生命周期中的事件,并在相應的時刻到來時運行用戶指定的程序代碼。
kubernetes在主容器的啟動之后和停止之前提供了兩個鉤子函數:
1、post start: # 容器創建之后執行,如果失敗了會重啟容器
2、pre stop : # 容器終止之前執行,執行完成之后容器將成功終止,在其完成之前會阻塞刪除容器的操作
鉤子處理器支持使用下面三種方式定義動作:
-
Exec命令:在容器內執行一次命令
…… lifecycle: postStart: exec: command: - cat - /tmp/healthy …… -
TCPSocket:在當前容器嘗試訪問指定的socket
…… lifecycle: postStart: tcpSocket: port: 8080 …… -
HTTPGet:在當前容器中向某url發起http請求
…… lifecycle: postStart: httpGet: path: / #URI地址 port: 80 #端口號 host: 192.168.15.100 #主機地址 scheme: HTTP #支持的協議,http或者https ……
接下來,以exec方式為例,演示下鉤子函數的使用,創建pod-hook-exec.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器啟動的時候執行一個命令,修改掉nginx的默認首頁內容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服務
command: ["/usr/sbin/nginx","-s","quit"]
# 1、創建pod
[root@k8s-m-01 ~]# kubectl create -f pod-hook-exec.yaml
pod/pod-hook-exec created
# 2、查看pod
[root@k8s-m-01 ~]# kubectl get pods pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-hook-exec 1/1 Running 0 29s 10.244.2.48 k8s-n-02
# 3、訪問pod
[root@k8s-m-01 ~]# curl 10.244.2.48
postStart...
4、容器探測
容器探測用于檢測容器中的應用實例是否正常工作,是保障業務可用性的一種傳統機制。如果經過探測,實例的狀態不符合預期,那么kubernetes就會把該問題實例" 摘除 ",不承擔業務流量。kubernetes提供了兩種探針來實現容器探測,分別是:
# 1、存活性檢查 :
容器是否正常啟動,探測失敗,立即刪除容器
# 2、就緒性檢查 :
容器是否能夠正常提供服務,探測失敗,立即移除負載均衡
-
liveness probes: 存活性探針,用于檢測應用實例當前是否處于正常運行狀態,如果不是,k8s會重啟容器
-
readiness probes:就緒性探針,用于檢測應用實例當前是否可以接收請求,如果不能,k8s不會轉發流量
livenessProbe 決定是否重啟容器,readinessProbe 決定是否將請求轉發給容器。
上面兩種探針目前均支持三種探測方式:
-
Exec命令:在容器內執行一次命令,如果命令執行的退出碼為0,則認為程序正常,否則不正常
…… livenessProbe: exec: command: - cat - /tmp/healthy …… -
TCPSocket:將會嘗試訪問一個用戶容器的端口,如果能夠建立這條連接,則認為程序正常,否則不正常
…… livenessProbe: tcpSocket: port: 8080 …… -
HTTPGet:調用容器內Web應用的URL,如果返回的狀態碼在200和399之間,則認為程序正常,否則不正常
…… livenessProbe: httpGet: path: / #URI地址 port: 80 #端口號 host: 127.0.0.1 #主機地址 scheme: HTTP #支持的協議,http或者https ……
下面以liveness probes為例,做幾個演示:
方式一:Exec
1、創建pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 執行一個查看文件的命令
2、 創建pod,觀察效果
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
# 2、查看Pod詳情
[root@k8s-m-01 ~]# kubectl describe pods pod-liveness-exec -n dev
......
Normal Created 20s (x2 over 50s) kubelet, k8s-n-01 Created container nginx
Normal Started 20s (x2 over 50s) kubelet, k8s-n-01 Started container nginx
Normal Killing 20s kubelet, k8s-n-01 Container nginx failed liveness probe, will be restarted
Warning Unhealthy 0s (x5 over 40s) kubelet, k8s-n-01 Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory
# 觀察上面的信息就會發現nginx容器啟動之后就進行了健康檢查
# 檢查失敗之后,容器被kill掉,然后嘗試進行重啟(這是重啟策略的作用,后面講解)
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-m-01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 2 3m19s
# 當然接下來,可以修改成一個存在的文件,比如/tmp/hello.txt,再試,結果就正常了......
# command: ["/bin/ls","/tmp/"] #正確配置(查看文件的命令)
方式二:TCPSocket
1、創建pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
tcpSocket:
port: 8080 # 嘗試訪問8080端口
2、創建pod,觀察效果
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
# 2、查看Pod詳情
[root@k8s-m-01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......
Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to k8s-n-02
Normal Pulled <invalid> kubelet, k8s-n-02 Container image "nginx:1.17.1" already present on machine
Normal Created <invalid> kubelet, k8s-n-02 Created container nginx
Normal Started <invalid> kubelet, k8s-n-02 Started container nginx
Warning Unhealthy <invalid> (x2 over <invalid>) kubelet, k8s-n-02 Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused
# 觀察上面的信息,發現嘗試訪問8080端口,但是失敗了
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-m-01 ~]# kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 2 3m19s
# 當然接下來,可以修改成一個可以訪問的端口,比如80,再試,結果就正常了......
# port: 80 (正確配置)
方式三:HTTPGet
1、創建pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet: # 其實就是訪問http://127.0.0.1:80/hello
scheme: HTTP #支持的協議,http或者https
port: 80 #端口號
path: /hello #URI地址
2、創建pod,觀察效果
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
# 2、查看Pod詳情
[root@k8s-m-01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......
Normal Pulled 6s (x3 over 64s) kubelet, k8s-n-01 Container image "nginx:1.17.1" already present on machine
Normal Created 6s (x3 over 64s) kubelet, k8s-n-01 Created container nginx
Normal Started 6s (x3 over 63s) kubelet, k8s-n-01 Started container nginx
Warning Unhealthy 6s (x6 over 56s) kubelet, k8s-n-01 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 6s (x2 over 36s) kubelet, k8s-n-01 Container nginx failed liveness probe, will be restarted
# 3、觀察上面信息,嘗試訪問路徑,但是未找到,出現404錯誤
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-m-01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 5 3m17s
# 當然接下來,可以修改成一個可以訪問的路徑path,比如/,再試,結果就正常了......
# path: / (正確配置)
? 至此,已經使用liveness Probe演示了三種探測方式,但是查看livenessProbe的子屬性,會發現除了這三種方式,還有一些其他的配置,在這里一并解釋下:
[root@k8s-m-01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器啟動后等待多少秒執行第一次探測
timeoutSeconds <integer> # 探測超時時間。默認1秒,最小1秒
periodSeconds <integer> # 執行探測的頻率。默認是10秒,最小1秒
failureThreshold <integer> # 連續探測失敗多少次才被認定為失敗。默認是3。最小值是1
successThreshold <integer> # 連續探測成功多少次才被認定為成功。默認是1
下面稍微配置兩個,演示下效果即可:
[root@k8s-m-01 ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /
initialDelaySeconds: 30 # 容器啟動后30s開始探測
timeoutSeconds: 5 # 探測超時時間為5s
5、重啟策略
- Always :容器失效時,自動重啟該容器,這也是默認值。
- OnFailure : 容器終止運行且退出碼不為0時重啟
- Never : 不論狀態為何,都不重啟該容器
# 1. Always: (默認)
當容器失效時,由 kubelet 自動重啟該容器。
# 2. OnFailure:
當容器終止運行且退出碼不為 0 時,由 kubelet 自動重啟該容器
# 3. Never:
不論容器運行狀態如何,kubelet 都不會重啟該容器。
kubelet 重啟失效容器的時間間隔以 sync-frequency 乘以 2n 來計算;例如 1、2、4、8 倍等,最長延時5min ,并且在成功重啟后的 10 min 后重置該時間。
Pod 的重啟策略與控制方式息息相關,當前可用于管理 Pod 的控制器包括 ReplicationController、Job、DaemonSet 及直接通過 kubelet 管理(靜態 Pod)。每種控制器對 Pod 的重啟策略要求如下:
# 1.RC 和 DaemonSet:必須設置為 Always,需要保證該容器持續運行。
# 2.Job 和 CronJob:OnFailure 或 Never,確保容器執行完成后不再重啟。
# 3.kubelet:在 Pod 失效時自動重啟它,不論將 RestartPolicy 設置為什么值,也不會對 Pod 進行健康檢查。
重啟策略適用于pod對象中的所有容器,首次需要重啟的容器,將在其需要時立即進行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進行,且反復的重啟操作的延遲時長以此為10s、20s、40s、80s、160s和300s,300s是最大延遲時長。
Pod 重啟策略( RestartPolicy )應用于 Pod 內的所有容器,井且僅在 Pod 所處的 Node 上由 kubelet 進行判斷和重啟操作。
當某個容器異常退出或者健康檢查失敗時, kubelet 將根據 RestartPolicy 設置來進行相應的操作。Pod 的重啟策略包括:Always、OnFailure 和 Never,默認值為 Always
1、創建pod-restartpolicy.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello
restartPolicy: Never # 設置重啟策略為Never (默認Always)
2、運行Pod測試
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-restartpolicy.yaml
pod/pod-restartpolicy created
# 2、查看Pod詳情,發現nginx容器失敗
[root@k8s-m-01 ~]# kubectl describe pods pod-restartpolicy -n dev
......
Warning Unhealthy 15s (x3 over 35s) kubelet, k8s-n-01 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 15s kubelet, k8s-n-01 Container nginx failed liveness probe
# 3、多等一會,再觀察pod的重啟次數,發現一直是0,并未重啟
[root@k8s-m-01 ~]# kubectl get pods pod-restartpolicy -n dev
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Running 0 5min42s
3、Pod是如何管理多個容器的
Pod中可以同時運行多個進程(作為容器運行)協同工作。
同一個 Pod 中的容器會自動的分配到同一個 node上。同一個 Pod 中的容器共享資源、網絡環境和依賴,所以它們總是被同時調度。在一個 Pod 中同時運行多個容器是一種比較高級的用法。只有當你的容器需要緊密配合協作的時候才考慮用這種模式。
4、Pod中數據持久性
Pod 在設計?持就不是作為持久化實體的。在調度失敗、節點故障、缺少資源或者節點維護的狀態下都會死
掉會被驅逐。通常,我們是需要借助類似于 Docker 存儲卷這樣的資源來做 Pod 的數據持久
四、Pod調度
在默認情況下,一個Pod在哪個Node節點上運行,是由Scheduler組件采用相應的算法計算出來的,這個過程是不受人工控制的。但是在實際使用中,這并不滿足的需求,因為很多情況下,我們想控制某些Pod到達某些節點上,那么應該怎么做呢?這就要求了解kubernetes對Pod的調度規則,kubernetes提供了四大類調度方式:
- 自動調度:運行在哪個節點上完全由Scheduler經過一系列的算法計算得出
- 定向調度:NodeName、NodeSelector
- 親和性調度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污點(容忍)調度:Taints、Toleration
1、定向調度
定向調度,指的是利用在pod上聲明nodeName或者nodeSelector,以此將Pod調度到期望的node節點上。注意,這里的調度是強制的,這就意味著即使要調度的目標Node不存在,也會向上面進行調度,只不過pod運行失敗而已。
1、NodeName
? NodeName用于強制約束將Pod調度到指定的Name的Node節點上。這種方式,其實是直接跳過Scheduler的調度邏輯,直接將Pod調度到指定名稱的節點。
創建一個pod-nodename.yaml文件
[root@k8s-m-01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-m-01 Ready control-plane,master 4d18h v1.21.3
k8s-n-01 Ready <none> 4d18h v1.21.3
k8s-n-02 Ready <none> 4d18h v1.21.3
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: k8s-n-01 # 指定調度到k8s-n-01節點上(node的主機名稱)
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
# 2、查看Pod調度到NODE屬性,確實是調度到了k8s-n-01節點上
[root@k8s-m-01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 1/1 Running 0 56s 10.244.1.87 k8s-n-01 ......
# 3、接下來,刪除pod,修改nodeName的值為node3(并沒有node3節點)
[root@k8s-m-01 ~]# kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@k8s-m-01 ~]# vim pod-nodename.yaml
[root@k8s-m-01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
# 4、再次查看,發現已經向Node3節點調度,但是由于不存在node3節點,所以pod無法正常運行
[root@k8s-m-01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 0/1 Pending 0 6s <none> node3 ......
2、NodeSelector
NodeSelector用于將pod調度到添加了指定標簽的node節點上。它是通過kubernetes的label-selector機制實現的,也就是說,在pod創建之前,會由scheduler使用MatchNodeSelector調度策略進行label匹配,找出目標node,然后將pod調度到目標節點,該匹配規則是強制約束。
1 首先分別為node節點添加標簽
[root@k8s-m-01 ~]# kubectl label nodes k8s-n-01 nodeenv=pro
node/k8s-n-02 labeled
[root@k8s-m-01 ~]# kubectl label nodes k8s-n-02 nodeenv=test
node/k8s-n-02 labeled
2 創建一個pod-nodeselector.yaml文件,并使用它創建Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定調度到具有nodeenv=pro標簽的節點上
# 1、創建Pod
[root@k8s-m-01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
# 2、查看Pod調度到NODE屬性,確實是調度到了k8s-n-01節點上
[root@k8s-m-01 ~]# kubectl get pods pod-nodeselector -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeselector 1/1 Running 0 47s 10.244.1.87 k8s-n-01 ......
# 3、接下來,刪除pod,修改nodeSelector的值為nodeenv: xxxx(不存在打有此標簽的節點)
[root@k8s-m-01 ~]# kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@k8s-m-01 ~]# vim pod-nodeselector.yaml
[root@k8s-m-01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
# 4、再次查看,發現pod無法正常運行,Node的值為none
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodeselector 0/1 Pending 0 2m20s <none> <none>
# 5、查看詳情,發現node selector匹配失敗的提示
[root@k8s-m-01 ~]# kubectl describe pods pod-nodeselector -n dev
.......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
2、親和性調度
上一節,介紹了兩種定向調度的方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的Node,那么Pod將不會被運行,即使在集群中還有可用Node列表也不行,這就限制了它的使用場景。
基于上面的問題,kubernetes還提供了一種親和性調度(Affinity)。它在NodeSelector的基礎之上的進行了擴展,可以通過配置的形式,實現優先選擇滿足條件的Node進行調度,如果沒有,也可以調度到不滿足條件的節點上,使調度更加靈活。
Affinity主要分為三類:
-
nodeAffinity(node親和性): 以node為目標,解決pod可以調度到哪些node的問題
-
podAffinity(pod親和性) : 以pod為目標,解決pod可以和哪些已存在的pod部署在同一個拓撲域中的問題
-
podAntiAffinity(pod反親和性) : 以pod為目標,解決pod不能和哪些已存在pod部署在同一個拓撲域中的問題
關于親和性(反親和性)使用場景的說明:
親和性:如果兩個應用頻繁交互,那就有必要利用親和性讓兩個應用的盡可能的靠近,這樣可以減少因網絡通信而帶來的性能損耗。
反親和性:當應用的采用多副本部署時,有必要采用反親和性讓各個應用實例打散分布在各個node上,這樣可以提高服務的高可用性。
3、NodeAffinity
首先來看一下NodeAffinity的可配置項:
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node節點必須滿足指定的所有規則才可以,相當于硬限制
nodeSelectorTerms 節點選擇列表
matchFields 按節點字段列出的節點選擇器要求列表
matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operator 關系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 優先調度到滿足指定的規則的Node,相當于軟限制 (傾向)
preference 一個節點選擇器項,與相應的權重相關聯
matchFields 按節點字段列出的節點選擇器要求列表
matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operator 關系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 傾向權重,在范圍1-100。
關系符的使用說明:
- matchExpressions:
- key: nodeenv # 匹配存在標簽的key為nodeenv的節點
operator: Exists
- key: nodeenv # 匹配標簽的key為nodeenv,且value是"xxx"或"yyy"的節點
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配標簽的key為nodeenv,且value大于"xxx"的節點
operator: Gt
values: "xxx"
1、接下來首先演示一下requiredDuringSchedulingIgnoredDuringExecution ,
創建pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設置
nodeAffinity: #設置node親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽
- key: nodeenv
operator: In
values: ["xxx","yyy"]
# 1、創建pod
[root@k8s-m-01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
# 2、查看pod狀態 (運行失敗)
[root@k8s-m-01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 0/1 Pending 0 16s <none> <none> ......
# 3、查看Pod的詳情
# 發現調度失敗,提示node選擇失敗
[root@k8s-m-01 ~]# kubectl describe pod pod-nodeaffinity-required -n dev
......
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
# 4、接下來,停止pod
[root@k8s-m-01 ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted
# 5、修改文件,將values: ["xxx","yyy"]------> ["pro","yyy"]
[root@k8s-m-01 ~]# vim pod-nodeaffinity-required.yaml
# 6、再次啟動
[root@k8s-m-01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
# 7、此時查看,發現調度成功,已經將pod調度到了k8s-n-01上
[root@k8s-m-01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 1/1 Running 0 11s 10.244.1.89 k8s-n-01 ......
2、接下來再演示一下requiredDuringSchedulingIgnoredDuringExecution
創建pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設置
nodeAffinity: #設置node親和性
preferredDuringSchedulingIgnoredDuringExecution: # 軟限制
- weight: 1
preference:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽(當前環境沒有)
- key: nodeenv
operator: In
values: ["xxx","yyy"]
# 1、創建pod
[root@k8s-m-01 ~]# kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
# 2、查看pod狀態 (運行成功)
[root@k8s-m-01 ~]# kubectl get pod pod-nodeaffinity-preferred -n dev
NAME READY STATUS RESTARTS AGE
pod-nodeaffinity-preferred 1/1 Running 0 40s
NodeAffinity規則設置的注意事項:
# 1 、如果同時定義了nodeSelector和nodeAffinity,那么必須兩個條件都得到滿足,Pod才能運行在指定的Node上
# 2 、如果nodeAffinity指定了多個nodeSelectorTerms,那么只需要其中一個能夠匹配成功即可
# 3 、如果一個nodeSelectorTerms中有多個matchExpressions ,則一個節點必須滿足所有的才能匹配成功
# 4 、如果一個pod所在的Node在Pod運行期間其標簽發生了改變,不再符合該Pod的節點親和性需求,則系統將忽略此變化
4、PodAffinity
PodAffinity主要實現以運行的Pod為參照,實現讓新創建的Pod跟參照pod在一個區域的功能。
1、首先來看一下PodAffinity的可配置項
pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution 硬限制
namespaces 指定參照pod的namespace
topologyKey 指定調度作用域
labelSelector 標簽選擇器
matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operator 關系符 支持In, NotIn, Exists, DoesNotExist.
matchLabels 指多個matchExpressions映射的內容
preferredDuringSchedulingIgnoredDuringExecution 軟限制
podAffinityTerm 選項
namespaces
topologyKey
labelSelector
matchExpressions
key 鍵
values 值
operator
matchLabels
weight 傾向權重,在范圍1-100
topologyKey用于指定調度時作用域,例如:
如果指定為kubernetes.io/hostname,那就是以Node節點為區分范圍
如果指定為beta.kubernetes.io/os,則以Node節點的操作系統類型來區分
2、接下來,演示下requiredDuringSchedulingIgnoredDuringExecution,
1、首先創建一個參照Pod,pod-podaffinity-target.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro #設置標簽
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: k8s-n-01 # 將目標pod名確指定到k8s-n-01上
# 啟動目標pod
[root@k8s-m-01 ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
# 查看pod狀況
[root@k8s-m-01 ~]# kubectl get pods pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 4s
2、創建pod-podaffinity-required.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設置
podAffinity: #設置pod親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽
- key: podenv
operator: In
values: ["xxx","yyy"]
topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,顯然現在沒有這樣pod,接下來,運行測試一下。
# 1、啟動pod
[root@k8s-m-01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 2、查看pod狀態,發現未運行
[root@k8s-m-01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 9s
# 3、查看詳細信息
[root@k8s-m-01 ~]# kubectl describe pods pod-podaffinity-required -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 2 node(s) didn't match pod affinity rules, 1 node(s) had taints that the pod didn't tolerate.
# 4、接下來修改 values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@k8s-m-01 ~]# vim pod-podaffinity-required.yaml
# 5、然后重新創建pod,查看效果
[root@k8s-m-01 ~]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@k8s-m-01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 6、發現此時Pod運行正常
[root@k8s-m-01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE LABELS
pod-podaffinity-required 1/1 Running 0 6s <none>
關于PodAffinity的 preferredDuringSchedulingIgnoredDuringExecution,這里不再演示。
5、PodAntiAffinity
PodAntiAffinity主要實現以運行的Pod為參照,讓新創建的Pod跟參照pod不在一個區域中的功能。
它的配置方式和選項跟PodAffinty是一樣的,這里不再做詳細解釋,直接做一個測試案例。
1、繼續使用上個案例中目標pod
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE LABELS
pod-podaffinity-required 1/1 Running 0 3m29s 10.244.1.38 k8s-n-01 <none>
pod-podaffinity-target 1/1 Running 0 9m25s 10.244.1.37 k8s-n-01 podenv=pro
2、創建pod-podantiaffinity-required.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設置
podAntiAffinity: #設置pod親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的標簽
- key: podenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=pro的pod不在同一Node上,運行測試一下。
# 創建pod
[root@k8s-m-01 ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
# 查看pod
# 發現調度到了k8s-n-02上
[root@k8s-m-01 ~]# kubectl get pods pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ..
pod-podantiaffinity-required 1/1 Running 0 30s 10.244.1.96 k8s-n-02 ..
3、污點和容忍
1、污點(Taints)
前面的調度方式都是站在Pod的角度上,通過在Pod上添加屬性,來確定Pod是否要調度到指定的Node上,其實我們也可以站在Node的角度上,通過在Node上添加**污點**屬性,來決定是否允許Pod調度過來。
Node被設置上污點之后就和Pod之間存在了一種相斥的關系,進而拒絕Pod調度進來,甚至可以將已經存在的Pod驅逐出去。
污點的格式為:key=value:effect, key和value是污點的標簽,effect描述污點的作用,支持如下三個選項:
- PreferNoSchedule:kubernetes將盡量避免把Pod調度到具有該污點的Node上,除非沒有其他節點可調度
- NoSchedule:kubernetes將不會把Pod調度到具有該污點的Node上,但不會影響當前Node上已存在的Pod
- NoExecute:kubernetes將不會把Pod調度到具有該污點的Node上,同時也會將Node上已存在的Pod驅離

使用kubectl設置和去除污點的命令示例如下:
# 1、設置污點
kubectl taint nodes k8s-n-01 key=value:effect
# 2、去除污點
kubectl taint nodes k8s-n-01 key:effect-
# 3、去除所有污點
kubectl taint nodes k8s-n-01 key-
接下來,演示下污點的效果:
- 準備節點k8s-n-01(為了演示效果更加明顯,暫時停止k8s-n-02節點)
- 為k8s-n-01節點設置一個污點:
tag=heima:PreferNoSchedule;然后創建pod1( pod1 可以 ) - 修改為k8s-n-01節點設置一個污點:
tag=heima:NoSchedule;然后創建pod2( pod1 正常 pod2 失敗 ) - 修改為k8s-n-01節點設置一個污點:
tag=heima:NoExecute;然后創建pod3 ( 3個pod都失敗 )
# 1、為k8s-n-01設置污點(PreferNoSchedule)
[root@k8s-m-01 ~]# kubectl taint nodes k8s-n-01 tag=heima:PreferNoSchedule
# 2、創建pod1
[root@k8s-m-01 ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 k8s-n-01
# 3、為k8s-n-01設置污點(取消PreferNoSchedule,設置NoSchedule)
[root@k8s-m-01 ~]# kubectl taint nodes k8s-n-01 tag:PreferNoSchedule-
[root@k8s-m-01 ~]# kubectl taint nodes k8s-n-01 tag=heima:NoSchedule
# 4、創建pod2
[root@k8s-m-01 ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@k8s-m-01 ~]# kubectl get pods taint2 -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 k8s-n-01
taint2-544694789-6zmlf 0/1 Pending 0 21s <none> <none>
# 5、為k8s-n-01設置污點(取消NoSchedule,設置NoExecute)
[root@k8s-m-01 ~]# kubectl taint nodes k8s-n-01 tag:NoSchedule-
[root@k8s-m-01 ~]# kubectl taint nodes k8s-n-01 tag=heima:NoExecute
# 6、創建pod3
[root@k8s-m-01 ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
taint1-7665f7fd85-htkmp 0/1 Pending 0 35s <none> <none> <none>
taint2-544694789-bn7wb 0/1 Pending 0 35s <none> <none> <none>
taint3-6d78dbd749-tktkq 0/1 Pending 0 6s <none> <none> <none>
總結:
使用kubeadm搭建的集群,默認就會給master節點添加一個污點標記,所以pod就不會調度到master節點上.
2、容忍(Toleration)
? 上面介紹了污點的作用,我們可以在node上添加污點用于拒絕pod調度上來,但是如果就是想將一個pod調度到一個有污點的node上去,這時候應該怎么做呢?這就要使用到容忍。

污點就是拒絕,容忍就是忽略,Node通過污點拒絕pod調度上去,Pod通過容忍忽略拒絕
下面先通過一個案例看下效果:
- 上一小節,已經在k8s-n-01節點上打上了
NoExecute的污點,此時pod是調度不上去的 - 本小節,可以通過給pod添加容忍,然后將其調度上去
1、創建pod-toleration.yaml,內容如下
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
tolerations: # 添加容忍
- key: "tag" # 要容忍的污點的key
operator: "Equal" # 操作符
value: "heima" # 容忍的污點的value
effect: "NoExecute" # 添加容忍的規則,這里必須和標記的污點規則相同
# 添加容忍之前的pod
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 0/1 Pending 0 3s <none> <none> <none>
# 添加容忍之后的pod
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 1/1 Running 0 3s 10.244.1.62 k8s-n-01 <none>
2、下面看一下容忍的詳細配置:
[root@k8s-m-01 ~]# kubectl explain pod.spec.tolerations
......
FIELDS:
key # 對應著要容忍的污點的鍵,空意味著匹配所有的鍵
value # 對應著要容忍的污點的值
operator # key-value的運算符,支持Equal和Exists(默認)
effect # 對應污點的effect,空意味著匹配所有影響
tolerationSeconds # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間
五、 Pod控制器詳解
1、Pod控制器介紹
Pod是kubernetes的最小管理單元,在kubernetes中,按照pod的創建方式可以將其分為兩類:
# 自主式pod:
kubernetes直接創建出來的Pod,這種pod刪除后就沒有了,也不會重建
# 控制器創建的pod:
kubernetes通過控制器創建的pod,這種pod刪除了之后還會自動重建
什么是Pod控制器Pod控制器是管理pod的中間層,使用Pod控制器之后,只需要告訴Pod控制器,想要多少個什么樣的Pod就可以了,它會創建出滿足條件的Pod并確保每一個Pod資源處于用戶期望的目標狀態。如果Pod資源在運行中出現故障,它會基于指定策略重新編排Pod。
在kubernetes中,有很多類型的pod控制器,每種都有自己的適合的場景,常見的有下面這些:
# 1、ReplicationController:
比較原始的pod控制器,已經被廢棄,由ReplicaSet替代
# 2、ReplicaSet:
保證副本數量一直維持在期望值,并支持pod數量擴縮容,鏡像版本升級
# 3、Deployment:
通過控制ReplicaSet來控制Pod,并支持滾動升級、回退版本
# 4、Horizontal Pod Autoscaler:
可以根據集群負載自動水平調整Pod的數量,實現削峰填谷
# 5、DaemonSet:
在集群中的指定Node上運行且僅運行一個副本,一般用于守護進程類的任務
# 6、Job:
它創建出來的pod只要完成任務就立即退出,不需要重啟或重建,用于執行一次性任務
# 7、Cronjob:
它創建的Pod負責周期性任務控制,不需要持續后臺運行
# 8、StatefulSet:
管理有狀態應用
2、ReplicaSet(RS)
? ReplicaSet的主要作用是保證一定數量的pod正常運行,它會持續監聽這些Pod的運行狀態,一旦Pod發生故障,就會重啟或重建。同時它還支持對pod數量的擴縮容和鏡像版本的升降級。

ReplicaSet的資源清單文件:
apiVersion: apps/v1 # 版本號
kind: ReplicaSet # 類型
metadata: # 元數據
name: # rs名稱
namespace: # 所屬命名空間
labels: #標簽
controller: rs
spec: # 詳情描述
replicas: 3 # 副本數量
selector: # 選擇器,通過它指定該控制器管理哪些pod
matchLabels: # Labels匹配規則
app: nginx-pod
matchExpressions: # Expressions匹配規則
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
在這里面,需要新了解的配置項就是spec下面幾個選項:
-
replicas:指定副本數量,其實就是當前rs創建出來的pod的數量,默認為1
-
selector:選擇器,它的作用是建立pod控制器和pod之間的關聯關系,采用的Label Selector機制
? 在pod模板上定義label,在控制器上定義選擇器,就可以表明當前控制器能管理哪些pod了
-
template:模板,就是當前控制器創建pod所使用的模板板,里面其實就是前一章學過的pod的定義
1、創建ReplicaSet
創建pc-replicaset.yaml文件,內容如下:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: pc-replicaset
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
# 1、創建rs
[root@k8s-m-01 ~]# kubectl create -f pc-replicaset.yaml
replicaset.apps/pc-replicaset created
# 2、查看rs
# DESIRED:期望副本數量
# CURRENT:當前副本數量
# READY:已經準備好提供服務的副本數量
[root@k8s-m-01 ~]# kubectl get rs pc-replicaset -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
pc-replicaset 3 3 3 22s nginx nginx app=nginx-pod
# 查看當前控制器創建出來的pod
# 這里發現控制器創建出來的pod的名稱是在控制器名稱后面拼接了-xxxxx隨機碼
[root@k8s-m-01 ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6vmvt 1/1 Running 0 54s
pc-replicaset-fmb8f 1/1 Running 0 54s
pc-replicaset-snrk2 1/1 Running 0 54s
2、擴縮容
# 1、編輯rs的副本數量,修改spec:replicas: 6即可
[root@k8s-m-01 ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited
# 2、查看pod
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6vmvt 1/1 Running 0 114m
pc-replicaset-cftnp 1/1 Running 0 10s
pc-replicaset-fjlm6 1/1 Running 0 10s
pc-replicaset-fmb8f 1/1 Running 0 114m
pc-replicaset-s2whj 1/1 Running 0 10s
pc-replicaset-snrk2 1/1 Running 0 114m
# 當然也可以直接使用命令實現
# 3、使用scale命令實現擴縮容, 后面--replicas=n直接指定目標數量即可
[root@k8s-m-01 ~]# kubectl scale rs pc-replicaset --replicas=2 -n dev
replicaset.apps/pc-replicaset scaled
# 命令運行完畢,立即查看,發現已經有4個開始準備退出了
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6vmvt 0/1 Terminating 0 118m
pc-replicaset-cftnp 0/1 Terminating 0 4m17s
pc-replicaset-fjlm6 0/1 Terminating 0 4m17s
pc-replicaset-fmb8f 1/1 Running 0 118m
pc-replicaset-s2whj 0/1 Terminating 0 4m17s
pc-replicaset-snrk2 1/1 Running 0 118m
# 4、稍等片刻,就只剩下2個了
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-fmb8f 1/1 Running 0 119m
pc-replicaset-snrk2 1/1 Running 0 119m
3、鏡像升級
# 1、編輯rs的容器鏡像 - image: nginx:1.17.2
[root@k8s-m-01 ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited
# 2、再次查看,發現鏡像版本已經變更了
[root@k8s-m-01 ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES ...
pc-replicaset 2 2 2 140m nginx nginx:1.17.2 ...
# 同樣的道理,也可以使用命令完成這個工作
# kubectl set image rs rs名稱 容器=鏡像版本 -n namespace
[root@k8s-m-01 ~]# kubectl set image rs pc-replicaset nginx=nginx -n dev
replicaset.apps/pc-replicaset image updated
# 3、再次查看,發現鏡像版本已經變更了
[root@k8s-m-01 ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES ...
pc-replicaset 2 2 2 145m nginx nginx ...
4、刪除ReplicaSet
# 1、使用kubectl delete命令會刪除此RS以及它管理的Pod
# 在kubernetes刪除RS前,會將RS的replicasclear調整為0,等待所有的Pod被刪除后,在執行RS對象的刪除
[root@k8s-m-01 ~]# kubectl delete rs pc-replicaset -n dev
replicaset.apps "pc-replicaset" deleted
[root@k8s-m-01 ~]# kubectl get pod -n dev -o wide
No resources found in dev namespace.
# 2、如果希望僅僅刪除RS對象(保留Pod),可以使用kubectl delete命令時添加--cascade=false選項(不推薦)。
[root@k8s-m-01 ~]# kubectl delete rs pc-replicaset -n dev --cascade=false
replicaset.apps "pc-replicaset" deleted
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-cl82j 1/1 Running 0 75s
pc-replicaset-dslhb 1/1 Running 0 75s
# 3、也可以使用yaml直接刪除(推薦)
[root@k8s-m-01 ~]# kubectl delete -f pc-replicaset.yaml
replicaset.apps "pc-replicaset" deleted
3、Deployment(Deploy)
1、deployment是通過標簽去管理pod的
2、deployment控制器會無限接近理想狀態
# 1、Deployment:一般用來部署長期運行的、無狀態的應用
特點:集群之中,隨機部署(每一次請求都不依賴歷史數據,也無數據持久化需求)
精確匹配: # matchLabels
模糊匹配: # matchExpressions
? 為了更好的解決服務編排的問題,kubernetes在V1.2版本開始,引入了Deployment控制器。值得一提的是,這種控制器并不直接管理pod,而是通過管理ReplicaSet來簡介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加強大。

Deployment主要功能有下面幾個:
- 支持ReplicaSet的所有功能
- 支持發布的停止、繼續
- 支持滾動升級和回滾版本
Deployment的資源清單文件:
apiVersion: apps/v1 # 版本號
kind: Deployment # 類型
metadata: # 元數據
name: # rs名稱
namespace: # 所屬命名空間
labels: #標簽
controller: deploy
spec: # 詳情描述
replicas: 3 # 副本數量
revisionHistoryLimit: 3 # 保留歷史版本
paused: false # 暫停部署,默認是false
progressDeadlineSeconds: 600 # 部署超時時間(s),默認是600
strategy: # 策略
type: RollingUpdate # 滾動更新策略
rollingUpdate: # 滾動更新
maxSurge: 30% # 最大額外可以存在的副本數,可以為百分比,也可以為整數
maxUnavailable: 30% # 最大不可用狀態的 Pod 的最大值,可以為百分比,也可以為整數
selector: # 選擇器,通過它指定該控制器管理哪些pod
matchLabels: # Labels匹配規則
app: nginx-pod
matchExpressions: # Expressions匹配規則
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

1、創建deployment
創建pc-deployment.yaml,內容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
# 1、創建deployment
[root@k8s-m-01 ~]# kubectl create -f pc-deployment.yaml --record=true
deployment.apps/pc-deployment created
# 2、查看deployment
# UP-TO-DATE 最新版本的pod的數量
# AVAILABLE 當前可用的pod的數量
[root@k8s-m-01 ~]# kubectl get deploy pc-deployment -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
pc-deployment 3/3 3 3 15s
# 3、查看rs
# 發現rs的名稱是在原來deployment的名字后面添加了一個10位數的隨機串
[root@k8s-m-01 ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
pc-deployment-6696798b78 3 3 3 23s
# 4、查看pod
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6696798b78-d2c8n 1/1 Running 0 107s
pc-deployment-6696798b78-smpvp 1/1 Running 0 107s
pc-deployment-6696798b78-wvjd8 1/1 Running 0 107s
2、擴縮容
# 1、變更副本數量為5個
[root@k8s-m-01 ~]# kubectl scale deploy pc-deployment --replicas=5 -n dev
deployment.apps/pc-deployment scaled
# 2、查看deployment
[root@k8s-m-01 ~]# kubectl get deploy pc-deployment -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
pc-deployment 5/5 5 5 2m
# 3、查看pod
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6696798b78-d2c8n 1/1 Running 0 4m19s
pc-deployment-6696798b78-jxmdq 1/1 Running 0 94s
pc-deployment-6696798b78-mktqv 1/1 Running 0 93s
pc-deployment-6696798b78-smpvp 1/1 Running 0 4m19s
pc-deployment-6696798b78-wvjd8 1/1 Running 0 4m19s
# 4、編輯deployment的副本數量,修改spec:replicas: 4即可
[root@k8s-m-01 ~]# kubectl edit deploy pc-deployment -n dev
deployment.apps/pc-deployment edited
# 5、查看pod
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6696798b78-d2c8n 1/1 Running 0 5m23s
pc-deployment-6696798b78-jxmdq 1/1 Running 0 2m38s
pc-deployment-6696798b78-smpvp 1/1 Running 0 5m23s
pc-deployment-6696798b78-wvjd8 1/1 Running 0 5m23s
3、鏡像更新
deployment支持兩種更新策略:重建更新和滾動更新,可以通過strategy指定策略類型,支持兩個屬性:
strategy:指定新的Pod替換舊的Pod的策略, 支持兩個屬性:
type:指定策略類型,支持兩種策略
Recreate:在創建出新的Pod之前會先殺掉所有已存在的Pod
RollingUpdate:滾動更新,就是殺死一部分,就啟動一部分,在更新過程中,存在兩個版本Pod
rollingUpdate:當type為RollingUpdate時生效,用于為RollingUpdate設置參數,支持兩個屬性:
maxUnavailable:用來指定在升級過程中不可用Pod的最大數量,默認為25%。
maxSurge: 用來指定在升級過程中可以超過期望的Pod的最大數量,默認為25%。
4、重建更新
- 編輯pc-deployment.yaml,在spec節點下添加更新策略
spec:
strategy: # 策略
type: Recreate # 重建更新
- 創建deploy進行驗證
# 1、變更鏡像
[root@k8s-m-01 ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated
# 2、觀察升級過程
[root@k8s-m-01 ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-65qcw 1/1 Running 0 31s
pc-deployment-5d89bdfbf9-w5nzv 1/1 Running 0 31s
pc-deployment-5d89bdfbf9-xpt7w 1/1 Running 0 31s
pc-deployment-5d89bdfbf9-xpt7w 1/1 Terminating 0 41s
pc-deployment-5d89bdfbf9-65qcw 1/1 Terminating 0 41s
pc-deployment-5d89bdfbf9-w5nzv 1/1 Terminating 0 41s
pc-deployment-675d469f8b-grn8z 0/1 Pending 0 0s
pc-deployment-675d469f8b-hbl4v 0/1 Pending 0 0s
pc-deployment-675d469f8b-67nz2 0/1 Pending 0 0s
5、滾動更新
- 編輯pc-deployment.yaml,在spec節點下添加更新策略
spec:
strategy: # 策略
type: RollingUpdate # 滾動更新策略
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
- 創建deploy進行驗證
# 1、變更鏡像
[root@k8s-m-01 ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated
# 2、觀察升級過程
[root@k8s-m-01 ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-deployment-c848d767-8rbzt 1/1 Running 0 31m
pc-deployment-c848d767-h4p68 1/1 Running 0 31m
pc-deployment-c848d767-hlmz4 1/1 Running 0 31m
pc-deployment-c848d767-rrqcn 1/1 Running 0 31m
# 至此,新版本的pod創建完畢,就版本的pod銷毀完畢
# 中間過程是滾動進行的,也就是邊銷毀邊創建
6、滾動更新的過程:

鏡像更新中rs的變化:
# 查看rs,發現原來的rs的依舊存在,只是pod數量變為了0,而后又新產生了一個rs,pod數量為4
# 其實這就是deployment能夠進行版本回退的奧妙所在,后面會詳細解釋
[root@k8s-m-01 ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
pc-deployment-6696798b78 0 0 0 7m37s
pc-deployment-6696798b11 0 0 0 5m37s
pc-deployment-c848d76789 4 4 4 72s
7、版本回退
deployment支持版本升級過程中的暫停、繼續功能以及版本回退等諸多功能,下面具體來看.
kubectl rollout: 版本升級相關功能,支持下面的選項:
-
status 顯示當前升級狀態
-
history 顯示 升級歷史記錄
-
pause 暫停版本升級過程
-
resume 繼續已經暫停的版本升級過程
-
restart 重啟版本升級過程
-
undo 回滾到上一級版本(可以使用–to-revision回滾到指定版本)
# 1、查看當前升級版本的狀態
[root@k8s-m-01 ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out
# 2、查看升級歷史記錄
[root@k8s-m-01 ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=pc-deployment.yaml --record=true
2 kubectl create --filename=pc-deployment.yaml --record=true
3 kubectl create --filename=pc-deployment.yaml --record=true
# 可以發現有三次版本記錄,說明完成過兩次升級
# 3、版本回滾
# 這里直接使用--to-revision=1回滾到了1版本, 如果省略這個選項,就是回退到上個版本,就是2版本
[root@k8s-m-01 ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back
# 4、查看發現,通過nginx鏡像版本可以發現到了第一版
[root@k8s-m-01 ~]# kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
pc-deployment 4/4 4 4 74m nginx nginx
# 查看rs,發現第一個rs中有4個pod運行,后面兩個版本的rs中pod為運行
# 其實deployment之所以可是實現版本的回滾,就是通過記錄下歷史rs來實現的,
# 一旦想回滾到哪個版本,只需要將當前版本pod數量降為0,然后將回滾版本的pod提升為目標數量就可以了
[root@k8s-m-01 ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
pc-deployment-6696798b78 4 4 4 78m
pc-deployment-966bf7f44 0 0 0 37m
pc-deployment-c848d767 0 0 0 71m
8、金絲雀發布
? Deployment控制器支持控制更新過程中的控制,如“暫停(pause)”或“繼續(resume)”更新操作。
? 比如有一批新的Pod資源創建完成后立即暫停更新過程,此時,僅存在一部分新版本的應用,主體部分還是舊的版本。然后,再篩選一小部分的用戶請求路由到新版本的Pod應用,繼續觀察能否穩定地按期望的方式運行。確定沒問題之后再繼續完成余下的Pod資源滾動更新,否則立即回滾更新操作。這就是所謂的金絲雀發布。
# 1、更新deployment的版本,并配置暫停deployment
[root@k8s-m-01 ~]# kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused
# 2、觀察更新狀態
[root@k8s-m-01 ~]# kubectl rollout status deploy pc-deployment -n dev
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 4 new replicas have been updated...
# 監控更新的過程,可以看到已經新增了一個資源,但是并未按照預期的狀態去刪除一個舊的資源,就是因為使用了pause暫停命令
[root@k8s-m-01 ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 3 3 3 19m nginx nginx
pc-deployment-675d469f8b 0 0 0 14m nginx nginx:1.17.2
pc-deployment-6c9f56fcfb 2 2 2 3m16s nginx nginx:1.17.4
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-rj8sq 1/1 Running 0 7m33s
pc-deployment-5d89bdfbf9-ttwgg 1/1 Running 0 7m35s
pc-deployment-5d89bdfbf9-v4wvc 1/1 Running 0 7m34s
pc-deployment-6c9f56fcfb-996rt 1/1 Running 0 3m31s
pc-deployment-6c9f56fcfb-j2gtj 1/1 Running 0 3m31s
# 3、確保更新的pod沒問題了,繼續更新
[root@k8s-m-01 ~]# kubectl rollout resume deploy pc-deployment -n dev
deployment.apps/pc-deployment resumed
# 4、查看最后的更新情況
[root@k8s-m-01 ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 0 0 0 21m nginx nginx
pc-deployment-675d469f8b 0 0 0 16m nginx nginx:1.17.2
pc-deployment-6c9f56fcfb 4 4 4 5m11s nginx nginx:1.17.4
[root@k8s-m-01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6c9f56fcfb-7bfwh 1/1 Running 0 37s
pc-deployment-6c9f56fcfb-996rt 1/1 Running 0 5m27s
pc-deployment-6c9f56fcfb-j2gtj 1/1 Running 0 5m27s
pc-deployment-6c9f56fcfb-rf84v 1/1 Running 0 37s
刪除Deployment
# 刪除deployment,其下的rs和pod也將被刪除
[root@k8s-m-01 ~]# kubectl delete -f pc-deployment.yaml
deployment.apps "pc-deployment" deleted
4、Horizontal Pod Autoscaler(HPA)
在前面的課程中,我們已經可以實現通過手工執行`kubectl scale`命令實現Pod擴容或縮容,但是這顯然不符合Kubernetes的定位目標--自動化、智能化。 Kubernetes期望可以實現通過監測Pod的使用情況,實現pod數量的自動調整,于是就產生了Horizontal Pod Autoscaler(HPA)這種控制器。
HPA可以獲取每個Pod利用率,然后和HPA中定義的指標進行對比,同時計算出需要伸縮的具體值,最后實現Pod的數量的調整。其實HPA與之前的Deployment一樣,也屬于一種Kubernetes資源對象,它通過追蹤分析RC控制的所有目標Pod的負載變化情況,來確定是否需要針對性地調整目標Pod的副本數,這是HPA的實現原理。

1 、安裝metrics-server
metrics-server可以用來收集集群中的資源使用情況
# 1、安裝git
[root@k8s-m-01 ~]# yum install git -y
# 2、獲取metrics-server, 注意使用的版本
[root@k8s-m-01 ~]# git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server
# 修改deployment, 注意修改的是鏡像和初始化參數
[root@k8s-m-01 ~]# cd /root/metrics-server/deploy/1.8+/
[root@master 1.8+]# vim metrics-server-deployment.yaml
按圖中添加下面選項
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

# 1、安裝metrics-server
[root@master 1.8+]# kubectl apply -f ./
# 2、查看pod運行情況
[root@master 1.8+]# kubectl get pod -n kube-system
metrics-server-6b976979db-2xwbj 1/1 Running 0 90s
# 3、使用kubectl top node 查看資源使用情況
[root@master 1.8+]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 98m 4% 1067Mi 62%
k8s-n-01 27m 1% 727Mi 42%
k8s-n-02 34m 1% 800Mi 46%
[root@master 1.8+]# kubectl top pod -n kube-system
NAME CPU(cores) MEMORY(bytes)
coredns-6955765f44-7ptsb 3m 9Mi
coredns-6955765f44-vcwr5 3m 8Mi
etcd-master 14m 145Mi
...
# 至此,metrics-server安裝完成
2、 準備deployment和servie
為了操作簡單,直接使用命令
# 1、創建deployment
[root@master 1.8+]# kubectl run nginx --image=nginx:latest --requests=cpu=100m -n dev
# 2、創建service
[root@master 1.8+]# kubectl expose deployment nginx --type=NodePort --port=80 -n dev
# 3、查看
[root@master 1.8+]# kubectl get deployment,pod,svc -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 47s
NAME READY STATUS RESTARTS AGE
pod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx NodePort 10.109.57.248 <none> 80:31136/TCP 35s
3 、部署HPA
創建pc-hpa.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: pc-hpa
namespace: dev
spec:
minReplicas: 1 #最小pod數量
maxReplicas: 10 #最大pod數量
targetCPUUtilizationPercentage: 3 # CPU使用率指標
scaleTargetRef: # 指定要控制的nginx信息
apiVersion: apps/v1
kind: Deployment
name: nginx
# 1、創建hpa
[root@master 1.8+]# kubectl create -f pc-hpa.yaml
horizontalpodautoscaler.autoscaling/pc-hpa created
# 2、查看hpa
[root@master 1.8+]# kubectl get hpa -n dev
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
pc-hpa Deployment/nginx 0%/3% 1 10 1 62s
4 、測試
使用壓測工具對service地址192.168.109.100:31136進行壓測,然后通過控制臺查看hpa和pod的變化
hpa變化
[root@k8s-m-01 ~]# kubectl get hpa -n dev -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
pc-hpa Deployment/nginx 0%/3% 1 10 1 4m11s
pc-hpa Deployment/nginx 0%/3% 1 10 1 5m19s
pc-hpa Deployment/nginx 22%/3% 1 10 1 6m50s
pc-hpa Deployment/nginx 22%/3% 1 10 4 7m5s
pc-hpa Deployment/nginx 22%/3% 1 10 8 7m21s
pc-hpa Deployment/nginx 6%/3% 1 10 8 7m51s
pc-hpa Deployment/nginx 0%/3% 1 10 8 9m6s
pc-hpa Deployment/nginx 0%/3% 1 10 8 13m
pc-hpa Deployment/nginx 0%/3% 1 10 1 14m
deployment變化
[root@k8s-m-01 ~]# kubectl get deployment -n dev -w
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 11m
nginx 1/4 1 1 13m
nginx 1/4 1 1 13m
nginx 1/4 1 1 13m
nginx 1/4 4 1 13m
nginx 1/8 4 1 14m
nginx 1/8 4 1 14m
nginx 1/8 4 1 14m
nginx 1/8 8 1 14m
nginx 2/8 8 2 14m
nginx 3/8 8 3 14m
nginx 4/8 8 4 14m
nginx 5/8 8 5 14m
nginx 6/8 8 6 14m
nginx 7/8 8 7 14m
nginx 8/8 8 8 15m
nginx 8/1 8 8 20m
nginx 8/1 8 8 20m
nginx 1/1 1 1 20m
pod變化
[root@k8s-m-01 ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
nginx-7df9756ccc-bh8dr 1/1 Running 0 11m
nginx-7df9756ccc-cpgrv 0/1 Pending 0 0s
nginx-7df9756ccc-8zhwk 0/1 Pending 0 0s
nginx-7df9756ccc-rr9bn 0/1 Pending 0 0s
nginx-7df9756ccc-cpgrv 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-8zhwk 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-rr9bn 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-m9gsj 0/1 Pending 0 0s
nginx-7df9756ccc-g56qb 0/1 Pending 0 0s
nginx-7df9756ccc-sl9c6 0/1 Pending 0 0s
nginx-7df9756ccc-fgst7 0/1 Pending 0 0s
nginx-7df9756ccc-g56qb 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-m9gsj 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-sl9c6 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-fgst7 0/1 ContainerCreating 0 0s
nginx-7df9756ccc-8zhwk 1/1 Running 0 19s
nginx-7df9756ccc-rr9bn 1/1 Running 0 30s
nginx-7df9756ccc-m9gsj 1/1 Running 0 21s
nginx-7df9756ccc-cpgrv 1/1 Running 0 47s
nginx-7df9756ccc-sl9c6 1/1 Running 0 33s
nginx-7df9756ccc-g56qb 1/1 Running 0 48s
nginx-7df9756ccc-fgst7 1/1 Running 0 66s
nginx-7df9756ccc-fgst7 1/1 Terminating 0 6m50s
nginx-7df9756ccc-8zhwk 1/1 Terminating 0 7m5s
nginx-7df9756ccc-cpgrv 1/1 Terminating 0 7m5s
nginx-7df9756ccc-g56qb 1/1 Terminating 0 6m50s
nginx-7df9756ccc-rr9bn 1/1 Terminating 0 7m5s
nginx-7df9756ccc-m9gsj 1/1 Terminating 0 6m50s
nginx-7df9756ccc-sl9c6 1/1 Terminating 0 6m50s
5、DaemonSet(DS)
# DaemonSet:每一個節點上部署一個Pod,刪除節點自動刪除對應的POD(zabbix-agent)
特點:每一臺上有且只有一臺
? DaemonSet類型的控制器可以保證在集群中的每一臺(或指定)節點上都運行一個副本。一般適用于日志收集、節點監控等場景。也就是說,如果一個Pod提供的功能是節點級別的(每個節點都需要且只需要一個),那么這類Pod就適合使用DaemonSet類型的控制器創建。

DaemonSet控制器的特點:
- 每當向集群中添加一個節點時,指定的 Pod 副本也將添加到該節點上
- 當節點從集群中移除時,Pod 也就被垃圾回收了
下面先來看下DaemonSet的資源清單文件
apiVersion: apps/v1 # 版本號
kind: DaemonSet # 類型
metadata: # 元數據
name: # rs名稱
namespace: # 所屬命名空間
labels: #標簽
controller: daemonset
spec: # 詳情描述
revisionHistoryLimit: 3 # 保留歷史版本
updateStrategy: # 更新策略
type: RollingUpdate # 滾動更新策略
rollingUpdate: # 滾動更新
maxUnavailable: 1 # 最大不可用狀態的 Pod 的最大值,可以為百分比,也可以為整數
selector: # 選擇器,通過它指定該控制器管理哪些pod
matchLabels: # Labels匹配規則
app: nginx-pod
matchExpressions: # Expressions匹配規則
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
創建pc-daemonset.yaml,內容如下:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pc-daemonset
namespace: dev
spec:
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx
# 1、創建daemonset
[root@k8s-m-01 ~]# kubectl create -f pc-daemonset.yaml
daemonset.apps/pc-daemonset created
# 2、查看daemonset
[root@k8s-m-01 ~]# kubectl get ds -n dev -o wide
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
pc-daemonset 2 2 2 2 2 24s nginx nginx
# 3、查看pod,發現在每個Node上都運行一個pod
[root@k8s-m-01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pc-daemonset-9bck8 1/1 Running 0 37s 10.244.1.43 k8s-n-01
pc-daemonset-k224w 1/1 Running 0 37s 10.244.2.74 k8s-n-02
# 4、刪除daemonset
[root@k8s-m-01 ~]# kubectl delete -f pc-daemonset.yaml
daemonset.apps "pc-daemonset" deleted
# 5、更新
[root@k8s-m-01 k8s]# kubectl edit daemonsets zabbix-agent
- image: zabbix/zabbix-agent:5.2.4-centos
zabbix版本是5.2.4
# 6、回滾
[root@k8s-m-01 k8s]# kubectl rollout undo daemonset zabbix-agent
daemonset.apps/zabbix-agent rolled back
# 回滾到指定版本
[root@k8s-m-01 k8s]# kubectl rollout history daemonset zabbix-agent
daemonset.apps/zabbix-agent
REVISION CHANGE-CAUSE
1 <none>
2 <none>
[root@k8s-m-01 k8s]# kubectl rollout undo daemonset zabbix-agent --to-revision=1
# 7、擴展 (刪除集群,重新添加)
[root@k8s-m-01 k8s]# kubectl delete nodes k8s-n-01 # 主節點刪除n2從節點
[root@k8s-n-01 ~]# kubeadm reset
[root@k8s-n-01 ~]# rm -rf /etc/kubernetes/*
[root@k8s-m-01 ~]# kubeadm token create --print-join-command # 主節點
從節點拿著token直接執行一下,就加入集群了
# 不支持彈性擴容,但是支持資源更新
6、Job
Job,主要用于負責**批量處理(一次要處理指定數量任務)短暫的一次性(每個任務僅運行一次就結束)**任務。Job特點如下:
- 當Job創建的pod執行成功結束時,Job將記錄成功結束的pod數量
- 當成功結束的pod達到指定的數量時,Job將完成執行

Job的資源清單文件:
apiVersion: batch/v1 # 版本號
kind: Job # 類型
metadata: # 元數據
name: # rs名稱
namespace: # 所屬命名空間
labels: #標簽
controller: job
spec: # 詳情描述
completions: 1 # 指定job需要成功運行Pods的次數。默認值: 1
parallelism: 1 # 指定job在任一時刻應該并發運行Pods的數量。默認值: 1
activeDeadlineSeconds: 30 # 指定job可運行的時間期限,超過時間還未結束,系統將會嘗試進行終止。
backoffLimit: 6 # 指定job失敗后進行重試的次數。默認是6
manualSelector: true # 是否可以使用selector選擇器選擇pod,默認是false
selector: # 選擇器,通過它指定該控制器管理哪些pod
matchLabels: # Labels匹配規則
app: counter-pod
matchExpressions: # Expressions匹配規則
- {key: app, operator: In, values: [counter-pod]}
template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never # 重啟策略只能設置為Never或者OnFailure
containers:
- name: counter
image: busybox:1.30
command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]
關于重啟策略設置的說明:
如果指定為OnFailure,則job會在pod出現故障時重啟容器,而不是創建pod,failed次數不變
如果指定為Never,則job會在pod出現故障時創建新的pod,并且故障pod不會消失,也不會重啟,failed次數加1
如果指定為Always的話,就意味著一直重啟,意味著job任務會重復去執行了,當然不對,所以不能設置為Always
創建pc-job.yaml,內容如下:
apiVersion: batch/v1
kind: Job
metadata:
name: pc-job
namespace: dev
spec:
manualSelector: true
selector:
matchLabels:
app: counter-pod
template:
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
# 1、創建job
[root@k8s-m-01 ~]# kubectl create -f pc-job.yaml
job.batch/pc-job created
# 2、查看job
[root@k8s-m-01 ~]# kubectl get job -n dev -o wide -w
NAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTOR
pc-job 0/1 21s 21s counter busybox:1.30 app=counter-pod
pc-job 1/1 31s 79s counter busybox:1.30 app=counter-pod
# 3、通過觀察pod狀態可以看到,pod在運行完畢任務后,就會變成Completed狀態
[root@k8s-m-01 ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-job-rxg96 1/1 Running 0 29s
pc-job-rxg96 0/1 Completed 0 33s
# 接下來,調整下pod運行的總數量和并行數量 即:在spec下設置下面兩個選項
# completions: 6 # 指定job需要成功運行Pods的次數為6
# parallelism: 3 # 指定job并發運行Pods的數量為3
# 然后重新運行job,觀察效果,此時會發現,job會每次運行3個pod,總共執行了6個pod
[root@k8s-m-01 ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-job-684ft 1/1 Running 0 5s
pc-job-jhj49 1/1 Running 0 5s
pc-job-pfcvh 1/1 Running 0 5s
pc-job-684ft 0/1 Completed 0 11s
pc-job-v7rhr 0/1 Pending 0 0s
pc-job-v7rhr 0/1 Pending 0 0s
pc-job-v7rhr 0/1 ContainerCreating 0 0s
pc-job-jhj49 0/1 Completed 0 11s
pc-job-5vg2j 0/1 Completed 0 12s
# 刪除job
[root@k8s-m-01 ~]# kubectl delete -f pc-job.yaml
job.batch "pc-job" deleted
7、CronJob(CJ)
? CronJob控制器以Job控制器資源為其管控對象,并借助它管理pod資源對象,Job控制器定義的作業任務在其控制器資源創建之后便會立即執行,但CronJob可以以類似于Linux操作系統的周期性任務作業計劃的方式控制其運行時間點及重復運行的方式。也就是說,CronJob可以在特定的時間點(反復的)去運行job任務。

1、CronJob的資源清單文件:
apiVersion: batch/v1beta1 # 版本號
kind: CronJob # 類型
metadata: # 元數據
name: # rs名稱
namespace: # 所屬命名空間
labels: #標簽
controller: cronjob
spec: # 詳情描述
schedule: # cron格式的作業調度運行時間點,用于控制任務在什么時間執行
concurrencyPolicy: # 并發執行策略,用于定義前一次作業運行尚未完成時是否以及如何運行后一次的作業
failedJobHistoryLimit: # 為失敗的任務執行保留的歷史記錄數,默認為1
successfulJobHistoryLimit: # 為成功的任務執行保留的歷史記錄數,默認為3
startingDeadlineSeconds: # 啟動作業錯誤的超時時長
jobTemplate: # job控制器模板,用于為cronjob控制器生成job對象;下面其實就是job的定義
metadata:
spec:
completions: 1
parallelism: 1
activeDeadlineSeconds: 30
backoffLimit: 6
manualSelector: true
selector:
matchLabels:
app: counter-pod
matchExpressions: 規則
- {key: app, operator: In, values: [counter-pod]}
template:
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]
2、需要重點解釋的幾個選項:
# schedule: cron表達式,用于指定任務的執行時間
*/1 * * * *
<分鐘> <小時> <日> <月份> <星期>
分鐘 值從 0 到 59.
小時 值從 0 到 23.
日 值從 1 到 31.
月 值從 1 到 12.
星期 值從 0 到 6, 0 代表星期日
多個時間可以用逗號隔開; 范圍可以用連字符給出;*可以作為通配符; /表示每...
# concurrencyPolicy:
Allow: 允許Jobs并發運行(默認)
Forbid: 禁止并發運行,如果上一次運行尚未完成,則跳過下一次運行
Replace: 替換,取消當前正在運行的作業并用新作業替換它
3、創建pc-cronjob.yaml,內容如下:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: pc-cronjob
namespace: dev
labels:
controller: cronjob
spec:
schedule: "*/1 * * * *"
jobTemplate:
metadata:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
4、查看
# 1、創建cronjob
[root@k8s-m-01 ~]# kubectl create -f pc-cronjob.yaml
cronjob.batch/pc-cronjob created
# 2、查看cronjob
[root@k8s-m-01 ~]# kubectl get cronjobs -n dev
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
pc-cronjob */1 * * * * False 0 <none> 6s
# 3、查看job
[root@k8s-m-01 ~]# kubectl get jobs -n dev
NAME COMPLETIONS DURATION AGE
pc-cronjob-1592587800 1/1 28s 3m26s
pc-cronjob-1592587860 1/1 28s 2m26s
pc-cronjob-1592587920 1/1 28s 86s
# 4、查看pod
[root@k8s-m-01 ~]# kubectl get pods -n dev
pc-cronjob-1592587800-x4tsm 0/1 Completed 0 2m24s
pc-cronjob-1592587860-r5gv4 0/1 Completed 0 84s
pc-cronjob-1592587920-9dxxq 1/1 Running 0 24s
# 5、刪除cronjob
[root@k8s-m-01 ~]# kubectl delete -f pc-cronjob.yaml
cronjob.batch "pc-cronjob" deleted
浙公網安備 33010602011771號