配置管理
两种特殊类型存储卷,其多数情况下目的不是给pod提供存储空间来用的,而是给管理员或用户提供了从 集群外部向 pod内部的应用注入配置信息的方式, 理论来源
配置容器应用方法
- 自定义命令行两种参数:
command, args:['xx','xx']
- 事先将配置文件直接写死镜像
- 环境变量: 应用程序应当支持环境变量,entrypoint(预处理脚本) 方式提供
- 如果程序镜像是云原生,或是entrypoint脚本通过传递环境变量接受参数完成配置,这是一种轻量化的配置方式,在容器启动时传递N个环境变量,分别就能代表N个重要的配置参数,从而完成应用程序配置;
- 当以nginx为例,当启动nginx时需要加载自定义的配置文件模块,或使用存储卷的方式进行配置文件的传递,将外部配置文件加载至容器内部;
- 但上述的配置方式是有缺陷的,当使用环境变量配置,env是在容器启动时被装入的,意味着如果想要修改某个变量,只能重新启动容器,如果使用存储卷,存储卷的内容也是在进程启动时被装入,后续就算是修改了存储卷的配置,也得需要手动触发进程 加载新的配置
- 存储卷
- dockerConfig (不是常用的方式)
使用场景
当我们在一个Pod控制器下,有多个Pod时,这些Pod的配置信息应该都是一样的,意味着它们会共享一个存储卷,当存储卷内容被修改,这些Pod都能加载到新的内容,但这些Pod都会被重载,尤其是当我们使用Hpa控制器时,它会根据用户访问量来自定义伸缩Pod副本,那此时就会不知道重载哪些pod,所以在这种环境下最好能提供一种机制,当我们修改配置信息后能自动关连相关的Pod重载,最好只需要改一次,所有加载该配置文件的Pod都能被自动重载;
k8s中用于实现配置自动重载功能的组件,它们分别是 ConfigMap、Secret,它们都是用于将配置文件配置为k8s资源
-
ConfigMap用于提供非敏感配置
-
Secre用于提供敏感配置信息(密码、密钥等)进行编码存放
它们在内部都是对键值对的方式存在,键做为配置参数,值可以是整个文件,比如说 可以将配置文件文件名做为键,整个文件的配置做为值
configMap
特殊类型的存储卷,configMap | secret也是存储卷,但它们目的不是为给pod提供存储空间,而是给管理员或用户提供从集群外部向pod内部的应用注入配置信息的方式,集群配置管理资源, 信息明文存放
configMap也属于名称空间级别的资源, 字段查看
kubectl explain cm
ConfigMap做为一个键值存储系统,我们的配置文件几乎都可以被实例为ConfigMap的键值对象,在k8s中pod的ConfigMap调用有两种方式:
- 在k8s的容器中,将每个key的value映射为这个容器内部环境变量的值,比如在Pod中某个环境变量名是MYSQL_USER它的值引用于ConfigMap的值,我们可以向ConfigMap赋值,而不是直接给容器的MYSQL_USER赋值,从而由ConfigMap替换,然后由我们的应用程序所引用;
- k8s的ConfigMap和Pod作为标准的k8s资源,当我们定义一个ConfigMap,其第一个参数键叫redis_host,值为10.1.1.100,第二个键为redis_port,值为6379,随后在配置一个filebeat的Pod,这个Pod通过环境变量引用redis主机和端口的配置,对应环境变量名是host和port,那么我就可以这样定义Pod,“host:redis-cfg.redis_host,port:redis-cfg.redis_port”,后续修改配置也只需要配置ConfigMap即可,但这种引用也有一种缺陷,当Pod启动之后就不会再获取值了,这也就意味着后续如果修改了ConfigMap里的数据也不会被Pod加载,这时就需要重建来重新加载新配置了,这也是环境变量的缺陷
配置文件引入方式
- 启动pod时可以将configMap资源关连至当前pod资源, 从中读一个配置以变量注入的方式传递配置
- 将configMap当成一个存储卷挂载至容器的某一个目录,该目录为项目的配置目录
核心作用
为了将配置文件从镜像中解耦,从而增强应用的可移植性以及可复用性,一个configMap就是一系列配置数据的集合,而这些数据将来可以注入到pod对象容器中所使用,而注入方式有两种
- 直接将configMap当存储卷;
- 使用env的valueFrom方式来引用configMap, 在configMap中所有的配置都保存为键值对的格式(kv),键值对中的值长度无限制
configMap临时使用
Aliases:
configmap, cm
Examples:
# --from-file=path/to/bar 文件名当键, 文件当值
kubectl create configmap my-config --from-file=path/to/bar
# --from-file=key1=/path/to/bar/file1.txt 指定键是key, 值是某个文件
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
# 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
获取环境变量
-
在pod容器上使用env或volueFrom来获取
~]# kubectl explain pods.spec.containers env name: 变量名 value: 数据是什么 valueFrom: 数据引用另一个变量 configMapKeyRef: configMap资源 fieldRef: 引用pod资源的字段 resourceFieldRef: 资源需求 | 资源限制 secretKeyRef: 引用secretKey配置资源
以键值对方式创建
-
创建
# 创建一个临时的键值对 ~]# kubectl create cm nginx-test --from-literal=NGINX_NAME=t1.xiong.com --from-literal=NGINX_PORT=80
-
查看
~]# kubectl get cm NAME DATA AGE nginx-test 2 6s # 查看详细内容 ~]# kubectl describe cm nginx-test Name: nginx-test Namespace: default Labels: <none> Annotations: <none> Data ==== NGINX_NAME: ---- t1.xiong.com NGINX_PORT: ---- 80 Events: <none>
-
创建pod
]# cat nginx_cm.yaml apiVersion: v1 kind: Pod metadata: annotations: configMap/test: "nginx" name: nginx-cm-test labels: app: nginx-cm release: qa spec: containers: - name: nginx-cm image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: NGINX_PORT valueFrom: configMapKeyRef: name: nginx-test key: NGINX_PORT - name: NGINX_NAME valueFrom: configMapKeyRef: # 来源 name: nginx-test key: NGINX_NAME ]# kubectl apply -f nginx_cm.yaml ]# kubectl exec -it nginx-cm-test -- /bin/sh / # env NGINX_PORT=80 NGINX_NAME=t1.xiong.com
-
编辑configMap
]# kubectl edit cm nginx-test # 将port修改为8080 # 在查看pod, env NGINX_PORT=80 # 当使用环境变量注入时,只在系统启动时有效
以存储卷创建
-
创建
]# cat t2.xiong.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } } ]# kubectl create cm nginx-test --from-file=t2.xiong.conf
-
查看
]# kubectl describe cm nginx-test Name: t2-xiong Namespace: default Data ==== t2.xiong.conf: # key就是文件名 ---- # 值是文本中的全部内容 server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } }
-
以存储卷方式挂载
]# cat t3_cm.yaml apiVersion: v1 kind: Pod metadata: annotations: configMap/test: "nginx" name: t3-xiong labels: app: t3-xiong release: qa spec: containers: - name: nginx-cm image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: nginx mountPath: /etc/nginx/conf.d/ readOnly: true volumes: - name: nginx configMap: name: nginx-test # 或挂载多个 volumes: - name: conf # 该volumes的名字 configMap: name: nginx-conf # 要使用configMap的名字 items: - key: server1 # 要使用指定configMap里面的键名 path: s1.conf # 挂载之后的文件名 - key: server2 # 要使用指定configMap里面的键名 path: s2.conf # 挂载之后的文件名
-
测试
~]# kubectl exec -it t3-xiong -- /bin/sh /etc/nginx/conf.d # ls -lh lrwxrwxrwx 1 root root 20 May 7 06:02 t2.xiong.conf -> ..data/t2.xiong.conf # root目录下创建xx.html 内容 hello world ]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE t3-xiong 1/1 Running 0 69s 10.244.1.2 slave1 ]# curl 10.244.1.2/xx.html hello world
- 使用该 ConfigMap 挂载的 Env 不会同步更新
- 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新
ENV 是在容器启动的时候注入的,启动之后 kubernetes 就不会再改变环境变量的值,且同一个 namespace 中的 pod 的环境变量是不断累加的,为了更新容器中使用 ConfigMap 挂载的配置,可以通过滚动更新 pod 的方式来强制重新挂载 ConfigMap,也可以在更新了 ConfigMap 后,先将副本数设置为 0,然后再扩容。
配置中心
Kubernetes用来配置分布式或者负载均衡集群化的配置中心,比如如果在nginx后面维护有20个tomcat,需要改修tomcat配置文件的一些配置信息,比如在修改tomcat连接redis的账号密码,那如何让20个tomcat都应用到新配置,此前的做法就是使用ansible,然后分批推,推的时候还需要以灰度的方式进行,如果一下全部推送那在某一时刻服务都无法访问了,所以即使借助了ansible来编排,也得一个一个来,但这种才20个,但是如果有30个或者更多这就比较麻烦了,所以很多规模非常大的网站都不会通过这种方式来配置应用程序,而是让应用程序所有配置都不通过本地配置文件加载,而是通过联系一个配置中心的服务去加载配置,所以称之为配置中心;
启动多个应用程序时,抛弃了传统从本地文件加载配置的方法,而是通过一个中心服务器来加载配置,这个服务叫做配置中心,随后我们在这个配置中心修改了配置以后,会通知给每一个进程,让进程自动进行重载,就能完成所有的进程进行重载,还可以以灰度的方式通知,那么他们就可以以灰度的方式去更新自己的配置信息;
国内有两个项目,为非容器化应用程序提供配置中心,协程的Apollo、百度的Distconf,所以在非容器化模式中,也可以使用配置中心,要使用配置中心还需要我们的程序支持从配置中心加载配置才行;
secret
功能与configMap一样,信息以 base64编码存放 (相当于加密)
ConfigMap和Secret都作为k8s的标准资源,用于实现为Pod中容器提供配置信息,而这个配置信息它还扮演着K8s系统之上一组类似应用的配置中心,当配置发生变量时,我们只需要修改配置中心的配置,而后配置中心应用程序会自动更新配置信息并让其重载应用;
配置中心都需要存储大量的配置数据,那么这些配置数据可以存储在各式各样的存储系统当中,KV存储最为常见,比如zookepper、etcd这些配置工具,ConfigMap提供的配置信息,一般而言可以被我们的Pod容器以两种方式进行加载,所以Pod中的容器加载ConfigMap资源中的信息
-
第一种为环境变量为容器提供环境变量实现;
-
第二种方式就是通过volumes存储卷的方式配置;
而因为ConfigMap和Pod都是名称空间级别的资源,因此而者必须在同一名称空间下面引用;
ConfigMap允许我们在必要时进行修改,修改存储卷中内容在过一段随机时间之后会同步到Pod的容器中,但如果容器本身不支持自动重载,我们可以在外部发送重载命令,让其进行重载,也可以以灰度的方式进行,从这个角度上来说它的发布可能会有一定的问题,有一部分建构在k8s之上的高级工具也能够帮助我们完成k8s所不支持的各式各样的功能;
构建ConfigMap资源有如下几种方式
- 命令行: 但使用命令行去维护的资源是没办法被k8s跟踪的,所以最好使用配置清单进行创建,使用命令行创建也有几种方式
- 第一种: --from-literal=key=value 可以定义多个, 同时写一行即可
- 第二种: --from-file=key=file_name 可以不指定key, 一般就是直接以文件名做为键名
- 配置清单: 如果嵌套的是一个文件内容,稍微复杂一点,如下示例
secret资源类型
kubectl create secret -h
- docker-registry: 创建一个给docker register使用的私有仓库帐号密码, 在docker-register创建pod时,在spec当中有一个imagePullSecrets,对象列表,可以给出多个Secret,一个Secret就是一组帐号密码,如果当第一个认证成功,那就继续往下,自上而下依次进行检索
- generic: 专用于存储非tls类型的敏感信息,比如MySQL密码
- tls: 专用于ssl或者tls的x509格式的证书和私钥打包进Secret,证书和私钥本身就是base64编码,因此需要特有的逻辑来组织,不管文件名叫做什么,在tls这种格式来组织的证书通通统一为tls.cert,私钥名为tls.key;
generic
generic的方式主要是存储一些用户自定义的密码,非tls类型的,比如MySQL密码等,示例如下
通过环境变量
-
可选参数
]# kubectl create secret generic -h Examples: # 可以指定文件,key为文件名 kubectl create secret generic my-secret --from-file=path/to/bar # 指定key名,value为文件内容 kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa --from-file=ssh-publickey=path/to/id_rsa.pub # 手动指定kv kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret
-
创建一个 generic 资源
]# kubectl create secret generic mysql-pass --from-literal=password=xiong@123 secret/mysql-pass created
-
查看
]# kubectl get secret NAME TYPE DATA AGE mysql-pass Opaque 1 7s ]# kubectl describe secrets mysql-pass Data ==== password: 9 bytes # key为password, 其值就是一个隐藏结果只显示字符串长度 # 查看更详细结果 ]# kubectl get secrets mysql-pass -o yaml apiVersion: v1 data: password: eGlvbmdAMTIz # 通过-o yaml可以查看到其结果为base64编码 kind: Secret metadata: creationTimestamp: "2020-05-07T06:34:29Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:data: .: { } f:password: { } f:type: { } manager: kubectl operation: Update time: "2020-05-07T06:34:29Z" name: mysql-pass namespace: default resourceVersion: "25605" selfLink: /api/v1/namespaces/default/secrets/mysql-pass uid: 4075a4d2-f81d-4e26-8d98-61df0f8eab51 type: Opaque # 解码 base64 ]# echo eGlvbmdAMTIz | base64 -d # 相对的来说如果有心加密的值也能被显示出来 xiong@123
-
通过环境变量方式注入
]# cat s1_secret.yaml apiVersion: v1 kind: Pod metadata: name: s1-secret namespace: default labels: app: s1-secret release: qa spec: containers: - name: s1-secret image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: mysql_pass valueFrom: secretKeyRef: name: mysql-pass # 指定secret定义的名称 key: password # 指定secret定义的key,注意这个key需要存在对应的资源中
-
验证
~]# kubectl exec s1-secret -- env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=s1-secret mysql_pass=xiong@123
通过volume
volume是通过文件的方式载入配置,比如tomcat的默认配置有指定一个管理的user,那么这个时候我们就可以把这个文件提取出来,载入Kubernetes作为Secret,而后通过volume的方式挂在到指定的目录下(可以挂载单个文件),覆盖原有文件;