一、ConfigMap和Secret基础

当某个服务需要修改配置文件时,如果涉及多个服务器,或者是多实例,而且不同的实例在不同的环境下配置要求还不一样,这样每台物理机或每个实例都得手动修改。还有一种方法是把配置文件嵌入到镜像中,那也要每修改一次配置文件就得打一次镜像,路径长且效率也不高。此外还能在Pod中定义环境变量,不过这种方法也有缺陷,即无法在容器应用运行过程中更新环境变量以达到更新应用的目的。因此,以上这些方式都难以满足线上大批量的配置变更需求。
      为此,Kubernetes引入了ConfigMap资源对象,可以把多个配置文件封装成卷来挂载到Pod中,Pod可引用该存储卷上的数据赋值给环境变量,也可直接挂载作为配置文件使用。

Kubernetes借助ConfigMap实现了将配置文件从容器镜像中解耦,使配置易于更改和管理。但ConfigMap资源用于存储和共享非敏感和未加密的配置信息。若要在集群中使用敏感信息(eg:密钥、证书),则需要使用Secret资源。二者均属于名称空间级别,只能被同一名称空间的Pod引用。
      ConfigMap和Secret都是数据承载类组件,主要负责提供key-value格式的数据,其值支持:

  • 单行字符串,常用于保存环境变量值,或者命令行参数
  • 多行字串,常用于保存配置文件的内容

Kubernetes的ConfigMap和Secret介绍_Secret

另外,它们的资源规范中不使用spec字段(与StorageClass资源类似),而是直接使用特定字段嵌套key-value数据

  • ConfigMap支持使用data或binaryData字段嵌套一个或多个键值数据项
  • Secret支持使用data或stringData(非base64编码的铭文格式)字段嵌套一个或多个键值数据项

自Kubernetes v1.19版本开始,ConfigMap和Secret支持使用immutable字段创建不可变实例。

二、ConfigMap

2.1 创建ConfigMap对象

常用方法有两种

2.1.1 命令式命令

  • 字面量,重复使用--from-literal选项可以一次性传递多个键值对。
kubectl create configmap NAME --from-literal=key1=value1 …
  • 从文件加载,通过--from-file选项一次加载一个配置文件的内容作为指定键的值,多个文件的加载可重复使用--from-file选项完成。省略键名时,将默认使用指定指定的目标文件作为键名。
kubectl create configmap NAME --from-file=[key]=<path-to-file> …
  • 从目录加载,在--from-file选项上附加一个目录路径就能将该目录下的所有文件创建于同一个ConfigMap资源中,各文件名即为键名,对应的文件内容为值。
kubectl create configmap NAME --from-file=<path-to-directory> …

2.1.2 配置文件

  • 命令式:kubectl create -f
  • 声明式:kubectl apply -f

先看一个示例,直接使用字面量创建ConfigMap。

kubectl create cm demoapp-config --from-literal=port="8080" --from-literal=host="0.0.0.0"

再看一个示例,是从目录加载的。

cd /root/Kubernetes_Advanced_Practical_2rd/chapter6/nginx-conf.d
#cm为configmap的简称
kubectl create cm nginx-cfg --from-file=.

此目录中包含myserver.conf、myserver-gzip.cfg和myserver-status.cfg三个配置文件,它们会被分别存储为3个键值数据。如下面的显示结果所示。

kubectl describe cm nginx-cfg
Name: nginx-cfg
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
myserver-gzip.cfg:    #键值数据1,describe命令中的输出中键和值使用“----”来分隔
----
gzip on;
gzip_comp_level 5;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/xml text/javascript;
myserver-status.cfg:    #键值数据2
----
location /nginx-status {
 stub_status on;
 access_log off;
}
myserver.conf:    #键值数据3
----
server {
 listen 8080;
server_name www.ik8s.io;
include /etc/nginx/conf.d/myserver-*.cfg;
 location / {
 root /usr/share/nginx/html;
 }
}
BinaryData
====
Events: <none>

其实为了方便起见,可以将命令式命令加载为配置文件的形式,再写入到某个yml文件中,之后修改完成后apply即可。

kubectl create cm nginx-cfg --from-file=. --dry-run=client -o yaml > nginx-cfg-configmap.yml

2.2 Pod引用ConfigMap对象

Pod引用ConfigMap资源对象的方式通常由两种:

2.2.1 环境变量

  • 引用ConfigMap对象上指定的key,以valueFrom赋值给Pod上指定的环境变量
  • 在Pod上使用envFrom一次性导入ConfigMap对象上的所有key-value,key(也可以统一加特定前缀)即为环境变量名,value为相应的变量值

使用格式如下:

env:
  #要赋值的环境变量名
- name <string>
  #定义变量值引用
  valueFrom:
    #变量值来自ConfigMap对象的某个指定键的值
    configMapKeyRef:
      #对象的名称
      name <string>
      #键名称
      key <string>
      #指定的ConfigMap对象或者指定的键名是否可选。如果为false,当ConfigMap对象或指定的键不存在时Pod初始化失败。为true则无影响
      optional <boolean>

2.2.2 ConfigMap卷

  • 在Pod上将ConfigMap对象引用为存储卷,而后由容器挂载到某个目录。其中key转为(容器挂载点下的)文件名,value为对应的文件内容
  • 在Pod上定义ConfigMap卷时,仅引用其中的部分key,而后由容器挂载到目录下

这里给出一个示例,通过环境变量引用ConfigMap

vim configmaps-env-demo.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: demoapp-config
  namespace: default
data:
  demoapp.port: "8080"
  demoapp.host: 0.0.0.0
---
apiVersion: v1
kind: Pod
metadata:
  name: configmaps-env-demo
  namespace: default
spec:
  containers:
  - image: ikubernetes/demoapp:v1.0
    name: demoapp
    env:
    - name: PORT
      valueFrom:
        configMapKeyRef:
          name: demoapp-config
          key: demoapp.port
          optional: false
    - name: HOST
      valueFrom:
        configMapKeyRef:
          name: demoapp-config
          key: demoapp.host
          optional: true
 
kubectl apply -f configmaps-env-demo.yaml

进入容器验证,看到Pod资源监听的是0.0.0.0的8080端口,正是demoapp-config对象中定义的内容。

Kubernetes的ConfigMap和Secret介绍_ConfigMap_02

注意:被引用的ConfigMap资源必须事先存在,否则无法在Pod对象中启动引用了ConfigMap对象的容器,但未引用或不存在ConfigMap资源的容器不受影响。

接下来再看一个示例,通过存储卷引用ConfigMap(挂载整个存储卷)

vim configmaps-volume-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmaps-volume-demo
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: nginx-server
    volumeMounts:
    - name: ngxconfs
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: ngxconfs
    configMap:
      name: nginx-config-files
      optional: false
#需要有相关的ConfigMap,先创建后再运行该yaml文件
cd /root/Kubernetes_Advanced_Practical_2rd/chapter6
kubectl create cm nginx-config-files --from-file=nginx-conf.d/
kubectl apply -f configmaps-volume-demo.yaml

进入容器查看发现/etc/nginx/conf.d目录下确实有如上描述的对应的三个文件,但都是链接,链接到..data隐藏属性的子目录下,而..data又链接到一个随机时间戳生成的临时目录,该目录才是存储卷的真正挂载点。

Kubernetes的ConfigMap和Secret介绍_Secret_03

在线修改ConfigMap里面的内容,看看在容器中是否实时生效

kubectl edit cm nginx-config-files

将gzip的压缩级别由5改成6(正常是修改ConfigMap涉及的目录下的内容再重新apply)。再次查看,发现压缩级别就是6了。同时..data链接的时间戳目录也会发生变化。

kubectl exec -it configmaps-volume-demo -- nginx -T

Kubernetes的ConfigMap和Secret介绍_ConfigMap_04

Kubernetes的ConfigMap和Secret介绍_Kubernetes_05

三、Secret

Secret主要用于存储密钥、OAuth令牌、ssh密钥等敏感信息,这些敏感信息采用base64编码保存。Secret根据用途不同,也有不同的类型,见下表。

内置类型

用法

Opaque

用户定义的任意数据

kubernetes.io/service-account-token

服务账号令牌

kubernetes.io/dockercfg

~/.dockercfg文件的序列化形式

kubernetes.io/dockerconfigjson

~/.docker/config.json文件的序列化形式

kubernetes.io/basic-auth

用于基本身份认证的凭据

kubernetes.io/ssh-auth

用于 SSH 身份认证的凭据

kubernetes.io/tls

用于 TLS 客户端或者服务器端的数据

bootstrap.kubernetes.io/token

启动引导令牌数据

Secret的创建及使用方式可用help选项查看

Kubernetes的ConfigMap和Secret介绍_ConfigMap_06

Secret对象不同类型的子类别介绍如下:

  • generic:基于本地文件、目录或字面值创建的Secret,一般用来存储密码、密钥、信息、证书等数据
  • docker-registry:用于认证到Docker Registry的Secret,用于使用私有容器镜像
  • tls:基于指定的公钥/私钥对创建TLS Secret,专用于TLS通信中。指定的公钥和私钥必须事先存在。

其中kubernetes.io/tls属于tls子类,kubernetes.io/dockercfg和kubernetes.io/dockerconfigjson属于docker-registry子类,其它的属于generic子类。

3.1 创建Secret资源

与ConfigMap资源的创建方式类似,也支持字面量、从目录加载等。但Secret有类型子命令。

##generic子类型
#除了docker-registry和tls命令之外的其它类型,都可以使用该命令中的--type选项进行定义
kubectl create secret generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1]
##tls子类型
#通常,其保存的cert文件(--cert指明证书文件)内容的key为tls.crt,而保存private key(--key指明私钥文件)的key为tls.key
kubectl create secret tls NAME --cert=cert文件路径 --key=key文件路径
##docker-registry子类型
kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-file=[key=]source]

先看一个generic子类型的Secret示例。

kubectl create secret generic mysql-secret --from-literal=username=root --from-literal=password=linux

可以看到,以generic子命令创建的Secret对象是Opaque类型,其键值数据都会以base64编码格式显示,可用base64命令解码。

Kubernetes的ConfigMap和Secret介绍_Secret_07

3.2 引用Secret对象

与ConfigMap类似,也支持环境变量和secret卷方式。

3.2.1 环境变量

  • 引用Secret对象上特定的key,以valueFrom赋值给Pod上指定的环境变量
  • 在Pod上使用envFrom一次性导入Secret对象上的所有key-value,key(可以统一加指定前缀)为环境变量名,value为相应的变量值

3.2.2 Secret卷

  • 在Pod上将Secret对象引用为存储卷,而后整体由容器挂载到某个目录下。key转为文件名,value为相应的文件内容
  • 在Pod上定义Secret卷时,引用其中的部分key,而后由容器挂载到目录下

注意:容器很可能会将环境变量打印到日志中,可能泄露敏感信息。因此不建议以环境变量方式引用Secret中的数据。

这里给出一个示例,是使用Secret存储卷的。它将ngins-ssl-secret对象关联为Pod对象上名为nginxcerts的存储卷,而后由容器ngxserver挂载到/etc/nginx/certs目录下。

vim secrets-volume-demo.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: secrets-volume-demo
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: ngxserver
    volumeMounts:
    - name: nginxcerts
      mountPath: /etc/nginx/certs/
      readOnly: true
    - name: nginxconfs
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: nginxcerts
    secret:
      secretName: nginx-ssl-secret
  - name: nginxconfs
      configMap:
        name: nginx-sslvhosts-confs
        optional: false

注意:ConfigMap对象nginx-sslvhosts-confs存储有证书文件tls.crt和私钥文件tls.key,这些文件是Pod挂载Secret对象nginx-ssl-secret在挂载目录/etc/nginx/serts生成的,并根据该证书和私钥文件在nginx配置文件中配置了有https类型的虚拟主机。并且所有发往80端口的流量都会重定向到https虚拟主机。虚拟主机的关键部分配置如下:

Kubernetes的ConfigMap和Secret介绍_ConfigMap_08

由于Pod引用的ConfigMap对象和Secret对象必须事先存在,需要先生成再运行Pod。创建Secret对象时无论用户提供的证书和私钥是什么名字,它们都会转为以tls.key(私钥)和tls.crt(证书)作为secret对象的键名。证书内容也被base64编码。

kubectl create secret tls nginx-ssl-secret --cert=nginx.crt --key=nginx.key
kubectl create configmap nginx-sslvhosts-confs --from-file=./nginx-ssl-conf.d

当Pod资源成功运行后使用如下命令访问Pod,可以看到对Pod内Nginx服务发起https请求确实能访问到。另外/etc/nginx/certs确实有对应私钥和证书文件,挂载到..data隐藏的子目录下,而..data又链接到一个随机时间戳生成的临时目录,该目录才是存储卷的真正挂载点。这与ConfigMap显示的结果类似。

curl -I -k https://Pod的ip地址

Kubernetes的ConfigMap和Secret介绍_ConfigMap_09

Kubernetes的ConfigMap和Secret介绍_ConfigMap_10

四、DownwardAPI和Projected

4.1 关于DownwardAPI

与ConfigMap和Secret不同,DownwardAPI并非是一种独立的资源类型,DownwardAPI只是一种将Pod的metadata、spec或status中的字段值注入到其内部Container里面的方式。

简单的理解就是,无需创建任何卷。将API Server当做数据源,从中读取Pod的相关信息并注入到容器中。

DownwardAPI提供了两种方式用于将Pod信息注入到容器内部:

  • 环境变量:用于单个变量,可将Pod信息和容器信息直接注入容器内部。
  • Volume挂载:将Pod信息生成为文件,直接挂载到容器内部中去。

在容器上基于DownwardAPI引用Pod元数据,可通过两种字段完成:

  • fieldRef:引用常规的元数据。
  • resourceFieldRef:引用同资源限制和资源需求相关的元数据。

以下字段使用fieldRef字段来引用,见下表。

字段

功能

是否可用于环境变量

是否可用卷

spec.nodeName

Pod运行所在节点的名称

status.hostIP

工作节点的IP地址

metadata.name

Pod的名称

metadata.namespace

Pod隶属的名称空间

status.podIP

Pod的IP地址

spec.serviceAccountName

Pod使用的ServiceAccount名称

metadata.uid

Pod的UID

metadata.labels['<KEY>']

Pod上指定Key的标签的值

metadata.annotations['<KEY>']

Pod上指定Key的注解的值

metadata.labels

Pod上的所有标签,每行一行,转为key=value的形式

metadata.annotations

Pod上的所有注解,每行一行,转为key=value的形式

有关容器资源限制和资源需求的信息要通过resourceFieldRef字段注入,相关字段见下表。

字段

功能

是否可用于环境变量

是否可用卷

requests.cpu

容器上定义的CPU资源需求量

requests.memory

容器上定义的内存资源需求量

requests.ephermera-storage

容器上定义的临时卷资源需求量

limits.cpu

容器上定义的CPU资源限制量

limits.memory

容器上定义的内存资源限制量

limits.ephermera-storage

容器上定义的临时卷资源限制量

以下是一个Pod使用DownwardAPI中的fieldRef字段的例子。

vim pod-dapi.yml
apiVersion: v1
kind: Pod
metadata:
  name: pod-dapi
spec:
  containers:
  - name: demoapp
    image: ikubernetes/demoapp:v1.0
    env:
    - name: HOST
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name

kubectl apply -f pod-dapi.yml

Pod创建成功后,进入Pod内部,发现POD_NAME这个环境变量的值为pod-dapi,HOST这个环境变量的值正是Pod IP->10.244.3.31。

Kubernetes的ConfigMap和Secret介绍_Kubernetes_11

4.2 Projected Volume

Projected Volume 是一种特殊的卷类型,它能够将已存在的多个卷投射进同一个挂载点目录中。

       Projected Volume仅支持对如下四种类型的卷(数据源)进行投射操作,这类的卷一般都是用于为容器提供预先定义好的数据。

  • Secret:投射Secret对象
  • ConfigMap:投射ConfigMap对象
  • DownwardAPI:投射Pod元数据
  • ServiceAccountToken:投射ServiceAccountToken

如下截图截取自某个Pod的信息片段,sources指定投射的数据源,其中Projected Volume分别投射ServiceAccountToken、configMap、DownwardAPI三类卷。挂载点是/var/run/secrets/kubernetes.io/serviceaccount,这意味着在该挂载点下生成三个文件。第一个文件是由ServiceAccountToken这个数据源生成的,文件名(path)为token。第二个文件是基于configMap这个数据源生成的,文件名(path)为ca.crt,文件内容来自于configMap中ca.crt这个键的内容。第三个文件是基于downwardAPI这个数据源生成的,文件名(path)为namespace,文件内容来自于fieldPath所指定的元数据的字段(metadata.namespace)中的数据。

Kubernetes的ConfigMap和Secret介绍_Kubernetes_12