在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。
kubernetes的Volume支持多种类型,比较常见的有下面几个:
- 简单存储:EmptyDir、HostPath、NFS
- 高级存储:PV、PVC
- 配置存储:ConfigMap、Secret
一、配置存储
1.1 ConfigMap
1.1.1.基于文件夹的创建方式
ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。
使用 kubectl create configmap -h 查看示例,构建 configmap 对象
[root@k8s-master ~]# mkdir config [root@k8s-master ~]# cd config [root@k8s-master config]# kubectl create configmap -h
创建两个配置文件
[root@k8s-master config]# mkdir test [root@k8s-master config]# cd test/ [root@k8s-master test]# ls [root@k8s-master test]# touch db.properties [root@k8s-master test]# ls db.properties [root@k8s-master test]# vi db.properties
username=root password=admin
[root@k8s-master test]# vi redis.properties
host:127.0.0.1 port:6379
#创建一个名为 test-dir-config 的 ConfigMap,其中包含来自指定目录的所有文件 [root@k8s-master config]# kubectl create configmap test-dir-config --from-file=test/ configmap/test-dir-config created
- kubectl create configmap: 这是用于在 Kubernetes 中创建 ConfigMap 的命令。
- test-dir-config: 这是您为新创建的 ConfigMap 指定的名称。
- –from-file=test/: 这个标志指定了 ConfigMap 的数据来自哪个目录kubectl create configmap: 这是用于在 Kubernetes 中创建 ConfigMap 的命令。
# 获取ConfigMap 列表 [root@k8s-master config]# kubectl get cm NAME DATA AGE kube-root-ca.crt 1 15d test-dir-config 2 103s
- kube-root-ca.crt: 包含了一个数据项 ,存储根证书的 ConfigMap
- test-dir-config: 包含了两个数据项。这是在之前的操作中创建的 ConfigMap,它从 test/ 目录中获取了两个文件的内容。
#查看详细信息 [root@k8s-master config]# kubectl describe cm test-dir-config
1.1.2指定文件的创建方式
[root@k8s-master config]# vi application.yml
spring: application: name: test app server: port:8080
[root@k8s-master config]# kubectl create cm spring-boot-test-yaml --from-file=/root/config/application.yml configmap/spring-boot-test-yaml created # 获取ConfigMap 列表 [root@k8s-master config]# kubectl get cm NAME DATA AGE kube-root-ca.crt 1 15d spring-boot-test-yaml 1 20s test-dir-config 2 12m [root@k8s-master config]# kubectl describe cm spring-boot-test-yaml
1.1.3 配置文件创建configmap
创建configmap.yaml,内容如下:
apiVersion: v1 kind: ConfigMap metadata: name: configmap namespace: dev data: info: | username:admin password:123456
接下来,使用此配置文件创建configmapp
创建configmap [root@k8s-master config]# kubectl create -f configmap.yaml configmap/configmap created # 查看configmap详情 [root@k8s-master config]# kubectl describe cm configmap -n dev Name: configmap Namespace: dev Labels: <none> Annotations: <none> Data ==== info: ---- username:admin password:123456 BinaryData ==== Events: <none>
接下来创建一个pod-configmap.yaml,将上面创建的configmap挂载进去
apiVersion: v1 kind: Pod metadata: name: pod-configmap namespace: dev spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 将configmap挂载到目录 - name: config mountPath: /configmap/config volumes: # 引用configmap - name: config configMap: name: configmap
# 创建pod [root@k8s-master config]# kubectl create -f pod-configmap.yaml pod/pod-configmap created # 查看pod [root@k8s-master config]# kubectl get pod pod-configmap -n dev NAME READY STATUS RESTARTS AGE pod-configmap 1/1 Running 0 15s #进入容器 [root@k8s-master ~]# kubectl exec -it pod-configmap -n dev -- /bin/sh # # cd /configmap/config/ # ls info # more info username:admin password:123456 # 可以看到映射已经成功,每个configmap都映射成了一个目录 # key--->文件 value---->文件中的内容 # 此时如果更新configmap的内容, 容器中的值也会动态更新
1.2 Secret
在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等。
1.2.1Secret的应用与Docker仓库 Secret设置
1. Kubernetes 中的 Secrets:
在 Kubernetes 中,Secrets 可以用于以下目的:
- 存储敏感数据: 比如数据库密码、API 密钥等。
- 挂载到 Pod 中: Secrets 可以通过 Volume 挂载到 Pod 中,使应用程序能够读取敏感数据。这有助于将敏感信息与应用程序的配置分离开。
- 用于身份验证: 在某些情况下,Secrets 也可以用于身份验证,例如在 Ingress 中配置 TLS 证书。
创建 Secret 示例:
1.首先使用base64对数据进行编码
#准备username [root@k8s-master ~]# echo -n 'admin' | base64 YWRtaW4= #准备password [root@k8s-master ~]# echo -n '123456' | base64 MTIzNDU2
2.接下来编写secret.yaml,并创建Secret
apiVersion: v1 kind: Secret metadata: name: secret namespace: dev type: Opaque data: username: YWRtaW4= password: MTIzNDU2
# 创建secret [root@k8s-master config]# kubectl create -f secret.yaml secret/secret created # 查看secret详情 [root@k8s-master config]# kubectl describe secret secret -n dev Name: secret Namespace: dev Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 6 bytes username: 5 bytes
3.创建pod-secret.yaml,将上面创建的secret挂载进去:
apiVersion: v1 kind: Pod metadata: name: pod-secret namespace: dev spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 将secret挂载到目录 - name: config mountPath: /secret/config volumes: - name: config secret: secretName: secret
# 创建pod [root@k8s-master config]# kubectl create -f pod-secret.yaml pod/pod-secret created # 查看pod [root@k8s-master config]# kubectl get pod pod-secret -n dev NAME READY STATUS RESTARTS AGE pod-secret 1/1 Running 0 2m28s # 进入容器,查看secret信息,发现已经自动解码了 [root@k8s-master config]# kubectl exec -it pod-secret -- /bin/sh -n dev / # ls /secret/config/ password username / # more /secret/config/username admin / # more /secret/config/password 123456
另一种创建方法:
kubectl create secret generic my-secret \ --from-literal=username=my-username \ --from-literal=password=my-password
将 Secret 挂载到 Pod 中的示例:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mycontainer image: myimage volumeMounts: - name: secret-volume mountPath: /etc/myapp volumes: - name: secret-volume secret: secretName: my-secret
2. Docker 仓库中的 Secret:
在 Docker 仓库中,Secrets 通常用于:
- 拉取私有镜像: 如果您的 Docker 镜像存储库是私有的,您需要将 Docker 仓库的凭据存储在 Kubernetes 中的 Secret 中,以便 Pods 能够拉取镜像。
在 Kubernetes 中设置 Docker 仓库的 Secret 示例:
kubectl create secret docker-registry my-docker-secret \ --docker-server=my-docker-registry.com \ --docker-username=my-username \ --docker-password=my-password \ --docker-email=my-email@example.com
在 Pod 中使用 Docker 仓库的 Secret 示例:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mycontainer image: my-docker-registry.com/myimage:latest imagePullSecrets: - name: my-docker-secret
这将使 Kubernetes 能够使用 my-docker-secret 中的凭据拉取 my-docker-registry.com/myimage:latest 这个私有 Docker 镜像
1.3SubPath 的使用
使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath
配置方式:
- 定义 volumes 时需要增加 items 属性,配置 key 和 path,且 path 的值不能从 / 开始
- 在容器内的 volumeMounts 中增加 subPath 属性,该值与 volumes 中 items.path 的值相同
containers: ...... volumeMounts: - mountPath: /etc/nginx/nginx.conf # 挂载到哪里 name: config-volume # 使用哪个 configmap 或 secret subPath: etc/nginx/nginx.conf # 与 volumes.[0].items.path 相同 volumes: - configMap: name: nginx-conf # configMap 名字 items: # subPath 配置 key: nginx.conf # configMap 中的文件名 path: etc/nginx/nginx.conf # subPath 路径
[root@k8s-master etc]# kubectl get po NAME READY STATUS RESTARTS AGE nginx-85b98978db-mkp29 1/1 Running 2 (3d3h ago) 12d nginx-deploy-c4986b7f-8tplt 1/1 Running 2 (3d3h ago) 6d6h nginx-deploy-c4986b7f-qltv6 1/1 Running 2 (3d3h ago) 6d6h
[root@k8s-master etc]# kubectl exec -it nginx-deploy-c4986b7f-8tplt -- sh # cd /etc # cd nginx # ls conf.d fastcgi_params koi-utf koi-win mime.types nginx.conf scgi_params uwsgi_params win-utf # cat nginx.conf user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } #复制配置信息 退出容器 # exit
#创建nginx.conf文件 [root@k8s-master config]# vi nginx-conf [root@k8s-master config]# ls nginx-conf [root@k8s-master config]# kubectl create configmap nginx-conf-cm --from-file=./nginx-conf configmap/nginx-conf-cm created [root@k8s-master config]# kubectl describe cm nginx-conf-cm Name: nginx-conf-cm Namespace: default Labels: <none> Annotations: <none> Data ==== nginx-conf: ---- user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } BinaryData ==== Events: <none>
存储的内容就是nginx-conf这个文件
[root@k8s-master config]# kubectl edit deploy nginx-deploy
volumeMounts: # 挂在数据卷 - name: nginx-conf # 数据卷的名称 mountPath: '/etc/nginx' # 挂载的路径 volumes: # 数据卷定义 - name: nginx-conf # 数据卷的名称 configMap: # 数据卷类型为 configmap name: nginx-conf-cm #configmap名字 items: # 要将 configmap中哪些数据挂在尽力啊 - key: nginx-conf # 指定挂在哪个key path: nginx.conf # 挂在后该key重命名什么名字
[root@k8s-master config]# kubectl get po NAME READY STATUS RESTARTS AGE nginx-85b98978db-mkp29 1/1 Running 2 (3d3h ago) 12d nginx-deploy-7c6975968-t5w5b 0/1 CrashLoopBackOff 4 (62s ago) 2m37s [root@k8s-master config]# kubectl edit deploy nginx-deploy deployment.apps/nginx-deploy edited # 在name: nginx后面加入 command: ["/bin/sh", "-c","nginx daemon off;sleep 3600"]
[root@k8s-master config]# kubectl get po NAME READY STATUS RESTARTS AGE nginx-deploy-55f7b46d59-lp5px 1/1 Running 0 22s nginx-deploy-55f7b46d59-wrfjm 1/1 Running 0 20s
[root@k8s-master config]# kubectl exec -it nginx-deploy-55f7b46d59-lp5px -- sh # cd /etc/nginx # ls nginx.conf
进入容器后发现原来的一堆配置文件都不见了
得出结果:使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉.此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath
1.3.1 使用SubPath
[root@k8s-master config]# kubectl edit deploy nginx-deploy volumeMounts: - mountPath: /etc/nginx/nginx.conf name: nginx-conf subPath: etc/nginx/nginx.conf volumes: - configMap: defaultMode: 420 items: - key: nginx-conf path: etc/nginx/nginx.conf name: nginx-conf-cm name: nginx-conf
[root@k8s-master config]# kubectl get po NAME READY STATUS RESTARTS AGE nginx-85b98978db-mkp29 1/1 Running 2 (3d20h ago) 12d nginx-deploy-5588c8bc86-bhj9l 1/1 Running 0 35s nginx-deploy-5588c8bc86-ltxvs 1/1 Running 0 34s #进入容器 [root@k8s-master config]# kubectl exec -it nginx-deploy-5588c8bc86-bhj9l -- sh # # cd /etc/nginx # ls conf.d fastcgi_params koi-utf koi-win mime.types nginx.conf scgi_params uwsgi_params win-utf
配置了subPath后进入容器后发现 原来那些文件没有被覆盖掉了,这样就解决了加载配置覆盖原目录的问题。
1.4ConfigMap的热更新
我们通常会将项目的配置文件作为 configmap 然后挂载到 pod,那么如果更新 configmap 中的配置,会不会更新到 pod 中呢?
这得分成几种情况:
默认方式:会更新,更新周期是更新时间 + 缓存时间
subPath:不会更新
变量形式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的
对于 subPath 的方式,我们可以取消 subPath 的使用,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置
但是如果目标位置原本就有文件,可能无法创建软链接,此时可以基于前面讲过的 postStart 操作执行删除命令,将默认的文件删除即可
- 通过 edit 命令直接修改 configmap
- 通过 replace 替换
由于 configmap 我们创建通常都是基于文件创建,并不会编写 yaml 配置文件,因此修改时我们也是直接修改配置文件,而 replace 是没有 --from-file 参数的,因此无法实现基于源配置文件的替换,此时我们可以利用下方的命令实现
# 该命令的重点在于 --dry-run 参数,该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -oyaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换
kubectl create cm --from-file=nginx.conf --dry-run -o yaml | kubectl replace -f-
#查看当前的 ConfigMap [root@k8s-master config]# kubectl get cm -n dev NAME DATA AGE configmap 1 3d19h [root@k8s-master config]# ls application.yml file1.txt nginx-conf nginx-pod-configmap.yaml secret.yaml configmap.yaml file2.txt nginx.conf pod-secret.yaml test #修改 ConfigMap 数据 [root@k8s-master config]# vi configmap.yaml username:AAA password:BBB #更新 ConfigMap 读取 configmap.yaml 文件中的数据, #并使用 kubectl replace 将其更新到 configmap ConfigMap 中 [root@k8s-master config]# kubectl create cm configmap -n dev --from-file=configmap.yaml --dry-run=client -o yaml | kubectl replace -f- configmap/configmap replaced [root@k8s-master config]# kubectl get pod pod-configmap -n dev NAME READY STATUS RESTARTS AGE pod-configmap 1/1 Running 0 15s # 验证更新是否传递到 Pod [root@k8s-master config]# kubectl exec -it pod-configmap -n dev -- /bin/sh # cd /configmap/config/ # ls configmap.yaml # cat configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: configmap namespace: dev data: info: | username:AAA password:BBB
确保挂载路径与 ConfigMap 中定义的路径一致,然后查看文件内容是否更新为新的用户名和密码。
这样,就成功地使用 kubectl 命令更新了 ConfigMap 并确保更新成功地传递到相关的 Pod 中
1.5不可变的 Secret 和 ConfigMap
**对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true 来禁止修改.**这个配置项会锁定 ConfigMap,使其变得只读,防止运维人员或其他用户无意中修改关键配置