k8s存储
概述
在Kubernetes(K8s)中,存储系统是一个关键的组成部分,用于管理容器化应用的数据持久性和共享性。K8s的存储分类可以从多个维度进行理解,但主要分为两大类:临时存储和持久存储。关于元数据和真实数据的分类,虽然这两个概念在存储系统中普遍存在,但在K8s的存储分类中,它们并不是直接用于分类存储类型的标准。不过,可以从K8s存储类型如何管理和使用这些数据的角度来探讨。
k8s支持的卷类型
持久卷:持久卷是集群中的存储资源,就像他的名字一样,在里面存储的数据不会随着 Pod 的删除而丢失。
临时卷:有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。卷会遵从 Pod的生命周期,与Pod一起创建和删除。
投射卷:它允许您将多个现有卷源映射到同一个目录。通过将这些不同类型的卷源组合成一个统一的卷,可以更方便地管理和使用这些资源
- 临时存储
EmptyDir:EmptyDir是一种在Pod中创建的空目录,用于在容器之间共享文件。它的数据存储在Pod所在节点的本地磁盘上,当Pod被删除时,数据也会被删除。这种存储方式适用于需要临时存储数据的场景,如缓存数据。在这种情况下,元数据(如目录结构、文件属性等)和真实数据(文件内容)都是临时的,与Pod的生命周期绑定。
- 持久存储
PersistentVolume (PV) 和 PersistentVolumeClaim (PVC):
PV是由管理员配置的存储资源,而PVC是用户请求的存储资源。PVC允许用户抽象地请求存储资源,而不需要关心具体的存储后端。PV和PVC的结合使用,可以动态地分配和释放存储资源,用于持久化存储真实数据。元数据(如PV和PVC的配置信息)存储在K8s的etcd数据库中,而真实数据则存储在配置的存储后端(如NFS、Ceph等)上。
NFS:
NFS卷将网络文件系统(NFS)挂载到容器中,允许跨多个Pod和节点共享数据。元数据(如NFS文件系统的目录结构、文件权限等)和真实数据都存储在NFS服务器上,实现了数据的持久化和共享。
ConfigMap 和 Secret:
虽然ConfigMap和Secret主要用于挂载配置文件和密钥到容器中,但它们也可以视为一种存储形式。这些资源对象的元数据(如配置项的名称、值等)和真实数据(配置文件内容、密钥值等)都存储在K8s的etcd数据库中。不过,它们的主要用途是配置和安全性,而非大规模的数据存储。
StatefulSet:
StatefulSet是一种用于管理有状态应用的控制器,它确保每个Pod都有稳定的标识和顺序。StatefulSet通常会为每个Pod分配一个独特的持久卷(通过PVC实现),以存储Pod的持久化数据。在这种情况下,元数据(如StatefulSet的配置、Pod的标识等)存储在K8s的etcd数据库中,而真实数据则存储在分配的持久卷上。
总结
在K8s中,元数据和真实数据的存储和管理是通过不同的机制实现的。元数据通常存储在K8s的etcd数据库中,用于管理集群的状态和配置。而真实数据则根据所选的存储类型(如PV、PVC、NFS等)存储在相应的存储后端上。通过合理配置和使用这些存储类型,K8s能够提供灵活、可靠的数据存储解决方案,满足各种应用场景的需求。
ConfigMap介绍与使用
K8s(Kubernetes)中的ConfigMap是一种用于存储配置数据的API对象,它属于Kubernetes中的核心对象。ConfigMap的主要作用是将应用程序的配置信息与容器镜像分离,以便在不重新构建镜像的情况下进行配置的修改和更新。
用途:
ConfigMap用于存储键值对形式的配置数据,这些数据可以包括环境变量、命令行参数、配置文件等。它提供了一种集中管理和传递配置信息的机制,使得应用程序能够从ConfigMap中获取配置数据,从而在不修改容器镜像的前提下,动态地修改应用程序的配置参数。
与Secret的区别:
ConfigMap主要用于存储非敏感的配置数据,如应用程序的配置文件、环境变量等,而Secret则用于存储敏感的数据,如密码、密钥等。Secret提供了更高的安全性和访问控制机制。
通过命令行管理ConfigMap示例:
使用文字值创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次
[root@k8s-master test02]# kubectl create cm literal-config --from-literal=name=lili --from-literal=passwd=123
configmap/literal-config created
# 查看指定cm的属性信息
[root@k8s-master test02]# kubectl describe cm literal-config
Name: literal-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
name:
----
lili
passwd:
----
123
BinaryData
====
Events: <none>
# 使用YAML格式输出
[root@k8s-master test02]# kubectl get cm literal-config -o yaml
apiVersion: v1
data:
name: lili
passwd: "123"
kind: ConfigMap
metadata:
creationTimestamp: "2025-01-24T03:33:55Z"
name: literal-config
namespace: default
resourceVersion: "391106"
uid: 860618b7-3177-4f8c-ac83-62d458da7dc0
通过目录创建:
--from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容
示例:
# 以下两个文件里分别有两个值
[root@k8s-master ~]# ls test_cm/
name passwd
# 通过目录创建
[root@k8s-master ~]# kubectl create cm test-cmdir --from-file=./test_cm/
configmap/test-cmdir created
# 查看configmap
[root@k8s-master ~]# kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 9d
literal-config 2 35m
test-cmdir 2 10s
# 查看属性信息
[root@k8s-master ~]# kubectl describe cm test-cmdir
Name: test-cmdir
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
name:
----
haha
lili
passwd:
----
1234
6789
BinaryData
====
Events: <none>
# 通过yaml格式输出
[root@k8s-master ~]# kubectl get cm test-cmdir -o yaml
apiVersion: v1
data:
name: |
haha
lili
passwd: |
1234
6789
kind: ConfigMap
metadata:
creationTimestamp: "2025-01-24T04:09:00Z"
name: test-cmdir
namespace: default
resourceVersion: "394234"
uid: 35476d3a-3ac0-4409-b04d-06936cde7d1e
通过文件创建:
--from-file 参数只要指定为一个文件就可以从单个文件中创建 ConfigMap。--from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的
示例:
[root@k8s-master test02]# kubectl create cm test-config --from-file=test_cm.file
configmap/test-config created
# 查看configmap
[root@k8s-master test02]# kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 9d
literal-config 2 108m
test-cmdir 2 73m
test-config 1 6s
# 查看cm的属性信息
[root@k8s-master test02]# kubectl describe cm test-config
Name: test-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
test_cm.file:
----
name=hehe
passwd=123456
BinaryData
====
Events: <none>
# 以yaml的格式输出指定cm
[root@k8s-master test02]# kubectl get cm test-config -o yaml
apiVersion: v1
data:
test_cm.file: |
name=hehe
passwd=123456
kind: ConfigMap
metadata:
creationTimestamp: "2025-01-24T05:22:24Z"
name: test-config
namespace: default
resourceVersion: "396512"
uid: bfa82c9a-2aea-425e-8623-fe26aee5473b
- ConfigMap使用
环境变量
环境变量:可以将ConfigMap中的数据设置为Pod中容器的环境变量。这样,容器在启动时就可以从环境变量中获取配置信息。
注意:使用该 ConfigMap 挂载的 Env 不会同步更新
示例:
[root@k8s-master test02]# vim config-env.yaml
---
kind: ConfigMap
apiVersion: v1
metadata:
name: literal-config
data:
name: hehe # 定义数据
passwd: admin
---
kind: ConfigMap
apiVersion: v1
metadata:
name: env-config
data:
log_level: INFO
---
kind: Pod # 定义Pod
apiVersion: v1
metadata:
name: env-pod
spec:
restartPolicy: Never # 重启策略 永不
containers:
- name: test
image: myos:nginx
command: # 启动命令 打印容器内部env环境变量
- sh
- -c
- env
env: # 定义env 为容器内部添加环境变量
- name: USERNAME # 环境变量名字
valueFrom: # 值来源
configMapKeyRef: # 值来源于configmap
name: literal-config # 值来源的configmap类别的名字
key: name # key的名字/字段
- name: PASSWORD
valueFrom:
configMapKeyRef:
name: literal-config
key: passwd
envFrom: # 直接引入名为env-config的configmap
- configMapRef:
name: env-config
#创建以上资源清单文件
[root@k8s-master test02]# kubectl apply -f config-env.yaml
configmap/literal-config unchanged
configmap/env-config unchanged
pod/env-pod created
[root@k8s-master test02]# kubectl get pods
NAME READY STATUS RESTARTS AGE
env-pod 0/1 Completed 0 5m10s
[root@k8s-master test02]# kubectl logs env-pod
NGINX_PORT_8888_TCP=tcp://10.245.8.8:8888
NGINX_PORT_8888_TCP_ADDR=10.245.8.8
HOSTNAME=env-pod
USERNAME=hehe # literal-config添加的环境变量
NGINX_PORT_8888_TCP_PORT=8888
PASSWORD=admin # literal-config添加的环境变量
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1
NGINX_PORT=tcp://10.245.8.8:8888
KUBERNETES_PORT=tcp://10.245.0.1:443
PWD=/usr/local/nginx/html
HOME=/root
NGINX_SERVICE_PORT=8888
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443
SHLVL=1
NGINX_PORT_8888_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT=443
log_level=INFO # env-config添加的环境变量
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/nginx/sbin
KUBERNETES_SERVICE_HOST=10.245.0.1
NGINX_SERVICE_HOST=10.245.8.8
_=/usr/bin/env
命令行参数
命令行参数:将ConfigMap中的数据作为命令行参数传递给容器中的应用程序。这通常需要先将ConfigMap的数据保存在环境变量中,然后通过环境变量的方式引用。
[root@k8s-master test02]# vim cm-command.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: cm-command-pod
spec:
restartPolicy: Never
containers:
- name: test
image: myos:nginx
command:
- sh
- -c
- |
echo ${USERNAME} ${PASSWORD}
env:
- name: USERNAME
valueFrom:
configMapKeyRef:
name: literal-config
key: name
- name: PASSWORD
valueFrom:
configMapKeyRef:
name: literal-config
key: passwd
[root@k8s-master test02]# kubectl apply -f cm-command.yaml
pod/cm-command-pod created
[root@k8s-master test02]# kubectl logs cm-command-pod
hehe admin
卷挂载
卷挂载:ConfigMap可以作为卷挂载到Pod中,使得容器可以直接读取ConfigMap中的配置文件。每个键值对都会生成一个文件,其中键为文件名,值为文件内容。这样,应用程序就可以根据需要读取配置文件中的配置信息。
示例:
[root@k8s-master test02]# vim cm-volume.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: cm-volume-pod
spec:
restartPolicy: Never
containers:
- name: test
image: myos:nginx
volumeMounts: # volume挂载
- name: config-volume # 挂载下面指定的 volume
mountPath: /etc/config # 挂载到的目录(容器内路径,该目录下,文件名就是键名,文件内容就是键值)
subPath:
volumes:
- name: config-volume # volume 名称
configMap: # 来自configmap
name: literal-config # 上边的示例已经定义过
[root@k8s-master test02]# kubectl apply -f cm-volume.yaml
pod/cm-volume-pod created
[root@k8s-master test02]# kubectl exec -it cm-volume-pod -- bash
[root@cm-volume-pod html]# ls /etc/config
name passwd
# 这种方式创建的是连接文件 热更新
[root@cm-volume-pod html]# cat /etc/config/name
hehe[root@cm-volume-pod html]# cat /etc/config/passwd
admin[root@cm-volume-pod html]# ls -l /etc/config
total 0
lrwxrwxrwx 1 root root 11 Jan 24 07:16 name -> ..data/name
lrwxrwxrwx 1 root root 13 Jan 24 07:16 passwd -> ..data/passwd
热更新
通过kubectl edit configmap [configmap name]命令直接修改内容就可以达到热更新
示例:
[root@k8s-master test02]# cat cm-update.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: log-config
namespace: default
data:
log_level: INFO
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: nginx
image: myos:nginx
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/config # 容器内这个目录下会有 log_level 这个文件,内容为 INFO
volumes:
- name: config-volume
configMap:
name: log-config
# 查看容器里的文件
[root@k8s-master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level
INFO
# 使用edit命令修改cm的键值为NOTICE
[root@k8s-master test02]# kubectl edit cm log-config
configmap/log-config edited
# 过个10秒再次查看容器里的文件,文件里的配置修改了
[root@k8s-master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level
NOTICE
补充:添加不可改变选项
# 添加不可改变选项 immutable: true
[root@k8s-master test02]# kubectl edit cm log-config
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
log_level: NOTICE
immutable: true # 添加不可改变选项
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"log_level":"INFO"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"log-config","namespace":"default"}}
creationTimestamp: "2025-01-24T08:56:32Z"
name: log-config
namespace: default
resourceVersion: "429330"
uid: 4e138ec5-861f-4205-8611-69c00035e973
# 添加之后就无法进行热更新了
# 添加之后是不可逆的 需要重新创建一个cm
Pod滚动更新
ConfigMap 更新后,并不会让相应的文件重载。例如,Nginx 在启动时,会加载一次配置文件(配置文件中有 ConfigMap 的相关参数),加载完成后,无论这个配置文件再怎么变化,Nginx 都不会再加载它。因此需要 ConfigMap 更新后滚动更新 Pod。
可以通过修改 pod annotations 的方式强制触发滚动更新。这里我们在 Deployment.spec.template.metadata.annotations 中添加 version/config字段来实现pod的滚动更新
kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations":{"version/config": "20250124" }}}}}'
注意:更新 ConfigMap 后:
- 使用该 ConfigMap 挂载的 Env 不会同步更新
- 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新
CM的优势
配置解耦:将配置信息与容器镜像解耦,使得配置可以在不重新构建镜像的情况下进行修改和管理。
动态更新:ConfigMap中的配置可以在运行时动态更新,而不需要重新启动应用程序。
版本控制:ConfigMap中的配置可以使用版本控制系统进行管理,随时回滚到之前的版本。
共享和复用:ConfigMap可以被多个应用程序共享和复用,提高了配置的一致性和可维护性。
综上所述,K8s ConfigMap是Kubernetes中用于存储和管理配置数据的重要组件,它提供了灵活的配置管理方式,使得应用程序的配置更加清晰、易于管理和更新。
Secret
概述
K8s(Kubernetes)中的Secret是一种用于保存敏感信息的资源对象,如密码、OAuth令牌、ssh密钥等。这些信息如果直接放在Pod的定义中或镜像中,可能会带来安全风险,因为Pod的定义和镜像都可能被存储在版本控制系统中,或者被不同的用户访问。通过使用Secret,可以更安全地管理这些敏感信息。
Secret特性
- 安全性:Secret中的信息被加密存储(实际上是Base64编码,但Kubernetes社区通常称之为加密),以减少敏感信息泄露的风险。
- 灵活性:Secret可以以多种方式被Pod使用,包括作为环境变量、挂载到Pod中的卷中的文件,或者在kubelet为Pod拉取镜像时使用。
- 可重用性:多个Pod可以引用同一个Secret,从而避免在多个地方重复存储相同的敏感信息。
- k8s通过仅仅将Secret分发到需要访问Secret的Pod所在的节点来保障安全性
- Secret只会存储在节点的内存中,永不写入物理存储,这样从节点删除secret时就不需要擦除磁盘数据
Secret的类型
- Opaque:这是默认的Secret类型,用于存储任意格式的敏感信息。数据以Base64编码的形式存储在Secret中。
- kubernetes.io/service-account-token:由Kubernetes自动创建,用于Pod与API Server之间的通信认证。
- kubernetes.io/dockerconfigjson:用于存储私有Docker Registry的认证信息。
- kubernetes.io/tls:用于存储TLS证书和私钥,以便Pod能够使用SSL/TLS协议进行安全通信。
- kubernetes.io/basic-auth:用于存储基本认证信息,如用户名和密码
OpaqueSecret
Opaque和configMap很像, 数据是一个 map 类型,要求 value 是 base64 编码格式,可以用于环境变量和数据卷挂载
示例:
# 使用base64编码
[root@k8s-master test02]# echo -n hehe | base64
aGVoZQ==
[root@k8s-master test02]# echo -n 321 | base64
MzIx
# 解码
[root@k8s-master test02]# echo -n aGVoZQ== | base64 -d
hehe
# 使用加密后的用户名和密码创建 Secret
[root@k8s-master test02]# vim secret.yaml
---
kind: Secret
apiVersion: v1
metadata:
name: mysecret
type: Opaque
data:
passwd: MzIx
username: aGVoZQ==
[root@k8s-master test02]# kubectl apply -f secret.yaml
secret/mysecret created
# 查看secret
[root@k8s-master test02]# kubectl get secret
NAME TYPE DATA AGE
mysecret Opaque 2 2m18s
# 查看secret属性信息
[root@k8s-master test02]# kubectl describe secrets mysecret
Name: mysecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
passwd: 3 bytes
username: 4 bytes
# 以yaml的格式输出指定的secret
[root@k8s-master test02]# kubectl get secrets mysecret -o yaml
apiVersion: v1
data:
passwd: MzIx
username: aGVoZQ==
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"passwd":"MzIx","username":"aGVoZQ=="},"kind":"Secret","metadata":{"annotations":{},"name":"mysecret","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2025-01-24T15:57:16Z"
name: mysecret
namespace: default
resourceVersion: "442203"
uid: 9bff22dc-aaf2-4c1f-af1f-d4bda8accd8c
type: Opaque
在pod中使用secret
环境变量
作为环境变量(不可以热更新)
[root@k8s-master test02]# vim mysecret.yaml
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: deploy-secret
spec:
replicas: 3
selector:
matchLabels:
app: secret
template:
metadata:
labels:
app: secret
spec:
containers:
- name: test
image: myos:httpd
command:
- sh
- -c
- |
echo ${TEST_USER} ${TEST_PASS}
env: # 添加环境变量
- name: TEST_USER # 环境变量名
valueFrom: # 值来源
secretKeyRef: # 从 Secret 中获取
name: mysecret # Secret 的名字,在以上示例已定义
key: username # Secret 中的键名
- name: TEST_PASS
valueFrom:
secretKeyRef:
name: mysecret
key: passwd
[root@k8s-master test02]# kubectl apply -f mysecret.yaml
deployment.apps/deploy-secret created
# secret中的键值会自动的解码
[root@k8s-master test02]# kubectl logs deploy-secret-6c848876bf-dcb2s
hehe 321
卷挂载
secret也是支持热更新的和configmap一样,但是使用secret作为子路径卷挂载的容器不会收到secret更新
将secret挂载到volume中示例:
[root@k8s-master test02]# vim secret_volume.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: secret-test
spec:
volumes: # 创建一个卷
- name: secret # 卷名
secret: # 卷使用的方案
secretName: mysecret # 来自于上面案例创建的 mysecret
containers:
- name: test
image: myos:nginx
volumeMounts: # 卷挂载
- name: secret # 挂载的是上面声明的 secrets
mountPath: /data # 挂载的目录(容器内目录)
readOnly: true # 只读
[root@k8s-master test02]# kubectl apply -f secret_volume.yaml
pod/secret-test created
[root@k8s-master test02]# kubectl get pods
NAME READY STATUS RESTARTS AGE
secret-test 1/1 Running 0 4s
# 进入容器
[root@k8s-master test02]# kubectl exec -it secret-test -- bash
[root@secret-test html]# cd /data
# Opaque Secret 中的用户名和密码都已经挂载进来了
[root@secret-test data]# ls
passwd username
# 查看内容,发现内容已经自动被解码
[root@secret-test data]# cat passwd
321[root@secret-test data]# cat username
hehe
注意事项:
- 当使用Secret时,应确保Pod有足够的权限来访问这些Secret。
- Secret中的信息虽然被加密(实际上是Base64编码),但应尽量避免将过于敏感的信息存储在Kubernetes集群中,以防止潜在的泄露风险。
- 定期检查并更新Secret中的敏感信息,以确保系统的安全性。
Downward API(将pod元数据反馈到容器内部)
在Kubernetes(k8s)中,Downward API 是一种特殊类型的 API,它允许 Pod 中的容器获取关于 Pod 本身及其所在环境的元数据信息。这些信息可以通过两种方式注入到容器内部:环境变量和卷挂载(Volume Mounts)。
- 提供容器元数据
- 动态配置
- 与kubernetes环境集成
Downward API 的两种注入方式
环境变量
环境变量是 Downward API 注入信息到容器的常用方式,适用于单个变量的情况。通过 Downward API,可以将 Pod 的 IP 地址、名称、命名空间等基本信息以环境变量的形式注入到容器内部。这样,容器内的应用程序就可以通过读取这些环境变量来获取 Pod 的相关信息。
示例:
[root@k8s-master test02]# vim test_downwardapi.yaml
kind: Pod
apiVersion: v1
metadata:
name: downward-api-pod
spec:
restartPolicy: Never
containers:
- name: test
image: myos:nginx
env: # 定义容器的环境变量
- name: POD_NAME # POD_NAME 环境变量,其值来源于 Pod 的元数据字段 metadata.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CPU_LIMIT # CPU_LIMIT 环境变量,其值来源于 Pod 的资源限制字段 limits.cpu
valueFrom:
resourceFieldRef:
resource: limits.cpu
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.cpu
[root@k8s-master test02]# kubectl apply -f test_downwardapi.yaml
pod/downward-api-pod created
[root@k8s-master test02]# kubectl get pods
NAME READY STATUS RESTARTS AGE
downward-api-pod 1/1 Running 0 9s
secret-test 1/1 Running 0 5h58m
# 验证效果
[root@k8s-master test02]# kubectl exec -it downward-api-pod -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/nginx/sbin
HOSTNAME=downward-api-pod
POD_IP=10.244.85.215 # 得到的podIP地址
CPU_LIMIT=2 # 限制2个cpu
CPU_REQUEST=0 # 0代表没有进行配额的设置
POD_NAME=downward-api-pod #pod的名称
NAMESPACE=default
NGINX_PORT_8888_TCP_PORT=8888
KUBERNETES_SERVICE_PORT_HTTPS=443
NGINX_PORT_8888_TCP_ADDR=10.245.8.8
NGINX_PORT=tcp://10.245.8.8:8888
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
NGINX_SERVICE_HOST=10.245.8.8
NGINX_PORT_8888_TCP=tcp://10.245.8.8:8888
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.245.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443
KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1
NGINX_SERVICE_PORT=8888
NGINX_PORT_8888_TCP_PROTO=tcp
KUBERNETES_SERVICE_HOST=10.245.0.1
TERM=xterm
HOME=/root
卷挂载
是将 Pod 的信息生成为文件,并通过卷挂载的方式将这些文件注入到容器内部。这种方式适用于需要批量处理或复杂查询 Pod 信息的情况。
示例:
[root@k8s-master test02]# vim test_downwardapi02.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: test-volume-pod
labels:
app: volume
spec:
restartPolicy: Never
containers:
- name: test
image: myos:nginx
resources:
limits:
cpu: 1
memory: 400Mi
requests:
cpu: 1
memory: 300Mi
volumeMounts:
- name: downwardapi-volume
mountPath: /podinfo
volumes:
- name: downwardapi-volume
downwardAPI:
items:
- path: "labels" # 挂载的文件路径
fieldRef:
fieldPath: metadata.labels # 引用的字段路径
- path: "name"
fieldRef:
fieldPath: metadata.name
- path: "namespace"
fieldRef:
fieldPath: metadata.namespace
- path: "uid"
fieldRef:
fieldPath: metadata.uid
- path: "cpuRequest"
resourceFieldRef:
containerName: test # 引用的容器名称
resource: requests.cpu # 引用的资源字段
- path: "memoryRequest" # 挂载的文件路径
resourceFieldRef:
containerName: test
resource: requests.memory
- path: "cpuLimit"
resourceFieldRef:
containerName: test
resource: limits.cpu
- path: "memoryLimit"
resourceFieldRef:
containerName: test
resource: limits.memory
[root@k8s-master test02]# kubectl get pods
NAME READY STATUS RESTARTS AGE
downward-api-pod 1/1 Running 0 136m
secret-test 1/1 Running 0 8h
test-volume-pod 1/1 Running 0 8m2s
# 进入容器验证效果
[root@k8s-master test02]# kubectl exec -it test-volume-pod -- bash
[root@test-volume-pod html]# cd /podinfo $$ ls
bash: cd: too many arguments
[root@test-volume-pod html]# cd /podinfo
[root@test-volume-pod podinfo]# ls
cpuLimit labels memoryRequest namespace
cpuRequest memoryLimit name uid
[root@test-volume-pod podinfo]# cat name
test-volume-pod[root@test-volume-pod podinfo]# cat labels
app="volume"
Downward API 支持的字段
Downward API 支持的字段包括但不限于:
spec.nodeName:宿主机名字
status.hostIP:宿主机 IP
metadata.name:Pod 的名字
metadata.namespace:Pod 的 Namespace
status.podIP:Pod 的 IP
spec.serviceAccountName:Pod 的 Service Account 的名字
metadata.uid:Pod 的 UID
metadata.labels[‘’]:指定 的 Label 值
metadata.annotations[‘’]:指定 的 Annotation 值
metadata.labels:Pod 的所有 Label
metadata.annotations:Pod 的所有 Annotation
使用 Downward API 的步骤
1、创建包含 Downward API 信息的 Pod:
编写 Pod 的 YAML 配置文件,定义需要注入的环境变量或卷挂载。
2、使用 kubectl 创建 Pod:
使用 kubectl apply -f <pod-config-file.yaml> 命令创建 Pod。
3、在容器中读取 Downward API 注入的信息:
进入 Pod 的容器内部,通过环境变量或文件来读取注入的信息。
通过以上步骤,你可以在 Kubernetes 中使用 Downward API 来获取 Pod 的相关信息,并将其注入到容器内部,以满足应用程序的需求。
Volume(真实数据存储方式,脱离容器生命周期以外的存储方式)
K8s(Kubernetes)中的Volume(存储卷)是一种用于在Pod中持久存储数据的机制,它为Pod中的容器提供了一个共享的存储空间
定义与用途
定义:在K8s中,Volume是一种抽象的概念,用于提供Pod中容器的持久化存储。它允许将数据存储在Pod的生命周期之外,以便在容器重启、迁移或重新调度时保留数据。
用途:
- 数据持久化:将数据存储在Volume中,确保容器重启后数据仍然存在。
- 数据共享:Volume可以连接到Pod中的一个或多个容器,使它们能够共享相同的数据。
- 数据备份和恢复:使用Volume来备份和还原应用程序的数据。
- 数据迁移和复制:将Volume从一个Pod迁移到另一个Pod,或将Volume复制到其他地方。
kubernets支持的卷的类型
官网:https://kubernetes.io/zh/docs/concepts/storage/volumes/
k8s支持的卷的类型如下:
awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csidownwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker、gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret、storageos、vsphereVolume
emptyDir
当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。该卷可以挂载到 Pod 每个容器中的相同或不同路径上,并且每个容器都可以读取和写入 emptyDir 卷中的文件。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除。
注意:容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器时是安全的
emptyDir 的用法有:
- 存放临时文件,例如用于基于磁盘的归并排序;
- 用作长时间计算崩溃恢复时的检查点,供容器崩溃后恢复使用;
- Web 服务器容器提供数据时,保存内容管理器容器提取的文件;
示例:
[root@k8s-master test02]# vim test_emptydir.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: test-emptydir-pod
spec:
containers:
- name: test01
image: myos:nginx
volumeMounts:
- name: emptydir-volume # 通过哪个 volume 挂载
mountPath: /test01_volume # 挂载到容器的哪个目录下
- name: test02
image: myos:php-fpm
volumeMounts:
- name: emptydir-volume
mountPath: /test02_volume
volumes:
- name: emptydir-volume # volume 名称
emptyDir: {} # volume 类型
[root@k8s-master test02]# kubectl apply -f test_emptydir.yaml
pod/test-emptydir-pod created
[root@k8s-master test02]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-emptydir-pod 2/2 Running 0 4s
# 在容器1的 /test 目录下,创建一个 info.txt 文件
[root@k8s-master test02]# kubectl exec -it test-emptydir-pod -c test01 -- touch /test01_volume/info.txt
# 查看容器2的 /test02_volume 目录
[root@k8s-master test02]# kubectl exec -it test-emptydir-pod -c test02 -- ls /test02_volume
info.txt
hostPath
hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中。
在Kubernetes(k8s)中,HostPath是一种特殊的卷类型,它允许将节点(Node)上的文件或目录直接挂载到Pod中。这种挂载方式使得Pod能够访问宿主机上的文件系统,从而实现了数据的持久化存储,即使Pod被删除或重建,只要宿主机上的文件或目录仍然存在,数据就不会丢失。
HostPath的配置参数
在Kubernetes中配置HostPath卷时,通常需要指定以下参数:
- path:指定宿主机上的目录或文件路径,这是必选字段。
- type(可选):指定节点之上存储类型,包括以下几种:
- DirectoryOrCreate:如果给定的路径不存在,则创建一个空目录,权限设置为755。
- Directory:目录必须存在。
- FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限设置为644。
- File:文件必须存在。
- Socket:UNIX套接字,必须存在。
- CharDevice:字符设备,必须存在。
- BlockDevice:块设备,必须存在。
HostPath卷适用于以下场景:
- 需要Pod直接访问宿主机上的特定文件或目录,例如访问Docker内部机制或系统文件。
- 在某些特定场景下,如运行管理任务的系统级Pod资源,需要访问节点上的特定资源。
示例:
[root@master ~]# vim web1.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: web1
spec:
volumes: # 卷定义
- name: logdata # 卷名称
hostPath: # 资源类型
path: /var/weblog # 宿主机路径
type: DirectoryOrCreate # 目录不存在就创建
containers:
- name: nginx
image: myos:nginx
volumeMounts: # mount 卷
- name: logdata # 卷名称
mountPath: /usr/local/nginx/logs # 容器内路径
[root@master ~]# kubectl apply -f web1.yaml
pod/web1 created
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web1 1/1 Running 0 45m 10.244.2.16 node-0002
[root@master ~]# curl http://10.244.2.16/
Nginx is running !
# 删除Pod ,日志数据也不会丢失
[root@master ~]# kubectl delete pod web1
pod "web1" deleted
# 来到 node 上查看日志
[root@node-0002 ~]# cat /var/weblog/access.log
10.244.0.0 - - [27/Jun/2022:02:00:12 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.29.0"
NFS
k8s 中允许将 nfs 存储以卷的方式挂载到你的 Pod 中。在删除 Pod 时,nfs 存储卷会被卸载(umount),而不是被删除。nfs 卷可以在不同节点的 Pod 之间共享数据。
NFS卷的用途
NFS最大的功能就是在不同节点的不同Pod中共享读写数据。本地 NFS 的客户端可以透明地读写位于远端 NFS 服务器上的文件,就像访问本地文件一样
示例:
# 创建共享目录,并部署测试页面,这里harbor主机充当nfs服务器
[root@harbor ~]# mkdir -p /var/webroot
[root@harbor ~]# echo "nfs server" >/var/webroot/index.html
# 部署 NFS 服务
[root@harbor ~]# dnf install -y nfs-utils
[root@harbor ~]# vim /etc/exports
/var/webroot 192.168.0.0/16(rw,no_root_squash)
[root@harbor ~]# systemctl enable --now nfs-server.service
#----------------------------------------------------------#
# 所有 node 节点都要安装 nfs 软件包
[root@node ~]# dnf install -y nfs-utils
[root@master day05]# vim web1.yaml
---
kind: Pod
apiVersion: v1
metadata:
name: test
spec:
volumes:
- name: logdata
hostPath:
path: /var/weblog
type: DirectoryOrCreate
- name: website
nfs:
server: 192.168.88.240 # nfs服务器的地址
path: /var/webroot # nfs共享的目录
containers:
- name: web
image: myos:nginx
volumeMounts:
- name: logdata
mountPath: /usr/local/nginx/logs
- name: website
mountPath: /usr/local/nginx/html # 映射到容器中的路径
[root@master ~]# kubectl apply -f web1.yaml
pod/web1 created
[root@master day05]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 9s 10.244.147.29 node-0002 <none> <none>
[root@master day05]# curl 10.244.147.29
nfs server
# 在nfs服务器上添加数据
[root@harbor ~]# echo wo shi k8s >> /var/webroot/index.html
[root@master day05]# curl 10.244.147.29
nfs server
wo shi k8s
使用流程
使用K8s Volume的一般流程如下:
- 创建存储卷:根据需求选择合适的Volume类型,并创建相应的存储卷资源。
- 挂载存储卷:在Pod的配置文件中指定要挂载的存储卷,并将其挂载到Pod中的容器上。
- 访问存储卷中的数据:在Pod的容器中,通过挂载路径访问存储卷中的数据。
注意事项
- Volume的生命周期与Pod相关,但与容器的生命周期不相关。当Pod被删除时,与其关联的Volume(除非设置为持久化存储)也会被删除。
- 在使用网络存储或持久化存储时,需要确保存储系统的稳定性和可靠性,以避免数据丢失或损坏。
- 对于敏感数据的存储,建议使用Secret或ConfigMap等机制来保护数据安全。
总之,K8s Volume是K8s中非常重要的一个概念,它为Pod中的容器提供了持久化存储和数据共享的能力。通过合理使用不同类型的Volume和正确的配置方法,可以确保应用程序的稳定性和可靠性。
PV/PVC
在Kubernetes(K8s)中,PV(Persistent Volume)和PVC(Persistent Volume Claim)是两个重要的概念,用于管理集群中的持久化存储资源。以下是对PV和PVC的详细解析:
PV(Persistent Volume)
定义与功能:
- PV是Kubernetes中用于表示持久化存储资源的API对象。它是一块网络存储,独立于Pod存在,可以是云提供商的存储、NFS、iSCSI、本地存储等多种类型。
- 管理员负责创建PV,并配置其细节,如容量、访问模式(ReadWriteOnce、ReadOnlyMany、ReadWriteMany)、存储类别等。
- PV有自己的生命周期,包括可用(Available)、绑定(Bound)、释放(Released)、回收(Retained)等状态。
访问模式:
- ReadWriteOnce(RWO):单个节点读写模式,即卷可以被一个节点以读写方式挂载。
- ReadOnlyMany(ROX):多个节点只读模式,即卷可以被多个节点以只读方式挂载。
- ReadWriteMany(RWX):多个节点读写模式,即卷可以被多个节点以读写方式挂载。
- ReadWriteOncePod(RWOP):卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
PVC(Persistent Volume Claim)
定义与功能:
- PVC是用户对PV的存储请求。用户在PVC中定义存储的大小、访问模式等需求,而不需要指定具体的PV。
- 当PVC被创建时,Kubernetes会尝试将其与满足其要求的PV进行绑定。如果没有合适的PV可以绑定,PVC将处于Pending状态,直到有合适的PV可用或动态创建一个新的PV为止。
- PVC的存在使得Pod与具体的存储实现解耦,提高了可移植性。
工作流程:
- 用户根据需求创建PVC,声明所需的存储资源规格。
- Kubernetes根据PVC中的需求寻找合适的PV进行绑定。
- 如果环境支持动态存储配额,当没有合适的PV可用时,可以根据PVC请求动态创建一个新的PV。
- Pod在定义中引用PVC,当Pod被调度到节点上时,PV会被挂载到Pod指定的路径上,供Pod使用。
PV与PVC的关系
- PV和PVC之间的关系是一种动态的匹配和绑定关系。PVC声明了对存储资源的需求,而PV则是提供这些资源的实际载体。
- 当PVC被创建时,Kubernetes会尝试将其与满足其要求的PV进行绑定。匹配的过程是根据PVC的标签选择器和PV的标签进行匹配,只有匹配成功的PV才能被绑定到PVC。
- 一旦绑定成功,Pod可以通过PVC访问PV提供的存储资源。如果没有合适的PV可以绑定,PVC将处于Pending状态,直到有合适的PV可用为止。
PV与PVC的关联条件
- 存储类一致:如果 PV 指定了存储类,PVC 必须请求相同的存储类,除非 PVC 不指定存储类。
- 访问模式兼容:PVC 请求的访问模式必须与 PV 支持的访问模式兼容。
- 容量足够:PVC 请求的存储容量不能超过 PV 的容量。
- 选择器匹配:如果 PV 定义了选择器(标签),PVC 必须匹配这些选择器才能绑定。
- 绑定策略:PV 可以指定绑定策略(动态分配或静态绑定),PVC 必须满足这些策略要求。
- 状态要求:PVC 必须处于待处理状态(Pending),PV 必须处于可用状态(Available),才能成功绑定。
简而言之,PVC 必须符合 PV 的要求,才能成功绑定并使用 PV 提供的持久化存储。
回收策略
Retain(保留):手动回收
Recycle(回收):基本擦除( 相当于执行了 rm -rf /thevolume/* )
Delete(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除
当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略
状态
卷可以处于以下的某种状态:
Available(可用):一块空闲资源还没有被任何声明绑定
Bound(已绑定):卷已经被声明绑定
Released(已释放):声明被删除,但是资源还未被集群重新声明
Failed(失败):该卷的自动回收失败
命令行会显示绑定到 PV 的 PVC 的名称。
示例:
在master部署nfs服务端,node01和node02为客户端
yum install -y nfs-common nfs-utils rpcbind
# 使用master上面的主机作nfs磁盘分享
mkdir /nfs
chmod 666 /nfs
chown nfsnobody /nfsdata # 没有nfsnobody 使用nobody
[root@k8s-master ~]# vim mkdirnfs.sh
#!/bin/bash
for i in {0..9}
do
mkdir /nfs/$i
echo "$i" > /nfs/$i/index.html
echo "/nfs/$i *(rw,no_root_squash,no_all_squash,sync)" >> /etc/exports
done
# 执行上面的脚本,查看/nfs
[root@k8s-master ~]# tree /nfs
/nfs
├── 0
│ └── index.html
├── 1
│ └── index.html
├── 2
│ └── index.html
├── 3
│ └── index.html
├── 4
│ └── index.html
├── 5
│ └── index.html
├── 6
│ └── index.html
├── 7
│ └── index.html
├── 8
│ └── index.html
└── 9
└── index.html
10 directories, 10 files
root@k8s-master ~]# cat /etc/exports
/nfs/0 *(rw,no_root_squash,no_all_squash,sync)
/nfs/1 *(rw,no_root_squash,no_all_squash,sync)
/nfs/2 *(rw,no_root_squash,no_all_squash,sync)
/nfs/3 *(rw,no_root_squash,no_all_squash,sync)
/nfs/4 *(rw,no_root_squash,no_all_squash,sync)
/nfs/5 *(rw,no_root_squash,no_all_squash,sync)
/nfs/6 *(rw,no_root_squash,no_all_squash,sync)
/nfs/7 *(rw,no_root_squash,no_all_squash,sync)
/nfs/8 *(rw,no_root_squash,no_all_squash,sync)
/nfs/9 *(rw,no_root_squash,no_all_squash,sync)
[root@k8s-master ~]# showmount -e localhost
Export list for localhost:
/nfs/9 *
/nfs/8 *
/nfs/7 *
/nfs/6 *
/nfs/5 *
/nfs/4 *
/nfs/3 *
/nfs/2 *
/nfs/1 *
/nfs/0 *
[root@k8s-master ~]# cat /nfs/9/index.html
9
# 验证nfs共享
[root@k8s-node01 ~]# mkdir /testnfs
[root@k8s-node01 ~]# mount k8s-master:/nfs/9 /testnfs
[root@k8s-node01 ~]# tree /testnfs/
/testnfs/
└── index.html
0 directories, 1 file
[root@k8s-node01 ~]# cat /testnfs/index.html
9
[root@k8s-node01 ~]# umount /testnfs
[root@k8s-node01 ~]# tree /testnfs/
/testnfs/
0 directories, 0 files
部署pv示例:
[root@k8s-master test02]# vim pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv0 # pv 名字
spec:
capacity: #容量
storage: 0.5Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/0 # nfs共享路径
server: 192.168.88.120 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteMany #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/1 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv2 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs1 # 存储类的名字
nfs:
path: /nfs/2 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv3 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Retain #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/3 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv4 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/4 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv5 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/5 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv6 # pv 名字
spec:
capacity: #容量
storage: 1.5Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/6 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv7 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/7 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv8 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/8 # nfs共享路径
server: 192.168.88.120
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv9 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfs/9 # nfs共享路径
server: 192.168.88.120
[root@k8s-master test02]# kubectl apply -f pv.yaml
persistentvolume/nfspv0 created
persistentvolume/nfspv1 created
persistentvolume/nfspv2 created
persistentvolume/nfspv3 created
persistentvolume/nfspv4 created
persistentvolume/nfspv5 created
persistentvolume/nfspv6 created
persistentvolume/nfspv7 created
persistentvolume/nfspv8 created
persistentvolume/nfspv9 created
[root@k8s-master test02]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 13s
nfspv1 1Gi RWX Recycle Available nfs <unset> 13s
nfspv2 1Gi RWO Recycle Available nfs1 <unset> 13s
nfspv3 1Gi RWO Retain Available nfs <unset> 13s
nfspv4 1Gi RWO Recycle Available nfs <unset> 13s
nfspv5 1Gi RWO Recycle Available nfs <unset> 13s
nfspv6 1536Mi RWO Recycle Available nfs <unset> 13s
nfspv7 1Gi RWO Recycle Available nfs <unset> 13s
nfspv8 1Gi RWO Recycle Available nfs <unset> 13s
nfspv9 1Gi RWO Recycle Available nfs <unset> 13s
创建服务与pvc示例:
本案例是基于StatefuSet控制器的方式创建的pvc:
[root@k8s-master test02]# vim pvc.yaml
---
apiVersion: v1
kind: Service #service类
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
# clusterIP模式没有vip IPVS工作方式 没有负载均衡
# 无头服务专门给StatefulSet使用
clusterIP: None #没有vip
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet #有状态服务 数据可能发生改变的服务 如mysql
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx" # 匹配无头服务service nginx
replicas: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: myos:nginx
ports: # 定义端口
- containerPort: 80 #端口80
name: web #端口名称web
volumeMounts: # 卷绑定
- name: www #卷名
mountPath: /usr/local/nginx/html # 卷挂载路径
volumeClaimTemplates: # pvc模版
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ] # 单节点读取
storageClassName: "nfs" #存储类
resources:
requests:
storage: 1Gi # 存储期望
[root@k8s-master test02]# kubectl apply -f pvc.yaml
service/nginx unchanged
statefulset.apps/web configured
# 查看 pvc,每个Pod有一个pvc,是一个一个创建的
[root@k8s-master test02]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-web-0 Bound nfspv5 1Gi RWO nfs <unset> 8m20s
www-web-1 Bound nfspv7 1Gi RWO nfs <unset> 6m24s
www-web-2 Bound nfspv8 1Gi RWO nfs <unset> 6m20s
www-web-3 Bound nfspv4 1Gi RWO nfs <unset> 6m16s
www-web-4 Bound nfspv3 1Gi RWO nfs <unset> 6m10s
# 查看pv
[root@k8s-master test02]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 53m
nfspv1 1Gi RWX Recycle Available nfs <unset> 53m
nfspv2 1Gi RWO Recycle Available nfs1 <unset> 53m
nfspv3 1Gi RWO Retain Bound default/www-web-4 nfs <unset> 53m
nfspv4 1Gi RWO Recycle Bound default/www-web-3 nfs <unset> 53m
nfspv5 1Gi RWO Recycle Bound default/www-web-0 nfs <unset> 53m
nfspv6 1536Mi RWO Recycle Available nfs <unset> 53m
nfspv7 1Gi RWO Recycle Bound default/www-web-1 nfs <unset> 53m
nfspv8 1Gi RWO Recycle Bound default/www-web-2 nfs <unset> 53m
nfspv9 1Gi RWO Recycle Available nfs <unset> 53m
# 获取podIP
[root@k8s-master test02]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 10m 10.244.58.193 k8s-node02 <none> <none>
web-1 1/1 Running 0 8m15s 10.244.85.193 k8s-node01 <none> <none>
web-2 1/1 Running 0 8m11s 10.244.85.194 k8s-node01 <none> <none>
web-3 1/1 Running 0 8m7s 10.244.58.195 k8s-node02 <none> <none>
web-4 1/1 Running 0 8m1s 10.244.85.195 k8s-node01 <none> <none>
[root@k8s-master test02]# curl 10.244.58.193
5
# 在 NFS 服务器的 /nfs/5 目录中添加数据,然后通过 nginx 来访问
[root@k8s-master test02]# echo hehe >> /nfs/5/index.html
[root@k8s-master test02]# curl 10.244.58.193
5
hehe
注意:StatefulSet是有序部署的,当有Pod的PVC没有绑定到一个PV,就会处于Pending状态,后序的Pod也没法创建了
尝试删除 Pod,pv 中的数据不会丢失
[root@k8s-master ~]# kubectl delete pods web-0
pod "web-0" deleted
# 删除pod后,重新创建的pod读取nfs数据,更改后的数据依然存在 pod级别数据持久化
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 15s 10.244.58.196 k8s-node02 <none> <none>
web-1 1/1 Running 0 5h24m 10.244.85.193 k8s-node01 <none> <none>
web-2 1/1 Running 0 5h23m 10.244.85.194 k8s-node01 <none> <none>
web-3 1/1 Running 0 5h23m 10.244.58.195 k8s-node02 <none> <none>
web-4 1/1 Running 0 5h23m 10.244.85.195 k8s-node01 <none> <none>
[root@k8s-master ~]# curl 10.244.58.196
5
hehe
# pod内部互相访问使用域名
# 域名格式
#podName.headlessSvcName.nsName.svc.cluster.local.
#statefulSetName-num.headlessSvcName.nsName.svc.cluster.local.
[root@k8s-master test]# kubectl exec -it web-0 -- bash
[root@web-0 html]# curl http://web-1.nginx.default.svc.cluster.local.
7
[root@web-0 html]# curl http://web-2.nginx.default.svc.cluster.local.
8
删除 StatefulSet 后,pvc 不会自动删除,pv也不会自动释放,需要手动删除
# 删除 StatefulSet 后,pvc 仍然存在
[root@k8s-master test]# kubectl delete statefulset web
statefulset.apps "web" deleted
[root@k8s-master test]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-web-0 Bound nfspv5 1Gi RWO nfs <unset> 5h53m
www-web-1 Bound nfspv7 1Gi RWO nfs <unset> 5h51m
www-web-2 Bound nfspv8 1Gi RWO nfs <unset> 5h51m
www-web-3 Bound nfspv4 1Gi RWO nfs <unset> 5h51m
www-web-4 Bound nfspv3 1Gi RWO nfs <unset> 5h50m
# 删除 pvc 后,pv 没有自动释放
[root@k8s-master test]# kubectl delete pvc --all
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
[root@k8s-master test]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 6h38m
nfspv1 1Gi RWX Recycle Available nfs <unset> 6h38m
nfspv2 1Gi RWO Recycle Available nfs1 <unset> 6h38m
nfspv3 1Gi RWO Retain Released default/www-web-4 nfs <unset> 6h38m
nfspv4 1Gi RWO Recycle Released default/www-web-3 nfs <unset> 6h38m
nfspv5 1Gi RWO Recycle Released default/www-web-0 nfs <unset> 6h38m
nfspv6 1536Mi RWO Recycle Available nfs <unset> 6h38m
nfspv7 1Gi RWO Recycle Failed default/www-web-1 nfs <unset> 6h38m
nfspv8 1Gi RWO Recycle Released default/www-web-2 nfs <unset> 6h38m
nfspv9 1Gi RWO Recycle Available nfs <unset>
# 手动释放pv
[root@k8s-master test]# kubectl edit pv nfspv3
# 将下面的 spec.claimRef 删除
spec:
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: www-web-0
namespace: default
resourceVersion: "619064"
uid: 99cea07e-339e-431c-bcb6-c398c884b29c
# 再次查看 pv nfspv3已经得到释放
[root@k8s-master test]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 6h41m
nfspv1 1Gi RWX Recycle Available nfs <unset> 6h41m
nfspv2 1Gi RWO Recycle Available nfs1 <unset> 6h41m
nfspv3 1Gi RWO Retain Available nfs <unset> 6h41m
nfspv4 1Gi RWO Recycle Released default/www-web-3 nfs <unset> 6h41m
nfspv5 1Gi RWO Recycle Released default/www-web-0 nfs <unset> 6h41m
nfspv6 1536Mi RWO Recycle Available nfs <unset> 6h41m
nfspv7 1Gi RWO Recycle Failed default/www-web-1 nfs <unset> 6h41m
nfspv8 1Gi RWO Recycle Released default/www-web-2 nfs <unset> 6h41m
nfspv9 1Gi RWO Recycle Available nfs <unset>