一、认识ConfigMap资源
在分布式环境中,基于负载、容错等需求的考虑,几乎所有的服务器都需要在不同的机器上分别部署不止一个实例。随着程序功能的日益复杂,同时配置文件的修改频率通常远远大于代码本身,这种情况下,有时仅仅是一个配置内容的修改,就不得不重新进行代码提交、打包、分发上线的流程。部署模式较大的场景中,分发上线工作即繁杂又沉重。
究其根本,所有的这些麻烦都是由于配置和代码在管理和发布过程中紧密耦合所致。配置本身源于代码,是为了提高代码的灵活性而提取出来的一些经常变化的或需要定制的内容,而正是配置的这种天生的变化特征为部署过程带来了不小的麻烦,也最终催生了分布式系统配置管理系统,从而将配置内容从代码完全分离出来,及时可靠高效的提供配置访问和更新服务。
作为分布式系统的Kubernetes也提供了统一配置管理方案–ConfigMap。Kubernetes基于ConfigMap对象实现了将配置文件从容器镜像中解耦,从而增强了容器应用的可移植性。简单来说,一个ConfigMap对象就是一系列配置数据的集合,这些数据可"注入"到Pod对象中,并为容器应用所使用,注入方式有挂载为存储卷和传递为环境变量两种。
ConfigMap对象将配置数据以键值对的形式进行存储,这些数据可以在Pod对象中使用或者为系统组件提供配置,例如控制器对象等。不过,无论应用程序如何使用ConfigMap对象中的数据,用户都完全可以通过在不同的环境中创建名称相同但是内容不同的ConfigMap对象,从而为不同环境中同一功能的Pod资源提供不同的配置信息,实现应用于配置的灵活勾兑。
二、创建ConfigMap
Kubernetes的不少资源既可以使用kubelet create命令创建,也可以使用清单创建,例如namespace。ConfigMap是另一个两种创建方式都可以常见的资源。而且,通过使用"kubectl create configmap"命令,用户可以根据目录、文件或直接值创建ConfigMap对象,命令语法格式如下所示:
kubectl create configmap <map-name> <data-source>
.
其中,
1)利用直接值创建
为"kubelet create configmap"命令使用"--from-literal"选项可在命令行直接给出键值对来创建ConfigMap对象,重复使用此选项则可以传递多个键值对。命令格式如下:
kubelet create configmap configmap_name --from-literal=key-name-1=value-1
例如,下面的命令创建了一个名为special-config时传递了两个键值对:
]# kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
configmap/special-config created
查看ConfigMap对象的详细信息:
]# kubectl get configmap -o wide
NAME DATA AGE
special-config 2 8s
]# kubectl get configmap special-config -o yaml
apiVersion: v1
data:
special.how: very
special.type: charm
kind: ConfigMap
metadata:
creationTimestamp: "2020-08-30T07:39:16Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:special.how: {}
f:special.type: {}
manager: kubectl
operation: Update
time: "2020-08-30T07:39:16Z"
name: special-config
namespace: default
resourceVersion: "6456083"
selfLink: /api/v1/namespaces/default/configmaps/special-config
uid: 6d5f473b-bd99-49eb-9729-00fac9dd8642
2)基于文件创建
为"kubectl create configmap"命令使用"--from-file"选项即可基于文件内容来创建ConfigMap对象,它的命令格式如下。可以重复多次使用"--from-file"选项以传递多个文件内容:
kubectl create configmap <configmap_name> --from-file=<path-to-file>
例如,下面的命令可以把事先准备好的Nginx配置文件模板保存于ConfigMap对象nginx-config中:
]# kubectl create configmap nginx-conf --from-file=/etc/nginx/nginx.conf.default
configmap/nginx-conf created
查看ConfigMap资源对象详细信息:
]# kubectl get configmap nginx-conf -o yaml
apiVersion: v1
data:
nginx.conf.default: |2
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2020-08-30T07:47:58Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:nginx.conf.default: {}
manager: kubectl
operation: Update
time: "2020-08-30T07:47:58Z"
name: nginx-conf
namespace: default
resourceVersion: "6457570"
selfLink: /api/v1/namespaces/default/configmaps/nginx-conf
uid: a485c228-2a05-43cb-a5b1-87a40a40958b
3)基于目录创建
如果配置文件数量较多且存储于有限的目录中时,kubelet还提供了基于目录直接将多个文件分别收纳为键值数据的ConfigMap资源创建方式。将"–from-file"选项后面所跟的路径指向一个目录路径就能将目录下的所有文件一同创建于同一ConfigMap资源中,命令格式如下:
]# kubectl create configmap nginx-conf-files --from-file=/data/nginx/
configmap/nginx-conf-files created
查看ConfigMap资源详细信息:
]# kubectl get configmap nginx-conf-files -o yaml
apiVersion: v1
data:
myserver-1.conf: |
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
myserver-2.conf: |
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2020-08-30T07:58:17Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:myserver-1.conf: {}
f:myserver-2.conf: {}
manager: kubectl
operation: Update
time: "2020-08-30T07:58:17Z"
name: nginx-conf-files
namespace: default
resourceVersion: "6459321"
selfLink: /api/v1/namespaces/default/configmaps/nginx-conf-files
uid: 5a1075a3-e427-4a8c-a9df-1e2f23385b8e
4)使用清单创建ConfigMap
基于配置文件创建ConfigMap资源时,它所使用的字段包括通常的apiVersion、kind和metadata字段,以及用于存储数据的关键字字段"data"。例如,如下配置资源清单文件:
]# cat my-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-demo
namespace: default
data:
log_level: INFO
log_file: /var/log/test.log
]# kubectl apply -f my-configmap.yaml
configmap/configmap-demo created
查看ConfigMap资源对象:
]# kubectl get configmap -o wide
NAME DATA AGE
configmap-demo 2 10s
]# kubectl describe configmap configmap-demo
Name: configmap-demo
Namespace: default
Labels: <none>
Annotations:
Data
====
log_file:
----
/var/log/test.log
log_level:
----
INFO
Events: <none>
三、在Pod中使用ConfigMap资源
Pod资源的环境变量值的获取方式之一包括引用ConfigMap对象中的数据,这一点通过在env中为valueFrom内嵌configMapKeyRef对象即可实现,其使用格式如下:
valueFrom:
configMapKeyRef:
key:
name:
optional:
.
其中,字段name的值为要引用的ConfigMap对象的名称,字段key可用于指定要引用ConfigMap对象中某键的键名,而字段optional则用于为当前Pod资源指明引用是否为可选的。此类环境变量的使用方式与直接定义的环境变量并无区别,它们可被用于容器的启动脚本或直接传递给容器应用等。
1)编写Pod与ConfigMap资源的yaml文件
]# cat configmap-demo.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: busybox-http-config
namespace: default
data:
http_port: "8080"
verbose_level: "-vv"
---
apiVersion: v1
kind: Pod
metadata:
name: configmap-env-demo
namespace: default
spec:
containers:
- name: busybox-httpd
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/httpd"]
args: ["-f","-p","$(HTTPD_PORT)","$(HTTPD_LOG_VERBOSE)"]
env:
- name: HTTPD_PORT
valueFrom:
configMapKeyRef:
name: busybox-http-config
key: http_port
- name: HTTPD_LOG_VERBOSE
valueFrom:
configMapKeyRef:
name: busybox-http-config
key: verbose_level
optional: true
]# kubectl apply -f configmap-demo.yaml
configmap/busybox-http-config created
pod/configmap-env-demo created
2)查看ConfigMap和Pod资源信息
]# kubectl get configmap busybox-http-config -o yaml
apiVersion: v1
data:
http_port: "8080"
verbose_level: -vv
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"http_port":"8080","verbose_level":"-vv"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"busybox-http-config","namespace":"default"}}
creationTimestamp: "2020-08-30T08:20:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:http_port: {}
f:verbose_level: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl
operation: Update
time: "2020-08-30T08:20:23Z"
name: busybox-http-config
namespace: default
resourceVersion: "6463086"
selfLink: /api/v1/namespaces/default/configmaps/busybox-http-config
uid: 741b6ced-919a-4ff8-ae37-eecfd376a643
]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
configmap-env-demo 1/1 Running 0 3s 10.244.1.116 node1 <none> <none>
]# kubectl describe pods configmap-env-demo
Name: configmap-env-demo
Namespace: default
Priority: 0
Node: node1/172.16.2.101
Start Time: Mon, 31 Aug 2020 16:23:53 +0800
Labels: <none>
Annotations: Status: Running
IP: 10.244.1.116
IPs:
IP: 10.244.1.116
Containers:
busybox-httpd:
Container ID: docker://dcb784ac9c189c847a75d06d8917a5eec672280ba1708a1652bbbdf8a7553219
Image: busybox
Image ID: docker-pullable://busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977
Port: <none>
Host Port: <none>
Command:
/bin/httpd
Args:
-f
-p
$(HTTPD_PORT)
$(HTTPD_LOG_VERBOSE)
State: Running
Started: Mon, 31 Aug 2020 16:23:54 +0800
Ready: True
Restart Count: 0
Environment:
HTTPD_PORT: <set to the key 'http_port' of config map 'busybox-http-config'> Optional: false
HTTPD_LOG_VERBOSE: <set to the key 'verbose_level' of config map 'busybox-http-config'> Optional: true
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-47pch:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-47pch
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/configmap-env-demo to node1
Normal Pulled <invalid> kubelet, node1 Container image "busybox" already present on machine
Normal Created <invalid> kubelet, node1 Created container busybox-httpd
Normal Started <invalid> kubelet, node1 Started container busybox-httpd
可以看到当前Pod已经成功使用了ConfigMap资源中的数据
3)验证Pod信息
]# kubectl exec -it configmap-env-demo -- ps aux
PID USER TIME COMMAND
1 root 0:00 /bin/httpd -f -p 8080 -vv
6 root 0:00 ps aux
.
创建ConfigMap资源的Pod对象时,被引用的资源必须事先存在,否则将无法启动相应的容器,直到被依赖的资源创建完成为止。不过,那些未引用不存在的ConfigMap资源的容器将不受此影响。另外,ConfigMap是名称空间级别的资源,它必须与引用它的Pod资源在同一个空间中。
四、ConfigMap存储卷
若ConfigMap对象中的键值来源于较长的文件内容,那么使用环境变量将其导入会使得变量值占据过多的内存空间而且不易处理。这类数据通常用于为容器应用提供配置文件,因此使得将其内容直接作为文件进行引用方式为较好的选择。其实现方式是,在定义Pod资源时,将此类ConfigMap对象配置为ConfigMap类型的存储卷,而后由容器将其挂载至特定的挂载点后直接访问。
1)挂载整个存储卷
关联为Pod资源的存储卷时,ConfigMap对象中的每个键都对应的表现为一个文件,键名转换为文件名,而键值则为相应文件的内容,即便是通过直接创建的键值数据,也一样表现为文件视图。挂载于容器之上,由键值数据表现出的文件位于挂载点目录中,容器中的进程可直接读取这些文件的内容。
配置Pod资源时,基于存储卷的方式引用ConfigMap对象的方法也非常简单,仅需要指明存储卷名称及要引用的ConfigMap的对象名称即可。
]# cat configmap-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-demo
namespace: default
spec:
containers:
- name: nginx-server
image: nginx:alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: ngxconfig
mountPath: /data/
readOnly: true
volumes:
- name: ngxconfig
configMap:
name: nginx-conf-files
]# kubectl apply -f configmap-volume.yaml
pod/configmap-volume-demo created
查看Pod资源详细信息:
]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
configmap-volume-demo 1/1 Running 0 11s 10.244.1.123 node1 <none> <none>
]# kubectl describe pods configmap-volume-demo
Name: configmap-volume-demo
Namespace: default
Priority: 0
Node: node1/172.16.2.101
Start Time: Mon, 31 Aug 2020 17:08:30 +0800
Labels: <none>
Annotations: Status: Running
IP: 10.244.1.123
IPs:
IP: 10.244.1.123
Containers:
nginx-server:
Container ID: docker://f32e43cc784d89bc6b547ff7d21a54c252d9bf3d0a328327065e5b83401aefde
Image: nginx:alpine
Image ID: docker-pullable://nginx@sha256:a97eb9ecc708c8aa715ccfb5e9338f5456e4b65575daf304f108301f3b497314
Port: <none>
Host Port: <none>
State: Running
Started: Mon, 31 Aug 2020 17:08:31 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/data/ from ngxconfig (ro)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
ngxconfig:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: nginx-conf-files
Optional: false
default-token-47pch:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-47pch
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/configmap-volume-demo to node1
Normal Pulled <invalid> kubelet, node1 Container image "nginx:alpine" already present on machine
Normal Created <invalid> kubelet, node1 Created container nginx-server
Normal Started <invalid> kubelet, node1 Started container nginx-server
查看Pod中的配置文件是否存在:
]# kubectl exec -it configmap-volume-demo ls -- /data/
myserver-1.conf myserver-2.conf
2)挂载存储卷中的部分键值
有的时候,用户很可能不期望在容器中挂载某个ConfigMap存储卷后于挂载点目录导出所有的文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见;所以ConfigMap存储卷也允许我们通过指定的字段来挂载特定的ConfigMap资源对象中的部分键值。
]# cat configmap-volume2.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-demo
namespace: default
spec:
containers:
- name: nginx-server
image: nginx:alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: ngxconfig
mountPath: /data/
readOnly: true
volumes:
- name: ngxconfig
configMap:
name: nginx-conf-files
items:
- key: myserver-1.conf
path: myserver-1.conf
mode: 0644
]# kubectl apply -f configmap-volume2.yaml
pod/configmap-volume-demo created
查看Pod资源详细信息:
]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
configmap-volume-demo 1/1 Running 0 9s 10.244.1.124 node1 <none> <none>
]# kubectl describe pods configmap-volume-demo
Name: configmap-volume-demo
Namespace: default
Priority: 0
Node: node1/172.16.2.101
Start Time: Mon, 31 Aug 2020 17:17:51 +0800
Labels: <none>
Annotations: Status: Running
IP: 10.244.1.124
IPs:
IP: 10.244.1.124
Containers:
nginx-server:
Container ID: docker://b7c88eba8c2f5c5a4bc6b025187bed9e5e3c3ab28ef544cd65310e08e0e6ad8f
Image: nginx:alpine
Image ID: docker-pullable://nginx@sha256:a97eb9ecc708c8aa715ccfb5e9338f5456e4b65575daf304f108301f3b497314
Port: <none>
Host Port: <none>
State: Running
Started: Mon, 31 Aug 2020 17:17:52 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/data/ from ngxconfig (ro)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
ngxconfig:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: nginx-conf-files
Optional: false
default-token-47pch:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-47pch
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/configmap-volume-demo to node1
Normal Pulled <invalid> kubelet, node1 Container image "nginx:alpine" already present on machine
Normal Created <invalid> kubelet, node1 Created container nginx-server
Normal Started <invalid> kubelet, node1 Started container nginx-server
验证ConfigMap资源是否已挂载:
]# kubectl exec -it configmap-volume-demo -- ls /data/
myserver-1.conf
使用ConfigMap资源的注意事项:
1)以存储卷方式引用的ConfigMap必须先于Pod存在,除非在Pod中将它们全部标记为"optional",否则将会导致Pod无法正常启动的错误;同样,即使存在ConfigMap,在引用的键不存在时,也会导致一样的错误。
2)当以环境变量方式注入的ConfigMap中的键不存在时会被忽略,Pod可以正常启动,但错误引用的信息会以"InvalidVariableNames"事件记录于日志中。
3)ConfigMap是名称空间级别的资源,因此,引用它的Pod必须处于同一名称空间中。
4)kubelet不支持引用Kubernetes API Server上不存在的ConfigMap资源,这包括那些通过kubelet的"–manifest-url"或"–config"选项,以及kubectl REST API创建的Pod。