持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第31天,点击查看活动详情
一. 存储的概述
k8s的存储一共有四种类型:configMap,Sercet,Persistent Volume,Volume.
1. configMap
configMap简称cm, cm主要是存储配置信息的, 比如:nginx的配置文件, pod的启动命令, nginx的端口号等, 我们都可以使用cm存储.
2. Sercet
只要是和安全有关的,需要加密保存的, 都通过sercet去存储, 例如: 进群认证的sa; 密码; 证书等
3. Volumn
Volumn就有点类似与docker中的卷, docker中的volumn可以保证容器死了以后, 目录还存在; 而k8s中的volumn可以保证pod死了, 目录还存在.
4. Persistent Volume
Persistent Volume简称pv, pv持久卷是一种抽象, 并不是真正的储存. 比如: 存一些网页数据
下面分别来详细研究.
二. Config Map的使用
ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制等对象.
研究configMap从两个方面入手:一个是创建, 一个是使用。
1. Config Map的创建
configMap的创建有3种方式
1)使用目录创建
使用目录创建,会把当前文件夹下所有的文件都转换文config map对象
目录中文件的格式是什么呢?其实没有强制要求,建议使用k-v对。
$ ls
game.file
ui.file
$ cat game.file
version=1.17
name=dave
age=18
$ cat ui.properties
level=2
color=yellow
创建configMap命令
kubectl create configmap game-config --from-file=/root/configMap
- create:创建命令
- configmap:创建命令的类型
- game-config:创建命令的名称
- --from-file : 指定目录, 目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容
问题
k8s生成的文件通常都是yaml格式的,而这种方式生成的config map怎么是k-v的?其实没关系,k8s依然会将其转换为yaml格式。我们可以通过-o yaml 查看
kubectl create configmap game-config --from-file=/root/configMap --dry-run -o yaml
- --dry-run: 本地(local)干运行而不与服务器通信:它没有服务器验证,也没有通过验证许可控制器(validating admission controller)
这样就生成了一个yaml格式的configmap。
通过命令查看config map
kubectl get cm
- DATA:表示里面有两个字段
查看config map的详情
有两种方式
- 方式一:
kubectl describe cm game-config
- 方式二
kubectl get cm game-config -o yaml
2)使用文件创建
使用文件创建, 把单个文件转换为config map。
创建命令
kubectl create configmap game-config --from-file=/root/configMap
这个创建命令和文件夹创建config map命令是一模一样的,也就是说--from-file既可以指定文件夹,也可以指定文件
3)使用字面值创建
使用文字值创建,利用 —from-literal 参数传递配置信息,该参数可以使用多次,格式如下
kubectl create configmap literal-config --from-literal=name=dave --from-literal=password=pass
- literal-config: 表示使用文字值创建
- --from-literal: 指定创建的k-v对
- 这里创建了两个字面量:一个是name=dave, 另一个是password=pass
使用describe查询cm 后者通过 -o yaml查询
我们看到,data一共有两个元素。
2. Config Map的使用
使用config map也有3种方式:
1)把 ConfigMap 替代为当前的环境变量
Config map 的第一种用法是使用Config Map来代替环境变量。下面举个例子
第一步:准备configMap资源清单1
apiVersion: v1
kind: ConfigMap
metadata:
name: literal-config
namespace: default
data:
name: dave
password: pass
- 资源清单的类型是ConfigMap
- 资源清单名称是literal-config
- 资源清单中定义了两个数据:name=dave,password=pass
第二步:准备configMap资源清单2
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
app: myapp
- 创建了一个名为env-config的资源
- 资源清单的类型是ConfigMap
- 资源清单中有两组数据:log_level=INFO,app=myapp
第三步:准备pod资源清单
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap-env
spec:
containers:
- name: test-container
image: wangyanglinux/myapp:v1
command: [ "/bin/sh", "-c", "env" ]
env:
- name: USERNAME
valueFrom:
configMapKeyRef:
name: literal-config
key: name
- name: PASSWORD
valueFrom:
configMapKeyRef:
name: literal-config
key: password
envFrom:
- configMapRef:
name: env-config
restartPolicy: Never
-
创建一个pod, pod的名字是pod-configmap-env
-
spec中指定了两个元素
-
restartPolicy:重启策略, 这里是Never。表示pod结束后不再重启。
-
containers:指定pod中的容器
-
name:容器名称是test-container
-
image:镜像名称是wangyanglinux/myapp:v1
-
command: [ "/bin/sh", "-c", "env" ] 表示容器启动以后执行的命令, 这里执行env命令,也就是打印环境变量。后面,我们会定义环境变量的内容
-
env:表示定义环境变量
-
name:系统中环境变量的名称,这里名称叫USERNAME
-
valueFrom:环境变量的值,这里不是直接一个value值,而是值的来源。我们这里的值来源于configMap。
-
configMapKeyRef:指的是值来源于configMap
- name:来源于哪一个config呢?也就是configMap的名字是什么。这里来源的configMap的名字是literal-config
- key:指定的configMap中有多组数据。我们这里的变量引用的是哪一个数据呢?key指定的是数据的name。这里key指定的是name。
- 表示的含义是:我们定义了一个系统环境变量,变量名叫 USERNAME。变量的值来源于名称为literal-config的configMap,data数据中key=name的字段的值,这个值是dave。因此,我们定义的这个系统变量是 USERNAME=dave
-
-
-
envFrom:表示引用一组环境变量。
-
configMapRef:引用环境变量来自 configMap
- name:指定引用的 configMap 的名字。这里是 env-config
- 在env-config中有两组数据,都会被引用进来。log_level: INFO 和 app: myapp
-
-
-
上面的代码的含义是什么呢?创建一个pod,里面定义一个容器,给容器创建了两组环境变量。第一组环境变量来源是名字为literal-config的configMap,变量内容是:USERNAME=dave 和PASSWORD=pass;第二组环境变量来源于名字是env-config的configMap,环境变量的内容是log_level=INFO 和app=myapp
第四步:执行资源文件
[root@master configMap]# touch literal-config-cm.yaml
[root@master configMap]# vi literal-config-cm.yaml
[root@master configMap]# touch env-config-cm.yaml
[root@master configMap]# vi env-config-cm.yaml
[root@master configMap]# touch pod-configmap-env.yaml
[root@master configMap]# vi pod-configmap-env.yaml
[root@master configMap]# kubectl create -f literal-config-cm.yaml
configmap/literal-config created
[root@master configMap]# kubectl create -f env-config-cm.yaml
configmap/env-config created
[root@master configMap]# kubectl create -f pod-configmap-env.yaml
pod/pod-configmap-env created
# 查看pod的日志
[root@master configMap]# kubectl logs pod pod/pod-configmap-env
第五步:总结
这样,就成功将configMap中的数据注入到环境变量了。
2) 把 ConfigMap 设置为命令行参数
其实第二种方式设置为命令行参数和第一种有些类似,也是定义环境变量,然后将环境变量作为命令行的调用参数。用案例来解释:
第一步:准备资源清单
apiVersion: v1
kind: Pod
metadata:
name: pod-command-cm
spec:
containers:
- name: test-container
image: wangyanglinux/myapp:v1
command: [ "/bin/sh", "-c", "echo $(USERNAME) $(PASSWORD)" ]
env:
- name: USERNAME
valueFrom:
configMapKeyRef:
name: literal-config
key: name
- name: PASSWORD
valueFrom:
configMapKeyRef:
name: literal-config
key: password
restartPolicy: Never
- pod的名字是pod-command-cm
- env:定义了环境变量,具体含义如上一个案例的解释。环境变量的内容是USERNAME=dave,PASSWORD=pass
- comamnd:容器启动后执行的命令。这里启动命令带参数"echo (PASSWORD)",这个逻辑和在linux中的shell是一样的。我们已经定义了环境变量了,那么在命令行可以直接调用环境变量
第二步:执行资源清单
[root@master configMap]# kubectl apply -f pod-command-cm.yaml
pod/pod-command-cm created
# 查看日志
kubectl logs pod-command-cm
3)将configMap当成文件使用,需要数据卷插件配合
前两种方式都是服务启动环境变量就创建了,中途是不可以修改的。而第三种方式,我们可以在中途修改环境变量。这种方式需要通过数据卷的方式实现。
之前说过配置中心。我们来看看配置中心的原理:
当我们的系统越来越庞大,管理变量就成了一个问题。如果每次修改变量,都要重新发版,这样也将是一个很大的工作量,因此引入了nacos。nacos可以自动实现环境变量实时同步且在不启动服务的情况下生效。在k8s中,config Map就可以起到配置中心的作用。 环境变量修改,在不重启的情况下,立即生效。这就涉及了configMap的另一个功能,热更新。
下面举例说明:
第一步:准备资源清单
apiVersion: v1
kind: ConfigMap
metadata:
name: log-config
namespace: default
data:
log_level: INFO
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hot-update
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: wangyanglinux/myapp:v1
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: log-config
-
这里定义了两个资源清单, 一个是configMap,数据内容是log_level = INFO;另一个是Deployment,在这里我们会定义数据卷挂载到config map。
-
Deployment的资源清单基础的就不多说了,说一下我们第一次见到的,从容器开始看
-
在template中定义了两个个性化资。一个是volumes,另一个是containers。
-
volumes:定义了资源数据卷。
-
name:数据卷的名字是config-volume
-
configMap:指定了数据卷数据来源的类型是configMap
- name:当前k8s中有很多cm,到底是哪一个呢?指定cm的名字是log-config
-
-
containers
-
volumeMounts:为当前容器指定挂载的数据卷
- name:挂载数据卷的名字
- mountPath:数据挂载的路径
-
-
-
总结:这个含义是,定义了一个数据卷,数据卷的名字是config-volume,数据来源是名字为log-config的cm。然后为名字为my-nginx的容器挂载数据卷,并指定数据卷挂载到容器的/etc/config目录下。
第二步:容器挂载的原理
这里分析一下容器挂载的原理
来分析一下pod是如何跟cm交互了。pod可不可以实时去cm读取数据?可以,但这样cm的压力会很大,而且交互会很频繁。最好的办法是在每个pod中缓存一份变量,当cm中变量变化了,同步更新本地的缓存。这样,变量的更新可能会产生延迟。
configMap配置文件在本地如何保存呢?之前我们在创建cm的时候可以从文件创建,那么保存的时候也会保存为文件。比如这个资源文件
[root@master configMap]# kubectl get cm env-config -o yaml
apiVersion: v1
data:
app: myapp
log_level: INFO
kind: ConfigMap
metadata:
creationTimestamp: "2022-04-07T07:07:11Z"
name: env-config
namespace: default
resourceVersion: "1204949"
selfLink: /api/v1/namespaces/default/configmaps/env-config
uid: 5192cf76-d5b1-4dfe-b058-cd137ccfed5f
保存为本地文件的时候,会保存为2个文件,
- 文件1:文件名是app,文件内容是myapp
- 文件2:文件名是log_level,文件内容是INFO
第三步:执行资源清单
[root@master configMap]# kubectl create -f hot-update.yaml
deployment.apps/hot-update created
执行结果
[root@master configMap]# kubectl get pod
NAME READY STATUS RESTARTS AGE
hot-update-75b6496495-2ff6h 1/1 Running 0 28s
第四步:cm热更新
我们写一个for循环,不停的打印环境变量log_level: INFO。
while 2>1; do kubectl exec hot-update-75b6496495-2ff6h -- cat /etc/config/log_level; sleep 2; date; done
[root@master configMap]# while 2>1; do kubectl exec hot-update-75b6496495-2ff6h -- cat /etc/config/log_level; sleep 2; date; done
INFOThu Apr 7 16:27:51 CST 2022
INFOThu Apr 7 16:27:53 CST 2022
INFOThu Apr 7 16:27:55 CST 2022
INFOThu Apr 7 16:27:57 CST 2022
INFOThu Apr 7 16:27:59 CST 2022
INFOThu Apr 7 16:28:02 CST 2022
INFOThu Apr 7 16:28:04 CST 2022
每隔两秒打印一下挂载数据卷中log_level文件的内容
然后我们修改cm中的内容, 将log_level的值从INFO改为DEBUG
kubectl edit cm log-config
来看结果
从图中可以看出,16:35:12 文件内容改为了DEBUG。其实我是16:34:39 就修改完文件了,经过了31s才变过来。这也说明,这里环境变量不是共享的,而是缓存(注入)到本地了。
注意:在edit文件的时候,要注意有没有annotations,如果有,那么这里面的内容也要修改,否则会不生效。
我们修改了cm的内容,容器挂载的数据卷也可以同步到修改的内容。但是,这个修改对应用生效么?很显然是不生效的。容器的镜像没变。所以,要想cm修改的配置最终在应用中生效,还要重载镜像。什么意思呢? 如下表格
| 配置生效 | 实现方式 |
|---|---|
| 修改配置文件 | Config Map |
| 重启容器 | ? |
如果想要实现修改配置文件,不手动重启, 自动生效, 需要有两步: 第一步: 配置文件实时更新, 这个通过cm可以实现 第二步: 重启容器. 如果容器不重启, 那配置还是原来的配置. 如何重启容器呢? 修改镜像的版本号
docker tag wangyanglinux/myapp:v1 wangyanglinux/myapp:v1.1
这样就会重启了。可是在k8s中怎么操作呢?cm提供了滚动更新策略。
第五步 Config Map的滚动更新策略
更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新。