Kubernetes之配置管理

前言:在k8s中,使用一个容器镜像来启动一个container,其实有很多需要配套的问题待解决:

  • 不能把一些可变的配置写到镜像里,当这个配置需要变化的时候,可能需要我们重新编译一次镜像
  • 一些敏感信息的存储和使用,比如token
  • 容器要访问自身群集,比如访问 kube-apiserver,那么本身就有一个身份认证的问题
  • 容器在节点上运行之后,它的资源需求
  • 对于容器的安全管控,毕竟容器在节点上,它们是共享内核的
  • 容器启动之前的一个前置条件检验。比如在容器启动之前需要确认DNS服务以及确认网络是否连通

那么这些其实就是一些前置的校验

文章目录

  • 一、Pod的配置管理
  • 二、Secret与ConfigMap
    • 1.Secret
    • 2.ConfigMap
  • 三、Secret解析
    • 1.类型
    • 2.Secret使用注意要点
    • 3.创建与使用
      • 1)内建的Secrets
      • 2)创建自己的Secret
        • 方式一
        • 方式二
      • 3)使用Secret
        • 模板
        • 第一种
        • 第二种
  • 四、ConfigMap解析
    • 1.作用
    • 2.ConfigMap注意要点
    • 3.创建与使用
      • 1)创建方式一 kubectl
      • 2)创建方式二 变量参数形式

一、Pod的配置管理

  • 如下图所示
    Kubernetes之配置管理_第1张图片
    • 可变配置就用 ConfigMap
    • 敏感信息是用 Secret
    • 身份认证是用 ServiceAccount 这几个独立的资源来实现的
    • 资源配置是用 Resources
    • 安全管控是用 SecurityContext
    • 前置校验是用 InitContainers 这几个在 spec 里面加的字段,来实现的这些配置管理

二、Secret与ConfigMap

1.Secret

  • Secret是用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险
  • 用户可以创建自己的secret,系统也会有自己的secret
  • Pod需要先引用才能使用某个secret,Pod有2种方式来使用secret:作为volume的一个域被一个或多个容器挂载;在拉取镜像的时候被kubelet引用

2.ConfigMap

  • 我们知道,在几乎所有的应用开发中,都会涉及到配置文件的变更,比如说在web的程序中,需要连接数据库,缓存甚至是队列等等
  • 而我们的一个应用程序从写第一行代码开始,要经历开发环境、测试环境、预发布环境只到最终的线上环境
  • 而每一个环境都要定义其独立的各种配置。如果不能很好的管理这些配置文件,运维工作将顿时变的无比的繁琐
  • 为此业内的一些大公司专门开发了自己的一套配置管理中心,如360的Qcon,百度的disconf等
  • kubernetes也提供了自己的一套方案,即ConfigMap。kubernetes通过ConfigMap来实现对容器中应用的配置管理

三、Secret解析

  • Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中
  • 用于加密数据并存放在Etcd中,让Pod的容器以挂载Volume方式访问
  • Secret使用:
    • Volume
    • 环境变量
  • 应用场景:凭据
  • 功能:
    • 对数据进行加密并且进行保存参数
    • 能够实现数据卷的挂载

1.类型

  • Secret有三种类型:
    • 1.Service Account:用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中
    • 2.Opaque:base64编码格式的Secret,用来存储密码、密钥等
    • 3.kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息

2.Secret使用注意要点

  • Secret 的文件大小限制
    • 这个跟 ConfigMap 一样,也是 1MB
  • Secret 采用 base-64 编码
    • 它跟明文也没有太大区别
    • 所以说,如果有一些机密信息要用 Secret 来存储的话,还是要很慎重考虑。也就是说谁会来访问你这个集群,谁会来用你这个 Secret,还是要慎重考虑,因为它如果能够访问这个集群,就能拿到这个 Secret
    • 如果是对 Secret 敏感信息要求很高,对加密这块有很强的需求,推荐可以使用 Kubernetes 和开源的 vault做一个解决方案,来解决敏感信息的加密和权限管理
  • Secret 读取的最佳实践
    • 建议不要用 list/watch,如果用 list/watch 操作的话,会把 namespace 下的所有 Secret 全部拉取下来,这样其实暴露了更多的信息
    • 推荐使用 GET 的方法,这样只获取你自己需要的那个 Secret

3.创建与使用

1)内建的Secrets

  • 由ServiceAccount创建的API证书附加的秘钥
  • k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信

2)创建自己的Secret

  • 官方文档:https://kubernetes.io/docs/concepts/configuration/secret/
    在这里插入图片描述

方式一

  • 将命令存放在文件中,将文件打包进行存储
  • 使用kubectl create secret命令创建Secret
  • 假如mougePod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt
[root@master01 ~]# echo -n 'admin' > ./username.txt
[root@master01 ~]# echo -n '123456' > ./password.txt
//kubectl create secret指令将用户名密码写到secret中,并在apiserver创建Secret
[root@master01 ~]# kubectl create secret generic db-user-pass  --from-file=./username.txt --from-file=./password.txt
secret/db-user-pass created
[root@master01 ~]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
db-user-pass          Opaque                                2      5s
[root@master01 ~]# kubectl describe secret db-user-pass
Name:         db-user-pass
Namespace:    default
Labels:       
Annotations:  

Type:  Opaque

Data
====
password.txt:  6 bytes
username.txt:  5 bytes
  • get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑

方式二

  • 直接存储参数信息,需要先转码
  • 创建一个secret.yaml文件,内容用base64编码
[root@master01 ~]# echo -n 'admin' | base64
YWRtaW4=
[root@master01 ~]# echo -n '123456' | base64
MTIzNDU2
[root@master01 demo]# vim secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MTIzNDU2
[root@master01 demo]# kubectl create -f secret.yaml 
secret/mysecret created
[root@master01 demo]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
db-user-pass          Opaque                                2      5m59s
default-token-z5x9k   kubernetes.io/service-account-token   3      30d
mysecret              Opaque                                2      6s
  • 解析Secret中内容
[root@master01 demo]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  password: MTIzNDU2
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: 2020-05-30T07:26:50Z
  name: mysecret
  namespace: default
  resourceVersion: "646326"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: ed5578b0-a246-11ea-9fb4-000c2930d5cb
type: Opaque

//key: username赋值给SECRET_USERNAME
//key: password 赋值给SECRET_PASSWORD
  • base64解码
[root@master01 demo]# echo 'MTIzNDU2' | base64 --decode
123456

3)使用Secret

  • secret可以作为数据卷挂载或者作为环境变量暴露给Pod中的容器使用,也可以被系统中的其他资源使用。比如可以用secret导入与外部系统交互需要的证书文件等
  • 在Pod中以文件的形式使用secret
    • 创建一个Secret,多个Pod可以引用同一个Secret
    • 修改Pod的定义,在spec.volumes[]加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName记录的是要引用的Secret名字
    • 在每个需要使用Secret的容器中添加一项spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = true,spec.containers[].volumeMounts[].mountPath要指向一个未被使用的系统路径
    • 修改镜像或者命令行使系统可以找到上一步指定的路径。此时Secret中data字段的每一个key都是指定路径下面的一个文件名

模板

  • 这里准备好了两个模板configmap.yaml和secret-simple.yaml
[root@master01 demo]# ls
configmap.yaml secret-simple.yaml
[root@master01 demo]# cat secret-simple.yaml   //为了让pod资源能够使用参数
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:         
          secretKeyRef:
            name: mysecret
            key: password

# echo $SECRET_USERNAME
# echo $SECRET_PASSWORD

---

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

# cat /etc/foo/username
# cat /etc/foo/password

[root@master01 demo]# cat configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfig
  namespace: default
data:
  special.level: info
  special.type: hello

---

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh", "-c", "echo $(LEVEL) $(TYPE)" ]
      env:
        - name: LEVEL
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.level
        - name: TYPE
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.type
  restartPolicy: Never


---

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
data:
  redis.properties: |
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456

---

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: redis-config
  restartPolicy: Never

第一种

  • 使用secret中的变量导入到pod中
  • 创建一个资源
[root@master01 demo]# vim secret-var.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
[root@master01 demo]# kubectl apply -f secret-var.yaml 
pod/mypod created
[root@master01 demo]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
mypod                    1/1     Running   0          21s
  • 容器中读取环境变量,已经是base64解码后的值了
[root@master01 demo]# kubectl exec -it mypod bash
root@mypod:/#  echo $SECRET_USERNAME
admin
root@mypod:/#  echo $SECRET_PASSWORD
123456

第二种

  • 以volume的形式挂载到pod的某个目录下
  • 创建一个资源
  • 每一个被引用的Secret都要在spec.volumes中定义
  • 如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了
[root@master01 demo]# vim secret-vol.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:    //指定数据卷名称为foo
    - name: foo
      mountPath: "/etc/foo"   //挂载路径位置
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
  • 上述文件表示将secret挂载到容器的/etc/foo路径
[root@master01 demo]# kubectl delete -f secret-var.yaml 
pod "mypod" deleted
[root@master01 demo]# kubectl apply -f secret-vol.yaml 
pod/mypod created
[root@master01 demo]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
mypod                    1/1     Running   0          26s
  • 从volume中读取secret的值
  • 值得注意的一点是,以文件的形式挂载到容器中的secret,他们的值已经是经过base64解码的了,可以直接读出来使用
//容器看见里面的文件被挂载
[root@master01 demo]# kubectl exec -it mypod bash
root@mypod:/# ls /etc/foo
password  username
root@mypod:/# cd /etc/foo
root@mypod:/etc/foo# cat username 
admin
root@mypod:/etc/foo# cat password 
123456
root@mypod:/etc/foo# 
  • 被挂载的secret内容自动更新
  • 也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。最长的时间将是一个同步周期加上缓存生命周期(period+ttl)

四、ConfigMap解析

  • 与Secret类似,区别在于ConfigMap保存的是不需要加密配置的信息
  • 应用场景:应用配置

1.作用

  • 主要是管理一些可变配置信息,让镜像根据不同的场景调用不同的配置文件,比如说我们应用的一些配置文件,或者说它里面的一些环境变量,或者一些命令行参数
  • 它的好处在于可以让一些可变配置和容器镜像进行解耦,这样也保证了容器的可移植性
  • 它包括两个部分
    • 一个是 ConfigMap 元信息,我们关注 name 和 namespace 这两个信息
    • 接下来这个 data 里面,可以看到它管理了两个配置文件。它的结构其实是这样的:从名字看 ConfigMap 中包含 Map 单词,Map 其实就是 key:value,key 是一个文件名,value 是这个文件的内容

2.ConfigMap注意要点

  • ConfigMap 文件的大小
    • 虽然说 ConfigMap 文件没有大小限制,但是在 ETCD 里面,数据的写入是有大小限制的,现在是限制在 1MB 以内
  • pod 引入 ConfigMap
  • 必须是相同的 Namespace 中的 ConfigMap,在ConfigMap.metadata 里面是有 namespace 字段的
  • pod 引用的 ConfigMap
    • 如这个 ConfigMap 不存在,那么这个 pod 是无法创建成功的,其实这也表示在创建 pod 前,必须先把要引用的 ConfigMap 创建好
  • 使用 envFrom 的方式
    • 把 ConfigMap 里面所有的信息导入成环境变量时,如果 ConfigMap 里有些 key 是无效的,比如 key 的名字里面带有数字,那么这个环境变量其实是不会注入容器的,它会被忽略
    • 但是这个 pod 本身是可以创建的。这个和上一点是不一样的方式,是 ConfigMap 文件存在基础上,整体导入成环境变量的一种形式
  • 什么样的 pod 才能使用 ConfigMap
    • 这里只有通过 K8s api 创建的 pod 才能使用 ConfigMap,比如说通过用命令行 kubectl 来创建的 pod,肯定是可以使用 ConfigMap 的,但其他方式创建的 pod,比如说 kubelet 通过 manifest 创建的 static pod,它是不能使用 ConfigMap 的

3.创建与使用

1)创建方式一 kubectl

  • 首先创建redis配置文件,用作演示,因为牵扯到java的配置,后缀要用properties
[root@master01 demo]# vim redis.properties  //创建JAVA配置文件
redis.host=127.0.0.1
redis.port=6379
redis.password=123456    //写入用作演示的内容
  • 创建configmap资源
[root@master01 demo]# kubectl create configmap redis-config --from-file=redis.properties
configmap/redis-config created
//查看资源
[root@master01 demo]# kubectl get configmap
NAME           DATA   AGE
redis-config   1      21s
//也可以用缩写查看
[root@master01 demo]# kubectl get cm
NAME           DATA   AGE
redis-config   1      95s
[root@master01 demo]# kubectl describe cm redis-config
Name:         redis-config
Namespace:    default
Labels:       
Annotations:  

Data
====
redis.properties:
----
redis.host=127.0.0.1
redis.port=6379
redis.password=123456

Events:  
  • 创建mypod资源查看文件导入
[root@master01 demo]# vim cm.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]  //执行一个命令查看redis配置文件
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config   //挂载路径
  volumes:     //挂载的类型和资源名称
    - name: config-volume
      configMap:
        name: redis-config
  restartPolicy: Never

//删掉之前创建重名的mypod
[root@localhost demo]# kubectl delete pod mypod
pod "mypod" deleted
[root@master01 demo]# kubectl apply -f cm.yaml 
pod/mypod created
[root@master01 demo]# kubectl get pods
NAME    READY   STATUS      RESTARTS   AGE
mypod   0/1     Completed   0          26s
[root@master01 demo]# kubectl logs mypod  //查看日志
redis.host=127.0.0.1 
redis.port=6379
redis.password=123456
  • 配置文件信息被记录到日志当中,表示mypod资源当中存在 redis.properties

2)创建方式二 变量参数形式

  • 创建configmap资源
[root@master01 demo]# vim myconfig.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfig
  namespace: default
data:    //两个参数
  special.level: info
  special.type: hello
[root@master01 demo]# kubectl apply -f myconfig.yaml 
configmap/myconfig created
  • 创建mypod使用configmap资源输出变量参数
[root@master01 demo]# vim config-var.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh", "-c", "echo $(LEVEL) $(TYPE)" ]   //输出之前创建的两个参数
      env:
        - name: LEVEL
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.level
        - name: TYPE
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.type
  restartPolicy: Never

//清除已有的mypod资源
[root@master01 demo]# kubectl delete pod mypod
pod "mypod" deleted
//创建mypod资源
[root@master01 demo]# kubectl apply -f config-var.yaml 
pod/mypod created
//查看变量的输出
[root@master01 demo]# kubectl logs mypod
info hello
  • 这表示这两个变量可以被其他的资源直接拿过来进行使用

你可能感兴趣的:(K8s)