k8s学习笔记之ConfigMap和Secret

一、简介

ConfigMap对像是一系列配置的集合,k8s会将这一集合注入到对应的Pod对像中,并为容器成功启动使用。注入的方式一般有两种,一种是挂载存储卷,一种是传递变量。ConfigMap被引用之前必须存在,属于名称空间级别,不能跨名称空间使用,内容明文显示。ConfigMap内容修改后,对应的pod必须重启或者重新加载配置。
Secret类似于ConfigMap,是用Base64加密,密文显示,一般存放敏感数据。一般有两种创建方式,一种是使用kubectl create创建,一种是用Secret配置文件。

使用场景

我们经常都需要为我们的应用程序配置一些特殊的数据,比如密钥、Token 、数据库连接地址或者其他私密的信息。你的应用可能会使用一些特定的配置文件进行配置,比如settings.py文件,或者我们可以在应用的业务逻辑中读取环境变量或者某些标志来处理配置信息。
我们要做到这个,有好多种方案,比如:
1.我们可以直接在打包镜像的时候写在应用配置文件里面,但是这种方式的坏处显而易见而且非常明显。
2.我们可以在配置文件里面通过 env 环境变量传入,但是这样的话我们要修改 env 就必须去修改 yaml 文件,而且需要重启所有的 container 才行。
3.我们可以在应用启动的时候去数据库或者某个特定的地方拿,没问题!但是第一,实现起来麻烦;第二,如果配置的地方变了怎么办?

当然还有别的方案,但是各种方案都有各自的问题。
而且,还有一个问题就是,如果说我的一个配置,是要多个应用一起使用的,以上除了第三种方案,都没办法进行配置的共享,就是说我如果要改配置的话,那得一个一个手动改。假如我们有 100 个应用,就得改 100 份配置,以此类推……

kubernetes 对这个问题提供了一个很好的解决方案,就是用 ConfigMap 和 Secret。    

二、ConfigMap创建及使用

1.查看命令帮助

[root@k8s-master-155-221 configmap]# kubectl create  configmap --help
......
Aliases:
configmap, cm  #可以使用cm替代

Examples:
  # Create a new configmap named my-config based on folder bar
  kubectl create configmap my-config --from-file=path/to/bar  #从目录创建  文件名称为键  文件内容为值?
  
  # Create a new configmap named my-config with specified keys instead of file basenames on disk
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt #从文件创建 key1为键 文件内容为值
  
  # Create a new configmap named my-config with key1=config1 and key2=config2
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2   #直接命令行给定,键为key1 值为config1
  
  # Create a new configmap named my-config from the key=value pairs in the file
  kubectl create configmap my-config --from-file=path/to/bar   #从文件创建 文件名为键 文件内容为值
  
  # Create a new configmap named my-config from an env file
  kubectl create configmap my-config --from-env-file=path/to/bar.en #不知道,没用过

2.ConfigMap主要有以下几种方式创建

通过literal方式创建ConfigMap

[root@k8s-master-155-221 ~]# mkdir manifests
[root@k8s-master-155-221 ~]# cd manifests/
[root@k8s-master-155-221 ~]# kubectl create configmap nginx-config --from-literal=nginx_port=80 --from-literal=server_name=myapp.qiangungun.com  #命令行给定 nginx_port为键 80为值
configmap/nginx-config created
[root@k8s-master-155-221 ~]# kubectl describe cm nginx-config
Name:         nginx-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
sever_name:  #键
----
myapp.qiangungun.com #值
nginx_port:  #键
----
80  #值
Events:  <none>
[root@k8s-master-155-221 ~]# kubectl get cm nginx-config -o yaml
apiVersion: v1
data:
  nginx_port: "8088"   #键:值
  server_name: myapp.qiangungun.com #键:值对
kind: ConfigMap
metadata:
  ......

通过file的方式创建

[root@k8s-master-155-221 manifests]# mkdir configmap
[root@k8s-master-155-221 manifests]# cd configmap/
[root@k8s-master-155-221 configmap]# vim www.conf
server{
  server_name myapp.magedu.com;
  listen 80;
  root /data/web/html;
}
[root@k8s-master-155-221 configmap]# kubectl create configmap www --from-file=./www.conf  #直接以文件名称为键,文件内容为值,也可以使用--from-file=key=/path/to/file自定义键
configmap/www created
[root@k8s-master-155-221 configmap]# kubectl get configmaps www
NAME   DATA   AGE
www    1      23s
[root@k8s-master-155-221 configmap]# kubectl get configmaps www -o yaml  #或者使用describe命令查看
apiVersion: v1
data:
  www.conf: |
    server{
      server_name myapp.magedu.com;
      listen 80;
      root /data/web/html;
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2020-01-13T14:21:13Z"
  name: www
  namespace: default
  resourceVersion: "7809631"
  selfLink: /api/v1/namespaces/default/configmaps/www
  uid: f3dd1cd4-360f-11ea-87ad-525400512eca

通过YAML文件创建

[root@k8s-master-155-221 configmap]# vim my-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: customer1
data:
  key1: value1
  key2: value2
[root@k8s-master-155-221 configmap]# kubectl apply -f my-configmap.yml
configmap/customer1 created
[root@k8s-master-155-221 configmap]# kubectl describe cm customer1 
Name:         customer1
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"key1":"value1","key2":"value2"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"customer1","namespace"...

Data
====
key1:
----
value1
key2:
----
value2
Events:  <none>

3.使用ConfigMap主要有以下几种方式:

ENV注入:

[root@k8s-master-155-221 configmap]# vim pod-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-1
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: httpd
      containerPort: 80
    env:
    - name: NGINX_SERVER_PORT  #pod中显示的变量名称
      valueFrom:
        configMapKeyRef:
          key: nginx_port   #引用的configmap中的键名称      
          name: nginx-config   #引用的configmap的名称
          optional: true      #该选项是否是必须的(true表示必须存在,否则pod无法启动)
    - name: NGINX_SERVER_NAME
      valueFrom:
        configMapKeyRef:
          key: server_name
          name: nginx-config
          optional: true
          

[root@k8s-master-155-221 configmap]# kubectl apply -f pod-configmap.yaml
pod/pod-cm-1 created
[root@k8s-master-155-221 configmap]# kubectl get pods |grep pod-cm-1
pod-cm-1                       1/1     Running   0          3m11s
[root@k8s-master-155-221 configmap]# kubectl get pods |grep pod-cm-1
pod-cm-1                       1/1     Running   0          13s
[root@k8s-master-155-221 configmap]# kubectl exec -it pod-cm-1 -- /bin/sh
/ # env|grep NGINX_SERVER_PORT
NGINX_SERVER_PORT=80
/ # env|grep NGINX_SERVER_NAME
NGINX_SERVER_NAME=myapp.qiangungun.com
/ # env|grep NGINX_SERVER_PORT #稍等片刻,等下面的修改完成后操作,端口没有改变,即使用环境变量的方式注入配置只会在pod启动时有效 NGINX_SERVER_PORT=80

#另起一个回话,修改当前使用的ConfigMap

[root@k8s-master-155-221 ~]# kubectl edit configmap nginx-config
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  nginx_port: "8080" #修改端口为8080
  server_name: myapp.qiangungun.com
kind: ConfigMap
metadata:
  ......
[root@k8s-master-155-221 ~]# kubectl describe cm nginx-config
Name:         nginx-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
nginx_port:
----
8080
server_name:
----
myapp.qiangungun.com
Events:  <none>

VOLUME挂载

root@k8s-master-155-221 configmap]# kubectl delete pod pod-cm-1
pod "pod-cm-1" deleted
[root@k8s-master-155-221 configmap]# cp pod-configmap.yaml pod-configmap-2.yaml
[root@k8s-master-155-221 configmap]# vim pod-configmap-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-2
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: httpd
      containerPort: 80
    volumeMounts:
    - name: nginx-conf
      mountPath: /etc/nginx/config.d/
      readOnly: true  #是否允许pod去修改
  volumes:
  - name: nginx-conf
    configMap:
      name: nginx-config
[root@k8s-master-155-221 configmap]# kubectl apply -f pod-configmap-2.yaml
pod/pod-cm-2 created
[root@k8s-master-155-221 configmap]# kubectl exec  -it pod-cm-2 -- /bin/sh
/ # ls /etc/nginx/config.d/nginx_port
/etc/nginx/config.d/nginx_port
/ # cat /etc/nginx/config.d/nginx_port   #注意,是8080,因为之前已经改过一次了
8080/ #
/ # cat /etc/nginx/config.d/server_name
myapp.qiangungun.com/ #
/ # cat /etc/nginx/config.d/nginx_port #等下面的修改完成后操作,稍等30秒左右,端口发生改变,及以文件挂载的方式可以同步改变
8088/ #

#另起一个回话,修改当前使用的ConfigMap

[root@k8s-master-155-221 ~]# kubectl edit configmap nginx-config  
        nginx_port: "8088"  #修改端口由8080改为8088
        
/ # cd /etc/nginx/config.d/
/etc/nginx/config.d # ls -l    #挂载的方式实质是一种映射关系
total 0
lrwxrwxrwx    1 root     root            17 Jan 13 15:23 nginx_port -> ..data/nginx_port
lrwxrwxrwx    1 root     root            18 Jan 13 15:23 server_name -> ..data/server_name

4.实际示例

[root@k8s-master-155-221 configmap]# vim pod-configmap-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-3
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: httpd
      containerPort: 80
    volumeMounts:
    - name: nginx-conf
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: nginx-conf
    configMap:
      name: www
[root@k8s-master-155-221 configmap]# kubectl apply -f pod-configmap-3.yaml
pod/pod-cm-3 created
[root@k8s-master-155-221 configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
www.conf   #文件名称即www configmap中的键
/etc/nginx/conf.d # cat www.conf  #文件内容为www.conf的值
server{
  server_name myapp.magedu.com;
  listen 80;
  root /data/web/html;
}

/etc/nginx/conf.d # nginx -T  #查看载入的配置
......
# configuration file /etc/nginx/conf.d/www.conf:
server{
  server_name myapp.magedu.com;
  listen 80;
  root /data/web/html;
}

/etc/nginx/conf.d # mkdir -p /data/web/html
/etc/nginx/conf.d # echo "<h1>Nginx Server configured by CM</h1>" > /data/web/html/index.html

在集群任意节点上

[root@k8s-master-155-221 configmap]# kubectl get pods -o wide|grep pod-cm-3
pod-cm-3                       1/1     Running   0          5m13s   172.17.248.4   172.16.155.224   <none>           <none>
[root@k8s-master-155-221 configmap]# echo "172.17.248.4 myapp.magedu.com" >> /etc/hosts
[root@k8s-master-155-221 configmap]# curl myapp.magedu.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master-155-221 configmap]# kubectl edit cm www 
apiVersion: v1
data:
  www.conf: |
    server{
      server_name myapp.magedu.com;
      listen 8080;
      root /data/web/html;
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2020-01-13T14:21:13Z"
  name: www
  namespace: default
  resourceVersion: "7809631"
  selfLink: /api/v1/namespaces/default/configmaps/www 
  uid: f3dd1cd4-360f-11ea-87ad-525400512eca

pod中查看

/etc/nginx/conf.d # cat www.conf #等上面的修改完成后操作,稍等30秒左右,端口发生改变
  server_name myapp.magedu.com;
  listen 8080;
  root /data/web/html;
}

/etc/nginx/conf.d # netstat -tnl  #配置虽然改变,但是服务监听端口不会发生改变,需要通过某种方式重载服务
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN

三、Secret创建及使用

通过上面的部分,我们可以看到 ConfigMap 是用来做一些配置信息的,那么如果我们有一些机密信息比如说密钥、密码之类的信息,应该存在哪里呢?看到这个名字大家应该就明白了吧,kubernetes 提供了 Secret 来存储相关的信息。

Secret命令帮助(secret创建和使用与ConfigMap大同小异,注意区别在于内容被base64加密了)

[root@k8s-master-155-221 manifests]# kubectl create secret --help
Create a secret using specified subcommand.

Available Commands:  #secret主要有以下三种类型
  docker-registry 创建一个给 Docker registry 使用的 secret   #docker-registry私有仓库的证书,pod中pod.spec.imagePullSecrets字段使用
  generic         从本地 file, directory 或者 literal value #通用的secret
  tls             创建一个 TLS secret                       #保存私钥和证书

1.创建secret

[root@k8s-master-155-221 manifests]# kubectl create secret generic mysql-root-passwd --from-literal=passwd=MyP@ss123
secret/mysql-root-passwd created
[root@k8s-master-155-221 manifests]# kubectl describe secrets mysql-root-passwd 
Name:         mysql-root-passwd
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
passwd:  9 bytes   #只能看到9个字节 但是看不到内容
[root@k8s-master-155-221 manifests]# kubectl get secrets mysql-root-passwd -o yaml
apiVersion: v1
data:
  passwd: TXlQQHNzMTIz   #实际是base64加密
kind: Secret
metadata:
  creationTimestamp: "2020-01-14T01:47:51Z"
  name: mysql-root-passwd
  namespace: default
  resourceVersion: "7896989"
  selfLink: /api/v1/namespaces/default/secrets/mysql-root-passwd
  uid: dfb541fa-366f-11ea-87ad-525400512eca
type: Opaque

[root@k8s-master-155-221 manifests]# echo "TXlQQHNzMTIz"|base64 -d  #实际上可以被解码的
MyP@ss123

2.简单使用

[root@k8s-master-155-221 configmap]# vim pod-secret-1.yaml
apiVersion: v1
kind: Pod
metadata: 
  name: pod-secret-1
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: httpd
      containerPort: 80
    env:
    - name: MYSQL_ROOT_PASSWORD
      valueFrom:
        secretKeyRef:
          key: passwd
          name: mysql-root-passwd
          optional: true
          
[root@k8s-master-155-221 configmap]# kubectl apply -f pod-secret-1.yaml
pod/pod-secret-1 created      
[root@k8s-master-155-221 configmap]# kubectl exec -it pod-secret-1 -- /bin/sh
/ # env|grep MYSQL
MYSQL_ROOT_PASSWORD=MyP@ss123   #实际是解密后注入进来的,所以不建议使用环境变量的方式注入密码

其他方式与configmap雷同,不在赘述

其实目前 Secret 的实现,就是 ConfigMap 把 value 用 base64 encode 了一下.所以,其实不存在任何安全性,只要 decode 一下就能出现原来结果,相当于明文存储. 

参考文档:

 https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/

 https://kubernetes.io/docs/concepts/configuration/secret/