本節課程要點
ConfigMaps 和 Secret 資源的建立和使用;Pod 身份認證的實現和原理;容器資源、安全、前置校驗等配置和使用。細分為以下八個方面:
需求來源#背景問題#首先一起來看一下需求來源。大家應該都有過這樣的經驗,就是用一個容器映象來啟動一個 container。要啟動這個容器,其實有很多需要配套的問題待解決:
第一,比如說一些可變的配置。因為我們不可能把一些可變的配置寫到映象裡面,當這個配置需要變化的時候,可能需要我們重新編譯一次映象,這個肯定是不能接受的;第二就是一些敏感資訊的儲存和使用。比如說應用需要使用一些密碼,或者用一些 token;第三就是我們容器要訪問叢集自身。比如我要訪問 kube-apiserver,那麼本身就有一個身份認證的問題;第四就是容器在節點上執行之後,它的資源需求;第五個就是容器在節點上,它們是共享核心的,那麼它的一個安全管控怎麼辦?最後一點我們說一下容器啟動之前的一個前置條件檢驗。比如說,一個容器啟動之前,我可能要確認一下 DNS 服務是不是好用?又或者確認一下網路是不是聯通的?那麼這些其實就是一些前置的校驗。Pod 的配置管理#在 Kubernetes 裡面,它是怎麼做這些配置管理的呢?如下圖所示:
可變配置就用 ConfigMap;敏感資訊是用 Secret;身份認證是用 ServiceAccount 這幾個獨立的資源來實現的;資源配置是用 Resources;安全管控是用 SecurityContext;前置校驗是用 InitContainers 這幾個在 spec 裡面加的欄位,來實現的這些配置管理。ConfigMap#ConfigMap 介紹#下面我們來介紹第一個部分,就是 ConfigMap。我們先來介紹 ConfigMap 它是用來做什麼的、以及它帶來的一個好處。它其實主要是管理一些可變配置資訊,比如說我們應用的一些配置檔案,或者說它裡面的一些環境變數,或者一些命令列引數。
它的好處在於它可以讓一些可變配置和容器映象進行解耦,這樣也保證了容器的可移植性。看一下下圖中右邊的編排檔案截圖。
主要管理容器執行時所需的配置檔案,環境變數,命令列引數等可變配置。用於解耦容器映象和可變配置,從而保障工作負載(Pod)的可移植性。
CopyapiVersion: v1kind: ConfigMapmetadata: labels: app: flannel tier: node name: kube-flannel-cfg namespace: kube-systemdata: cni-conf.json: | { "name": "cbr0", "type": "flannel", "delegate":{ "isDefaultGateway": true } } net-conf.json: | { "Network": "172.27.0.0/16". "Backend": { "Type": "vxlan" } }這是 ConfigMap 本身的一個定義,它包括兩個部分:一個是 ConfigMap 元資訊,我們關注 name 和 namespace 這兩個資訊。接下來這個 data 裡面,可以看到它管理了兩個配置檔案。它的結構其實是這樣的:從名字看ConfigMap中包含Map單詞,Map 其實就是 key:value,key 是一個檔名,value 是這個檔案的內容。
ConfigMap 建立#看過介紹之後,再具體看一下它是怎麼建立的。我們推薦用 kubectl 這個命令來建立,它帶的引數主要有兩個:一個是指定 name,第二個是 DATA。其中 DATA 可以通過指定檔案或者指定目錄,以及直接指定鍵值對,下面可以看一下這個例子。
建立命令:kubectl create configmap [NAME] [DATA]
其中DATA:
指定檔案或者目錄指定檔案建立示例:Copykubectl create configmap kube-flannel-cfg --from-file=configure-pod-container/configmap/cni-conf.json -n kube-systemCopykubectl get configmap kube-flannel-cfg -o yamlapiVersion:v1kind: ConfigMapmetadata: labels: app: flannel tier: node name: kube-flannel-cfg namespace: kube-systemdata: cni-conf.json: | # 檔名字作為key { # 檔案內容作為value "name": "cbr0", "type": "flannel", "delegate": { "isDefaultGateway": true } }指定鍵值對建立Copykubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charmCopykubectl get configmap special-config -o yamlapiVersion: v1kind: ConfigMapmetadata: name: special-config namespace: defaultdata: # 輸入的鍵值對 special.how: very special.type: charm指定檔案的話,檔名就是 Map 中的 key,檔案內容就是 Map 中的 value。然後指定鍵值對就是指定資料鍵值對,即:key:value 形式,直接對映到 Map 的key:value。
ConfigMap 使用#建立完了之後,應該怎麼使用呢?
ConfigMap主要被Pod使用,一般用於掛載Pod用的配置檔案,環境變數 ,命令列引數等。
示例1:(用ConfigMap配置環境變數)
CopyapiVersion:kind: Podmetadata: namp: cm-env-testspec: containers: - name: test-container iamge: k8s.gcr.io/busybox command: ["/bin/sh","-c","env"] env: # 用apecial-config中的specila.how定義環境變數 - name: SPECILA_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how restartPolicy: Never示例2:(用configMap配置管控命令列引數)
CopyapiVersion: v1kind: Podmetadata: name: cm-cmd-testspec: containers: - name: test-container iamge: k8s.gcr.io/busybox command: ["/bin/sh","-c","echo ${SPECILA_LEVEL_KEY}"] env: - name: SPECILA_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: SPECIAL_LEVEL restartPolicy: Never示例3:用ConfigMap掛載配置檔案
CopyapiVersion: v1kind: Podmetadata: name: cm-value-testspec: containers: - name: test-container image: k8s.gcr.io/busybox command: ["/bin/sh","-c","ls /etc/config"] valumeMonuts: - name: config-value mountPath: /etc/config valumes: - name: config-valume configMap: name: special-config restartPolicy: Never如上圖所示,主要是在 pod 裡來使用 ConfigMap:
第一種是環境變數。環境變數的話通過 valueFrom,然後 ConfigMapKeyRef 這個欄位,下面的 name 是指定 ConfigMap 名,key 是 ConfigMap.data 裡面的 key。這樣的話,在 busybox 容器啟動後容器中執行 env 將看到一個 SPECIALLEVELKEY 環境變數;第二個是命令列引數。命令列引數其實是第一行的環境變數直接拿到 cmd 這個欄位裡面來用;最後一個是通過 volume 掛載的方式直接掛到容器的某一個目錄下面去。上面的例子是把 special-config 這個 ConfigMap 裡面的內容掛到容器裡面的 /etc/config 目錄下,這個也是使用的一種方式。ConfigMap 注意要點#現在對 ConfigMap 的使用做一個總結,以及它的一些注意點,注意點一共列了以下五條:
ConfigMap 檔案的大小。雖然說 ConfigMap 檔案沒有大小限制,但是在 ETCD 裡面,資料的寫入是有大小限制的,現在是限制在 1MB 以內;第二個注意點是 pod 引入 ConfigMap 的時候,必須是相同的 Namespace 中的 ConfigMap,前面其實可以看到,ConfigMap.metadata 裡面是有 namespace 欄位的;第三個是 pod 引用的 ConfigMap。假如這個 ConfigMap 不存在,那麼這個 pod 是無法建立成功的,其實這也表示在建立 pod 前,必須先把要引用的 ConfigMap 建立好;第四點就是使用 envFrom 的方式。把 ConfigMap 裡面所有的資訊匯入成環境變數時,如果 ConfigMap 裡有些 key 是無效的,比如 key 的名字裡面帶有數字,那麼這個環境變數其實是不會注入容器的,它會被忽略。但是這個 pod 本身是可以建立的。這個和第三點是不一樣的方式,是 ConfigMap 檔案存在基礎上,整體匯入成環境變數的一種形式;最後一點是:什麼樣的 pod 才能使用 ConfigMap?這裡只有通過 K8s api 建立的 pod 才能使用 ConfigMap,比如說通過用命令列 kubectl 來建立的 pod,肯定是可以使用 ConfigMap 的,但其他方式建立的 pod,比如說 kubelet 通過 manifest 建立的 static pod,它是不能使用 ConfigMap 的。Secret#Secret 介紹#現在我們講一下 Secret,Secret 是一個主要用來儲存密碼 token 等一些敏感資訊的資源物件。其中,敏感資訊是採用 base-64 編碼儲存起來的,我們來看下圖中 Secret 資料的定義。
CopyapiVersion: v1kind: Secret # Secret元資料metadata: name: mysecret namespace: kube-systemtype: Opaquedata: username: YYUYYYLKSD password: sjdfksdklflkll=元資料的話,裡面主要是 name、namespace 兩個欄位;接下來是 type,它是非常重要的一個欄位,是指 Secret 的一個型別。Secret 型別種類比較多,下面列了常用的四種類型:
第一種是 Opaque,它是普通的 Secret 檔案;第二種是 service-account-token,是用於 service-account 身份認證用的 Secret;第三種是 dockerconfigjson,這是拉取私有倉庫映象的用的一種 Secret;第四種是 bootstrap.token,是用於節點接入叢集校驗用的 Secret。再接下來是 data,是儲存的 Secret 的資料,它也是 key-value 的形式儲存的。
Secret 建立#接下來我們看一下 Secret 的建立。
有兩種建立方式:
系統建立:比如 K8s 為每一個 namespace 的預設使用者(default ServiceAccount)建立 Secret;使用者手動建立:手動建立命令,推薦 kubectl 這個命令列工具,它相對 ConfigMap 會多一個 type 引數。其中 data 也是一樣,它也是可以指定檔案和鍵值對的。type 的話,要是你不指定的話,預設是 Opaque 型別。Secret建立可以是使用者自己建立,也有Secret是系統自動建立
比如:K8S 為每個namespace的預設使用者(default ServiceAccount)建立的Secret手動建立命令:kubectl create secrt generic [NAME] [DATA] [TYPE]
其中DATA: 可以指定檔案/鍵值對
另外TYPE: 預設為Opaque
指定檔案建立:
Copykubectl create secret generic myregistrykey --from-file=.dockerconfigjson=/root/.docker/config.json –type=kubernetes.io/dockerconfigjsonCopyapiVersion: V1kind: Secretmetadata: name: myregistrykey namespace: defaultdata: .dockerconfigjson: jflsdkjfklsdjflksdjflksjdfkjsdklfjalkqqqqqqqqqqqqqqqqqqqqqqqkljdflksjdfdsftype: kubernetes.io/dockerconfigjson指定鍵值對:
Copykubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11CopyapiVersion: v1data: password: tttttslejkljlk username: ChjsldfiwenkHHkind: Secretmeatdata: name: prod-db-secret namespace: defaulttype: Opaque上圖中兩個例子。第一個是通過指定檔案,建立了一個拉取私有倉庫映象的 Secret,指定的檔案是 /root/.docker/config.json。type 的話指定的是 dockerconfigjson,另外一個我們指定鍵值對,我們 type 沒有指定,預設是 Opaque。鍵值對是 key:value 的形式,其中對 value 內容進行 base64 加密。建立 Secret 就是這麼一個情況。
Secret 使用#建立完 Secret 之後,再來看一下如何使用它。它主要是被 pod 來使用,一般是通過 volume 形式掛載到容器裡指定的目錄,然後容器裡的業務程序再到目錄下讀取 Secret 來進行使用。另外在需要訪問私有映象倉庫時,也是通過引用 Secret 來實現。
示例1:(Sectet掛載在使用者指定目錄下)
CopyapiVersion: v1kind: Podmetadata: name: mypodspec: containers: - name: mypod image: redis volumeMounts: # secret以檔案形式掛載在/etc/foo下 - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: # 指定要掛載的secret secrteName: mysecret示例2:(SeviceAccount用的secret自動掛載在固定目錄下(生成ca.crt和token兩個檔案))
CopyapiVersion: v1kind: Podmetadata: name: nginx-sjdlrkj325-mdfp5 namespace: defaultspec: containers: - iamge: nginx: 1-alpine name: nginx - volumeMounts: # 固定掛載到容器中的/var/run/secret/kubernetes.io/serviceaccount目錄下 /var/run/secret/kubernetes.io/serviceaccount name: default-token-666i8 readonly: true serviceAccount: default serviceAccountName: default # 使用namespace預設的serviceaccount volumes: - name: default-token-6lsdkjf secret: defaultMode: 420 # 掛載namespace預設的secret secretName: default-token-6fjsi我們先來看一下掛載到使用者指定目錄的方式:
第一種方式:如上圖左側所示,使用者直接指定,把 mysecret 掛載到容器 /etc/foo 目錄下面;第二種方式:如上圖右側所示,系統自動生成,把 serviceaccount-secret 自動掛載到容器 /var/run/secrets/kubernetes.io/serviceaccount 目錄下,它會生成兩個檔案,一個是 ca.crt,一個是 token。這是兩個儲存了認證資訊的證書檔案。使用私有映象庫#下面看一下用 Secret 來使用私有映象倉庫。首先,私有映象倉庫的資訊是儲存在 Secret 裡面的(具體參照上述的Secret建立章節),然後拉取私有倉庫映象,那麼通過下圖中兩種方法的配置就可以:
第一種方式:如下圖左側所示,直接在 pod 裡面,通過 imagePullSecrets 欄位來配置;第二種方式是自動注入。使用者提前在 pod 會使用的 serviceaccount 裡配置 imagePullSecrets,Pod建立時系統自動注入這個 imagePullSecrets。指定imagePullSecret
CopyapiVersion: v1kind: Podmetadata: name: private-regspec: containers: - name: private-reg-container image: <你的映象> imagePullSecret: - name: regcredSecret 使用注意要點#
最後來看一下 Secret 使用的一些注意點,下面列了三點:
第一個是 Secret 的檔案大小限制。這個跟 ConfigMap 一樣,也是 1MB;第二個是 Secret 採用了 base-64 編碼,但是它跟明文也沒有太大區別。所以說,如果有一些機密資訊要用 Secret 來儲存的話,還是要很慎重考慮。也就是說誰會來訪問你這個叢集,誰會來用你這個 Secret,還是要慎重考慮,因為它如果能夠訪問這個叢集,就能拿到這個 Secret。如果是對 Secret 敏感資訊要求很高,對加密這塊有很強的需求,推薦可以使用 Kubernetes 和開源的 vault做一個解決方案,來解決敏感資訊的加密和許可權管理。
第三個就是 Secret 讀取的最佳實踐,建議不要用 list/watch,如果用 list/watch 操作的話,會把 namespace 下的所有 Secret 全部拉取下來,這樣其實暴露了更多的資訊。推薦使用 GET 的方法,這樣只獲取你自己需要的那個 Secret。ServiceAccount#ServiceAccount 介紹#接下來,我們講一下 ServiceAccount。ServiceAccount 首先是用於解決 pod 在叢集裡面的身份認證問題,身份認證資訊是存在於 Secret 裡面。
先看一下上面的左側截圖,可以看到最下面的紅框裡,有一個 Secret 欄位,它指定 ServiceAccount 用哪一個 Secret,這個是 K8s 自動為 ServiceAccount 加上的。然後再來看一下上圖中的右側截圖,它對應的 Secret 的 data 裡有兩塊資料,一個是 ca.crt,一個是 token。ca.crt 用於對服務端的校驗,token 用於 Pod 的身份認證,它們都是用 base64 編碼過的。然後可以看到 metadata 即元資訊裡,其實是有關聯 ServiceAccount 資訊的(這個 secret 被哪個 ServiceAccount 使用)。最後我們注意一下 type,這個就是 service-account-token 這種型別。
舉例:Pod 裡的應用訪問它所屬的 K8s 叢集#介紹完 ServiceAccount 以及它對應的 secret 後,我們來看一下,pod 是怎麼利用 ServiceAccount 或者說它是怎麼利用 secret 來訪問所屬 K8s 叢集的。
其實 pod 建立的時候,首先它會把這個 secret 掛載到容器固定的目錄下,這是 K8s 功能上實現的。它要把這個 ca.crt 和 token 這兩個檔案掛載到固定目錄下面。
pod 要訪問叢集的時候,它是怎麼來利用這個檔案的呢?我們看一下下面的程式碼截圖:
我們在 Go 裡面實現 Pod 訪問 K8s 叢集時,一般直接會調一個 InClusterConfig 方法,來生成這個訪問服務 Client 的一些資訊。然後可以看一下,最後這個 Config 裡面有兩部分資訊:
一個是 tlsClientConfig,這個主要是用於 ca.crt 校驗服務端;第二個是 Bearer Token,這個就是 pod 的身份認證。在服務端,會利用 token 對 pod 進行一個身份認證。再次回到上圖左側。認證完之後 pod 的身份資訊會有兩部分:一個是 Group,一個是 User。身份認證是就是認證這兩部分資訊。接著可以使用 RBAC 功能,對 pod 進行一個授權管理。
假如 RBAC 沒有配置的話,預設的 pod 具有資源 GET 許可權,就是可以從所屬的 K8s 叢集裡 get 資料。如果是需要更多的許可權,那麼就需要 自行配置 RBAC 。RBAC 的相關知識,我們在後面的課程裡面會詳細介紹,大家可以關注一下。
Resource#容器資源配合管理#下面介紹一下 Resource,即:容器的一個資源配置管理。
目前內部支援型別有三種:CPU、記憶體,以及臨時儲存。當用戶覺得這三種不夠,有自己的一些資源,比如說 GPU,或者其他資源,也可以自己來定義,但配置時,指定的數量必須為整數。目前資源配置主要分成 request 和 limit 兩種型別,一個是需要的數量,一個是資源的界限。CPU、記憶體以及臨時儲存都是在 container 下的 Resource 欄位裡進行一個宣告。
支援資源型別:
CPU: 單位: millicore(1 Core=1000millicore)Memory:單位:Byteephemeral storage(臨時儲存):單位:Byte自定義資源:配置時必須為整數配置方法:
資源配置分為request/limit兩種型別
CPUspec.containers[].resources.limits.cpuspec.containers[].resources.request.cpuMemoryspec.containers[].resources.limits.memoryspec.containers[].resources.request.memoryephemeral storage(臨時儲存)spec.containers[].resources.limit.epherneral-storagespec.containers[].resources.request.epherneral-storageCopyapiVersion: v1kind: Podmetadata: name: frontendspec: containers: - name: wp iamge: wordpress resources: # 申明需要的資源 request: memory: "64Mi" cpu: "250m" ephemeral-storage: "2Gi" limits: memory: "128Mi" cpu: "500m" ephemeral-storage: "4Gi"
舉個例子,wordpress 容器的資源需求,一個是 request ,一個是 limits,它分別對需要的資源和資源臨界進行一個宣告。
Pod 服務品質 (QoS) 配置#根據 CPU 對容器記憶體資源的需求,我們對 pod 的服務品質進行一個分類,分別是 Guaranteed、Burstable 和 BestEffort。
Guaranteed :pod 裡面每個容器都必須有記憶體和 CPU 的 request 以及 limit 的一個宣告,且 request 和 limit 必須是一樣的,這就是 Guaranteed;Burstable:Burstable 至少有一個容器存在記憶體和 CPU 的一個 request;BestEffort:只要不是 Guaranteed 和 Burstable,那就是 BestEffort。那麼這個服務品質是什麼樣的呢?資源配置好後,當這個節點上 pod 容器執行,比如說節點上 memory 配額資源不足,kubelet會把一些低優先順序的,或者說服務品質要求不高的(如:BestEffort、Burstable)pod 驅逐掉。它們是按照先去除 BestEffort,再去除 Burstable 的一個順序來驅逐 pod 的。
SecurityContext#SecurityContext 介紹#SecurityContext 主要是用於限制容器的一個行為,它能保證系統和其他容器的安全。這一塊的能力不是 Kubernetes 或者容器 runtime 本身的能力,而是 Kubernetes 和 runtime 通過使用者的配置,最後下傳到核心裡,再通過核心的機制讓 SecurityContext 來生效。所以這裡講的內容,會比較簡單或者說比較抽象一點。
SecurityContext 主要分為三個級別:
第一個是容器級別,僅對容器生效;第二個是 pod 級別,對 pod 裡所有容器生效;第三個是叢集級別,就是 PSP,對叢集內所有 pod 生效。許可權和訪問控制設定項,現在一共列有七項(這個數量後續可能會變化):
第一個Discretionary Access Control就是通過使用者 ID 和組 ID 來控制檔案訪問許可權;第二個是 SELinux,它是通過策略配置來控制使用者或者程序對檔案的訪問控制;第三個privileged是特權容器;第四個是Linux Capabilities,它也是給特定程序來配置一個 privileged 能力;第五個是 AppArmor,它也是通過一些配置檔案來控制可執行檔案的一個訪問控制權限,比如說一些埠的讀寫;第六個Seccomp是一個對系統呼叫的控制;第七個AllowPrivilegeEscalation是對子程序能否獲取比父親更多的許可權的一個限制。最後其實都是落到核心來控制它的一些許可權。
上圖是對 pod 級別和容器級別配置 SecurityContext 的一個例子,如果大家對這些內容有更多的需求,可以根據這些資訊去搜索更深入的資料來學習。
InitContainer#InitContainer 介紹#接下來看一下 InitContainer,首先介紹 InitContainer 和普通 container 的區別,有以下三點內容:
InitContainer 首先會比普通 container 先啟動,並且直到所有的 InitContainer 執行成功後,普通 container 才會被啟動;InitContainer 之間是按定義的次序去啟動執行的,執行成功一個之後再執行第二個,而普通的 container 是併發啟動的;InitContainer 執行成功後就結束退出,而普通容器可能會一直在執行。它可能是一個 longtime 的,或者說失敗了會重啟,這個也是 InitContainer 和普通 container 不同的地方。根據上面三點內容,我們看一下 InitContainer 的一個用途。它其實主要為普通 container 服務,比如說它可以為普通 container 啟動之前做一個初始化,或者為它準備一些配置檔案, 配置檔案可能是一些變化的東西。再比如做一些前置條件的校驗,如網路是否聯通。
上面的截圖是 flannel 元件的 InitContainer 的一個配置,它的 InitContainer 主要是為 kube-flannel 這個普通容器啟動之前準備一些網路配置檔案。
結束語#ConfigMap 和 Secret: 首先介紹了 ConfigMap 和 Secret 的建立方法和使用場景,然後對 ConfigMap 和 Secret 的常見使用注意點進行了分類和整理。最後介紹了私有倉庫映象的使用和配置;Pod 身份認證: 首先介紹了 ServiceAccount 和 Secret 的關聯關係,然後從原始碼角度對 Pod 身份認證流程和實現細節進行剖析,同時引出了 Pod 的許可權管理(即 RBAC 的配置管理);容器資源和安全: 首先介紹了容器常見資源型別 (CPU/Memory) 的配置,然後對 Pod 服務品質分類進行詳細的介紹。同時對 SecurityContext 有效層級和許可權配置項進行簡要說明;InitContainer: 首先介紹了 InitContainer 和普通 container 的區別以及 InitContainer 的用途。然後基於實際用例對 InitContainer 的用途進行了說明。