做爲 Kubernetes 項目裏最核心的編排對象,Pod 攜帶的信息很是豐富。其中,資源定義(好比 CPU、內存等),以及調度相關的字段、在本篇,咱們就先從一種特殊的 Volume 開始,來幫助你更加深刻地理解 Pod 對象各個重要字段的含義。node
這種特殊的 Volume,叫做 Projected Volume,你能夠把它翻譯爲「投射數據卷」。 (備註:Projected Volume 是 Kubernetes v1.11 以後的新特性)mysql
這是什麼意思呢?nginx
在 Kubernetes 中,有幾種特殊的 Volume,它們存在的意義不是爲了存放容器裏的數據,也不是用來進行容器和宿主機之間的數據交換。這些特殊 Volume 的做用,是爲容器提供預先定義好的數據。因此,從容器的角度來看,這些 Volume 裏的信息就是彷彿是被 Kubernetes「投射」(Project)進入容器當中的。這正是 Projected Volume 的含義。web
到目前爲止,Kubernetes 支持的 Projected Volume 一共有四種:sql
我首先和你分享的是 Secret。它的做用,是幫你把 Pod 想要訪問的加密數據,存放到 Etcd 中。而後,你就能夠經過在 Pod 的容器裏掛載 Volume 的方式,訪問到這些 Secret 裏保存的信息了。 Secret 最典型的使用場景,莫過於存放數據庫的 Credential 信息,好比下面這個例子:數據庫
test-projected-volume.yaml編程
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 對象,分別對應的是數據庫的用戶名和密碼。api
這裏用到的數據庫的用戶名、密碼,正是以 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 命令就能夠了: app
kubectl get secrets NAME TYPE DATA AGE user Opaque 1 51s pass Opaque 1 51s
刪除secret
kubectl delete secret user
固然,除了使用 kubectl create secret 指令外,我也能夠直接經過編寫 YAML 文件的
my-secret.yaml
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 的加密插件,加強數據的安全性。關於開啓 Secret 加密插件的內容,我會在後續專門講解 Secret 的時候,再作進一步說明 接下來,咱們嘗試一下建立這個 Pod:
my_pod.yaml
kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: busybox imagePullPolicy: IfNotPresent stdin: true tty: true volumeMounts: - name: my-pod-vol mountPath: "/mypod-vol" readOnly: true volumes: - name: my-pod-vol projected: sources: - secret: name: mysecret
執行一下命令
kubectl apply -f my-secret.yaml kubectl apply -f my_pod.yaml kubectl exec -it my-pod /bin/sh cat /mypod-vol/user cat /mypod-vol/pass
從返回結果中,咱們能夠看到,保存在 Etcd 裏的用戶名和密碼信息,已經以文件的形式出如今了容器的 Volume 目錄裏。而這個文件的名字,就是 kubectl create secret 指定的 Key,或者說是 Secret 對象的 data 字段指定的 Key。
更重要的是,像這樣經過掛載方式進入到容器裏的 Secret,一旦其對應的 Etcd 裏的數據被更新,這些 Volume 裏的文件內容,一樣也會被更新。其實,這是 kubelet 組件在定時維護這些 Volume。
須要注意的是,這個更新可能會有必定的延時。因此在編寫應用程序時,在發起數據庫鏈接的代碼處寫好重試和超時的邏輯,絕對是個好習慣。 與 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 的方式展現出來。
接下來是 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: 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"
目前,DownwardAPI支持的字段已經很是豐富了,好比:
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 文件的方式獲取這些信息。
在明白了 Secret 以後,我再爲你講解 Pod 中一個與它密切相關的概念:Service Account。 相信你必定有過這樣的想法:我如今有了一個 Pod,我能不能在這個 Pod 裏安裝一個 Kubernetes 的 Client,這樣就能夠從容器裏直接訪問而且操做這個 Kubernetes 的 API 了呢?
這固然是能夠的。不過,你首先要解決 API Server 的受權問題。 Service Account 對象的做用,就是 Kubernetes 系統內置的一種「服務帳戶」,它是 Kubernetes 進行權限分配的對象。好比,Service Account A,能夠只被容許對 Kubernetes API 進行 GET 操做,而 Service Account B,則能夠有KubernetesAPI的全部操做的權限。像這樣的ServiceAccount的受權信息和文件,實際上保存在它所綁定的一個特殊的Secret對象裏的。
這個特殊的Secret對象,就叫做ServiceAccountToken。任何運行在Kubernetes集羣上的應用,都必須使用這個ServiceAccountToken裏保存的受權信息,也就是Token,才能夠合法地訪問APIServer。因此說,Kubernetes項目的ProjectedVolume其實只有三種,由於第四種ServiceAccountToken,只是一種特殊的Secret而已。另外,爲了方便使用,Kubernetes已經爲你提供了一個的默認「服務帳戶」(defaultServiceAccount)。而且,任何一個運行在Kubernetes裏的Pod,均可以直接使用這個默認的ServiceAccount,而無需顯示地聲明掛載它。
這是如何作到的呢?固然仍是靠ProjectedVolume機制。若是你查看一下任意一個運行在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對應的Service AccountToken。因此說,Kubernetes其實在每一個Pod建立的時候,自動在它的spec.volumes部分添加上了默認Service AccountToken的定義,而後自動給每一個容器加上了對應的volumeMounts字段。這個過程對於用戶來講是徹底透明的。這樣,一旦Pod建立完成,容器裏的應用就能夠直接從這個默認ServiceAccountToken的掛載目錄裏訪問到受權信息和文件。這個容器內的路徑在Kubernetes裏是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount,而這個Secret類型的Volume裏面的內容以下所示:
$ ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
因此,你的應用程序只要直接加載這些受權文件,就能夠訪問並操做KubernetesAPI了。並且,若是你使用的是Kubernetes官方的Client包(k8s.io/client-go)的話,它還能夠自動加載這個目錄下的文件,你不須要作任何配置或者編碼操做。這種把Kubernetes客戶端以容器的方式運行在集羣裏,而後使用defaultServiceAccount自動受權的方式,被稱做「InClusterConfig」,也是我最推薦的進行KubernetesAPI編程的受權方式。
固然,考慮到自動掛載默認ServiceAccountToken的潛在風險,Kubernetes容許你設置默認不爲Pod裏的容器自動掛載這個Volume。除了這個默認的ServiceAccount外,咱們不少時候還須要建立一些咱們本身定義的ServiceAccount,來對應不一樣的權限設置。這樣,咱們的Pod裏的容器就能夠經過掛載這些Service Account對應的Service AccountToken,來使用這些自定義的受權信息。在後面講解爲Kubernetes開發插件的時候,咱們將會實踐到這個操做。
接下來,咱們再來看Pod的另外一個重要的配置:容器健康檢查和恢復機制。在Kubernetes中,你能夠爲Pod裏的容器定義一個健康檢查「探針」(Probe)。這樣,kubelet就會根據這個Probe的返回值決定這個容器的狀態,而不是直接以容器進行是否運行(來自Docker返回的信息)做爲依據。這種機制,是生產環境中保證應用健康存活的重要手段。咱們一塊兒來看一個Kubernetes文檔中的例子。
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 兩種狀況: 在實際使用時,咱們須要根據應用運行的特性,合理設置這三種恢復策略。 好比,一個 Pod,它只計算 1+1=2,計算完成輸出結果後退出,變成 Succeeded 狀態。這時,你 若是再用 restartPolicy=Always 強制重啓這個 Pod 的容器,就沒有任何意義了。
而若是你要關心這個容器退出後的上下文環境,好比容器退出後的日誌、文件和目錄,就須要將 restartPolicy 設置爲 Never。由於一旦容器被自動從新建立,這些內容就有可能丟失掉了(被垃圾 回收了)。 值得一提的是,Kubernetes 的官方文檔,把 restartPolicy 和 Pod 裏容器的狀態,以及 Pod 狀態 的對應關係,
實際上,你根本不須要死記硬背這些對應關係,只要 記住以下兩個基本的設計原理便可:
$ 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 服務類的應用中很是經常使用。
在 Kubernetes 的 Pod 中,還有一個叫 readinessProbe 的字段。雖然它的用法與 livenessProbe 相似,但做用卻大不同。readinessProbe 檢查結果的成功與否,決定的這個 Pod 是否是能被通 過 Service 的方式訪問到,而並不影響 Pod 的生命週期。這部份內容,我會留在講解 Service 時再 重點介紹。 在講解了這麼多字段以後,想必你對 Pod 對象的語義和描述能力,已經有了一個初步的感受。
這時,你有沒有產生這樣一個想法:Pod 的字段這麼多,我又不可能全記住,Kubernetes 能不能 自動給 Pod 填充某些字段呢? 這個需求實際上很是實用。好比,開發人員只須要提交一個基本的、很是簡單的 Pod YAML, Kubernetes 就能夠自動給對應的 Pod 對象加上其餘必要的信息,好比 labels,annotations, volumes 等等。而這些信息,能夠是運維人員事先定義好的。
這麼一來,開發人員編寫 Pod YAML 的門檻,就被大大下降了。 因此,這個叫做 PodPreset(Pod 預設置)的功能 已經出如今了 v1.11 版本的 Kubernetes 中。 舉個例子,如今開發人員編寫了以下一個 pod.yaml 文件:
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 要作的修改。而若是它們要作 的修改有衝突的話,這些衝突字段就不會被修改。
在今天這篇文章中,我和你詳細介紹了 Pod 對象更高階的使用方法,但願經過對這些實例的講解, 你能夠更深刻地理解 Pod API 對象的各個字段。 而在學習這些字段的同時,你還應該認真體會一下 Kubernetes「一切皆對象」的設計思想:好比應 用是 Pod 對象,應用的配置是 ConfigMap 對象,應用要訪問的密碼則是 Secret 對象。 因此,也就天然而然地有了 PodPreset 這樣專門用來對 Pod 進行批量化、自動化修改的工具對 象。在後面的內容中,我會爲你講解更多的這種對象,還會和你介紹 Kubernetes 項目如何圍繞着 這些對象進行容器編排。 在本專欄中,Pod 對象相關的知識點很是重要,它是接下來 Kubernetes 可以描述和編排各類複雜 應用的基石所在,但願你可以繼續多實踐、多體會。