天天看點

深入解析Pod對象(二)

一、Volume

Pod作為 Kubernetes 項目裡最核心的編排對象,Pod 攜帶的資訊非常豐富。其中,資源定義(比如 CPU、記憶體等),以及排程相關的字段,本文從volume入手,深入了解Pod對象各個重要字段的含義。

Volume,叫作Projected Volume,可以把它翻譯為“投射資料卷”。(kubernetes v1.11之後的新特性)

在 Kubernetes 中,有幾種特殊的 Volume,它們存在的意義不是為了存放容器裡的資料,也不是用來進行容器和主控端之間的資料交換。這些特殊 Volume 的作用,是為容器提供預先定義好的資料。是以,從容器的角度來看,這些 Volume 裡的資訊就是仿佛是被 Kubernetes“投射”(Project)進入容器當中的。這正是 Projected Volume 的含義。

到目前為止,Kubernetes 支援的 Projected Volume 一共有四種:

  1. Secret;
  2. ConfigMap;
  3. Downward API;
  4. ServiceAccountToken。

1.1、Secret

Secret它的作用,是把 Pod 想要通路的加密資料,存放到 Etcd 中。然後,你就可以通過在 Pod 的容器裡挂載 Volume 的方式,通路到這些 Secret 裡儲存的資訊了。

Secret 最典型的使用場景:存放資料庫的 Credential 資訊,如下

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume 
spec:
  containers:
  - name: test-secret-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass      

在這個 Pod 中,定義了一個簡單的容器。它聲明挂載的 Volume,并不是常見的 emptyDir 或者 hostPath 類型,而是 projected 類型。而這個 Volume 的資料來源(sources),則是名為 user 和 pass 的 Secret 對象,分别對應的是資料庫的使用者名和密碼。

這裡用到的資料庫的使用者名、密碼,正是以 Secret 對象的方式交給 Kubernetes 儲存的。

完成這個操作的操作指令如下:

$ cat ./username.txt
admin
$ cat ./password.txt
c1oudc0w!

$ kubectl create secret generic user --from-file=./username.txt
$ kubectl create secret generic pass --from-file=./password.txt      

其中,username.txt 和 password.txt 檔案裡,存放的就是使用者名和密碼;而 user 和 pass,則是我為 Secret 對象指定的名字。而我想要檢視這些 Secret 對象的話,隻要執行一條​

​ kubectl get​

​ 指令就可以了:

$ kubectl get secrets
NAME           TYPE                                DATA      AGE
user          Opaque                                1         51s
pass          Opaque                                1         51s      

除了使用​

​kubectl create secret ​

​指令外,也可以直接通過編寫 YAML 檔案的方式來建立這個 Secret 對象,比如:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  user: YWRtaW4=
  pass: MWYyZDFlMmU2N2Rm      

可以看到,通過編寫 YAML 檔案建立出來的 Secret 對象隻有一個。但它的 data 字段,卻以 Key-Value 的格式儲存了兩份 Secret 資料。其中,“user”就是第一份資料的 Key,“pass”是第二份資料的 Key。

注意:Secret 對象要求這些資料必須是經過 Base64 轉碼的,以免出現明文密碼的安全隐患。

$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm      

注意:像這樣建立的 Secret 對象,它裡面的内容僅僅是經過了轉碼,而并沒有被加密。在真正的生産環境中,你需要在 Kubernetes 中開啟 Secret 的加密插件,增強資料的安全性。

接下來,嘗試建立這個Pod:

$ kubectl create -f test-projected-volume.yaml      

當 Pod 變成 Running 狀态之後,再驗證一下這些 Secret 對象是不是已經在容器裡了:

$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
user
pass
$ cat /projected-volume/user
root
$ cat /projected-volume/pass
1f2d1e2e67df      

從傳回結果中,可以看到,儲存在 Etcd 裡的使用者名和密碼資訊,已經以檔案的形式出現在了容器的 Volume 目錄裡。而這個檔案的名字,就是 kubectl create secret 指定的 Key,或者說是 Secret 對象的 data 字段指定的 Key。

更重要的是,像這樣通過挂載方式進入到容器裡的 Secret,一旦其對應的 Etcd 裡的資料被更新,這些 Volume 裡的檔案内容,同樣也會被更新。其實,這是 kubelet 元件在定時維護這些 Volume。

注意:這個更新可能會有一定的延時。是以在編寫應用程式時,在發起資料庫連接配接的代碼處寫好重試和逾時的邏輯,絕對是個好習慣。

1.2、ConfigMap

與 Secret 類似的是 ConfigMap,它與 Secret 的差別在于,ConfigMap 儲存的是不需要加密的、應用所需的配置資訊。而 ConfigMap 的用法幾乎與 Secret 完全相同:可以使用 kubectl create configmap 從檔案或者目錄建立 ConfigMap,也可以直接編寫 ConfigMap 對象的 YAML 檔案。

如:一個 Java 應用所需的配置檔案(.properties 檔案),就可以通過下面這樣的方式儲存在 ConfigMap 裡:

# .properties檔案的内容
$ cat example/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

# 從.properties檔案建立ConfigMap
$ kubectl create configmap ui-config --from-file=example/ui.properties

# 檢視這個ConfigMap裡儲存的資訊(data)
$ kubectl get configmaps ui-config -o yaml
apiVersion: v1
data:
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  name: ui-config
  ...      

備注:​

​kubectl get -o yaml​

​ 這樣的參數,會将指定的 Pod API 對象以 YAML 的方式展示出來。

1.3、Downward API

它的作用是:讓 Pod 裡的容器能夠直接擷取到這個 Pod API 對象本身的資訊。

例子:

apiVersion: v1
kind: Pod
metadata:
  name: test-downwardapi-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
          readOnly: false
  volumes:
    - name: podinfo
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels      

在這個 Pod 的 YAML 檔案中,定義了一個簡單的容器,聲明了一個 projected 類型的 Volume。隻不過這次 Volume 的資料來源,變成了 Downward API。而這個 Downward API Volume,則聲明了要暴露 Pod 的 metadata.labels 資訊給容器。

通過這樣的聲明方式,目前 Pod 的 Labels 字段的值,就會被 Kubernetes 自動挂載成為容器裡的 /etc/podinfo/labels 檔案。

而這個容器的啟動指令,則是不斷列印出 /etc/podinfo/labels 裡的内容。是以,當我建立了這個 Pod 之後,就可以通過 kubectl logs 指令,檢視到這些 Labels 字段被列印出來,如下所示:

$ kubectl create -f dapi-volume.yaml
$ kubectl logs test-downwardapi-volume
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"      

目前,Downward API 支援的字段已經非常豐富了,比如:

1. 使用fieldRef可以聲明使用:
spec.nodeName - 主控端名字
status.hostIP - 主控端IP
metadata.name - Pod的名字
metadata.namespace - Pod的Namespace
status.podIP - Pod的IP
spec.serviceAccountName - Pod的Service Account的名字
metadata.uid - Pod的UID
metadata.labels['<KEY>'] - 指定<KEY>的Label值
metadata.annotations['<KEY>'] - 指定<KEY>的Annotation值
metadata.labels - Pod的所有Label
metadata.annotations - Pod的所有Annotation

2. 使用resourceFieldRef可以聲明使用:
容器的CPU limit
容器的CPU request
容器的memory limit
容器的memory request      

上面這個清單的内容,随着 Kubernetes 項目的發展肯定還會不斷增加。是以這裡列出來的資訊僅供參考,在使用 Downward API 時,還是要記得去查閱一下官方文檔。

注意:Downward API 能夠擷取到的資訊,一定是 Pod 裡的容器程序啟動之前就能夠确定下來的資訊。而如果你想要擷取 Pod 容器運作後才會出現的資訊,比如,容器程序的 PID,那就肯定不能使用 Downward API 了,而應該考慮在 Pod 裡定義一個 sidecar 容器。

其實,Secret、ConfigMap,以及 Downward API 這三種 Projected Volume 定義的資訊,大多還可以通過環境變量的方式出現在容器裡。但是,通過環境變量擷取這些資訊的方式,不具備自動更新的能力。是以,一般情況下,建議你使用 Volume 檔案的方式擷取這些資訊。

二、Service Account

疑問:現在有了一個Pod,能不能在這個Pod裡安裝一個kubernetes的Client,這樣就可以從容器裡直接通路并且操作這個kubernetes的API了呢?

答案是肯定的,不過首先要解決API Server的授權問題。

Service Account 對象的作用,就是 Kubernetes 系統内置的一種“服務賬戶”,它是 Kubernetes 進行權限配置設定的對象。比如,Service Account A,可以隻被允許對 Kubernetes API 進行 GET 操作,而 Service Account B,則可以有 Kubernetes API 的所有操作權限。

​像這樣的 Service Account 的授權資訊和檔案,實際上儲存在它所綁定的一個特殊的 Secret 對象裡的。這個特殊的 Secret 對象,就叫作 ServiceAccountToken。任何運作在 Kubernetes 叢集上的應用,都必須使用這個 ServiceAccountToken 裡儲存的授權資訊,也就是 Token,才可以合法地通路 API Server。

是以說,Kubernetes 項目的 Projected Volume 其實隻有三種,因為第四種 ServiceAccountToken,隻是一種特殊的 Secret 而已。

​另外,為了友善使用,Kubernetes 已經為你提供了一個預設“服務賬戶”(default Service Account)。并且,任何一個運作在 Kubernetes 裡的 Pod,都可以直接使用這個預設的 Service Account,而無需顯示地聲明挂載它。

這是靠Projected Volume 機制做到的。

​如果你檢視一下任意一個運作在 Kubernetes 叢集裡的 Pod,就會發現,每一個 Pod,都已經自動聲明一個類型是 Secret、名為 default-token-xxxx 的 Volume,然後 自動挂載在每個容器的一個固定目錄上。比如:

$ kubectl describe pod nginx-deployment-5c678cfb6d-lg9lw
Containers:
...
  Mounts:
    /var/run/secrets/kubernetes.io/serviceaccount from default-token-s8rbq (ro)
Volumes:
  default-token-s8rbq:
  Type:       Secret (a volume populated by a Secret)
  SecretName:  default-token-s8rbq
  Optional:    false      

這個 Secret 類型的 Volume,正是預設 Service Account 對應的 ServiceAccountToken。是以說,Kubernetes 其實在每個 Pod 建立的時候,自動在它的 spec.volumes 部分添加上了預設 ServiceAccountToken 的定義,然後自動給每個容器加上了對應的 volumeMounts 字段。這個過程對于使用者來說是完全透明的。

​這樣,一旦 Pod 建立完成,容器裡的應用就可以直接從這個預設 ServiceAccountToken 的挂載目錄裡通路到授權資訊和檔案。這個容器内的路徑在 Kubernetes 裡是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount ,而這個 Secret 類型的 Volume 裡面的内容如下所示:

$ ls /var/run/secrets/kubernetes.io/serviceaccount 
ca.crt namespace  token      

​是以,你的應用程式隻要直接加載這些授權檔案,就可以通路并操作 Kubernetes API 了。而且,如果你使用的是 Kubernetes 官方的 Client 包(​​​k8s.io/client-go)的話,它還可以自動加載這個目錄下的檔案,你不需要做任何配置或者編碼操作。​​

這種把 Kubernetes 用戶端以容器的方式運作在叢集裡,然後使用 default Service Account 自動授權的方式,被稱作“InClusterConfig”,也是最推薦的進行 Kubernetes API 程式設計的授權方式。

當然,考慮到自動挂載預設 ServiceAccountToken 的潛在風險,Kubernetes 允許你設定預設不為 Pod 裡的容器自動挂載這個 Volume。

​除了這個預設的 Service Account 外,我們很多時候還需要建立一些我們自己定義的 Service Account,來對應不同的權限設定。這樣,我們的 Pod 裡的容器就可以通過挂載這些 Service Account 對應的 ServiceAccountToken,來使用這些自定義的授權資訊。

三、容器健康檢查和恢複機制

3.1、livenessProbe字段

在 Kubernetes 中,可以為 Pod 裡的容器定義一個健康檢查“探針”(Probe)。這樣,kubelet 就會根據這個 Probe 的傳回值決定這個容器的狀态,而不是直接以容器鏡像是否運作(來自 Docker 傳回的資訊)作為依據。這種機制,是生産環境中保證應用健康存活的重要手段。

例子:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: test-liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5      

在這個 Pod 中,它在啟動之後做的第一件事,就是在 /tmp 目錄下建立了一個 healthy 檔案,以此作為自己已經正常運作的标志。而 30 s 過後,它會把這個檔案删除掉。

與此同時,我們定義了一個這樣的 livenessProbe(健康檢查)。它的類型是 exec,這意味着,它會在容器啟動後,在容器裡面執行一條我們指定的指令,比如:“cat /tmp/healthy”。這時,如果這個檔案存在,這條指令的傳回值就是 0,Pod 就會認為這個容器不僅已經啟動,而且是健康的。這個健康檢查,在容器啟動 5 s 後開始執行(initialDelaySeconds: 5),每 5 s 執行一次(periodSeconds: 5)。

下面來具體實踐一下這個過程:

首先,建立這個Pod:

$ kubectl create -f test-liveness-exec.yaml      

然後,檢視這個Pod的狀态:

$ kubectl get pod
NAME                READY     STATUS    RESTARTS   AGE
test-liveness-exec   1/1       Running   0          10s      

可以看到,由于已經通過了健康檢查,這個 Pod 就進入了 Running 狀态。

​而 30 s 之後,我們再檢視一下 Pod 的 Events:

$ kubectl describe pod test-liveness-exec      

你會發現,這個 Pod 在 Events 報告了一個異常:

FirstSeen LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
2s        2s      1   {kubelet worker0}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory      

顯然,這個健康檢查探查到 /tmp/healthy 已經不存在了,是以它報告容器是不健康的。那麼接下來會發生什麼呢?

再次檢視這個Pod狀态:

$ kubectl get pod test-liveness-exec
NAME           READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m      

此時,Pod并沒有進入Failed狀态,而是保持了Running狀态。這是為什麼呢?

​其實,如果你注意到 RESTARTS 字段從 0 到 1 的變化,就明白原因了:這個異常的容器已經被 Kubernetes 重新開機了。在這個過程中,Pod 保持 Running 狀态不變。

​需要注意的是:Kubernetes 中并沒有 Docker 的 Stop 語義。是以雖然是 Restart(重新開機),但實際卻是重新建立了容器。

​這個功能就是 Kubernetes 裡的 Pod 恢複機制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一個标準字段(pod.spec.restartPolicy),預設值是 Always,即:任何時候這個容器發生了異常,它一定會被重新建立。

​但一定要強調的是,Pod 的恢複過程,永遠都是發生在目前節點上,而不會跑到别的節點上去。事實上,一旦一個 Pod 與一個節點(Node)綁定,除非這個綁定發生了變化(pod.spec.node 字段被修改),否則它永遠都不會離開這個節點。這也就意味着,如果這個主控端當機了,這個 Pod 也不會主動遷移到其他節點上去。

而如果你想讓 Pod 出現在其他的可用節點上,就必須使用 Deployment 這樣的“控制器”來管理 Pod,哪怕你隻需要一個 Pod 副本。這也是一個單Pod的Deployment與一個Pod最主要的差別。

​而作為使用者,你還可以通過設定 restartPolicy,改變 Pod 的恢複政策。除了 Always,它還有 OnFailure 和 Never 兩種情況:

  • ​Always​

    ​:在任何情況下,隻要容器不在運作狀态,就自動重新開機容器;
  • ​OnFailure​

    ​: 隻在容器 異常時才自動重新開機容器;
  • ​Never​

    ​: 從來不重新開機容器。

​在實際使用時,我們需要根據應用運作的特性,合理設定這三種恢複政策。

​比如,一個 Pod,它隻計算 1+1=2,計算完成輸出結果後退出,變成 Succeeded 狀态。這時,你如果再用 restartPolicy=Always 強制重新開機這個 Pod 的容器,就沒有任何意義了。

​而如果你要關心這個容器退出後的上下文環境,比如容器退出後的日志、檔案和目錄,就需要将 restartPolicy 設定為 Never。因為一旦容器被自動重新建立,這些内容就有可能丢失掉了(被垃圾回收了)。

​Kubernetes 的官方文檔,把 restartPolicy 和 Pod 裡容器的狀态,以及 Pod 狀态的對應關系,​​總結了非常複雜的一大堆情況​​。無需死記硬背,隻需記住下面兩個基本的設計原理即可:

  1. 隻要 Pod 的 restartPolicy 指定的政策允許重新開機異常的容器(比如:Always),那麼這個 Pod 就會保持 Running 狀态,并進行容器重新開機。否則,Pod 就會進入 Failed 狀态 。
  2. 對于包含多個容器的 Pod,隻有它裡面所有的容器都進入異常狀态後,Pod 才會進入 Failed 狀态。在此之前,Pod都是Running狀态。此時,Pod 的 READY 字段會顯示正常容器的個數。

如:

$ kubectl get pod test-liveness-exec
NAME           READY     STATUS    RESTARTS   AGE
liveness-exec   0/1       Running   1          1m      

​是以,假如一個 Pod 裡隻有一個容器,然後這個容器異常退出了。那麼,隻有當 restartPolicy=Never 時,這個 Pod 才會進入 Failed 狀态。而其他情況下,由于 Kubernetes 都可以重新開機這個容器,是以 Pod 的狀态保持 Running 不變。

​而如果這個 Pod 有多個容器,僅有一個容器異常退出,它就始終保持 Running 狀态,哪怕即使 restartPolicy=Never。隻有當所有容器也異常退出之後,這個 Pod 才會進入 Failed 狀态。

​其他情況,都可以以此類推出來。

回看livenessProbe,除了在容器中執行指令外,livenessProbe 也可以定義為發起 HTTP 或者 TCP 請求的方式,定義格式如下:

...
livenessProbe:
     httpGet:
       path: /healthz
       port: 8080
       httpHeaders:
       - name: X-Custom-Header
         value: Awesome
       initialDelaySeconds: 3
       periodSeconds: 3      
...
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20      

​是以,你的 Pod 其實可以暴露一個健康檢查 URL(比如 /healthz),或者直接讓健康檢查去檢測應用的監聽端口。這兩種配置方法,在 Web 服務類的應用中非常常用。

3.2、readinessProbe字段

​在 Kubernetes 的 Pod 中,還有一個叫 readinessProbe 的字段。雖然它的用法與 livenessProbe 類似,但作用卻大不一樣。readinessProbe 檢查結果的成功與否,決定的這個 Pod 是不是能被通過 Service 的方式通路到,而并不影響 Pod 的生命周期。

疑問:Pod 的字段這麼多,又不可能全記住,Kubernetes 能不能自動給 Pod 填充某些字段呢?

​這個需求實際上非常實用。比如,開發人員隻需要送出一個基本的、非常簡單的 Pod YAML,Kubernetes 就可以自動給對應的 Pod 對象加上其他必要的資訊,比如 labels,annotations,volumes 等等。而這些資訊,可以是運維人員事先定義好的。

​這麼一來,開發人員編寫 Pod YAML 的門檻,就被大大降低了。

​是以,這個叫作 PodPreset(Pod 預設定)的功能 已經出現在了 v1.11 版本的 Kubernetes 中。

例子:開發人員編寫了如下一個pod.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: nginx
      ports:
        - containerPort: 80      

​作為 Kubernetes 的初學者,你肯定眼前一亮:這不就是我最擅長編寫的、最簡單的 Pod 嘛。沒錯,這個 YAML 檔案裡的字段,想必你現在閉着眼睛也能寫出來。

可是,如果運維人員看到了這個 Pod,說這種 Pod 在生産環境裡根本不能用啊!

​是以,這個時候,運維人員就可以定義一個 PodPreset 對象。在這個對象中,凡是他想在開發人員編寫的 Pod 裡追加的字段,都可以預先定義好。比如這個 preset.yaml:

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: allow-database
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}      

​在這個 PodPreset 的定義中,首先是一個 selector。這就意味着後面這些追加的定義,隻會作用于 selector 所定義的、帶有“role: frontend”标簽的 Pod 對象,這就可以防止“誤傷”。

然後,定義了一組 Pod 的 Spec 裡的标準字段,以及對應的值。比如,env 裡定義了 DB_PORT 這個環境變量,volumeMounts 定義了容器 Volume 的挂載目錄,volumes 定義了一個 emptyDir 的 Volume。

​接下來,我們假定運維人員先建立了這個 PodPreset,然後開發人員才建立 Pod:

$ kubectl create -f preset.yaml
$ kubectl create -f pod.yaml      

​這時,Pod 運作起來之後,我們檢視一下這個 Pod 的 API 對象:

$ kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
    - name: website
      image: nginx
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
  volumes:
    - name: cache-volume
      emptyDir: {}      

​這個時候,我們就可以清楚地看到,這個 Pod 裡多了新添加的 labels、env、volumes 和 volumeMount 的定義,它們的配置跟 PodPreset 的内容一樣。此外,這個 Pod 還被自動加上了一個 annotation 表示這個 Pod 對象被 PodPreset 改動過。

​需要說明的是,PodPreset 裡定義的内容,隻會在 Pod API 對象被建立之前追加在這個對象本身上,而不會影響任何 Pod 的控制器的定義。

​比如,我們現在送出的是一個 nginx-deployment,那麼這個 Deployment 對象本身是永遠不會被 PodPreset 改變的,被修改的隻是這個 Deployment 建立出來的所有 Pod。這一點請務必區厘清楚。

如果你定義了同時作用于一個 Pod 對象的多個 PodPreset,會發生什麼呢?

​實際上,Kubernetes 項目會幫你合并(Merge)這兩個 PodPreset 要做的修改。而如果它們要做的修改有沖突的話,這些沖突字段就不會被修改。

繼續閱讀