Kubernetes 使用 PodPreset 注入信息到 Pods

1、PodPreset 介紹

PodPreset 是一種 K8s API 資源,用於在創建 Pod 時注入其他運行時需要的信息,這些信息包括 secrets、volume mounts、environment variables 等,我們可以使用標籤選擇器來指定某個或某些 Pod,來將 PodPreset 預設信息應用上去。使用 PodPreset 的好處就是我們可以將一些常用 Pod 預設信息配置爲模板,這樣就不需要顯式爲每個 Pod 提供所有信息,簡化 Pod 初始化配置,還能起到配置統一的效果。

2、環境、軟件準備

本次演示環境,我是在本機 MAC OS 上操作,以下是安裝的軟件及版本:

  • Docker: 18.06.3-ce
  • Oracle VirtualBox: 6.0.8 r130520 (Qt5.6.3)
  • Linux: 7.6.1810
  • Minikube: v0.30.0
  • Kubernetes: 1.12.1

注意:這裏 Kubernetes 集羣搭建使用 Minikube 來完成,Minikube 啓動的單節點 k8s Node 實例是需要運行在本機的 VM 虛擬機裏面,所以需要提前安裝好 VM,這裏我選擇 Oracle VirtualBox。k8s 運行底層使用 Docker 容器,所以本機需要安裝好 Docker 環境,這裏忽略 Docker、VirtualBox、Minikube、Kubectl 的安裝過程,着重介紹一下如何配置 PodPreset 以及使用。

3、K8s 啓用 PodPreset 配置

K8s 默認不開啓 PodPreset 支持的,其 API 類型爲 settings.k8s.io/v1alpha1,如果不確認集羣是否已開啓 PodPreset 支持,可以通過 kubectl api-versions 命令查看是否存在該類型,或者 kubectl get podpreset 命令查看,如果沒開啓會提示 error: the server doesn't have a resource type "podpreset" 錯誤。

啓用 PodPreset,可以通過 K8s ApiServer 增加 --runtime-config=settings.k8s.io/v1alpha1=true 配置即可。Minikube 方式啓動集羣,可以在啓動時追加如下命令:

minikube start --extra-config=apiserver.runtime-config=settings.k8s.io/v1alpha1=true --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodPreset`

如果覺得以上命令太複雜,還可以通過修改 Yaml 方式配置,因爲 Minikube 通過 Static Pod 的方式用 Kubelet 啓動各組件服務,所以可以更改對應組件的 Yaml 來激活 PodPreset,通過修改 /etc/kubernetes/manifests/kube-apiserver.yaml 文件增加如下配置,修改完成後 Kubelet 會自動重啓 kube-apiserver 各組件。

$ vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --runtime-config=settings.k8s.io/v1alpha1=true  #新增該配置
- --enable-admission-plugins=NamespaceLifecycle...,PodPreset  #最後邊增加 ,PodPreset 支持

截取修改後部分 kube-apiserver.yaml 文件如下:

spec:
  containers:
  - command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    - --enable-admission-plugins=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodPreset
    - --runtime-config=settings.k8s.io/v1alpha1=true
    - --advertise-address=172.30.12.98
    - --allow-privileged=true
    - --client-ca-file=/var/lib/minikube/certs/ca.crt
    - --enable-bootstrap-token-auth=true

待組件重啓完畢後,可以再次檢查是否已支持 PodPreset。

$ kubectl api-versions | grep settings.k8s.io
settings.k8s.io/v1alpha1

4、PodPreset 注入信息示例

現在我們演示一下,如何使用 PodPreset 注入信息到 Pod 中,這裏舉兩個示例,一個是匹配指定 Pod 加載配置,另一個是匹配某個 Namespace 下所有 Pod 加載配置。

4.1、匹配指定 Pod 加載配置

上邊提到過 使用標籤選擇器來指定某個或某些 Pod,來將 PodPreset 預設信息應用上去,這裏我們來演示下如何匹配指定 Pod 加載配置。首先新建 PodPreset Yaml 資源文件。

$ vim podpreset-busybox-hwy.yaml
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: busybox-hwy
  namespace: wanyang3
spec:
  selector:
    matchLabels:
      role: busybox-hwy
  env:
    - name: PODPRESET_MESSAGE
      value: "This is podpreset message."
  volumeMounts:
    - mountPath: /opt/logs
      name: logs-volume
  volumes:
    - name: logs-volume
      emptyDir: {}

可以看到如下幾個關鍵信息:

  • PodPreset 資源 ApiVersion 爲 settings.k8s.io/v1alpha1
  • 這裏 selector. matchLabels 通過 Labels 匹配標籤包含 role: busybox-why 的 Pod
  • 注入了 env 環境變量 PODPRESET_MESSAGE=This is podpreset message. 到匹配的 Pod 中
  • 注入了 volumes 卷掛載目錄到匹配的 Pod 中的 /opt/logs 目錄

創建一下該 PodPreset 資源。

$ kubectl create ns wanyang3
namespace/wanyang3 created
$ kubectl apply -f podpreset-busybox-hwy.yaml
podpreset.settings.k8s.io/busybox-hwy created
$ kubectl get podpreset -n wanyang3
NAME          CREATED AT
busybox-hwy   2019-07-07T02:10:31Z

接下來,我們來創建 Pod Yaml 資源文件。

$ vim pod-busybox-hwy.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: busybox-hwy
  namespace: wanyang3
  labels:
    app: busybox-hwy
    role: busybox-hwy
spec:
  containers:
    - name: busybox-hwy
      image: busybox:latest
      command: ["sleep", "60000"]

注意:這裏我以 busybox 容器來測試,這裏 labels role: busybox-hwy 要跟上邊 PodPreset selector. matchLabels 匹配上,否則將沒法注入信息。那麼創建一下該 Pod 資源。

$ kubectl apply -f pod-busybox-hwy.yaml
pod/busybox-hwy created
$ kubectl get pod -n wanyang3
NAME          READY   STATUS    RESTARTS   AGE
busybox-hwy   1/1     Running   0          15s

成功創建,進入到容器內驗證一下 PodPreset 信息是否正確注入吧!

$ kubectl exec -it busybox-hwy -n wanyang3 /bin/sh
/ # printenv |grep PODPRESET
PODPRESET_MESSAGE=This is podpreset message.
/ # df -h
Filesystem                Size      Used Available Use% Mounted on
overlay                  36.0G      9.3G     26.7G  26% /
tmpfs                    64.0M         0     64.0M   0% /dev
tmpfs                     1.8G         0      1.8G   0% /sys/fs/cgroup
/dev/mapper/centos-root
                         36.0G      9.3G     26.7G  26% /opt/logs
/dev/mapper/centos-root
                         36.0G      9.3G     26.7G  26% /dev/termination-log
/dev/mapper/centos-root
                         36.0G      9.3G     26.7G  26% /etc/resolv.conf
/dev/mapper/centos-root
                         36.0G      9.3G     26.7G  26% /etc/hostname
/dev/mapper/centos-root
                         36.0G      9.3G     26.7G  26% /etc/hosts
......                         

驗證沒有問題,PodPreset 信息注入到 Pod 裏面了,而我們的 Pod yaml 文件就非常簡潔了,從而避免了某一個配置更改,所有相關的 Pod 都需要更新 Yaml 配置的麻煩,是不是很方便!咱們在更深入瞭解一下 PodPreset 實現的方式,此時獲取 Pod 的 Yaml 文件看下。

# kubectl get pod busybox-hwy -n wanyang3 -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    podpreset.admission.kubernetes.io/podpreset-busybox-hwy: "24187"
  creationTimestamp: 2019-07-07T02:22:27Z
  labels:
    app: busybox-hwy
    role: busybox-hwy
  name: busybox-hwy
  namespace: wanyang3
  resourceVersion: "24302"
  selfLink: /api/v1/namespaces/wanyang3/pods/busybox-hwy
  uid: 1066b217-a05e-11e9-aab0-080027a076a9
spec:
  containers:
  - command:
    - sleep
    - "60000"
    env:
    - name: PODPRESET_MESSAGE
      value: This is podpreset message.
    image: busybox:latest
    imagePullPolicy: Always
    name: busybox-hwy
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /opt/logs
      name: logs-volume
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-nnbrm
      readOnly: true
  dnsPolicy: ClusterFirst
  nodeName: minikube
  ......

可以看到,經過 k8s admission controller 之後此時的 Yaml 文件是將 PodPreset 和 Pod 資源 Merge 合併了,同時增加了 podpreset.admission.kubernetes.io/podpreset-busybox-hwy: "24187" 這樣的註解,類似模板的 Include 功能,而這個 24187 資源號就是上邊 podpreset-busybox-hwy 創建完成之後的 resourceVersion: "24187" 資源版本號。

4.2、匹配某個 Namespace 下所有 Pod 加載配置

上邊演示了匹配一個或多個指定 Pod 注入信息,如果我們想針對某個 Namespace 下的所有的 Pod 注入信息該如何配置呢?方法就是配置 selector.matchLabels 時匹配所有即可。

$ vim podpreset-ns-test.yaml 
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: podpreset-ns-test
  namespace: podpreset-test
spec:
  selector:
    matchLabels:  #關鍵在這裏,匹配所有
  env:
    - name: DB_PORT
      value: "6379"
    - name: TZ
      value: Asia/Shanghai

說明一下:這裏 matchLabels: 設置爲空,表示匹配該 Namespaces 下所有,這裏匹配 podpreset-test 命名空間下所有 Pod,並注入了 DB_PORT= 6379TZ=Asia/Shanghai 兩個公用環境變量配置。注意:這裏的 TZ=Asia/Shanghai 環境變量配置可以用於修改 Pod 所屬時區,之前我們修改 Centos 容器內部時間方式,可以參照 Docker/K8s 解決容器內時區不一致方案 文章,這裏提供一個新的簡便的方式配置時區,從而實現該命名空間所有的 Pod 統一更改時區(畢竟 Centos 默認時間爲 UTC)。接下來,創建一下該 PodPreset 資源。

$ kubectl create ns podpreset-test
namespace/podpreset-test created
$ kubectl apply -f podpreset-ns-test.yaml
podpreset.settings.k8s.io/podpreset-ns-test created
$ kubectl get podpreset -n podpreset-test
NAME                CREATED AT
podpreset-ns-test   2019-07-07T03:52:42Z

然後,分別創建多個 Pod 來注入該 PodPreset 信息。

$ vim pod-nginx-test-1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-test-1
  namespace: podpreset-test
  labels:
    app: nginx-test-1
spec:
  containers:
    - name: nginx-test-1
      image: nginx:latest
$ vim pod-nginx-test-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-test-2
  namespace: podpreset-test
  labels:
    app: nginx-test-2
spec:
  containers:
    - name: nginx-test-2
      image: nginx:latest    

創建以上 Pod 資源,並分別驗證是否注入信息成功。

$ kubectl apply -f pod-nginx-test-1.yaml
pod/nginx-test-1 created
$ kubectl apply -f pod-nginx-test-2.yaml
pod/nginx-test-2 created
$ kubectl get pod -n podpreset-test
NAME           READY   STATUS    RESTARTS   AGE
nginx-test-1   1/1     Running   0          26s
nginx-test-2   1/1     Running   0          20s

# 分別進入容器內驗證是否注入成功
$ kubectl exec -it nginx-test-1 -n podpreset-test /bin/sh
# printenv
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=nginx-test-1
DB_PORT=6379
HOME=/root
PKG_RELEASE=1~stretch
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
NGINX_VERSION=1.17.1
......
PWD=/
TZ=Asia/Shanghai
# date
Sun Jul  7 12:16:53 CST 2019

$ kubectl exec -it nginx-test-2 -n podpreset-test /bin/sh
# printenv
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=nginx-test-2
DB_PORT=6379
HOME=/root
PKG_RELEASE=1~stretch
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
NGINX_VERSION=1.17.1
......
PWD=/
TZ=Asia/Shanghai
# date
Sun Jul  7 12:17:26 CST 2019

可以看到,該 Namespace 下的兩個 Pod 都成功注入了配置信息,時區也改過來了,是不是很方便了。不過,如果想指定該 Namespace 下某個 Pod 不使用該 PodPreset 該如何配置呢?畢竟有些個性化的 Pod 不使用通用配置。我們可以配置 podpreset.admission.kubernetes.io/exclude: "true" 註解來註明該 Pod 不注入 PodPreset,接下來演示一下。

$ vim pod-nginx-test-3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-test-3
  namespace: podpreset-test
  annotations:
    podpreset.admission.kubernetes.io/exclude: "true"
  labels:
    app: nginx-test-3
spec:
  containers:
    - name: nginx-test-3
      image: nginx:latest

創建該 Pod 資源,驗證信息是否注入容器進去。

$ kubectl apply -f pod-nginx-test-3.yaml
$ kubectl exec -it nginx-test-3 -n podpreset-test /bin/sh
$ printenv | grep TZ
$ printenv | grep DB_PORT
$ date
Sun Jul  7 04:18:54 UTC 2019

可以看到,添加了忽略 PodPreset 注入 annotations 後,沒有將信息注入進去,時間還是默認的 UTC 時間。

PodPreset 除了上邊演示的兩種用法外,還支持多 PodPreset 應用到同一 Pod,支持多種資源類型(ReplicaSet 等),支持從 ConfigMap 中取值。同時要說明一下,當 PodPreset 跟 Pod 配置有衝突時,例如 Pod Yaml 容器掛載配置跟 PodPreset 容器掛載配置爲同一路徑時,會報錯提示衝突。

最後要提一下注意的問題:

  • 目前 PodPreset 的預設功能這塊還在演進中,不過已經能大大簡化了相關的管理工作,將這些公用配置從開發者手中分離出來,變成系統管理配置。
  • PodPreset 是 Namespace 級別的對象,其作用範圍只能是同一個命名空間下容器。
  • 目前爲 v1alpha1 版本,還不成熟,例如當我們對已創建的 PodPreset 執行非常少量的修改時,重新 apply 或者 replace 時,服務端並沒有更新過來(親測會有問題,只能刪除重建),大家可以自己嘗試下。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章