参考:
https://kubernetes.io/docs/concepts/workloads/pods/podpreset/
https://kubernetes.io/docs/tasks/inject-data-application/podpreset/
Pod Preset是一种API资源,用来在创建pod时向其注入运行时需要的额外信息,通过使用label selector确定为pod应用那些Presets。
Preset允许用户创建具有通用性的模板而无需显式提供pod运行时才需要的信息,而模板的使用者只需通过Preset提供pod运行时需要的信息而无需关注pod的实现细节。
Kubernetes为Preset提供了一个允许控制器(admission controller),当创建创建pod的请求到达时,通过这个允入控制器将label selector选中的Preset应用到pod中,具体流程如下:
每个pod可以被0到多个Preset匹配,每个Preset可以应用到0到多个pod。当Preset被应用到pod时,kubernetes会修改pod的spec。当注入Env、EnvFrom、VolumeMounts时,kubernetes会修改pod中除init container以外的其它所有container的spec,当注入volume时,修改pod的spec。
在某些情况下,不希望所创建的pod被Preset修改,可以在pod的注解中加入如下内容禁止掉此项功能:
podpreset.admission.kubernetes.io/exclude: "true"
首先是简单的pod Preset定义,保存在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: {}
创建这个pod Preset:
kubectl create -f https://k8s.io/examples/podpreset/preset.yaml
检察创建的Preset:
$ kubectl get podpreset
NAME AGE
allow-database 1m
从以上Preset的label selector可以看出,这个Preset会作用到所有相同namespace下拥有role: frontend标签的pod上,以下是一个pod示例:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
ports:
- containerPort: 80
创建pod:
$ kubectl create -f https://k8s.io/examples/podpreset/pod.yaml
列出运行中的pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
website 1/1 Running 0 4m
Preset的允入控制器会将刚才创建的Preset应用到这个pod上,并且修改pod的spec,修改后的结果如下:
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: {}
运行如下命令输出以上内容:
$ kubectl get pod website -o yaml
本例中Preset向pod模板提供的是ConfigMap类型的资源,ConfigMap中是环境变量。
以下是pod定义:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
ports:
- containerPort: 80
以下是ConfigMap定义:
apiVersion: v1
kind: ConfigMap
metadata:
name: etcd-env-config
data:
number_of_members: "1"
initial_cluster_state: new
initial_cluster_token: DUMMY_ETCD_INITIAL_CLUSTER_TOKEN
discovery_token: DUMMY_ETCD_DISCOVERY_TOKEN
discovery_url: http://etcd_discovery:2379
etcdctl_peers: http://etcd:2379
duplicate_key: FROM_CONFIG_MAP
REPLACE_ME: "a value"
以下是Preset定义:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: allow-database
spec:
selector:
matchLabels:
role: frontend
env:
- name: DB_PORT
value: "6379"
- name: duplicate_key
value: FROM_ENV
- name: expansion
value: $(REPLACE_ME)
envFrom:
- configMapRef:
name: etcd-env-config
volumeMounts:
- mountPath: /cache
name: cache-volume
- mountPath: /etc/app/config.json
readOnly: true
name: secret-volume
volumes:
- name: cache-volume
emptyDir: {}
- name: secret-volume
secret:
secretName: config-details
以上可以看出Preset已经将定义好的ConfigMap引入其中。
以下内容是Preset被应用到pod上,pod的spec被Preset的允入控制器修改后的内容:
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
- mountPath: /etc/app/config.json
readOnly: true
name: secret-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
- name: duplicate_key
value: FROM_ENV
- name: expansion
value: $(REPLACE_ME)
envFrom:
- configMapRef:
name: etcd-env-config
volumes:
- name: cache-volume
emptyDir: {}
- name: secret-volume
secret:
secretName: config-details
以下是ReplicaSet定义:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
role: frontend
matchExpressions:
- {key: role, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
role: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
以下是Proset的定义:
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: {}
需要注意的是,Preset不会修改ReplicaSet的spec,被修改的是被ReplicaSet控制的pod的spec,被修改后的内容如下:
apiVersion: v1
kind: Pod
metadata:
name: frontend
labels:
app: guestbook
role: frontend
annotations:
podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources:
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- mountPath: /cache
name: cache-volume
env:
- name: GET_HOSTS_FROM
value: dns
- name: DB_PORT
value: "6379"
ports:
- containerPort: 80
volumes:
- name: cache-volume
emptyDir: {}
以下是pod spec:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
ports:
- containerPort: 80
以下是Preset1 spec:
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: {}
以下是Preset2 spec:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: proxy
spec:
selector:
matchLabels:
role: frontend
volumeMounts:
- mountPath: /etc/proxy/configs
name: proxy-volume
volumes:
- name: proxy-volume
emptyDir: {}
被两个Preset修改后的pod spec:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
annotations:
podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
podpreset.admission.kubernetes.io/podpreset-proxy: "resource version"
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
- mountPath: /etc/proxy/configs
name: proxy-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
volumes:
- name: cache-volume
emptyDir: {}
- name: proxy-volume
emptyDir: {}
Preset1 spec:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
volumes:
- name: cache-volume
emptyDir: {}
Preset2 spec:
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: other-volume
volumes:
- name: other-volume
emptyDir: {}
被冲突Preset修改后的pod spec:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
volumes:
- name: cache-volume
emptyDir: {}
由以上结果看出,冲突的Preset没有起作用,并且会在pod上报出Preset冲突的异常事件,如下:
$ kubectl describe ...
....
Events:
FirstSeen LastSeen Count From SubobjectPath Reason Message
Tue, 07 Feb 2017 16:56:12 -0700 Tue, 07 Feb 2017 16:56:12 -0700 1 {podpreset.admission.kubernetes.io/podpreset-allow-database } conflict Conflict on pod preset. Duplicate mountPath /cache.
$ kubectl delete podpreset allow-database
podpreset "allow-database" deleted