在制作Docker镜像时,Dockerfile中的ENTRYPOINT和CMD指令可用于指定容器启动时要运行的程序及相关参数。CMD指令以列表的形式指定要运行的程序和相关参数,但是如果同时存在ENTRYPOINT指令,则CMD指令中列表的所有元素都将被作为由ENTRYPOINT指定程序的命令行参数。另外在基于某镜像使用Docker命令创建容器时,可以在命令行向ENTRYPOINT中的程序传递额外的自定义参数,甚至还可以修改要运行的应用程序本身,例如以下命令使用docker run创建并启动容器的格式为: docker run [OPTINS] IMAGE [COMMAND] [ARG]
COMMAND
为自定义运行的程序,ARG
为传递给程序的参数,假如定义相关镜像文件时使用了ENTRYPOINT
指令,则COMMAND
和ARG
都会被当作命令行参数传递给ENTRYPOINT
指令中指定的程序,除非运行docker run命令时额外使用--entrypoint
选项来覆盖镜像文件中的ENTRYPOINT
指定的程序。
在Kubernetes系统上创建Pod资源时,也能够向容器化应用传递命令行参数,甚至指定运行其它应用程序,相关的字段分别为pods.spec.containers.command
和pods.spec.containers.args
[root@localhost configmap]# kubectl create -f nginx.yaml
[root@localhost configmap]# cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f" ]
Kubernetes配置文件中的command对应于Dockerfile中的ENTRYPOINT,而配置文件的args则对应于Dockerfile中的CMD。在Kubernetes中只给出command字段时,他会覆盖Dockerfile中的ENTRYPOINT和CMD,只给出args字段时,它仅覆盖CMD,而同时给出command和args时,它会对应覆盖ENTRYPOINT和CMD。
在通过Dockerfile制作镜像时,可以使用COPY或者ADD指定将定义好的配置文件直接复制到镜像文件系统上的相应位置,或者使用RUN指令调用sed或echo一类的命令修改配置文件从而达到为容器化应用提供自定义配置文件之目的。使用Docker Hub上的某镜像文件额外添加配置文件即能符合需要,则克隆其Dockerfile文件修改至符合需求之后再将之推送至GitHub,并由Docker Hub自动构建出镜像文件即可
Docker存储卷(volumes)能够将宿主机之上的任何文件或目录映射到容器文件系统之上,因此,可以事先将配置文件放置于宿主机之上的某个路径中,而后在启动容器时进行加载。这种方式灵活易用,但也依赖于用户需要事先将配置数据提供在宿主机上的特定路径下,而且在多主机模型中,若容器存在被调度至任一主机运行的可能性时,用户还需要将配置共享到任一宿主机来确保容器能够正常地获取到它们。
通过环境变量为容器提供配置信息是Docker Hub上最常见的使用方式,例如,使用MySQL官方提供的镜像文件启动MySQL容器时使用的MYSQL_ROOT_PASSWORD
环境变量,它用于为MYSQL服务器的root用户设置登陆密码。
在基于此类镜像启动容器时,用户为 docker run 命令通过 -e 选项向环境变量传值即能实现应用配置,命令的使用格式为 docker run -e SETTING1=foo -e SETTING2=bar ...
。启动时,容器的ENTRYPOINT
启动脚本会抓取到这些环境变量,并在启动容器应用之前,通过sed或echo等一类的命令将变量值替换到配置文件中。
在Kubernetes中使用镜像启动容器时,可以在Pod资源或Pod模版资源为容器配置使用env参数来定义所使用的环境变量列表,即便容器中的应用本身没定义环境变量,也一样可以向容器传递环境变量,只不过它不被使用罢了。环境变量配置容器化应用时,需要在容器配置段中嵌套使用env字段,它的值是一个由环境变量构建的列表。环境变量由name和value(或valueFrom)字段构成。
name
:环境变量的名称,必须字段。value
:环境变量的值,通过 ${VAR_NAME} 引用,默认值为空。valueFrom
:环境变量值的引用源,例如,当前Pod资源的名称、名称空间、标签等,不能与非空值的value字段同时使用,即环境变量的值要么源于value字段,要么源于valueFrom字段,二者不可同时提供数据。valueFrom字段可引用的值有多种来源,包括当前Pod资源的属性值,容器相关的系统资源配置、ConfigMap对象中的Key以及Secret对象中的Key,它们应分别使用不同的嵌套字段进行定义。fieldRef
:当前Pod资源的指定字段,目前支持使用的字段包括 metadata.name、metadata.namespace、metadata.labels、metadata.annotations、spec.nodeName、spec.serviceAccountName、status.hostIP和status.podIP。configMapKeyRef
:ConfigMap对象中的特定Key。secretKeyRef
:Secret对象中的特定Key。resourceFieldRef
:当前容器的特定系统资源的最小值(配额)或最大值(限额),目前支持的引用包括limits.cpu、limts.ephemeral-storage、requests、cpu、requests.memory、requests.ephemeral-storage。valueFrom属性下面可以有fieldRef【下面有
fieldPath】,configMapKeyRef,secretKeyRef和resourceFieldRef
下面是定义在配置文件 nginx.yaml 中的Pod资源,其通过环境变量引用当前Pod资源及其所在节点的相关属性值配置容器,fieldRef字段的值是一个对象,它一般由apiVersion(创建当前Pod资源的API版本)或fieldPath嵌套字段所定义:
[root@localhost configmap]# kubectl create -f nginx.yaml
[root@localhost configmap]# cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f" ]
env:
- name: HELLO_WORLD
value: just a demo
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
#restartPolicy: OnFailure
然后打印它的环境变量列表
[root@localhost configmap]# kubectl exec -it busybox-7d84ccb8cf-dztmv -- printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=busybox-7d84ccb8cf-dztmv
MY_NODE_IP=172.16.10.21
MY_POD_NAMESPACE=default
HELLO_WORLD=just a demo
MY_NODE_NAME=172.16.10.21
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
KUBERNETES_SERVICE_HOST=10.43.0.1
TERM=xterm
HOME=/root
容器的启动脚本或应用程序调用或处理这些环境变量、即可实现容器化应用的配置。相较于命令行参数的方式来说,使用环境变量的配置方式更清晰、易懂,尤其是对于首次使用相关容器的用户来说,这种方式能够快速了解容器的配置方式,不过这两种配置方式有一个共同缺陷:无法在容器应用运行过程中更新环境变量从而达到更新应用目的。这通常意味着用户不得不为production、development和qa等不同的环境分别配置Pod资源。好在,用户还有ConfigMap资源可用
分布式环境中,基于负载、容错性等需求的考虑,几乎所有的服务都需要在不同的机器节点上部署不止一个实例。随着程序功能的日益复杂,程序的配置日益增多,而且配置文件的修改频率通常远远大于代码本身,这种情况下,有时仅仅是一个配置内容的修改,就不得不重新将代码提交到SVN/Git、打包、分发上线的流程。部署规则较大的场景中,分发上线工作即繁杂又沉重。
究其根本,所有的这些麻烦都是由于配置和代码在管理和发布的过程中不加区分所致。配置本身源于代码,是为了提高代码的灵活性而提取出来的一些经常变化的或需要定制的内容,而正是配置的这种天生变化特性为部署过程中带来了不小的麻烦,也最终催生了分布式系统配置管理系统,将配置内容从代码中完全分离出来,及时可靠高效地提供配置访问和更新服务。
国内分布式配置中心相关的开源项目有 Diamond(阿里)、Apollo(携程)、Qconf(奇虎360)和disconf(百度)等。
ConfigMap作为分布式系统的Kubernetes也提供了统一配置管理方案——ConfigMap。Kubernetes基于ConfigMap对象实现了将配置文件从容器镜像中解耦,从而增强了容器应用的可移植性。简单来说,一个ConfigMap对象就是一系列配置数据的集合,这些数据可“注入”到Pod对象中,并为容器应用所使用,注入方式有挂载为存储卷和传递为环境变量两种
ConfigMap创建的方式与其它资源一样有两种:
ConfigMap以上两种对于ConfigMap都算是比较常用的创建方式,通过kubectl create configmap命令,用户可以根据目录、文件或者直接创建ConfigMap对象,命令的语法格式如下: kubectl create configmap
为ConfigMap对象的名称,
是数据源,数据源可以直接给定K/V类型的数据,也可以指定文件以及目录来获取,无论是哪一种数据源,它都要转换为ConfigMap对象中的Key-Value数据,其中Key由用户在命令行给出或是文件数据源的文件名,它仅能由字母、数字、连接号和点号组成,而Value则是直接值或文件数据源的内容。
利用kubectl create configmap
命令使用--from-literal
选项可在命令行直接给出键值对来创建ConfigMap对象,重复使用此选项则可以传递多个键值对,命令格式如下: kubectl create configmap cm1 --from-literal=name=wubo
例如下面用命令创建special-config configmap时传递来两个键值对: 键值对第一个key为mysql_ip值为172.16.10.5键值对第二个key为mysql_port值为3306
[root@localhost configmap]# kubectl create configmap mysql-config --from-literal=mysql_ip=172.16.10.5 --from-literal=mysql_port=3306 -n default
[root@localhost configmap]# kubectl get configmap mysql-config
NAME DATA AGE
mysql-config 2 53s
使用kubectl describe或-o yaml可以看到configmap中的原始数据
[root@localhost configmap]# kubectl get configmap mysql-config -o yaml
apiVersion: v1
data:
mysql_ip: 172.16.10.5
mysql_port: "3306"
kind: ConfigMap
metadata:
creationTimestamp: "2022-01-26T03:54:51Z"
name: mysql-config
namespace: default
resourceVersion: "601548"
uid: 866f5011-7f14-43ba-88d1-53682a12ac6e
[root@localhost configmap]# kubectl describe configmap mysql-config
Name: mysql-config
Namespace: default
Labels:
Annotations:
Data
====
mysql_ip:
----
172.16.10.5
mysql_port:
----
3306
BinaryData
====
Events:
此类方式提供的数据量有限,一般是在仅通过有限的几个数据项即可为Pod资源提供足够的配置信息时使用。
利用kubectl create configmap
命令使用--from-file
选项可基于文件内容来创建ConfigMap对象,它的命令格式如下: kubectl create configmap
1.准备配置文件 我们先准备好要载入容器的配置文件,等下通过kubectl create configmap config_name --from-file 来指定我们的配置文件即可创建configmap,以下准备了一个elasticsearch.yaml的配置文件
[root@localhost configmap]# cat config/elasticsearch.yaml
cluster.name: elasticsearch
node.name: elastic
path.data: /usr/local/elastic7.4/data
path.logs: /usr/local/elastic7.4/logs
bootstrap.memory_lock: true
network.host: 0.0.0.0
network.tcp.no_delay: true
network.tcp.keep_alive: true
network.tcp.reuse_address: true
network.tcp.send_buffer_size: 256mb
network.tcp.receive_buffer_size: 256mb
transport.tcp.port: 9300
transport.tcp.compress: true
http.max_content_length: 200mb
http.cors.enabled: true
http.cors.allow-origin: "*"
http.port: 9200
cluster.initial_master_nodes: ["127.0.0.1:9300"]
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true
xpack.monitoring.collection.enabled: true
2.通过文件创建configMap 如果指定文件创建configmap的时候没有指定key,那么kubernetes则以文件名称为key
[root@localhost configmap]# kubectl create configmap elastic-configmap --from-file=config/elasticsearch.yaml
[root@localhost configmap]# kubectl describe configmap elastic-configmap
Name: elastic-configmap
Namespace: default
Labels:
Annotations:
Data
====
elasticsearch.yaml: #我们没有指定key,默认以文件名称为key
----
cluster.name: elasticsearch
node.name: elastic
path.data: /usr/local/elastic7.4/data
path.logs: /usr/local/elastic7.4/logs
bootstrap.memory_lock: true
network.host: 0.0.0.0
network.tcp.no_delay: true
network.tcp.keep_alive: true
network.tcp.reuse_address: true
network.tcp.send_buffer_size: 256mb
network.tcp.receive_buffer_size: 256mb
transport.tcp.port: 9300
transport.tcp.compress: true
http.max_content_length: 200mb
http.cors.enabled: true
http.cors.allow-origin: "*"
http.port: 9200
cluster.initial_master_nodes: ["127.0.0.1:9300"]
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true
xpack.monitoring.collection.enabled: true
BinaryData
====
Events:
如果需要指定键名称,如下在文件前面写入key名称即可
[root@localhost configmap]# kubectl create configmap elastic-configmap --from-file=jettech-elastic-key=config/elasticsearch.yaml
configmap/elastic-configmap created
[root@localhost configmap]# kubectl describe configmap elastic-configmap
Name: elastic-configmap
Namespace: default
Labels:
Annotations:
Data
====
jettech-elastic-key: #指定的key
----
cluster.name: elasticsearch
node.name: elastic
path.data: /usr/local/elastic7.4/data
path.logs: /usr/local/elastic7.4/logs
bootstrap.memory_lock: true
network.host: 0.0.0.0
network.tcp.no_delay: true
network.tcp.keep_alive: true
network.tcp.reuse_address: true
network.tcp.send_buffer_size: 256mb
network.tcp.receive_buffer_size: 256mb
transport.tcp.port: 9300
transport.tcp.compress: true
http.max_content_length: 200mb
http.cors.enabled: true
http.cors.allow-origin: "*"
http.port: 9200
cluster.initial_master_nodes: ["127.0.0.1:9300"]
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true
xpack.monitoring.collection.enabled: true
BinaryData
====
Events:
如果配置文件数量较多时,kubectl还提供了基于目录直接将多个文件分别收纳为键值数据的ConfigMap资源创建方式,将--from-file
选项后所跟的路径指向一个目录路径就能把目录下的所有文件一同创建同一个 ConfigMap 资源中,命令格式如下: kubectl create configmap
如下命令所示,将/data/configs/nginx/conf.d目录下的所有文件都保存于nginx-config-files对象中
[root@localhost configmap]# ls config/
elasticsearch.yaml my.cnf nginx.conf
[root@localhost configmap]# kubectl create configmap elastic-configmap --from-file=config/
configmap/elastic-configmap created
[root@localhost configmap]# kubectl describe configmap elastic-configmap
查看创建的configmap对象
注意:kubectl describe 和 kubectl get -o yaml 命令都可以显示由文件创建的键值,不过两者使用的键和值之间的分隔符不同
基于配置文件创建ConfigMap时,它所使用的字段通常包括apiVersion
、kind
和metadata字段,以及用于存储数据的关键字段data
。 下面就使用配置清单创建一个ConfigMap
[root@localhost configmap]# cat cm1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-demo
namespace: default
data:
log_level: INFO
es_file: config/elasticsearch.yaml
[root@localhost configmap]# kubectl create -f cm1.yaml
[root@localhost configmap]# kubectl describe configmap configmap-demo
如果配置信息来自文件内容时,则使用配置清单创建ConfigMap资源的便捷性还不如直接通过命令行的方式,因此建议直接使用命令行加载文件或目录的方式进行创建,为了便于配置留存,可以在创建完成后使用 get -o yaml命令获取到相关信息后在进行编辑留存。
pod使用:两种方式,注入方式有挂载为存储卷和传递为环境变量两种
在Pod中,获取环境变量值的方式之一就包括ConfigMap对象中的数据,这一点通过在env字段中为valueFrom内嵌configMapKeyRef对象即可实现,格式如下:
valueFrom:
configMapKeyRef:
key:
name:
optional:
字段name值为要引用的ConfigMap对象的名称 字段key可用于指定要引用ConfigMap对象中某键的键名 字段optional用于为当前Pod资源指明此引用是否为可选
此类环境变量的使用方式与直接定义的环境变量并无区别,它们可被用于容器的启动脚本或直接传递给容器应用等。
下面示例中包含了两个资源,彼此之间使用 "--" 相分隔,第一个资源是名为 configmap-demo 的 ConfigMap 对象,它包含了两个键值数据;第二个资源是名为 busybox 的 Pod对象,它通过环境变量引用了configmap-demo对象中的键值数据,并将其直接传给了自定义运行的容器应用httpd:
[root@localhost configmap]# cat cm1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-demo
namespace: default
data:
log_level: INFO
httpd_port: "8080"
es_file: config/elasticsearch.yaml
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f","-p","$(HTTPD_PORT)","$(ES_FILE)" ]
env:
- name: HELLO_WORLD
value: just a demo
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: HTTPD_PORT #定义第一个变量名称
valueFrom: #引用变量
configMapKeyRef: #引用来自configMap的变量
name: configmap-demo #指定ConfigMap的名称
key: httpd_port #指定ConfigMap中要引用的key名称
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: configmap-demo
key: log_level
- name: ES_FILE
valueFrom:
configMapKeyRef:
name: configmap-demo
key: es_file
注意,在command和args字段中引用环境变量要使用$(VAR_NAME)
的格式,待上面配置文件中的资源创建完成后,可以通过如下命令验证Pod资源监听的端口等配置信息是否为 configmap-demo中定义的内容:
[root@localhost configmap]# kubectl exec -it busybox-86d74c5877-dqnw2 -- ps aux
PID USER TIME COMMAND
1 root 0:00 httpd -f -p 8080 config/elasticsearch.yaml
20 root 0:00 ps aux
[root@localhost configmap]# kubectl exec -it busybox-86d74c5877-dqnw2 -- printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=busybox-86d74c5877-dqnw2
MY_POD_NAMESPACE=default
HTTPD_PORT=8080
LOG_LEVEL=INFO
ES_FILE=config/elasticsearch.yaml
HELLO_WORLD=just a demo
MY_NODE_NAME=172.16.10.21
MY_NODE_IP=172.16.10.21
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
KUBERNETES_SERVICE_HOST=10.43.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
TERM=xterm
HOME=/root
[root@localhost configmap]# kubectl exec -it busybox-86d74c5877-dqnw2 -- env | grep HTTPD
HTTPD_PORT=8080
注意:创建引用了ConfigMap资源的Pod对象时,被引用的资源必须事先存在,否则将无法启动相应的容器,直到被依赖的资源创建完成为止。不过,那些未引用不存在的ConfigMap资源的容器将不受此影响。另外,ConfigMap是名称空间级别的资源,它必须与引用它的Pod资源在同一个名称空间中。
如果我们要引入的变量有很多呢?此时,为容器依次配置相应的环境变量是一件很烦躁的事情,而且容易出错,对此,Pod资源支持在容器中使用envFrom
字段直接将ConfigMap资源中的所有键值一次性地完整导入。格式如下:
spec:
containers:
- name: busybox
image: docker.io/library/busybox:1.28.4
envFrom:
- prefix: HTCPG_
configMapRef:
name: configmap_name
optional: true
envFrom字段是对象列表,可用于同时从多个ConfigMap对象导入键值数据。 为了避免从多个ConfigMap引入键值数据时产生键key重名(名称冲突),可以在每个引用中将被导入的键使用 prefix 字段指定一个特性的前缀,如 HTCPG_
一类的字符串,于是,ConfigMap对象中的httpd_port
将成为Pod资源中名为HTCPG_httpd_port
的变量。
如果键名中使用了连接线 "-",那么在转换为变量时,连接线将自动被替换为下划线 "_"。
如下:
[root@localhost configmap]# cat cm1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-demo
namespace: default
data:
log_level: INFO
httpd_port: "8080"
es_file: config/elasticsearch.yaml
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f","-p","$(HTCPG_httpd_port)","$(HTCPG_es_file)" ]
envFrom:
- prefix: HTCPG_
configMapRef:
name: configmap-demo
optional: false
#- prefix: HTCPG1_
# configMapRef:
# name: configmap-demo1
# optional: false
待Pod资源创建完成后,可通过查看其环境变量验证其导入的结果:
[root@localhost configmap]# kubectl exec -it busybox-6c685648cc-ltkdb -- ps aux
PID USER TIME COMMAND
1 root 0:00 httpd -f -p 8080 config/elasticsearch.yaml
50 root 0:00 ps aux
[root@localhost configmap]# kubectl exec -it busybox-6c685648cc-ltkdb -- printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=busybox-6c685648cc-ltkdb
HTCPG_es_file=config/elasticsearch.yaml
HTCPG_httpd_port=8080
HTCPG_log_level=INFO
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
KUBERNETES_SERVICE_HOST=10.43.0.1
TERM=xterm
HOME=/root
注意:从ConfigMap对象导入资源时,prefix为可选字段,不定义时,所有变量名同ConfigMap中的键名。如果不存在键名冲突的可能性,例如从单个ConfigMap对象导入变量或在ConfigMap对象中定义键名时依然添加了特定的前缀,那么省略前缀的定义即不会导致键名冲突,又能保持变量的简洁。
若ConfigMap对象中的键值来源于较长的文件内容,那么使用环境变量将其导入会使得变量值占据过多的内存空间而不易清理。此类数据通常用于为容器应用提供配置文件,因此将其内存直接作为文件进行引用方为较好的选择,其实现方式是,在定义Pod资源时,将此类ConfigMap对象配置为ConfigMap类型的存储卷,而后由容器将其挂载至特定的挂载点后直接进行访问。
关联为Pod资源的存储卷时,ConfigMap对象中的每个键都对应地对应为一个文件,键名转为文件名,而值则为相应文件的内容,即便是通过直接创建的键值数据,也一样表现为文件视图。挂载于容器上之后,由键值数据表现出的文件位于挂载点目录中,容器中的进程可直接读取这些文件的内容。
配置Pod资源时,基于存储卷的方式引用ConfigMap对象的方法非常简单,仅需要指明卷名称及要应用的ConfigMap对象名称即可。
2.1.1).创建配置文件 我们下面创建三个Nginx配置文件,然后将这三个配置文件挂载到Nginx的配置目录中
[root@localhost configmap]# ls config/
elasticsearch.yaml my.cnf nginx.conf
2.1.2).创建ConfigMap对象
基于目录创建config-files对象
[root@localhost configmap]# kubectl create configmap config-files --from-file=config/
[root@localhost configmap]# kubectl describe cm config-files -n default
2.1.3).创建Pod资源清单来引用ConfigMap对象并将其挂载至相应指定的目录中
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
#args: [ "-f","-p","$(HTCPG_httpd_port)","$(HTCPG_es_file)" ]
args: [ "-f" ]
volumeMounts: #卷挂载配置
- name: config #卷名称
mountPath: /tmp #挂载到容器中的路径目录
readOnly: true #是否只读
volumes: #卷配置
- name: config #定义一个卷名称
configMap: #configMap配置
name: config-files #指定configMap名称
[root@localhost configmap]# kubectl describe pod/busybox-679b55c4fb-x6wdc | grep -A 2 Mounts
Mounts:
/tmp from config (ro)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-cztvq (ro)
[root@localhost configmap]# kubectl exec -it pod/busybox-679b55c4fb-x6wdc -- ls /tmp/ -al
total 0
drwxrwxrwx 3 root root 117 Jan 26 07:20 .
drwxr-xr-x 1 root root 51 Jan 26 07:20 ..
drwxr-xr-x 2 root root 64 Jan 26 07:20 ..2022_01_26_07_20_49.547544433
lrwxrwxrwx 1 root root 31 Jan 26 07:20 ..data -> ..2022_01_26_07_20_49.547544433
lrwxrwxrwx 1 root root 25 Jan 26 07:20 elasticsearch.yaml -> ..data/elasticsearch.yaml
lrwxrwxrwx 1 root root 13 Jan 26 07:20 my.cnf -> ..data/my.cnf
lrwxrwxrwx 1 root root 17 Jan 26 07:20 nginx.conf -> ..data/nginx.conf
有时候,用户很可能不期望在容器中挂载某ConfigMap存储卷中的所有文件,这在通过一个ConfigMap对象为单个Pod资源中的多个容器分别提供配置时尤其常见。例如前面示例中,用户可能只期望在容器中挂载ConfigMap存储卷后只“导出”其中出my.cnf和elasticsearch.yaml文件.此时将其 volumes 配置段改为如下所示的内容即可。
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
#args: [ "-f","-p","$(HTCPG_httpd_port)","$(HTCPG_es_file)" ]
args: [ "-f" ]
volumeMounts: #卷挂载配置
- name: config #卷名称
mountPath: /tmp #挂载到容器中的路径目录
readOnly: true #是否只读
volumes: #卷配置
- name: config #定义一个卷名称
configMap: #configMap配置
name: config-files #指定configMap名称
items: #config-files ConfigMap中的键
- key: my.cnf #指定要挂载键 默认文件名就是key,原文件名(key的名称)
path: my.cnf #挂载的路径,修改之后的文件名(key的名称)
mode: 0644 #挂载后的文件权限
- key: elasticsearch.yaml
path: elasticsearch.yaml
mode: 0644
1)假如不想以key名作为配置文件名可以引入items
字段,在其中逐个指定要用相对路径path
替换的key
2)items还有一个作用,就是只有items下的key对应的文件会被挂载到容器中。
ConfigMap存储卷的items字段的值是一个对象列表,可嵌套使用的字段有三个,具体如下:
验证:
[root@localhost configmap]# kubectl exec -it pod/busybox-67c86587bf-5hjfm -- ls /tmp/
elasticsearch.yaml my.cnf
[root@localhost configmap]# kubectl describe pod/busybox-67c86587bf-5hjfm | grep -A 2 Mounts
Mounts:
/tmp from config (ro)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4tsq4 (ro)
上面的两种方式中,无论是装载所有文件还是部分文件,挂载点目录下原有的文件都会被隐藏或者称为覆盖,在我们没有挂载的时候,/etc/nginx/conf.d目录下有default.conf文件,当我们挂载之后default.conf就被隐藏或者说覆盖掉了,但有时候我们希望挂载进的文件不覆盖相应目录下的其它文件,这个时候就可以通过volumeMounts
属性中的subPath
字段来解决,它可以支持用户从存储卷挂载单个文件或者单个目录而非整个存储卷。
subPath的使用方法一共有两种:
1. 同一个pod中多容器挂载同一个卷时提供隔离
2. 将configMap和secret作为文件挂载到容器中而不覆盖挂载目录下的文件
主要解释第一点,按照k8s官网的解释,subPath在是挂载卷中的存储目录,不指定默认存储在卷的根目录
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
#args: [ "-f","-p","$(HTCPG_httpd_port)","$(HTCPG_es_file)" ]
args: [ "-f" ]
volumeMounts: #卷挂载配置
- name: mysql-config #卷名称
mountPath: /etc/my.cnf #挂载到容器中的路径目录 不指定subPath 那么/etc/my.cnf当做目录
subPath: my.cnf #subPath此时指的就是configMap中的key,也就是文件名
readOnly: true #是否只读
- name: es-config #卷名称
mountPath: /etc/elasticsearch.yaml #挂载到容器中的路径目录
subPath: elasticsearch.yaml
readOnly: true #是否只读
volumes: #卷配置
- name: mysql-config #定义一个卷名称
configMap: #configMap配置
name: mysql-config-files #指定configMap名称
- name: es-config #定义一个卷名称
configMap: #configMap配置
# 如果没有subPath,则//etc/my.cnf和/etc/elasticsearch.yaml将被当成一个目录而不是文件,根据Linux挂载的概念,该目录下的所有文件会被覆盖。
subPath此时指的就是configMap中的key,也就是文件名
测试一下:
首先创建一个包含两个container的pod,对应的yaml文件如下,两个container挂载目录下的文件都会存储在卷的根目录下
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox-mysql
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f" ]
volumeMounts:
#- name: mysql-config
- name: es-config
mountPath: /etc/wubo
#subPath: my.cnf
#readOnly: false
- name: busybox-es
image: docker.io/library/busybox:1.28.4
command: [ "/bin/sh" ]
args: [ "-c","sleep 30000" ]
volumeMounts:
- name: es-config
mountPath: /etc/wubo
#subPath: elasticsearch.yaml
#readOnly: false
volumes:
- name: es-config
hostPath:
path: /opt/wubo
type: Directory
接下来做一个简单的验证,首先进到busybox-mysql中在/etc/wubo/a.txt下创建一个a.txt的文件,退出之后进入busybox-es的/etc/wubo/a.txt目录,因为未指定subPath,文件会直接存储在卷的根目录下,所以在busybox-es下可以看见busybox-mysqlr刚刚新建的a.txt文件
root@localhost configmap]# kubectl exec -it pod/busybox-6fbbc45c5c-qfxpw -c busybox-mysql -- sh
/etc # cd etc/wubo/
/etc/wubo # mkdir wuqi
/etc/wubo # cd wuqi/
/etc/wubo/wuqi # touch a
/etc/wubo/wuqi # echo aaa > a
/etc/wubo/wuqi # cat a
aaa
[root@localhost configmap]# kubectl exec -it pod/busybox-6fbbc45c5c-qfxpw -c busybox-es -- sh
/etc # cd etc/wubo/wuqi
/etc/wubo # cd wuqi/
/etc/wubo/wuqi # cat a
aaa
接下来试着加入subPath,对应的yaml文件如下
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox-mysql
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
args: [ "-f" ]
volumeMounts:
#- name: mysql-config
- name: es-config
mountPath: /etc/wubo
subPath: mysql
#readOnly: false
- name: busybox-es
image: docker.io/library/busybox:1.28.4
command: [ "/bin/sh" ]
args: [ "-c","sleep 30000" ]
volumeMounts:
- name: es-config
mountPath: /etc/wubo
subPath: es
#readOnly: false
volumes:
- name: es-config
hostPath:
path: /opt/wubo
type: Directory
同样进入两个容器的挂载目录下创建新文件,可以看到subPath起到了隔离的作用,在另一个容易看不到目录和文件了
[root@localhost configmap]# kubectl exec -it pod/busybox-f6966c85-b4fvl -c busybox-mysql -- sh
/ # cd etc/wubo
/etc/wubo # mkdir wuqi
/etc/wubo # cd wuqi/
/etc/wubo/wuqi # echo aaa > a
/etc/wubo/wuqi # cat a
aaa
[root@localhost configmap]# kubectl exec -it pod/busybox-f6966c85-b4fvl -c busybox-es -- sh
/ # cd etc/
/etc # ls
group hostname hosts localtime network passwd resolv.conf shadow wubo
/etc # cd wubo/
/etc/wubo # ls
/etc/wubo # pwd
/etc/wubo
例如下面示例/etc 目录中挂载一个elasticsearch.yaml和my.cnf 文件,并且不覆盖原来目录etc下的原有的 文件。
删除之前看下容器里面的文件内容
[root@localhost configmap]# kubectl exec -it pod/busybox-67c86587bf-5hjfm -- ls /etc
group hosts network resolv.conf
hostname localtime passwd shadow
是没有 elasticsearch.yaml和my.cnf两个文件的
2.3.1) 删除原来创建的ConfigMap和Pod
[root@localhost configmap]# kubectl delete cm config-files
[root@localhost configmap]# kubectl delete -f busybox.yaml
2.3.2)创建两个ConfigMap,分别用于测试文件elasticsearch.yaml和my.cnf
[root@localhost configmap]# kubectl create configmap mysql-config-files --from-file=./config/mysql/my.cnf
configmap/mysql-config-files created
[root@localhost configmap]# kubectl create configmap es-config-files --from-file=./config/es/elasticsearch.yaml
configmap/es-config-files created
2.3.3)创建Pod资源配置清单
[root@localhost configmap]# cat busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
labels: {name: busybox}
spec:
replicas: 1
selector:
matchLabels: {name: busybox}
template:
metadata:
name: busybox
labels: {name: busybox}
spec:
containers:
- name: busybox
#image: harbor.jettech.com/jettechtools/busybox:1.21.4
#image: 172.16.10.5:5000/library/busybox:1.21.4
image: docker.io/library/busybox:1.28.4
command: [ "httpd" ]
#args: [ "-f","-p","$(HTCPG_httpd_port)","$(HTCPG_es_file)" ]
args: [ "-f" ]
volumeMounts: #卷挂载配置
- name: mysql-config #卷名称
mountPath: /etc/my.cnf #挂载到容器中的路径目录
subPath: my.cnf
readOnly: true #是否只读
- name: es-config #卷名称
mountPath: /etc/elasticsearch.yaml #挂载到容器中的路径目录
subPath: elasticsearch.yaml
readOnly: true #是否只读
volumes: #卷配置
- name: mysql-config #定义一个卷名称
configMap: #configMap配置
name: mysql-config-files #指定configMap名称
- name: es-config #定义一个卷名称
configMap: #configMap配置
name: es-config-files #指定configMap
2.3.4)查看Pod对象挂载状态
[root@localhost configmap]# kubectl exec -it pod/busybox-7548665cfc-26kc6 -- ls /etc/
elasticsearch.yaml localtime resolv.conf
group my.cnf shadow
hostname network
hosts passwd
[root@localhost configmap]# kubectl describe pod/busybox-7548665cfc-26kc6 | grep -A 3 Mounts
Mounts:
/etc/elasticsearch.yaml from es-config (ro,path="elasticsearch.yaml")
/etc/my.cnf from mysql-config (ro,path="my.cnf")
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7sfvm (ro)
在Pod资源中调用ConfigMap对象时需要注意以下几个问题。
optional
,否则会导致Pod无法正常启动的错误,同样即使存在ConfigMap,在引用ConfigMap中的键不存在时,也会导致一样的错误。InvalidVariableNames
事件记录于日志中。--manifest-url
或--config
选项,以及 kubelet REST API 创建的Pod。redis完整案例:
[root@jettoloader redis]# cat jettoloader-redis-develop-poc.yaml
apiVersion: v1
kind: Namespace
metadata:
name: jettoloader-poc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jettoloader-redis-poc-pvc
namespace: jettoloader-poc
spec:
accessModes: #访问模式
#- ReadWriteOnce
#- ReadWriteOncePod
- ReadWriteMany
#- ReadOnlyMany
resources: #申请资源,1Gi存储空间
requests:
storage: 1Gi
storageClassName: jettech-nfs-storage
#selector:
# matchLabels:
# name: "wubo-pv1"
# matchExpressions:
# - {key: environment, operator: In, values: [dev]}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: jettoloader-redis-poc-conf
namespace: jettoloader-poc
data:
redis.conf: |
bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
#pidfile /var/run/redis_6379.pid
pidfile /data/redis_6379.pid
loglevel notice
logfile /var/log/redis/redis.log
#logfile /data/redis.log
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
#dir /var/lib/redis
dir /data
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
requirepass "123456aA"
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
---
apiVersion: v1
kind: Service
metadata:
labels: {name: jettoloader-redis-poc-server}
name: jettoloader-redis-poc-server
namespace: jettoloader-poc
spec:
ports:
- {name: t16379, nodePort: 16379, port: 6379, protocol: TCP, targetPort: t6379}
selector: {name: jettoloader-redis-poc-pod}
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels: {name: jettoloader-redis-poc-deploy}
name: jettoloader-redis-poc-deploy
namespace: jettoloader-poc
spec:
replicas: 1
selector:
matchLabels: {name: jettoloader-redis-poc-pod}
template:
metadata:
labels: {name: jettoloader-redis-poc-pod}
name: jettoloader-redis-poc-pod
spec:
#affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: envtype
# operator: In
# values:
# - poc
#initContainers:
containers:
- name: jettoloader-redis-poc-container
image: 172.16.10.21:5000/jettechtools/redis:3.2
#lifecycle:
# postStart:
# exec:
# command: ["/bin/sh","-c","mkdir -p /var/log/redis && ln -sf /data/redis.log /var/log/redis/redis.log"]
#preStop:
#env:
# - {name: APPLY_PORT, value: '8097'}
securityContext:
privileged: true
ports:
- {containerPort: 6379, name: t6379, protocol: TCP }
#volumeMounts:
#- name: redis-conf
# mountPath: /etc/redis/
#- name: redis-data
# mountPath: /data
volumeMounts:
- name: jettoloader-redis-poc-conf
mountPath: /etc/redis/redis.conf
subPath: redis.conf #cm中的key
readOnly: true
- name: jettoloader-redis-poc-data
mountPath: /data
command:
- "redis-server"
args:
- "/etc/redis/redis.conf"
- "--appendonly yes"
imagePullPolicy: Always #[Always | Never | IfNotPresent]
securityContext:
privileged: true
#hostNetwork: true
restartPolicy: Always #Never
volumes:
- name: jettoloader-redis-poc-conf
configMap:
name: jettoloader-redis-poc-conf
items:
- key: redis.conf
path: redis.conf
- name: jettoloader-redis-poc-data
persistentVolumeClaim:
claimName: jettoloader-redis-poc-pvc
#volumes:
#- name: redis-conf
# nfs:
# server: 172.16.10.4
# path: /var/ftp/jettech/jettoloader/tools/poc/redis/conf
#- name: redis-data
# nfs:
# server: 172.16.10.4
# path: /var/ftp/jettech/jettoloader/tools/poc/redis/data