截止目前k8s1.18,k8s已經支持標准容器,sidecar容器,init 容器,ephemeral 容器 4種類型的containers。本文我們詳細介紹一下這4種容器的特性已經使用場景。
Ephemeral 容器
臨時容器與其他容器的不同之處在於,它們缺少對資源或執行的保證,並且永遠不會自動重啟,因此不適用於構建應用程序。臨時容器使用與常規容器相同的 ContainerSpec
段進行描述,但許多字段是不相容且不允許的。
- 臨時容器沒有端口配置,因此像
ports
,livenessProbe
,readinessProbe
這樣的字段是不允許的。 - Pod 資源分配是不可變的,因此
resources
配置是不允許的。 - 有關允許字段的完整列表,請參見臨時容器參考文檔。
臨時容器是使用 API 中的一種特殊的 ephemeralcontainers
處理器進行創建的,而不是直接添加到 pod.spec
段,因此無法使用 kubectl edit
來添加一個臨時容器。
與常規容器一樣,將臨時容器添加到 Pod 后,將不能更改或刪除臨時容器。
為什么我們需要Ephemeral 容器?
我們知道容器的優點是它們通過使用不變方法提供所有必需的依賴項來運行隔離的進程。通過僅將所需的依賴項添加到鏡像中,容器可以降低攻擊面並提供更快的啟動和部署。使用“distroless”方法構建容器鏡像(基於scratch),通過僅包含已編譯的應用程序二進制文件,將容器鏡像提升到了一個新的水平。與普通的容器鏡像不同,它們不基於任何種類的Linux發行版,因此不包含任何其他可通過kubectl exec執行以進行故障排除的二進制文件和工具。這就決定了該容器有助於提供安全可靠的運行時環境,但也很難在問題發生時進行調試。
在這種情況下,臨時容器發揮作用。它們實現了調試容器附加到主進程的功能,然后你可以用於調試任何類型的問題。調試容器可以基於任何鏡像,因此可以根據您的需求進行定制。您可以構建自己的調試鏡像,其中包含特殊的調試二進制文件或僅包含curl,OpenSSL和MongoDB客戶端之類的工具。但是,您也可以選擇Linux發行版(如Ubuntu)或僅運行Busybox鏡像,這兩個鏡像都已經包含了許多有用的工具。
如何使用臨時容器?
臨時容器是alpha功能,因此默認情況下處於禁用狀態。您將需要激活以下功能門才能使用它們:
- 臨時容器
- PodShareProcessNamespace(v1.16中的beta版,因此默認情況下已啟用)
本節中的示例演示了臨時容器如何出現在 API 中。 通常,您可以使用 kubectl
插件進行故障排查,從而自動化執行這些步驟。
臨時容器是使用 Pod 的 ephemeralcontainers
子資源創建的,可以使用 kubectl --raw
命令進行顯示。首先描述臨時容器被添加為一個 EphemeralContainers
列表:
{
"apiVersion": "v1",
"kind": "EphemeralContainers",
"metadata": {
"name": "example-pod"
},
"ephemeralContainers": [{
"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger",
"stdin": true,
"tty": true,
"terminationMessagePolicy": "File"
}]
}
使用如下命令更新已運行的臨時容器 example-pod
:
kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers -f ec.json
這將返回臨時容器的新列表:
{
"kind":"EphemeralContainers",
"apiVersion":"v1",
"metadata":{
"name":"example-pod",
"namespace":"default",
"selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
"uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
"resourceVersion":"15886",
"creationTimestamp":"2019-08-29T06:41:42Z"
},
"ephemeralContainers":[
{
"name":"debugger",
"image":"busybox",
"command":[
"sh"
],
"resources":{
},
"terminationMessagePolicy":"File",
"imagePullPolicy":"IfNotPresent",
"stdin":true,
"tty":true
}
]
}
可以使用以下命令查看新創建的臨時容器的狀態:
kubectl describe pod example-pod
...
Ephemeral Containers:
debugger:
Container ID: docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
Image: busybox
Image ID: docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
Port: <none>
Host Port: <none>
Command:
sh
State: Running
Started: Thu, 29 Aug 2019 06:42:21 +0000
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
可以使用以下命令連接到新的臨時容器:
kubectl attach -it example-pod -c debugger
如果啟用了進程命名空間共享,則可以查看該 Pod 所有容器中的進程。 例如,運行上述 attach
操作后,在調試器容器中運行 ps
操作:
# 在 "debugger" 臨時容器內中運行此 shell 命令
ps auxww
運行命令后,輸出類似於:
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 nginx: master process nginx -g daemon off;
11 101 0:00 nginx: worker process
12 101 0:00 nginx: worker process
13 101 0:00 nginx: worker process
14 101 0:00 nginx: worker process
15 101 0:00 nginx: worker process
16 101 0:00 nginx: worker process
17 101 0:00 nginx: worker process
18 101 0:00 nginx: worker process
19 root 0:00 /pause
24 root 0:00 sh
29 root 0:00 ps auxww
Init 容器
在Kubernetes中,init容器是在同一Pod中的其他容器之前開始並執行的容器。它旨在為Pod上托管的主應用程序執行初始化邏輯。例如,創建必要的用戶帳戶,執行數據庫遷移,創建數據庫結構等。
創建initcontainer時應考慮一些注意事項:
- 它們總是在Pod中的其他容器之前執行。因此,它們不應包含需要很長時間才能完成的復雜邏輯。啟動腳本通常很小而簡潔。如果發現要向初始化容器添加太多邏輯,則應考慮將其中的一部分移至應用程序容器本身。
- 初始化容器按順序啟動和執行。除非一個初始化容器被成功執行,否則下一個初始化容器不會被開始執行。因此,如果啟動任務很長,則可以考慮將其分為多個步驟,每個步驟都由一個初始化容器處理,以便您知道哪些步驟失敗。
- 如果任何初始化容器失敗,則將重新啟動整個Pod(除非您將restartPolicy設置為Never)。重新啟動Pod意味着再次重新執行所有容器,包括任何初始化容器。因此,您可能需要確保啟動邏輯允許多次執行而不會導致重復。例如,如果數據庫遷移已經完成,則應僅忽略再次執行遷移命令。
- 初始化容器是延遲應用程序初始化直到一個或多個依賴項可用的很好的選擇。例如,如果您的應用程序依賴於施加API請求速率限制的API,則您可能需要等待一段時間才能接收來自該API的響應。在應用程序容器中實現此邏輯可能很復雜;因為它需要與健康和就緒狀態探測器結合使用。一種更簡單的方法是創建一個初始化容器,該容器要等到API准備好后才能成功退出。只有在初始化容器成功完成其工作之后,應用程序容器才會啟動。
- 初始化容器不能像應用程序容器那樣使用運行狀況和就緒探針。原因是它們要成功啟動和退出,就像Jobs和CronJobs的行為一樣。
- 同一Pod上的所有容器共享相同的卷和網絡。您可以利用此功能在應用程序及其初始化容器之間共享數據。
正如我們剛剛討論的那樣,init容器總是比同一個Pod上的其他應用程序容器先啟動。結果,調度程序對init容器的資源和限制賦予了更高的優先級。必須仔細考慮這種行為,因為這可能會導致不良后果。例如,如果您有一個初始化容器和一個應用程序容器,並且將初始化容器的資源和限制設置為高於應用程序容器的資源和限制,那么只有在有一個可用節點滿足初始化的情況下,才調度整個Pod容器要求。換句話說,即使有一個未使用的節點可以在其中運行應用程序容器,但如果初始化容器具有該節點可以處理的更高資源先決條件,則Pod也不會部署到該節點。因此,在定義初始化容器的請求和限制時,您應盡可能嚴格。最佳做法是,除非絕對必要,否則請勿將這些參數設置為高於應用程序容器的值。
標准容器和Sidecar容器
在k8s1.18 之前,這兩種容器從k8s管理的角度來看,並沒有什么區別。只不過人為從功能上做了區分。
使用sidecar容器(模塊化)具有的優點
- 加速應用程序開發,因為容器可以在團隊甚至更大的社區之間重復使用
- 整理專家知識,因為每個人都在一個容器化的實現上進行協作,該實現反映了最佳實踐,而不是無數種功能大致相同的自家生產的不同容器
- 啟用敏捷團隊,因為容器邊界是自然邊界,是團隊職責的契約
- 提供關注點分離,並專注於特定功能,以減少意大利面條的依賴性和不可測的組件
對於Sidecar容器一般來說主要體現在以下4種角色:
代理
。例如現在Istio中的Envoy。
通過這種Sidercar 模式,代理可以攔截進出主容器的流量從而Istio可以提取有關流量行為的大量信號作為屬性。 Istio可以使用這些屬性來執行策略決策,並將其發送到監視系統以提供有關整個網格行為的信息。
Sidecar代理模型還允許您將Istio功能添加到現有部署中,而無需重新構造或重寫代碼。
適配器
。適配器容器對輸出進行標准化。考慮監視N個不同應用程序的任務。可以使用不同的導出監視數據的方式來構建每個應用程序。 (例如JMX,StatsD,特定於應用程序的統計信息),但每個監控系統都希望其收集的監控數據具有一致且統一的數據模型。通過使用復合容器的適配器模式,您可以通過創建Pod來將來自不同系統的異構監視數據轉換為一個統一的表示形式,該Pod將應用程序容器與知道如何進行轉換的適配器分組在一起。同樣,由於這些Pod共享名稱空間和文件系統,因此這兩個容器的協調非常簡單明了。
增強主容器功能
。Sidecar容器擴展並增強了“主”容器,它們可以使用現有的容器並使它們變得更好。例如,考慮一個運行Nginx Web服務器的容器。添加另一個將文件系統與git存儲庫同步的容器,在這些容器之間共享文件系統,並且您已經構建了Git Push-to-deploy。但是您已經以模塊化的方式完成了此工作,其中git同步器可以由不同的團隊構建,並且可以在許多不同的Web服務器(Apache,Python,Tomcat等)上重復使用。由於這種模塊化,您只需編寫和測試git同步器一次,即可在眾多應用程序中重復使用它。而且,如果有人編寫它,您甚至不需要這樣做。實現輔助功能
。這種場景一般出現在DevOps中。比如將收集日志的組件以Sidecar的方式部署,實現收集日志的用途,或是部署一個Sidecar組件從配置中心監聽配置變化,實時更新本地配置。
生命周期
Sidecar容器的所有問題都與容器生命周期相關性有關。由於Pod中的常規容器之間沒有區別,因此無法控制哪個容器首先啟動或最后終止,但是先正確運行Sidecar容器通常是應用程序容器正確運行的要求。
從1.18版本開始,K8S內置的Sidecar功能將確保Sidecar容器在正常業務流程開始之前就啟動並運行,即通過更改pod的啟動生命周期,在init容器完成后啟動sidecar容器,在sidecar容器就緒后啟動業務容器,從啟動流程上保證順序性。

通過更改Pod規范中的container.lifecycle.type
將容器標記為Sidecar類型:Sidecar
,默認為Standard
,如下:
apiVersion: v1
kind: Pod
metadata:
name: bookings-v1-b54bc7c9c-v42f6
labels:
app: demoapp
spec:
containers:
- name: bookings
image: banzaicloud/allspark:0.1.1
...
- name: istio-proxy
image: docker.io/istio/proxyv2:1.4.3
lifecycle:
type: Sidecar
...
總結
本文簡單介紹了標准容器,sidecar容器,init 容器,ephemeral 容器 4種類型的containers。隨着kubernetes日益普及,我們需要充分掌握着幾種類型容器原理和使用方法,才能更好地服務業務。
此外Sidecar容器將會成為未來軟件交付的一種新的方式,參照Dapr等,不同的團隊提供自己的功能容器,然后選擇性注入Sidecar到主業務容器,實現解耦