Pod
Last updated
Last updated
Pod 是一組緊密關聯的容器集合,它們共享 IPC 和 Network namespace,是 Kubernetes 調度的基本單位。Pod 的設計理念是支持多個容器在一個 Pod 中共享網絡和文件系統,可以通過進程間通信和文件共享這種簡單高效的方式組合完成服務。
Pod 的特徵
包含多個共享 IPC 和 Network namespace 的容器,可直接通過 localhost 通信
所有 Pod 內容器都可以訪問共享的 Volume,可以訪問共享數據
無容錯性:直接創建的 Pod 一旦被調度後就跟 Node 綁定,即使 Node 掛掉也不會被重新調度(而是被自動刪除),因此推薦使用 Deployment、Daemonset 等控制器來容錯
優雅終止:Pod 刪除的時候先給其內的進程發送 SIGTERM,等待一段時間(grace period)後才強制停止依然還在運行的進程
特權容器(通過 SecurityContext 配置)具有改變系統配置的權限(在網絡插件中大量應用)
Kubernetes v1.8+ 支持容器間共享 PID namespace,需要 docker >= 1.13.1,並配置 kubelet
--docker-disable-shared-pid=false
。在 Kubernetes v1.10+
--docker-disable-shared-pid
已被棄用,如果要共享 PID namespace,需要設置 v1.PodSpec 中的 ShareProcessNamespace 爲 true,如下所示
v1.5+
core/v1
是
通過 yaml 或 json 描述 Pod 和其內容器的運行環境以及期望狀態,比如一個最簡單的 nginx pod 可以定義爲
在生產環境中,推薦使用 Deployment、StatefulSet、Job 或者 CronJob 等控制器來創建 Pod,而不推薦直接創建 Pod。
目前,Kubernetes 僅支持使用 Docker 鏡像來創建容器,但並非支持 Dockerfile 定義的所有行爲。如下表所示
ENTRYPOINT
啓動命令
是
containerSpec.command
CMD
命令的參數列表
是
containerSpec.args
ENV
環境變量
是
containerSpec.env
EXPOSE
對外開放的端口
否
使用 containerSpec.ports.containerPort 替代
VOLUME
數據卷
是
使用 volumes 和 volumeMounts
USER
進程運行用戶以及用戶組
是
securityContext.runAsUser/supplementalGroups
WORKDIR
工作目錄
是
containerSpec.workingDir
STOPSIGNAL
停止容器時給進程發送的信號
是
SIGKILL
HEALTHCHECK
健康檢查
否
使用 livenessProbe 和 readinessProbe 替代
SHELL
運行啓動命令的 SHELL
否
使用鏡像默認 SHELL 啓動命令
Kubernetes 以 PodStatus.Phase
抽象 Pod 的狀態(但並不直接反映所有容器的狀態)。可能的 Phase 包括
Pending: API Server已經創建該Pod,但一個或多個容器還沒有被創建,包括通過網絡下載鏡像的過程。
Running: Pod中的所有容器都已經被創建且已經調度到 Node 上面,但至少有一個容器還在運行或者正在啓動。
Succeeded: Pod 調度到 Node 上面後均成功運行結束,並且不會重啓。
Failed: Pod中的所有容器都被終止了,但至少有一個容器退出失敗(即退出碼不爲 0 或者被系統終止)。
Unknonwn: 狀態未知,因爲一些原因Pod無法被正常獲取,通常是由於 apiserver 無法與 kubelet 通信導致。
可以用 kubectl 命令查詢 Pod Phase:
PodSpec 中的 restartPolicy
可以用來設置是否對退出的 Pod 重啓,可選項包括 Always
、OnFailure
、以及 Never
。比如
單容器的 Pod,容器成功退出時,不同 restartPolicy
時的動作爲
Always: 重啓 Container; Pod phase
保持 Running.
OnFailure: Pod phase
變成 Succeeded.
Never: Pod phase
變成 Succeeded.
單容器的 Pod,容器失敗退出時,不同 restartPolicy
時的動作爲
Always: 重啓 Container; Pod phase
保持 Running.
OnFailure: 重啓 Container; Pod phase
保持 Running.
Never: Pod phase
變成 Failed.
2個容器的 Pod,其中一個容器在運行而另一個失敗退出時,不同 restartPolicy
時的動作爲
Always: 重啓 Container; Pod phase
保持 Running.
OnFailure: 重啓 Container; Pod phase
保持 Running.
Never: 不重啓 Container; Pod phase
保持 Running.
2個容器的 Pod,其中一個容器停止而另一個失敗退出時,不同 restartPolicy
時的動作爲
Always: 重啓 Container; Pod phase
保持 Running.
OnFailure: 重啓 Container; Pod phase
保持 Running.
Never: Pod phase
變成 Failed.
單容器的 Pod,容器內存不足(OOM),不同 restartPolicy
時的動作爲
Always: 重啓 Container; Pod phase
保持 Running.
OnFailure: 重啓 Container; Pod phase
保持 Running.
Never: 記錄失敗事件; Pod phase
變成 Failed.
Pod 還在運行,但磁盤不可訪問時
終止所有容器
Pod phase
變成 Failed
如果 Pod 是由某個控制器管理的,則重新創建一個 Pod 並調度到其他 Node 運行
Pod 還在運行,但由於網絡分區故障導致 Node 無法訪問
Node controller等待 Node 事件超時
Node controller 將 Pod phase
設置爲 Failed.
如果 Pod 是由某個控制器管理的,則重新創建一個 Pod 並調度到其他 Node 運行
Volume 可以爲容器提供持久化存儲,比如
更多掛載存儲卷的方法參考 Volume。
在使用私有鏡像時,需要創建一個 docker registry secret,並在容器中引用。
創建 docker registry secret:
比如使用 Azure Container Registry(ACR):
在引用 docker registry secret 時,有兩種可選的方法:
第一種是直接在 Pod 描述文件中引用該 secret:
第二種是把 secret 添加到 service account 中,再通過 service account 引用(一般是某個 namespace 的 default service account):
支持三種 RestartPolicy
Always:當容器失效時,由Kubelet自動重啓該容器。RestartPolicy的默認值。
OnFailure:當容器終止運行且退出碼不爲0時由Kubelet重啓。
Never:無論何種情況下,Kubelet都不會重啓該容器。
注意,這裏的重啓是指在 Pod 所在 Node 上面本地重啓,並不會調度到其他 Node 上去。
環境變量爲容器提供了一些重要的資源,包括容器和 Pod 的基本信息以及集群中服務的信息等:
(1) hostname
HOSTNAME
環境變量保存了該 Pod 的 hostname。
(2)容器和 Pod 的基本信息
Pod 的名字、命名空間、IP 以及容器的計算資源限制等可以以 Downward API 的方式獲取並存儲到環境變量中。
(3) 集群中服務的信息
容器的環境變量中還可以引用容器運行前創建的所有服務的信息,比如默認的 kubernetes 服務對應以下環境變量:
由於環境變量存在創建順序的侷限性(環境變量中不包含後來創建的服務),推薦使用 DNS 來解析服務。
支持三種 ImagePullPolicy
Always:不管本地鏡像是否存在都會去倉庫進行一次鏡像拉取。校驗如果鏡像有變化則會覆蓋本地鏡像,否則不會覆蓋。
Never:只是用本地鏡像,不會去倉庫拉取鏡像,如果本地鏡像不存在則Pod運行失敗。
IfNotPresent:只有本地鏡像不存在時,纔會去倉庫拉取鏡像。ImagePullPolicy的默認值。
注意:
默認爲 IfNotPresent
,但 :latest
標籤的鏡像默認爲 Always
。
拉取鏡像時 docker 會進行校驗,如果鏡像中的 MD5 碼沒有變,則不會拉取鏡像數據。
生產環境中應該儘量避免使用 :latest
標籤,而開發環境中可以藉助 :latest
標籤自動拉取最新的鏡像。
通過設置 dnsPolicy 參數,設置 Pod 中容器訪問 DNS 的策略
ClusterFirst:優先基於 cluster domain (如 default.svc.cluster.local
) 後綴,通過 kube-dns 查詢 (默認策略)
Default:優先從 Node 中配置的 DNS 查詢
通過設置 spec.hostIPC
參數爲 true,使用主機的 IPC 命名空間,默認爲 false。
通過設置 spec.hostNetwork
參數爲 true,使用主機的網絡命名空間,默認爲 false。
通過設置 spec.hostPID
參數爲 true,使用主機的 PID 命名空間,默認爲 false。
通過 spec.hostname
參數實現,如果未設置默認使用 metadata.name
參數的值作爲 Pod 的 hostname。
通過 spec.subdomain
參數設置 Pod 的子域名,默認爲空。
比如,指定 hostname 爲 busybox-2 和 subdomain 爲 default-subdomain,完整域名爲 busybox-2.default-subdomain.default.svc.cluster.local
,也可以簡寫爲 busybox-2.default-subdomain.default
:
注意:
默認情況下,DNS 爲 Pod 生成的 A 記錄格式爲 pod-ip-address.my-namespace.pod.cluster.local
,如 1-2-3-4.default.pod.cluster.local
上面的示例還需要在 default namespace 中創建一個名爲 default-subdomain
(即 subdomain)的 headless service,否則其他 Pod 無法通過完整域名訪問到該 Pod(只能自己訪問到自己)
注意,必須爲 headless service 設置至少一個服務端口(spec.ports
,即便它看起來並不需要),否則 Pod 與 Pod 之間依然無法通過完整域名來訪問。
從 v1.9 開始,可以在 kubelet 和 kube-apiserver 中設置 --feature-gates=CustomPodDNS=true
開啓設置每個 Pod DNS 地址的功能。
注意該功能在 v1.10 中爲 Beta 版,v1.9 中爲 Alpha 版。
對於舊版本的集群,可以使用 ConfigMap 來自定義 Pod 的 /etc/resolv.conf
,如
Kubernetes 通過 cgroups 限制容器的 CPU 和內存等計算資源,包括 requests(請求,調度器保證調度到資源充足的 Node 上,如果無法滿足會調度失敗)和 limits(上限)等:
spec.containers[].resources.limits.cpu
:CPU 上限,可以短暫超過,容器也不會被停止
spec.containers[].resources.limits.memory
:內存上限,不可以超過;如果超過,容器可能會被終止或調度到其他資源充足的機器上
spec.containers[].resources.limits.ephemeral-storage
:臨時存儲(容器可寫層、日誌以及 EmptyDir 等)的上限,超過後 Pod 會被驅逐
spec.containers[].resources.requests.cpu
:CPU 請求,也是調度 CPU 資源的依據,可以超過
spec.containers[].resources.requests.memory
:內存請求,也是調度內存資源的依據,可以超過;但如果超過,容器可能會在 Node 內存不足時清理
spec.containers[].resources.requests.ephemeral-storage
:臨時存儲(容器可寫層、日誌以及 EmptyDir 等)的請求,調度容器存儲的依據
比如 nginx 容器請求 30% 的 CPU 和 56MB 的內存,但限制最多隻用 50% 的 CPU 和 128MB 的內存:
注意
CPU 的單位是 CPU 個數,可以用 millicpu (m)
表示少於 1 個 CPU 的情況,如 500m = 500millicpu = 0.5cpu
,而一個 CPU 相當於
AWS 上的一個 vCPU
GCP 上的一個 Core
Azure 上的一個 vCore
物理機上開啓超線程時的一個超線程
內存的單位則包括 E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki
等。
從 v1.10 開始,可以設置 kubelet ----cpu-manager-policy=static
爲 Guaranteed(即 requests.cpu 與 limits.cpu 相等)Pod 綁定 CPU(通過 cpuset cgroups)。
爲了確保容器在部署後確實處在正常運行狀態,Kubernetes 提供了兩種探針(Probe)來探測容器的狀態:
LivenessProbe:探測應用是否處於健康狀態,如果不健康則刪除並重新創建容器。
ReadinessProbe:探測應用是否啓動完成並且處於正常服務狀態,如果不正常則不會接收來自 Kubernetes Service 的流量,即將該Pod從Service的endpoint中移除。
Kubernetes 支持三種方式來執行探針:
exec:在容器中執行一個命令,如果 命令退出碼 返回 0
則表示探測成功,否則表示失敗
tcpSocket:對指定的容器 IP 及端口執行一個 TCP 檢查,如果端口是開放的則表示探測成功,否則表示失敗
httpGet:對指定的容器 IP、端口及路徑執行一個 HTTP Get 請求,如果返回的 狀態碼 在 [200,400)
之間則表示探測成功,否則表示失敗
Pod 能夠具有多個容器,應用運行在容器裏面,但是它也可能有一個或多個先於應用容器啓動的 Init 容器。Init 容器在所有容器運行之前執行(run-to-completion),常用來初始化配置。
如果爲一個 Pod 指定了多個 Init 容器,那些容器會按順序一次運行一個。 每個 Init 容器必須運行成功,下一個才能夠運行。 當所有的 Init 容器運行完成時,Kubernetes 初始化 Pod 並像平常一樣運行應用容器。
下面是一個 Init 容器的示例:
因爲 Init 容器具有與應用容器分離的單獨鏡像,使用 init 容器啓動相關代碼具有如下優勢:
它們可以包含並運行實用工具,出於安全考慮,是不建議在應用容器鏡像中包含這些實用工具的。
它們可以包含使用工具和定製化代碼來安裝,但是不能出現在應用鏡像中。例如,創建鏡像沒必要 FROM 另一個鏡像,只需要在安裝過程中使用類似 sed、 awk、 python 或 dig 這樣的工具。
應用鏡像可以分離出創建和部署的角色,而沒有必要聯合它們構建一個單獨的鏡像。
它們使用 Linux Namespace,所以對應用容器具有不同的文件系統視圖。因此,它們能夠具有訪問 Secret 的權限,而應用容器不能夠訪問。
它們在應用容器啓動之前運行完成,然而應用容器並行運行,所以 Init 容器提供了一種簡單的方式來阻塞或延遲應用容器的啓動,直到滿足了一組先決條件。
Init 容器的資源計算,選擇一下兩者的較大值:
所有 Init 容器中的資源使用的最大值
Pod 中所有容器資源使用的總和
Init 容器的重啓策略:
如果 Init 容器執行失敗,Pod 設置的 restartPolicy 爲 Never,則 pod 將處於 fail 狀態。否則 Pod 將一直重新執行每一個 Init 容器直到所有的 Init 容器都成功。
如果 Pod 異常退出,重新拉取 Pod 後,Init 容器也會被重新執行。所以在 Init 容器中執行的任務,需要保證是冪等的。
容器生命週期鉤子(Container Lifecycle Hooks)監聽容器生命週期的特定事件,並在事件發生時執行已註冊的回調函數。支持兩種鉤子:
postStart: 容器創建後立即執行,注意由於是異步執行,它無法保證一定在 ENTRYPOINT 之前運行。如果失敗,容器會被殺死,並根據 RestartPolicy 決定是否重啓
preStop:容器終止前執行,常用於資源清理。如果失敗,容器同樣也會被殺死
而鉤子的回調函數支持兩種方式:
exec:在容器內執行命令,如果命令的退出狀態碼是 0
表示執行成功,否則表示失敗
httpGet:向指定 URL 發起 GET 請求,如果返回的 HTTP 狀態碼在 [200, 400)
之間表示請求成功,否則表示失敗
postStart 和 preStop 鉤子示例:
默認情況下,容器都是以非特權容器的方式運行。比如,不能在容器中創建虛擬網卡、配置虛擬網絡。
Kubernetes 提供了修改 Capabilities 的機制,可以按需要給容器增加或刪除。比如下面的配置給容器增加了 CAP_NET_ADMIN
並刪除了 CAP_KILL
。
可以通過給 Pod 增加 kubernetes.io/ingress-bandwidth
和 kubernetes.io/egress-bandwidth
這兩個 annotation 來限制 Pod 的網絡帶寬
僅 kubenet 支持限制帶寬
目前只有 kubenet 網絡插件支持限制網絡帶寬,其他 CNI 網絡插件暫不支持這個功能。
kubenet 的網絡帶寬限制其實是通過 tc 來實現的
可以通過 nodeSelector、nodeAffinity、podAffinity 以及 Taints 和 tolerations 等來將 Pod 調度到需要的 Node 上。
也可以通過設置 nodeName 參數,將 Pod 調度到指定 node 節點上。
比如,使用 nodeSelector,首先給 Node 加上標籤:
接着,指定該 Pod 只想運行在帶有 disktype=ssd
標籤的 Node 上:
nodeAffinity、podAffinity 以及 Taints 和 tolerations 等的使用方法請參考 調度器章節。
默認情況下,容器的 /etc/hosts
是 kubelet 自動生成的,並且僅包含 localhost 和 podName 等。不建議在容器內直接修改 /etc/hosts
文件,因爲在 Pod 啓動或重啓時會被覆蓋。
默認的 /etc/hosts
文件格式如下,其中 nginx-4217019353-fb2c5
是 podName:
從 v1.7 開始,可以通過 pod.Spec.HostAliases
來增加 hosts 內容,如
v1.8 + 支持給容器分配 HugePages,資源格式爲 hugepages-<size>
(如 hugepages-2Mi
)。使用前要配置
開啓 --feature-gates="HugePages=true"
在所有 Node 上面預分配好 HugePage ,以便 Kubelet 統計所在 Node 的 HugePage 容量
使用示例
注意事項
HugePage 資源的請求和限制必須相同
HugePage 以 Pod 級別隔離,未來可能會支持容器級的隔離
基於 HugePage 的 EmptyDir 存儲卷最多隻能使用請求的 HugePage 內存
使用 shmget()
的 SHM_HUGETLB
選項時,應用必須運行在匹配 proc/sys/vm/hugetlb_shm_group
的用戶組(supplemental group)中
從 v1.8 開始,可以爲 Pod 設置一個優先級,保證高優先級的 Pod 優先調度。
優先級調度功能目前爲 Beta 版,在 v1.11 版本中默認開啓。對 v1.8-1.10 版本中使用前需要開啓:
--feature-gates=PodPriority=true
--runtime-config=scheduling.k8s.io/v1alpha1=true --admission-control=Controller-Foo,Controller-Bar,...,Priority
爲 Pod 設置優先級前,先創建一個 PriorityClass,並設置優先級(數值越大優先級越高):
Kubernetes 自動創建了
system-cluster-critical
和system-node-critical
等兩個 PriorityClass,用於 Kubernetes 核心組件。
爲 Pod 指定優先級
當調度隊列有多個 Pod 需要調度時,優先調度高優先級的 Pod。而當高優先級的 Pod 無法調度時,Kubernetes 會嘗試先刪除低優先級的 Pod 再將其調度到對應 Node 上(Preemption)。
注意:受限於 Kubernetes 的調度策略,搶佔並不總是成功。
PodDisruptionBudget (PDB) 用來保證一組 Pod 同時運行的數量,這些 Pod 需要使用 Deployment、ReplicationController、ReplicaSet 或者 StatefulSet 管理。
Sysctls 允許容器設置內核參數,分爲安全 Sysctls 和非安全 Sysctls:
安全 Sysctls:即設置後不影響其他 Pod 的內核選項,只作用在容器 namespace 中,默認開啓。包括以下幾種
kernel.shm_rmid_forced
net.ipv4.ip_local_port_range
net.ipv4.tcp_syncookies
非安全 Sysctls:即設置好有可能影響其他 Pod 和 Node 上其他服務的內核選項,默認禁止。如果使用,需要管理員在配置 kubelet 時開啓,如 kubelet --experimental-allowed-unsafe-sysctls 'kernel.msg*,net.ipv4.route.min_pmtu'
v1.6-v1.10 示例:
從 v1.11 開始,Sysctls 升級爲 Beta 版本,不再區分安全和非安全 sysctl,統一通過 podSpec.securityContext.sysctls 設置,如
很多容器都是配置了 UTC 時區,與國內集群的 Node 所在時區有可能不一致,可以通過 HostPath 存儲插件給容器配置與 Node 一樣的時區: