三、Pod
1 基礎介紹
我們在上一章成功搭建了k8s集群並運行了一個應用,你可能想通過一個命令顯示所有正在運行的容器,就像docker ps
一樣,但這並不是Kubernetes的工作。Kubernetes不直接處理單個容器,而是通過pod管理一組容器。
一個pod可以包含多個和單個容器。pod將容器綁定在一起,並將它們作為一個單元進行管理。這些容器彼此是緊密相關的,一個pod中的容器共享相同的linux命名空間、IP和網絡接口。
列出pod命令
kubectl get pods
# 返回結果
NAME READY STATUS RESTARTS AGE
nginx-deployment-9b8d7cb9b-8z7ff 1/1 Running 1 5h
可以看到pod已經處於運行狀態。如果想查看pod的詳細信息,使用
kubectl describe pod [pod名]
你可能想知道應用運行在哪個節點上,其實這並不重要,k8s會自動調度一個可供運行pod的節點,可以使用-o wide
選項顯示出更多信息:
kubectl get pod -o wide
為何需要使用pod,為什么不直接用容器?一個容器運行多個進程和多個容器運行一個進程到底有什么區別?原因是多個進程組成的應用,進程之間的通信等都要求它們運行在同一台機器上,盡管容器可以運行多個進程,但並不合理。每個容器一般只運行一個進程(主進程產生子進程除外),如果一個容器運行了多個不相關的進程,那么容器就要保證它們之間的通信、運行、日志等運行,這不是我們願意看到的。k8s和docker期望使用容器的方式是每個進程運行在自己的容器中。基於此,我們就需要另一種結構將容器綁定在一起,並將它們作為一個基礎單元管理,這就是產生pod的原因。如果不加配置,容器之間是完全隔離的,而k8s通過配置讓pod內的所有容器可以共享相同的linux命名空間、IP和網絡接口。
因此,需要注意的是,同一個pod中的容器運行的進程不能綁定相同的端口,否則會導致沖突。這僅限於同一個pod,pod與pod之間則永遠不會遇到端口沖突。另外,容器可以通過localhost與同一pod的其他容器進行通信。
還有一個概念是扁平化網絡結構:集群中的所有pod都在同一個共享網絡地址中,一個pod可以通過另一個pod的ip地址相互訪問。
2 資源清單
首先是一些名詞的解釋:
-
資源是什么?對象是什么?
k8s中所有的內容都抽象為資源,資源實例化之后,叫做對象,對象是持久化的實體。 Kubernetes 使用這些實體去表示整個集群的狀態。比如
- 哪些容器在運行(以及在哪些節點上)
- 可以被應用使用的資源
- 關於應用運行時表現的策略,比如重啟策略、升級策略,以及容錯策略
-
對象規約(Spec)與狀態(Status)是什么?
幾乎每個 Kubernetes 對象包含這兩個字段。
spec
描述你希望對象所具有的特征:期望狀態(Desired State),比如你希望應用有兩個副本,容器掛掉之后不需要重啟等等。期望狀態需要你創建對象時設置。status
描述了對象的當前狀態(Current State),不需要創建時設置,k8s每時每刻都在監視着這個狀態,以使它達到期望狀態。 -
什么是資源清單?
前面已經說過,我們如果想要指定一些期望,就需要在
.yaml
文件中為 kubectl 提供這些信息。這樣的文件我們一般稱為資源清單。
2.1 資源類型
先來看看k8s中都有哪些資源。
名稱空間級別:僅在此名稱空間下生效
- Pod: k8s中最小單元
- ReplicaSet:調度器,通過標簽控制 pod 的副本數目
- Deployment:控制器,通過控制 rs 的創建來創建 pod
- StatefulSet:為有狀態服務創建的管理器
- DaemonSet:可以在每個節點運行 pod 主鍵
- Job:為批處理而生
- 服務發現及負載均衡型資源(ServiceDiscoveryLoadBalance):Service、Ingress、…
- 配置與存儲型資源:Volume(存儲卷)、CSI(容器存儲接口,可以擴展各種各樣的第三方存儲卷)
- 特殊類型的存儲卷:
- ConfigMap(當配置中心來使用的資源類型):通過他可以創建一些配置文件,達到熱更新
- Secret(保存敏感數據):加密方案存儲數據,可以用它存儲一些密碼、秘鑰等
- DownwardAPI(把外部環境中的信息輸出給容器):下載文件的接口,可以下載、上傳
集群級別:整個集群中都可用
- Namespace:名稱空間
- Node:工作節點
- Role
- ClusterRole
- RoleBinding
- ClusterRoleBinding
元數據類型:
- HPA
- PodTemplate(pod模板)
- LimitRange(資源限制)
2.2 必須存在的屬性
這些屬性必須有。
參數名 | 字段類型 | 說明 |
---|---|---|
apiVersion |
String | 這里是指的是k8s API的版本,目前基本上是v1,可以用 kubectl api-versions 或者 kubectl explain pod 命令查詢 |
kind |
String | 這里指的是yaml文件定義的資源類型和角色,比如: Pod |
metadata |
Object | 元數據對象,固定值就寫metadata |
metadata.name |
String | 元數據對象的名字,這里由我們編寫,比如命名Pod的名字 |
metadata.namespace |
String | 元數據對象的命名空間,由我們自身定義 |
metadata.labels |
map[string]string | 鍵值數據,常被用作挑選條件 |
spec |
Object | 詳細定義對象,固定值就寫Spec |
spec.containers[] |
List | 這里是Spec對象的容器列表定義,是個列表 |
spec.containers[].name |
String | 這里定義容器的名字 |
spec.containers[].image |
String | 這里定義要用到的鏡像名稱,如果鏡像的標簽是 latest,每次使用該鏡像都會從遠程下載 |
總結一下就是資源清單 .yaml
文件中,必須配置如下字段:
apiVersion
- 創建該對象所使用的 Kubernetes API 的版本kind
- 想要創建的對象的類別metadata
- 幫助唯一性標識對象的一些數據spec
- 你所期望的該對象的狀態,對每個 Kubernetes 對象來說它是不同的
2.3 pod的資源清單
pod或其他k8s資源通常是通過json或者yaml文件創建的,當然也可以通過前面演示的命令行kubectl run
創建,但是命令行只能配置一些有限的屬性。
除了2.2列出的必須要有的屬性,還有一些其他屬性。
下面展示的是pod資源的資源清單
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,資源類型,例如Pod
metadata: #必選,元數據
name: string #必選,Pod名稱
namespace: string #Pod所屬的命名空間,默認為“default”
labels: #Pod自定義的標簽列表
- name: string #標簽名和值
spec: #必選,Pod中容器的詳細定義
containers: #必選,Pod中的容器列表
- name: string #必選,容器名字
image: string #必選,要用到的容器的鏡像名稱
imagePullPolicy: [Always | Never | IfNotPresent] #鏡像的下載策略。默認值是Always。建議改為IfNOtPresent
# (1)Always:每次都嘗試從遠程倉庫拉取鏡像
# (2)Never:僅使用本地鏡像
# (3)IfNOtPresent:如果本地有鏡像就使用本地鏡像,沒有就拉取遠程鏡像。
command: [string] #指定容器啟動時的命令,因為是列表可以指定多個,不指定則使用鏡像打包時使用的啟動命令。
args: [string] #容器啟動的命令參數列表
workingDir: string #容器的工作目錄
volumeMounts: #掛載到容器內部的存儲卷配置
- name: string #引用Pod定義的共享存儲卷的名稱,需用volumes[]部分定義的卷名
mountPath: string #存儲卷在容器內部mount的絕對路徑,應少於512字符
readOnly: boolean #是否只讀模式,默認為false讀寫模式
ports: #指定需要暴露的端口號列表
- name: string #端口的名稱
containerPort: int #容器監聽的端口號
hostPort: int #容器所在主機需要監聽的端口號,默認與containerPort相同
protocol: string #端口協議,支持TCP和UDP,默認TCP
env: #容器運行前需設置的環境變量的列表
- name: string #環境變量名稱
value: string #環境變量的值
resource: #資源限制和請求的設置(從這里開始就是限制容器使用資源的上限)
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 #對容器健康檢查探測等待響應的超時時間,單位秒
periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認1秒
successThreshold: 0
failureThreshold: 0
sucurityContext:
privileged: false
restartPolicy: [Always|Never|OnFailure] #對Pod的重啟策略。默認值為 Always。
#1.Always: Pod 一旦終止運行,則無論容器是如何終止的,Kubelet服務都將重啟它。
#2.OnFailure:只有Pod以非零退出碼終止時,kubelet才會重啟該容器。如果容器正常結束〔退出碼為0),則kubelet將不會重啟它。
#3.Never:Pod終止后,kubelet將退出碼報給Master,不會重啟該Pod。
nodeName: <string> #設置NodeName表示將該Pod調度到指定名稱的node節點上
nodeSelector: object #設置NodeSelector表示將該pod調度到包含這個label的Node上
imagePullSecurets: #pull鏡像時使用的secret名稱,以key:secretKey格式指定
- name: string
hostNetwork: false #是否使用主機網絡模式。默認值為false。
#false表示不使用宿主機網絡
#設置true表示使用宿主機網絡,不使用docker網橋,設置了true將無法在同一台宿主機上啟動第二個副本。
volumes: #在該pod上定義共享存儲卷列表
- name: string #共享存儲卷名稱
emptyDir: {} #類型為emptyDir的存儲卷,與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
這里並不會詳細解釋每個屬性的細節,因此如果想了解更多,強烈建議去官方文檔查看所有的Kubernetes API,文檔上包含了所有資源的資源清單以及詳細解釋。你也可以通過kubectl explain [資源類型]
命令可以查看API。
你可以通過命令kubectl get pod -o yaml
來查看當前運行的pod的資源清單,
3 通過資源清單創建pod
接下來嘗試通過yaml文件創建一個pod。你可以在任意目錄下定義這個文件vim createpod.yaml
:
apiVersion: v1
kind: Pod # 這里必須是大寫P
metadata:
name: mypod
spec:
containers:
- image: hub.example.com/library/helloworld
name: test
接下來創建pod
kubectl apply -f createpod.yaml
查看pod運行狀態
kubectl get pod
kubectl describe pod mypod
可以看到已經運行成功了。
補充說明,通過文件創建pod有兩個命令:
kubectl create
:創建資源,如果再次運行該命令,則會拋出錯誤。
kubectl apply
:將配置應用於資源,如果該資源不存在就創建,如果存在則更新配置,如果再次運行該命令,不會拋出錯誤。
二者是聲明式管理和命令式管理的區別,各有利弊,至於使用哪個需要具體分析。你可以在官方文檔查看對象管理的更多信息。
4 查看應用運行日志
docker容器運行時,想查看運行日志可以通過以下命令
docker log 容器id
在k8s上如何獲取呢?可以通過ssh連接到pod正在運行的節點,然后運行上面的命令查看日志;k8s為我們提供了更簡便的方法:
kubectl logs Pod名稱
比如我們要查看剛剛創建的pod的日志(其實是容器的日志),只需要在master節點運行
[root@k8s-master ~]# kubectl logs mypod
2021-12-15 22:57:56 Listening on port 8080
這里的2021-12-15 22:57:56 Listening on port 8080
就是容器運行的日志,它提示我們這個容器正在監聽8080端口,我們來驗證一下。首先查看pod運行在哪個節點,以及它的ip
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mypod 1/1 Running 0 27m 10.244.2.3 k8s-node-1 <none> <none>
可以看到pod運行在node-1節點,接下來嘗試訪問這個ip
curl 10.244.2.3:8080
結果如下
由於我們的pod內只有一個容器,所以不需要指定即可查看日志。如果pod內運行了多個容器,我們需要使用-c
指定容器名稱查看對應容器的日志:
kubectl logs pod名稱 -c 容器名稱
需要注意:我們只能獲取當前存在的pod日志,當一個pod被刪除時,對應的logs也會被刪除。如果想要將日志信息持久化,需要進行額外配置,這里暫時不深入研究。
5 pod階段
pod的創建起始於 Pending
階段,如果至少其中有一個主要容器正常啟動,則進入 Running
,之后取決於 Pod 中是否有容器以 失敗狀態結束而進入 Succeeded
或者 Failed
階段。可以看出pod和容器一樣都是臨時的實體,一個生命周期結束后,pod就死亡了。任何pod一旦完成分配,就不會再被重新調度到其他節點,如果pod死亡,k8s會刪除它然后重新創建一個。
pod的status字段是一個PodStatus對象,其中包含一個 phase 字段。
下面是phase可能的值:
取值 | 描述 |
---|---|
Pending (懸決) |
Pod 已被 Kubernetes 系統接受,但有一個或者多個容器尚未創建亦未運行。此階段包括等待 Pod 被調度的時間和通過網絡下載鏡像的時間, |
Running (運行中) |
Pod 已經綁定到了某個節點,Pod 中所有的容器都已被創建。至少有一個容器仍在運行,或者正處於啟動或重啟狀態。 |
Succeeded (成功) |
Pod 中的所有容器都已成功終止,並且不會再重啟。 |
Failed (失敗) |
Pod 中的所有容器都已終止,並且至少有一個容器是因為失敗終止。也就是說,容器以非 0 狀態退出或者被系統終止。 |
Unknown (未知) |
因為某些原因無法取得 Pod 的狀態。這種情況通常是因為與 Pod 所在主機通信失敗。 |
如果某節點死掉或者與集群中其他節點失聯,Kubernetes會實施一種策略,將失去的節點上運行的所有 Pod 的phase設置為 Failed
。
6 pod的生命周期
我們一般將pod對象從創建至終止這段時間范圍稱為pod的生命周期,它主要包含以下的過程:
下面來分別看看生命周期中的不同階段pod做了什么。
6.0 基礎容器
其實在init c容器創建之前,首先會創建一個基礎容器運行着/pause
命令,它的目的是保存所有的名稱空間,后創建的容器都使用這個名稱空間,以及網絡和數據卷初始化。
6.1 初始化容器
基礎容器創建好后,開始創建初始化容器(init c)。初始化容器是在pod的主容器(main container)啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有以下特征:
- 初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么k8s需要重啟它直到成功完成
- 初始化容器必須按照定義的順序執行,當僅當前一個執行成功之后,后面的init才能運行。如果某容器因為容器運行時的原因無法啟動,或以錯誤狀態退出,kubelet 會根據 Pod 的
restartPolicy
策略進行重試。 然而,如果 Pod 的restartPolicy
設置為 "Always",Init 容器失敗時會使用restartPolicy
的 "OnFailure" 策略。 - 在所有的 Init 容器沒有成功之前,Pod 將不會變成
Ready
狀態。 - 如果 Pod 重啟,所有 Init 容器必須重新執行。
因為 Init 容器具有與應用容器分離的單獨鏡像,其啟動相關代碼具有如下優勢:
- Init 容器可以包含一些安裝過程中應用容器中不存在的實用工具或個性化代碼。例如,沒有必要僅為了在安裝過程中使用類似
sed
、awk
、python
或dig
這樣的工具而去FROM
一個鏡像來生成一個新的鏡像。 - Init 容器可以安全地運行這些工具,在init容器中使用,而不是在主容器中使用,以避免這些工具導致應用鏡像的安全性降低。
- 主容器啟動時可能需要前置代碼,構建這些代碼的工具只在開始時運行,因此可以分離到init c中,沒必要在主容器一直運行。
- Init 容器能以不同於 Pod 內應用容器的文件系統視圖運行。因此,Init 容器可以訪問應用容器不能訪問的 Secret 的權限。(可以類比為init創建時sudo提權,而創建完運行時沒有權限訪問隱私內容)
- 由於 Init 容器必須在應用容器啟動之前運行完成,因此 Init 容器 提供了一種機制來阻塞或延遲應用容器的啟動,直到滿足了一組先決條件。 一旦前置條件滿足,Pod 內的所有的應用容器會並行啟動。
下面的例子定義了一個具有 2 個 Init 容器的簡單 Pod。 第一個等待 myservice
啟動, 第二個等待 mydb
啟動。 一旦這兩個Init容器都啟動完成,Pod 將啟動 spec
中的應用容器。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers: # 定義初始化容器
- name: init-myservice
image: busybox
command: ['sh', '-c', "until nslookup myservice.; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox
command: ['sh', '-c', "until nslookup mydb; do echo waiting for mydb; sleep 2; done"]
你通過運行下面的命令啟動 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
由於沒有啟動這兩個服務,Init 容器將會等待至發現名稱為 mydb
和 myservice
的 Service。如下為創建這些 Service 的配置文件:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
創建 mydb
和 myservice
服務的命令:
kubectl create -f services.yaml
這樣你將能看到這些 Init 容器執行完畢。隨后 my-app
的 Pod 進入 Running
狀態:
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
6.2 PostStart和Pre Stop
post start是在主容器被創建之后立即被執行的回調鈎子。
pre stop是在容器被終止之前的回調鈎子,在用來停止容器的 TERM 信號被發出之前,回調必須執行結束。 Pod 的終止寬限周期在 PreStop回調被執行之前即開始計數,所以無論回調函數的執行結果如何,容器最終都會在 Pod 的終止寬限期內被終止。沒有參數會被傳遞給處理程序。
下面的例子展示了資源清單同時使用postStart 和 preStop :
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
在上述配置文件中,你可以看到 postStart 命令在容器的 /usr/share
目錄下寫入文件 message
。 命令 preStop 負責優雅地終止 nginx 服務。當因為失效而導致容器終止時,這一處理方式很有用。
6.3 探針
探針是由 kubelet 對容器執行的定期診斷。要執行診斷,kubelet 調用由容器實現的 Handler (處理程序)。有三種類型的處理程序:
- ExecAction: 在容器內執行指定命令。如果命令退出時返回碼為 0 則認為診斷成功,其他任何返回碼都認為失敗。
- TCPSocketAction: 對容器的 IP 地址上的指定端口執行 TCP 檢查。如果端口打開能被連接,則診斷被認為是成功的,否則失敗。
- HTTPGetAction: 對容器的 IP 地址上指定端口和路徑執行 HTTP Get 請求。如果響應的狀態碼大於等於 200 且小於 400,則診斷被認為是成功的。
每次探測都將獲得以下三種結果之一:
Success
(成功):容器通過了診斷。Failure
(失敗):容器未通過診斷。Unknown
(未知):診斷失敗,因此不會采取任何行動。
針對運行中的容器,kubelet可以選擇是否執行以下三種探針,以及如何針對探測結果作出反應:
livenessProbe(存活檢測)
:指示容器是否正在運行。如果存活態探測失敗,則 kubelet 會殺死容器, 並且容器將根據其重啟策略決定動作。如果容器不提供存活探針, 則默認狀態為Success
。readinessProbe(就緒檢測)
:指示容器是否准備好為請求提供服務。如果就緒態探測失敗, 端點控制器將從與 Pod 匹配的所有服務的端點列表中刪除該 Pod 的 IP 地址。 初始延遲之前的就緒態的狀態值默認為Failure
。 如果容器不提供就緒態探針,則默認狀態為Success
。
下面的例子展示了資源清單同時使用就緒和存活探測器:
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test
image: k8s.gcr.io/busybox
# 就緒探測
# kubelet 會在容器啟動 5 秒后發送第一個就緒探測。 這會嘗試連接 busybox 容器的 8080 端口。 如果探測成功,這個 Pod 會被標記為就緒狀態,kubelet 將繼續每隔 10 秒運行一次檢測。
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5 # initialDelaySeconds 字段告訴 kubelet 在執行第一次探測前應該等待 5 秒
periodSeconds: 10 # periodSeconds 字段指定了 kubelet 應該每 10 秒執行一次探測
# 存活探測
# kubelet 會在容器啟動 5 秒后進行第一次存活探測, kubelet 會向容器內運行的服務80端口發送一個 HTTP GET 請求來執行探測。 如果服務器上 /index.html 響應的狀態碼大於等於 200 且小於 400,則 kubelet 認為容器是健康存活的。 否則 kubelet 會殺死這個容器並且重新啟動它。
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 5 # initialDelaySeconds 字段告訴 kubelet 在執行第一次探測前應該等待 5 秒。
periodSeconds: 5 # periodSeconds 字段指定了 kubelet 每隔 5 秒執行一次存活探測
探針有很多配置字段,可以使用這些字段精確的控制存活和就緒檢測的行為:
initialDelaySeconds
:容器啟動后要等待多少秒后存活和就緒探測器才被初始化,默認是 0 秒,最小值是 0。periodSeconds
:執行探測的時間間隔(單位是秒)。默認是 10 秒。最小值是 1。timeoutSeconds
:探測的超時后等待多少秒。默認值是 1 秒。最小值是 1。successThreshold
:探測器在失敗后,被視為成功的最小連續成功數。默認值是 1。 存活和啟動探測的這個值必須是 1。最小值是 1。failureThreshold
:當探測失敗時,Kubernetes 的重試次數。 存活探測情況下的放棄就意味着重新啟動容器。 就緒探測情況下的放棄 Pod 會被打上未就緒的標簽。默認值是 3。最小值是 1。
6.4 pod的終止
關於pod的終止詳情,請查閱官方文檔。
7 通過標簽組織pod
7.1 介紹標簽
標簽是一種組織pod和其他k8s資源的方式。標簽是可以附加到資源的任意鍵值對,一個資源可以擁有多個標簽(當然標簽的key不能重復)。一般在創建資源時會附加一個標簽,在之后也可以再添加其他標簽,或者修改現有標簽的值。
舉個例子,通過給pod添加標簽,可以得到一個更組織化的系統,以便我們理解。每個pod都標有兩個標簽:
- app,它指定pod屬於哪個應用、組件或微服務。
- rel,它顯示在pod中運行的應用程序版本是stable(穩定版)、beta(測試版)還是canary(嘗鮮體驗版)。
如下圖所示, 通過添加這兩個標簽基本上可以將pod組織為兩個維度(基於應用的橫向維度和基於版本的縱向維度)。
7.2 創建pod時添加標簽
有如下資源清單:
apiVersion: vl
kind: Pod
metadata:
name: kubia-manual-v2
labels: # 創建兩個標簽
creation_method: manual
env: prod
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
根據這個清單創建的pod,就會有兩個標簽。
kubectl get pod --show-labels
# 返回結果
NAME READY STATUS RESTARTS AGE LABLES
kubia-manual-v2 1/1 Running 0 3m creation_method=manual,env=prod
如果你只對某些標簽感興趣, 可以使用-L
選項指定它們並將它們分別顯示在自己的列中:
kubectl get pod -L creation_method,env
# 返回結果
NAME READY STATUS RESTARTS AGE CREATION METHOD ENV
kubia-manual-v2 1/1 Running 0 3m manual prod
7.3 修改現有pod的標簽
標簽也可以在現有pod上進行添加和修改。
將kubia-manual-v2
上的env =prod
標簽更改為env=debug
:
kubectl label pod kubia-manual-v2 env=debug --overwrite
# 在更改現有標簽時, 需要使用--overwrite選項
也可以為它添加一個標簽:
kubectl label pod kubia-manual-v2 ver=v1
# 添加一個沒有的標簽時可以不用--overwrite
再次查看:
kubectl get pod -L creation_method,env,ver
# 返回結果
NAME READY STATUS RESTARTS AGE CREATION METHOD ENV VER
kubia-manual-v2 1/1 Running 0 7m manual debug v1
7.4 通過標簽選擇器來篩選資源
我們一旦定義好標簽,就相當於為這個資源做了分類。接下來就可以通過標簽選擇器篩選出特定標簽的資源。
標簽選擇器根據資源的以下條件來選擇資源:
- 包含(或不包含)使用特定鍵的標簽
- 包含具有特定鍵和值的標簽
- 包含具有特定鍵的標簽, 但其值與我們指定的不同
舉例:
kubectl get pod -l creation_method=manual # 篩選出creation_method標簽值為manual的所有pod
kubectl get pod -l env # 只有具有env標簽,就把這些pod篩選出來
kubectl get pod -l '!env' # 篩選出沒有env標簽的pod,注意要用單引號括起來轉義,因為bash中感嘆號!有特殊含義
kubectl get pod -l 'creation_method=!manual' # 篩選出creation_method標簽值不為manual的所有pod
kubectl get pod -l 'env in (prod,debug)' # 篩選出具有env標簽,且env標簽值為prod或者debug的pod
kubectl get pod -l 'env notin (prod,debug)' # 篩選出具有env標簽,且env標簽值不為prod或者debug的pod
kubectl get pod -l creation_method=manual,env=prod # 通過逗號,使用多個條件篩選,與邏輯,只有全都匹配成功才算成功
8 名稱空間
標簽可以將pod或其他資源組織成組,但是每個對象可以有多個標簽,所以這些組內的資源可能是重疊的。而且,如果沒有指定標簽,我們kubectl get
時會返回所有的對象。
我們可以使用名稱空間(命名空間)對資源進行分組。這種分組方式可以將對象分割成完全獨立且不重疊的組,你操作時,只在一個名稱空間下操作,其他名稱空間的資源是看不到的。
首先, 查看一下集群中的所有名稱空間
kubectl get namespaces
kubectl get ns # 簡寫
返回如下結果:
NAME STATUS AGE
default Active 2d
kube-node-lease Active 2d
kube-public Active 2d
kube-system Active 2d
我們之前只在default命名空間中進行操作。當使用kubectl get
命令列出資源時,我們從未明確指定命名空間,因此kubectl總是默認為
default
命名空間,只顯示該命名空間下的對象。但從列表中我們可以看到還存在其他的命名空間,接下來我們看看這些命名空間下都有什么資源:
kubectl get pod --namespace kube-system # 可以使用 -n 代替 --namespace
# 返回結果如下
NAME READY STATUS RESTARTS AGE
coredns-5c98db65d4-dl6fz 1/1 Running 4 2d
coredns-5c98db65d4-lwv77 1/1 Running 4 2d
etcd-k8s-master 1/1 Running 3 2d
kube-apiserver-k8s-master 1/1 Running 3 2d
kube-controller-manager-k8s-master 1/1 Running 4 2d
kube-flannel-ds-h2bvx 1/1 Running 3 46h
kube-flannel-ds-ptqd6 1/1 Running 3 47h
kube-flannel-ds-tp584 1/1 Running 3 46h
kube-proxy-hd68r 1/1 Running 3 46h
kube-proxy-lqslr 1/1 Running 3 2d
kube-proxy-q2xld 1/1 Running 3 46h
kube-scheduler-k8s-master 1/1 Running 5 2d
顧名思義,kube-system
名稱空間下運行着一些與k8s系統運行有關的資源。多虧有了名稱空間!不然的話,如果它們都在默認的命名空間中,同時與我們自己創建的資源混合在一起,那么我們很難區分這些資源屬於哪里,並且也可能會無意中刪除一些系統資源。
就像是c++中的namespace
一樣,k8s的名稱空間也起着類似的效果,它隔離一組獨立的資源集合,形成了一個作用域。除了隔離資源,命名空間還可用於僅允許某些用戶訪問某些特定資源,甚至限制單個用戶可用的計算資源數量。
8.1 創建名稱空間
命名空間是一種和其他資源一樣的Kubernetes資源,因此可以通過將yaml文件提交到Kubernetes API服務器來創建該資源:
# custom-namespace.yaml
apiVersion: v1
kind: Namespace # 之前我們創建的資源是Pod,實際上還有很多資源,比如這里的Namespace
metadata:
name: custom-namespace # 名稱空間的名字
接下來將它提交到Kubernetes API Sever:
[root@k8s-master ~]# kubectl apply -f custom-namespace.yaml
namespace/custom-namespace created
雖然寫出上面這樣的文件並不困難,但這仍然是一件麻煩事。k8s提供了一個專用命令創建名稱空間:
kubectl create namespace custom-namespace-2
這比編寫創建文件快得多。此時再次查看會發現多個兩個名稱空間:
8.2 管理其他名稱空間中的對象
如果想要在剛創建的名稱空間中創建資源,可以選擇在metadata
宇段中添加一個namespace : custom-namespace
屬性,也可以用kubectl create/apply
命令創建資源時指定命名空間:
kubectl create -f createpod.yaml -n custom-namespace
此時我們有兩個同名的pod (mypod) 。一個在default
命名空間中,另一個在custom-namespace
中。
當你需要管理其他名稱空間的對象時,需要-n
指定,否則默認是default
名稱空間。
8.3 刪除名稱空間
刪除命令如下:
kubectl delete namespace 名稱空間的名字
9 停止和刪除pod
到目前為止,我們己經創建了一些正在運行的pod,這些pod在不同的名稱空間中,接下來我們嘗試停止它們。
9.1 按名稱刪除pod
按名稱刪除mypod:
kubectl delete pod mypod
在刪除pod的過程中,實際上我們在指示k8s終止該pod中的所有容器。k8s向進程發送一個SIGTERM信號並等待一定的秒數(默認30)使其正常關閉。如果超過這個時間,則通過SIGKILL終止該進程。
可以通過空格刪除多個pod:
kubectl delete pod mypod1 mypod2 mypod3
9.2 使用標簽選擇器刪除pod
我們還可以使用標簽選擇器來刪除pod,為了演示效果,這里創建了兩個pod,它們都有標簽env
和creation_method
。
kubectl delete pod -l env=prod # 刪除標簽值為prod的所有pod
如果你的項目有成百上千個pod,使用標簽刪除一組pod比按名稱一個一個刪除快速得多。
9.3 通過刪除整個命名空間來刪除pod
我們之前只刪除了默認名稱空間的pod,但custom-namespace
這個名稱空間下還有pod,如果你不需要這些pod,也不需要這個名稱空間,那就可以這樣:
kubectl delete ns custom-namespace
9.4 刪除名稱空間中的所有pod但保留名稱空間
如果想刪除所有pod但保留名稱空間,可以這樣:
kubectl delete pod -n custom-namespace --all
9.5 刪除名稱空間中的所有資源
通過使用單個命令刪除當前命名空間中的所有資源:
kubectl delete all --all
命令中的第一個all
指定正在刪除所有資源類型, 而--all
選項指定將刪除所有資源實例而不是按名稱指定它們。
注意,使用all
關鍵字刪除所有內容並不是真的完全刪除所有內容,一些資源會被保留下來(比如Secret)並且需要被明確指定刪除。
另外,這個命令也會刪除名為kubernetes
的Service,但它應該會在幾分鍾后自動重新創建。
10 在運行的容器中遠程執行命令
可以使用kubectl exec
命令遠程地在一個已經存在的pod容器上執行任何命令。這樣就可以很方便地了解pod的內容、狀態及環境。
命令如下:
kubectl exec POD [-c CONTAINER] -- COMMAND [args...]
選項說明:
POD
:任意一個已經運行的pod名稱,你可以用kubectl get pod
命令列出所有的pod,並且選擇其中一個作為exec命令的執行目標。-c CONTAINER
:可選參數,如果pod內運行多個容器,需要指定在哪個容器內執行命令-- COMMAND [args...]
:雙橫杠代表着kubectl命令項的結束,在兩個橫杠之后的內容是指在pod內部需要執行的命令。-it
:控制台輸入發送到容器,並將標准輸入控制台作為容器的控制台輸入。
舉例:
kubectl exec kubia-7nogl -- curl http://10.111.249.153 # 運行名為kubia-7nogl內的容器 執行curl命令
kubectl exec -it kubia-3inly bash # 運行名為kubia-3inly的pod內的容器,打開一個bash窗口用於在容器內執行命令