kubernetes实践(八)Kubernetes的Configmap和Secret

By prince No comments

一.Configmap

1.简介

核心用途就是容器和配置的分离解耦。

应用场景:镜像往往是一个应用的基础,还有很多需要自定义的参数或配置,例如资源的消耗、日志的位置级别等等,这些配置可能会有很多,因此不能放入镜像中,Kubernetes中提供了Configmap来实现向容器中提供配置文件或环境变量来实现不同配置,从而实现了镜像配置与镜像本身解耦,使容器应用做到不依赖于环境配置。

如启用一个mysql容器,mysql容器重要的文件有两部分,一部分为存储数据文件,一部分为配置文件my.cnf,存储数据可以用持久存储实现和容器的分离解耦,配置文件也能够实现和容器的分离解耦,也就是说mysql容器能够直接读取并使用预先配置好的配置文件(而不是使用容器中默认自带的配置文件).这就是configMap的功能。

2.创建ConfigMap

创建ConfigMap的方式有4种:

  • 通过直接在命令行中指定configmap参数创建,即--from-literal
  • 通过指定文件创建,即将一个配置文件创建为一个ConfigMap--from-file=<文件>
  • 通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,--from-file=<目录>
  • 事先写好标准的configmap的yaml文件,然后kubectl create -f 创建

2.1 通过命令行参数--from-literal创建

示例

创建命令:

kubectl create configmap my-config1 --from-literal=db.port='3306'

详情信息:

kubectl get configmap my-config1 -o yaml

结果如data内容所示:

apiVersion: v1
data:
  db.port: "3306"
kind: ConfigMap
metadata:
  creationTimestamp: "2020-03-03T02:12:29Z"
  name: my-config1
  namespace: default
  resourceVersion: "608193"
  selfLink: /api/v1/namespaces/default/configmaps/my-config1
  uid: 6f1e515b-5cf4-11ea-b1e8-5254008afee6

2.2 指定文件创建

示例

配置文件app.properties的内容:

name=xiaoxiao
age=18

创建命令(可以有多个--from-file):

kubectl create configmap my-config2 --from-file=./app.properties

详情信息:

kubectl get configmap my-config2 -o yaml

结果如data内容所示:

apiVersion: v1
data:
  app.properties: |
    name=xiaoxiao
    age=18
kind: ConfigMap
metadata:
  creationTimestamp: "2020-03-03T02:21:26Z"
  name: my-config2
  namespace: default
  resourceVersion: "609204"
  selfLink: /api/v1/namespaces/default/configmaps/my-config2
  uid: af389bb6-5cf5-11ea-b1e8-5254008afee6

可以看到指定文件创建时configmap会创建一个key/value对,key是文件名,value是文件内容。

假如不想configmap中的key为默认的文件名,还可以在创建时指定key名字:

kubectl create configmap my-config3 --from-file=<my-key-name>=<path-to-file>

2.3 指定目录创建

示例

configs 目录下的config1和config2内容如下所示:

config1

aaaaa
bbbbb
cc=cc
dd=dd

config2

11111
22222
33=33
44=44

创建命令:

kubectl create configmap my-config4 --from-file=./configs

详情信息:

kubectl get configmap my-config4 -o yaml

结果如data内容所示:

apiVersion: v1
data:
  config1: |
    aaaaa
    bbbbb
    cc=cc
    dd=dd
  config2: |
    11111
    22222
    33=33
    44=44
kind: ConfigMap
metadata:
  creationTimestamp: "2020-03-03T02:23:15Z"
  name: my-config4
  namespace: default
  resourceVersion: "609412"
  selfLink: /api/v1/namespaces/default/configmaps/my-config4
  uid: f00cadb7-5cf5-11ea-b1e8-5254008afee6

可以看到指定目录创建时configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。

2.4 通过yaml文件创建

configmaps.yaml文件如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very 
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO

创建命令:

kubectl apply -f configmaps.yaml

详情信息:

kubectl get configmap special-config -o yaml
kubectl get configmap env-config -o yaml

结果如data内容所示:

apiVersion: v1
data:
  special.how: very
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"special.how":"very"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"special-config","namespace":"default"}}
  creationTimestamp: "2020-03-03T02:27:42Z"
  name: special-config
  namespace: default
  resourceVersion: "609916"
  selfLink: /api/v1/namespaces/default/configmaps/special-config
  uid: 8f967787-5cf6-11ea-b1e8-5254008afee6

 

apiVersion: v1
data:
log_level: INFO
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"log_level":"INFO"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"env-config","namespace":"default"}}
creationTimestamp: "2020-03-03T02:27:42Z"
name: env-config
namespace: default
resourceVersion: "609917"
selfLink: /api/v1/namespaces/default/configmaps/env-config
uid: 8f973a81-5cf6-11ea-b1e8-5254008afee6

 

3.使用ConfigMap

使用ConfigMap有三种方式:

  • 第一种是通过环境变量的方式,直接传递给pod
    • 使用configmap中指定的key
    • 使用configmap中所有的key
  • 第二种是通过在pod的命令行下运行的方式(启动命令中)
  • 第三种是作为volume的方式挂载到pod内

注意:

  • ConfigMap必须在Pod使用它之前创建
  • 使用envFrom时,将会自动忽略无效的键
  • Pod只能使用同一个命名空间的ConfigMap

3.1 通过环境变量使用

(1)使用valueFromconfigMapKeyRefnamekey指定要用的key

test-pod.yaml文件如下

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
  restartPolicy: Never

创建pod

kubectl apply -f test-pod.yaml

查询pod

kubectl logs dapi-test-pod

(2)还可以通过envFromconfigMapRefname使得configmap中的所有key/value对都自动变成环境变量:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - configMapRef:
          name: special-config
  restartPolicy: Never

3.2用作命令行参数

在命令行下引用时,需要先设置为环境变量,之后可以通过$(VAR_NAME)设置容器启动命令的启动参数:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_TYPE
  restartPolicy: Never

3.3作为volume挂载使用

(1)将创建的ConfigMap直接挂载至Pod的/etc/config目录下,其中每一个key-value键值对都会生成一个文件,key为文件名,value为内容

pod-myconfigmap-v2.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap2
spec:
  containers:
  - name: test-container
    image: busybox
    command: [ "/bin/sh", "-c", "ls /etc/config/" ] 
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
   - name: config-volume
     configMap:
       name: special-config 
  restartPolicy: Never

创建

kubectl apply -f pod-myconfigmap-v2.yaml

进入pod查看

kubectl exec -it pod-configmap2 bash

进入容器中/etc/config查看:

cd /etc/config
ls

special.how

cat special.how

very

可以看到,在/etc/config文件夹下以每一个key为文件名value为值创建了多个文件。

查询日志

kubectl logs pod-configmap2

结果

special.how

(2)假如不想以key名作为配置文件名可以引入items字段,在其中逐个指定要用相对路径path替换的key

      volumes:
      - name: config-volume
        configMap:
          name: special-config 
          items:
          - key: special.how
            path: special_how

kubectl exec -it pod-configmap2 bash

进入容器中/etc/config查看:

cd /etc/config
ls

special_how

cat special_how

very

查询日志

kubectl logs pod-configmap2

结果

special_how

备注:

删除configmap后原pod不受影响;然后再删除pod后,重启的pod的events会报找不到cofigmap的volume;
pod起来后再通过kubectl edit configmap …修改configmap,过一会pod内部的配置也会刷新。
在容器内部修改挂进去的配置文件后,过一会内容会再次被刷新为原始configmap内容

 

3.4configmap的热更新研究

更新 ConfigMap 后:

使用该 ConfigMap 挂载的 Env 不会同步更新
使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

ENV 是在容器启动的时候注入的,启动之后 kubernetes 就不会再改变环境变量的值,且同一个 namespace 中的 pod 的环境变量是不断累加的,参考 Kubernetes中的服务发现与docker容器间的环境变量传递源码探究。为了更新容器中使用 ConfigMap 挂载的配置,可以通过滚动更新 pod 的方式来强制重新挂载 ConfigMap,也可以在更新了 ConfigMap 后,先将副本数设置为 0,然后再扩容。

3.4ConfigMap在Ingress Controller中实战

在之前ingress网络中的mandatory.yaml文件中使用了ConfigMap,于是我们可以打开

可以发现有nginx-configuration、tcp-services等名称的cm 而且也可以发现最后在容器的参数中使用了这些cm

containers:
- name: nginx-ingress-controller

image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1 args:

- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
验证
#查找ingress的CONTAINER ID
docker ps | grep ingress

#进入容器
docker exec -it 86739e3e4240 bash

#验证配置文件
ls /etc/nginx

#查询配置文件内容
cat nginx.conf | grep cicd.demo.princeli.com 

server_name cicd.demo.princeli.com ;

从上面验证过程可以看出

nginx ingress controller就是一个nginx,而所谓的ingress.yaml文件中 配置的内容像cicd.demo.princeli.com就会对应到nginx.conf中。

修改配置

如果需要修改配置是否只需要修改对应的ConfigMap 文件就可以了

新建nginx-config.yaml文件

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration 
  namespace: ingress-nginx 
  labels:
     app: ingress-nginx 
data:
  proxy-read-timeout: "208"

创建

kubectl apply -f nginx-config.yaml

验证

#查找ingress的CONTAINER ID
docker ps | grep ingress

#进入容器
docker exec -it 86739e3e4240 bash

#验证配置文件
ls /etc/nginx

#查询配置文件内容
cat nginx.conf |grep proxy_read_timeout

proxy_read_timeout 208s;

其实configmap定义规则都在nginx ingress controller的官网中

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/

二.Secret

Secret对象与ConfigMap对象类似,但它主要用于存储以下敏感信息,例如密码,OAuth token和SSH key等等。将这些信息存储在secret中,和直接存储在Pod的定义中,或Docker镜像定义中相比,更加安全和灵活。

Secret有三种类型:

  • Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。
  • kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息。
  • kubernetes.io/service-account-token:用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。

1. Opaque Secret

Opaque类型的Secret的value为base64位编码后的值

1.1从文件创建

(1)创建username.txt,password.txt

echo -n "admin" > ./username.txt
echo -n "1f2d1e2e67df" > ./password.txt

(2)创建secret

kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt

(3)查询

kubectl get secret db-user-pass

(4)结果

NAME           TYPE     DATA   AGE
db-user-pass   Opaque   2      28s

1.2使用yaml创建

(1)对数据进行64位编码

echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64

(2)定义mysecret.yaml文件

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

(3)根据yaml文件创建资源并查看

kubectl create -f ./secret.yaml 
kubectl get secret
kubectl get secret mysecret -o yaml

1.3 Secret的使用

创建好Secret之后,可以通过两种方式使用:

以Volume方式
以环境变量方式

(1)将Secret挂载到Volume中

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

进入Pod查看挂载的Secret:

kubectl exec -it mypod bash

# ls /etc/foo
password username
# cat /etc/foo/username
admin
# cat /etc/foo/password
1f2d1e2e67df
也可以只挂载Secret中特定的key:apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

在这种情况下:

  • username 存储在/etc/foo/my-group/my-username中
  • password未被挂载

(2)将Secret设置为环境变量

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

2.kubernetes.io/dockerconfigjson

kubernetes.io/dockerconfigjson用于存储docker registry的认证信息,可以直接使用kubectl create secret命令创建:

kubectl create secret docker-registry myregistrykey \ 
-n namespace \ 
--docker-server=DOCKER_REGISTRY_SERVER \ 
--docker-username=DOCKER_USER \ 
--docker-password=DOCKER_PASSWORD \ 
--docker-email=DOCKER_EMAIL \

例如

kubectl create secret docker-registry aliyun-key \
-n demo \
--docker-server=registry.cn-hangzhou.aliyuncs.com \
--d[email protected] \
--docker-password=xxxxxx \
[email protected]

可在yaml文件中使用拉取image

... 
spec:  
    imagePullSecrets:  
    - name: aliyun-key
...

3. kubernetes.io/service-account-token

用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。

# 可以看到service-account-token
kubectl get secret 

kubectl run nginx --image nginx
kubectl get pods

kubectl exec -it nginx-pod-name bash
ls /run/secrets/kubernetes.io/serviceaccount

ca.crt
namespace
token


kubectl get pods pod-name -o yaml

# 找到volumes选项,定位到-name,secretName
  volumes:
- name: default-token-8jfch
  secret:
    defaultMode: 420
    secretName: default-token-8jfch

# 找到volumeMounts选项,定位到mountPath
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
  name: default-token-8jfch
  readOnly: true

小结:无论是ConfigMap,Secret,还是DownwardAPI,都是通过ProjectedVolume实现的,可以通过APIServer将 信息放到Pod中进行使用。

发表评论

 

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据