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 时,服务端并没有更新过来(亲测会有问题,只能删除重建),大家可以自己尝试下。

参考资料

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐