k8s 存储
k8s存储
01.存储分类
在Kubernetes(K8s)中,存储系统是一个关键的组成部分,用于管理容器化应用的数据持久性和共享性。K8s的存储分类可以从多个维度进行理解,但主要分为两大类:临时存储和持久存储。关于“元数据”和“真实数据”的分类,虽然这两个概念在存储系统中普遍存在,但在K8s的存储分类中,它们并不是直接用于分类存储类型的标准。不过,可以从K8s存储类型如何管理和使用这些数据的角度来探讨。
- 临时存储
- 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数据库中,而真实数据则存储在分配的持久卷上。
- PersistentVolume (PV) 和 PersistentVolumeClaim (PVC):
总结
在K8s中,“元数据”和“真实数据”的存储和管理是通过不同的机制实现的。元数据通常存储在K8s的etcd数据库中,用于管理集群的状态和配置。而真实数据则根据所选的存储类型(如PV、PVC、NFS等)存储在相应的存储后端上。通过合理配置和使用这些存储类型,K8s能够提供灵活、可靠的数据存储解决方案,满足各种应用场景的需求。
02.configmap(存储资源分类,存储配置文件)
K8s(Kubernetes)中的ConfigMap是一种用于存储配置数据的API对象,它属于Kubernetes中的核心对象。ConfigMap的主要作用是将应用程序的配置信息与容器镜像分离,以便在不重新构建镜像的情况下进行配置的修改和更新。以下是关于K8s ConfigMap的详细解析:
一、ConfigMap的基本概念
- 用途:
- ConfigMap用于存储键值对形式的配置数据,这些数据可以包括环境变量、命令行参数、配置文件等。它提供了一种集中管理和传递配置信息的机制,使得应用程序能够从ConfigMap中获取配置数据,从而在不修改容器镜像的前提下,动态地修改应用程序的配置参数。
- 与Secret的区别:
- ConfigMap主要用于存储非敏感的配置数据,如应用程序的配置文件、环境变量等,而Secret则用于存储敏感的数据,如密码、密钥等。Secret提供了更高的安全性和访问控制机制。
二、ConfigMap的创建与管理
- 创建ConfigMap
- 通过命令行创建:可以使用kubectl create configmap命令从文件、目录或者key-value字符串创建ConfigMap。
[root@master configmap]# kubectl create cm literal-config --from-literal=name=noz --from-literal=pass=123 configmap/literal-config created [root@master configmap]# kubectl get cm literal-config -o yaml apiVersion: v1 data: name: noz pass: "123" kind: ConfigMap metadata: creationTimestamp: "2024-08-25T11:14:23Z" name: literal-config namespace: default resourceVersion: "173941" uid: 5ca9e6d2-bd85-4a4b-afe8-b82062f8c137
- 通过YAML文件创建:可以编写一个YAML文件,定义ConfigMap的元数据和数据内容,然后使用kubectl apply -f .yaml命令创建ConfigMap。
vim test_configmap.file name=noziroh passwd=root [root@master configmap]# kubectl create cm test-config --from-file=test_configmap.file configmap/test-config created
- 查看ConfigMap
- 使用kubectl get configmaps命令查看集群中所有的ConfigMap。
[root@master configmap]# kubectl get cm NAME DATA AGE kube-root-ca.crt 1 4d21h test-config 1 89s
- 使用kubectl describe configmap 命令查看特定ConfigMap的详细信息。
[root@master configmap]# kubectl describe cm test-config Name: test-config Namespace: default Labels: <none> Annotations: <none> Data ==== test_configmap.file: ---- name=noziroh passwd=root BinaryData ==== Events: <none>
- 使用kubectl get configmap -o yaml命令以YAML格式输出特定ConfigMap的详细信息。
[root@master configmap]# kubectl get cm test-config -o yaml apiVersion: v1 data: test_configmap.file: | name=noziroh passwd=root kind: ConfigMap metadata: creationTimestamp: "2024-08-25T11:09:41Z" name: test-config namespace: default resourceVersion: "173518" uid: 85037d36-4313-4cf7-a222-2c2c50c541d9
- 更新与删除ConfigMap
- 更新ConfigMap通常需要重新创建它,因为ConfigMap是不可变的。可以使用相同的YAML文件(但包含更新后的数据)重新应用来更新ConfigMap。
- 使用kubectl delete configmap 命令删除特定的ConfigMap。
三、ConfigMap的使用
-
ConfigMap中的数据可以通过以下方式在Pod中使用:
- 环境变量:可以将ConfigMap中的数据设置为Pod中容器的环境变量。这样,容器在启动时就可以从环境变量中获取配置信息。
vim config-env.yaml apiVersion: v1 kind: ConfigMap #configmap类别 metadata: name: literal-config # configmap名字 namespace: default # 命名空间名称 data: name: dave #定义数据name:dave password: pass # 定义数据password:pass --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO --- apiVersion: v1 kind: Pod # pod类型 metadata: name: cm-env-pod # pod名字 spec: # 期望 containers: - name: myapp-container image: wangyanglinux/myapp:v1.0 command: [ "/bin/sh", "-c", "env" ] # 启动命令 打印容器内部env环境变量 env: #定义env 为容器内部添加环境变量 - name: USERNAME # 环境变量名字 valueFrom: #值来源 configMapKeyRef: name: literal-config #值来源的configmap类别的名字 key: name # key的名字/字段 - name: PASSWORD valueFrom: configMapKeyRef: name: literal-config key: password envFrom: #直接引入名为env-config的configmap - configMapRef: name: env-config restartPolicy: Never #重启策略 永不 [root@master configmap]# kubectl create -f config-env.yaml configmap/literal-config created configmap/env-config created pod/cm-env-pod created [root@master calico]# kubectl get pods NAME READY STATUS RESTARTS AGE cm-env-pod 0/1 Completed 0 5m8s [root@master calico]# kubectl logs cm-env-pod KUBERNETES_SERVICE_PORT=443 KUBERNETES_PORT=tcp://10.0.0.1:443 HOSTNAME=cm-env-pod SHLVL=1 HOME=/root USERNAME=dave # literal-config添加的环境变量 KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_PROTO=tcp log_level=INFO # env-config添加的环境变量 KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443 KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_SERVICE_HOST=10.0.0.1 PWD=/ PASSWORD=pass # literal-config添加的环境变量
- 命令行参数:将ConfigMap中的数据作为命令行参数传递给容器中的应用程序。这通常需要先将ConfigMap的数据保存在环境变量中,然后通过环境变量的方式引用。
vim configmap-command.yaml apiVersion: v1 kind: Pod metadata: name: cm-command-pod spec: containers: - name: myapp-container image: wangyanglinux/myapp:v1.0 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 [root@master configmap]# kubectl create -f configmap-command.yaml pod/cm-command-pod created [root@master configmap]# kubectl logs cm-command-pod dave pass
- 卷挂载:ConfigMap可以作为卷挂载到Pod中,使得容器可以直接读取ConfigMap中的配置文件。每个键值对都会生成一个文件,其中键为文件名,值为文件内容。这样,应用程序就可以根据需要读取配置文件中的配置信息。
vim configmap-volume.yaml apiVersion: v1 kind: ConfigMap #configmap类别 metadata: name: literal-config # configmap名字 namespace: default # 命名空间名称 data: name: dave #定义数据name:dave password: pass # 定义数据password:pass --- apiVersion: v1 kind: Pod metadata: name: cm-volume-pod spec: containers: - name: myapp-container image: wangyanglinux/myapp:v1.0 volumeMounts: # 卷绑定 - name: config-volume # 卷名 mountPath: /etc/config #卷路径 volumes: - name: config-volume configMap: name: literal-config restartPolicy: Never [root@master configmap]# kubectl create -f configmap-volume.yaml pod/cm-volume-pod created configmap/literal-config created [root@master configmap]# kubectl exec -it cm-volume-pod -- bash cm-volume-pod:/# cat /etc/config/name dave cm-volume-pod:/# cat /etc/config/password pass # 这种方式创建的是连接文件 热更新 cm-volume-pod:/etc/config# ls -l total 0 lrwxrwxrwx 1 root root 11 Aug 25 20:11 name -> ..data/name lrwxrwxrwx 1 root root 15 Aug 25 20:11 password -> ..data/password
演示热更新
# 创建nginx默认配置文件 将nginx配置文件保存成configmap
[root@master configmap]# cat > default.conf << EOF
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
[root@master configmap]# kubectl create cm default-nginx --from-file=default.conf
configmap/default-nginx created
[root@master configmap]# kubectl get cm default-nginx -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: "2024-08-25T14:15:24Z"
name: default-nginx
namespace: default
resourceVersion: "189187"
uid: cc45e694-4ece-4fd2-959b-b1f6fd94ddc0
# deployment 控制器 会挂在configmap 变成配置文件
[root@master configmap]# vim deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hotupdate-deploy
name: hotupdate-deploy
spec:
replicas: 5
selector:
matchLabels:
app: hotupdate-deploy
template:
metadata:
labels:
app: hotupdate-deploy
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d/
volumes:
- name: config-volume
configMap:
name: default-nginx
[root@master configmap]# kubectl create -f deployment.yaml
deployment.apps/hotupdate-deploy created
[root@master configmap]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hotupdate-deploy-5565cdb98f-22nqd 1/1 Running 0 70s 10.244.166.145 node1 <none> <none>
hotupdate-deploy-5565cdb98f-728kv 1/1 Running 0 70s 10.244.166.144 node1 <none> <none>
hotupdate-deploy-5565cdb98f-b4stt 1/1 Running 0 70s 10.244.104.14 node2 <none> <none>
hotupdate-deploy-5565cdb98f-wkgjb 1/1 Running 0 70s 10.244.104.15 node2 <none> <none>
hotupdate-deploy-5565cdb98f-wpsgk 1/1 Running 0 70s 10.244.166.146 node1 <none> <none>
[root@master configmap]# curl 10.244.166.145
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@master configmap]# kubectl edit cm default-nginx
configmap/default-nginx edited
# 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:
default.conf: |
server {
listen 8080 default_server;
server_name example.com www.example.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2024-08-25T14:22:12Z"
name: default-nginx
namespace: default
resourceVersion: "189988"
uid: e9f98e38-7b49-4b34-97e3-c98f46f4575f
[root@master configmap]# kubectl exec -it hotupdate-deploy-5565cdb98f-22nqd -- bash
root@hotupdate-deploy-5565cdb98f-22nqd:/# cat /etc/nginx/conf.d/default.conf
server {
listen 8080 default_server;
server_name example.com www.example.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
[root@master configmap]# curl 10.244.104.15:8080
curl: (7) Failed to connect to 10.244.104.15 port 8080: Connection refused
# 文件已经修改了但是nginx服务没重启
kubectl patch deployment hotupdate-deploy --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "new version"}}}}}'
deployment.apps/hotupdate-deploy patched
[root@master configmap]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hotupdate-deploy-5dc5768545-4m26s 1/1 Running 0 50s 10.244.166.148 node1 <none> <none>
hotupdate-deploy-5dc5768545-5djgf 1/1 Running 0 58s 10.244.104.17 node2 <none> <none>
hotupdate-deploy-5dc5768545-5gkm2 1/1 Running 0 46s 10.244.104.18 node2 <none> <none>
hotupdate-deploy-5dc5768545-6btp8 1/1 Running 0 58s 10.244.104.16 node2 <none> <none>
hotupdate-deploy-5dc5768545-qs8bm 1/1 Running 0 58s 10.244.166.147 node1 <none> <none>
[root@master configmap]# curl 10.244.166.148:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 添加不可改变选项 immutable: true
[root@master ~]# kubectl edit cm default-nginx
apiVersion: v1
data:
default.conf: |
server {
listen 8080 default_server;
server_name example.com www.example.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
immutable: true #添加不可改变选项
kind: ConfigMap
metadata:
creationTimestamp: "2024-08-25T14:22:12Z"
name: default-nginx
namespace: default
resourceVersion: "190367"
uid: e9f98e38-7b49-4b34-97e3-c98f46f4575f
# 添加之后就无法进行热更新了
# 添加之后是不可逆的 需要重新创建一个cm
四、ConfigMap的优势
- 配置解耦:将配置信息与容器镜像解耦,使得配置可以在不重新构建镜像的情况下进行修改和管理。
- 动态更新:ConfigMap中的配置可以在运行时动态更新,而不需要重新启动应用程序。
- 版本控制:ConfigMap中的配置可以使用版本控制系统进行管理,随时回滚到之前的版本。
- 共享和复用:ConfigMap可以被多个应用程序共享和复用,提高了配置的一致性和可维护性。
综上所述,K8s ConfigMap是Kubernetes中用于存储和管理配置数据的重要组件,它提供了灵活的配置管理方式,使得应用程序的配置更加清晰、易于管理和更新。
03.Secret(加密方式数据保存-编码)
K8s(Kubernetes)中的Secret是一种用于保存敏感信息的资源对象,如密码、OAuth令牌、ssh密钥等。这些信息如果直接放在Pod的定义中或镜像中,可能会带来安全风险,因为Pod的定义和镜像都可能被存储在版本控制系统中,或者被不同的用户访问。通过使用Secret,可以更安全地管理这些敏感信息。
Secret的特性
- 安全性:Secret中的信息被加密存储(实际上是Base64编码,但Kubernetes社区通常称之为加密),以减少敏感信息泄露的风险。
- 灵活性:Secret可以以多种方式被Pod使用,包括作为环境变量、挂载到Pod中的卷中的文件,或者在kubelet为Pod拉取镜像时使用。
- 可重用性:多个Pod可以引用同一个Secret,从而避免在多个地方重复存储相同的敏感信息。
Secret的类型
Kubernetes支持多种类型的Secret,以满足不同的使用场景:
- Opaque:这是默认的Secret类型,用于存储任意格式的敏感信息。数据以Base64编码的形式存储在Secret中。
[root@master script]# echo -n "admin" | base64
YWRtaW4=
[root@master script]# echo -n "root" | base64
cm9vdA==
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: cm9vdA==
username: YWRtaW4=
[root@master 6]# kubectl create -f secret_1.yaml
secret/mysecret created
[root@master 6]# kubectl get secret
NAME TYPE DATA AGE
mysecret Opaque 2 23s
[root@master 6]# kubectl describe secret
Name: mysecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 4 bytes
username: 5 bytes
[root@master 6]# kubectl describe secret
Name: mysecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 4 bytes
username: 5 bytes
[root@master 6]# kubectl get secret -o yaml
apiVersion: v1
items:
- apiVersion: v1
data:
password: cm9vdA==
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: "2024-08-26T06:38:30Z"
name: mysecret
namespace: default
resourceVersion: "217900"
uid: 90820edc-8182-48d6-a737-2ff024a885ec
type: Opaque
kind: List
metadata:
resourceVersion: ""
[root@master 6]# echo -n "cm9vdA==" |base64 -d
root
- 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:用于存储基本认证信息,如用户名和密码。
Secret的创建和使用
Secret可以通过命令行工具(如kubectl)或YAML文件来创建。创建Secret时,可以指定其类型并添加敏感信息。然后,Pod可以通过在其定义中引用Secret来使用这些信息。
创建Secret的示例(使用kubectl)
kubectl create secret generic mysecret --from-literal=username=admin --from-literal=password=root
这个命令会创建一个名为mysecret的Opaque类型Secret,其中包含两个键值对:username和password。
在Pod中使用Secret的示例
作为环境变量(不可热更新)
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: nginx
env:
- name: MY_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: MY_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
作为卷挂载(不使用子目录创建可以热更新)
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: nginx
volumeMounts:
- name: mysecret-volume
mountPath: "/etc/secret-volume"
readOnly: true
volumes:
- name: mysecret-volume
secret:
secretName: mysecret
[root@master 6]# echo "Noziroh" |base64
Tm96aXJvaAo=
[root@master 6]# kubectl edit secret
secret/mysecret edited
root@mypod:/# cat etc/secret-volume/username
Noziroh
# 也可使用immutable: true 标记不可修改
在这个例子中,mysecret Secret被挂载到Pod中的/etc/secret-volume目录下,Pod中的容器可以通过访问这个目录来获取Secret中的信息。
注意事项
- 当使用Secret时,应确保Pod有足够的权限来访问这些Secret。
- Secret中的信息虽然被加密(实际上是Base64编码),但应尽量避免将过于敏感的信息存储在Kubernetes集群中,以防止潜在的泄露风险。
- 定期检查并更新Secret中的敏感信息,以确保系统的安全性。
04.Downward API(将pod元数据反馈到容器内部)
在Kubernetes(k8s)中,Downward API 是一种特殊类型的 API,它允许 Pod 中的容器获取关于 Pod 本身及其所在环境的元数据信息。这些信息可以通过两种方式注入到容器内部:环境变量和卷挂载(Volume Mounts)。
- 提供容器元数据
- 动态配置
- 与kubernetes环境集成
一、Downward API 的两种注入方式
- 环境变量:
- 环境变量是 Downward API 注入信息到容器的常用方式,适用于单个变量的情况。通过 Downward API,可以将 Pod 的 IP 地址、名称、命名空间等基本信息以环境变量的形式注入到容器内部。这样,容器内的应用程序就可以通过读取这些环境变量来获取 Pod 的相关信息。
示例:
apiVersion: v1
kind: Pod
metadata:
name: downward-api-pod
spec:
containers:
- name: downward-api-container
image: nginx:latest
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.cpu
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.cpu
- name: MEMORY_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.memory
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
restartPolicy: Never
[root@master downwardAPI]# kubectl create -f downwardAPI_1.yaml
pod/downward-api-pod created
[root@master downwardAPI]# kubectl get pod
NAME READY STATUS RESTARTS AGE
downward-api-pod 1/1 Running 0 52s
mypod 1/1 Running 0 77m
[root@master downwardAPI]# kubectl exec -it downward-api-pod -- bash
root@downward-api-pod:/# env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=downward-api-pod
CPU_REQUEST=0
POD_NAME=downward-api-pod
PWD=/
NAMESPACE=default
PKG_RELEASE=1~bookworm
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443
DYNPKG_RELEASE=2~bookworm
NJS_VERSION=0.8.5
TERM=xterm
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1
POD_IP=10.244.104.21
CPU_LIMIT=2
MEMORY_LIMIT=1722679296
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_PORT=tcp://10.0.0.1:443
MEMORY_REQUEST=0
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_VERSION=1.27.1
NJS_RELEASE=1~bookworm
_=/usr/bin/env
在这个例子中,POD_NAME 和 NAMESPACE 这两个环境变量分别被设置为 Pod 的名称和命名空间。
- 卷挂载:
- 另一种方式是将 Pod 的信息生成为文件,并通过卷挂载的方式将这些文件注入到容器内部。这种方式适用于需要批量处理或复杂查询 Pod 信息的情况。
示例:
apiVersion: v1
kind: Pod
metadata:
name: test-volume-pod
labels:
k8s-app: test-volume
node-env: test
spec:
containers:
- name: test-volume-pod-container
image: nginx:latest
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
在这个例子中,Pod 的所有 Label 被生成为文件 /etc/podinfo/labels,并挂载到容器的 /etc/podinfo 目录下。
apiVersion: v1
kind: Pod
metadata:
name: downward-api-volume-example
spec:
containers:
- name: my-container
image: nginx:lastest
resources: # 对容器做资源限制
limits: # 最大值
cpu: "1"
memory: "512Mi"
requests: # 初始值
cpu: "0.5"
memory: "256Mi"
volumeMounts: # 卷绑定
- name: downward-api-volume # 卷名
mountPath: /etc/podinfo # 绑定目录
volumes: # 声明卷
- name: downward-api-volume # 卷名
downwardAPI:
items:
- path: "annotations" # 挂在到此目录下
fieldRef:
fieldPath: metadata.annotations
- 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: my-container
resource: requests.cpu
- path: "memoryRequest"
resourceFieldRef:
containerName: my-container
resource: requests.memory
- path: "cpuLimit"
resourceFieldRef:
containerName: my-container
resource: limits.cpu
- path: "memoryLimit"
resourceFieldRef:
containerName: my-container
resource: limits.memory
restartPolicy: Never
[root@master downwardAPI]# kubectl create -f downwardAPI_2.yaml
pod/downward-api-volume-example created
基于阿皮Server访问集群
cat RBAC.yaml
# 创建RBAC的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: test-api-cluster-admin-binding
subjects:
- kind: ServiceAccount
name: test-api
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
[root@master downwardAPI]# kubectl create -f RBAC.yaml
clusterrolebinding.rbac.authorization.k8s.io/test-api-cluster-admin-binding created
[root@master downwardAPI]# kubectl create sa test-api
serviceaccount/test-api created
cat pod.yaml
# 创建pod
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
serviceAccountName: test-api
containers:
- name: main
image: curlimages/curl
command: ["sleep", "9999"]
[root@master downwardAPI]# kubectl create -f pod.yaml
pod/curl created
# 进入pod curl
# 定义一个环境变量token 管理员持有此token
root@curl:/# TOKEN=$( cat /var/run/secrets/kubernetes.io/serviceaccount/token )
# 当前认证的ca证书 https认证
root@curl:/# CAPATH="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
# 指定当前命名空间
root@curl:/# NS=$( cat /var/run/secrets/kubernetes.io/serviceaccount/namespace )
# curl发起命令定义head 基于token认证 证书为环境变量$CAPATH 访问当前所在的命名空间的pod
root@curl:/# curl -H "Authorization: Bearer $TOKEN" --cacert $CAPATH https://kubernetes/api/v1/namespaces/$NS/pods
# https://kubernetes/api/v1/namespaces/$NS/pods 相当于 api服务器接口
[root@master downwardAPI]# kubectl exec -it curl -- sh
二、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 的步骤
-
创建包含 Downward API 信息的 Pod:
- 编写 Pod 的 YAML 配置文件,定义需要注入的环境变量或卷挂载。
-
使用 kubectl 创建 Pod:
- 使用 kubectl apply -f <pod-config-file.yaml> 命令创建 Pod。
-
在容器中读取 Downward API 注入的信息:
- 进入 Pod 的容器内部,通过环境变量或文件来读取注入的信息。
通过以上步骤,你可以在 Kubernetes 中使用 Downward API 来获取 Pod 的相关信息,并将其注入到容器内部,以满足应用程序的需求。
05.volume(真实数据存储方式,脱离容器生命周期以外的存储方式)
K8s(Kubernetes)中的Volume(存储卷)是一种用于在Pod中持久存储数据的机制,它为Pod中的容器提供了一个共享的存储空间。以下是关于K8s Volume的详细解释:
一、定义与用途
- 定义:在K8s中,Volume是一种抽象的概念,用于提供Pod中容器的持久化存储。它允许将数据存储在Pod的生命周期之外,以便在容器重启、迁移或重新调度时保留数据。
- 用途:
- 数据持久化:将数据存储在Volume中,确保容器重启后数据仍然存在。
- 数据共享:Volume可以连接到Pod中的一个或多个容器,使它们能够共享相同的数据。
- 数据备份和恢复:使用Volume来备份和还原应用程序的数据。
- 数据迁移和复制:将Volume从一个Pod迁移到另一个Pod,或将Volume复制到其他地方。
二、类型
-
K8s支持多种类型的Volume,这些类型大致可以分为以下几类:
-
本地存储:
- emptyDir:初始内容为空的本地临时目录,与Pod一起创建和删除,生命周期与Pod相同。主要用于临时数据的存储,如缓存、日志等。Pod级别,生命周期与Pod关联.
初始化空目录
vim volume-emptydir-disk-pod.yaml apiVersion: v1 kind: Pod metadata: name: volume-emptydir-disk-pod namespace: default spec: containers: - name: myapp image: wangyanglinux/myapp:v1.0 ports: - containerPort: 80 #定义端口80 volumeMounts: #卷绑定 - name: logs-volume # 卷名 mountPath: /usr/local/nginx/logs # 绑定路径 - name: busybox image: wangyanglinux/tools:busybox command: ["/bin/sh","-c","touch / logs/access.log && tail -f /logs/ access.log"] # 创建文件后监听文件 volumeMounts: - name: logs-volume mountPath: /logs volumes: #创建卷 - name: logs-volume # 卷名 emptyDir: {} # 以空的方式初始化 [root@master Volume]# kubectl create -f volume-emptydir-disk-pod.yaml pod/volume-emptydir-disk-pod created [root@master Volume]# while true; do curl 10.244.166.156/hostname.html; done [root@master ~]# kubectl exec -it volume-emptydir-disk-pod -c busybox -- sh / # tail -f /logs/access.log 10.0.17.100 - - [26/Aug/2024:20:30:41 +0800] "GET /hostname.html HTTP/1.1" 200 25 "-" "curl/7.76.1" 10.0.17.100 - - [26/Aug/2024:20:30:44 +0800] "GET /hostname.html HTTP/1.1" 200 25 "-" "curl/7.76.1" [root@master Volume]# kubectl get po volume-emptydir-disk-pod -o yaml uid: 35499012-6b95-4dbd-a157-b4d56fbf0f02 [root@node1 ~]# cd /var/lib/kubelet/ pods/ 35499012-6b95-4dbd-a157-b4d56fbf0f02/ [root@node1 35499012-6b95-4dbd-a157-b4d56fbf0f02]# tree . ├── containers │ ├── busybox │ │ └── a941dbb1 │ └── myapp │ └── 63b0aaf6 ├── etc-hosts ├── plugins │ └── kubernetes.io~empty-dir │ ├── logs-volume │ │ └── ready │ └── wrapped_kube-api-access-bxvwn │ └── ready └── volumes ├── kubernetes.io~empty-dir │ └── logs-volume │ ├── access.log │ ├── error.log │ └── nginx.pid └── kubernetes.io~projected └── kube-api-access-bxvwn ├── ca.crt -> ..data/ca.crt ├── namespace -> ..data/ namespace └── token -> ..data/token 12 directories, 11 files
共享内存
vim volume-emptydir-mem.yaml apiVersion: v1 kind: Pod metadata: name: volume-emptydir-mem namespace: default spec: containers: - name: myapp image: wangyanglinux/myapp:v1.0 ports: - containerPort: 80 resources: #资源显示 limits: # 最大资源 cpu: "1" memory: 1024Mi requests: #出事资源 cpu: "1" memory: 1024Mi volumeMounts: # 卷绑定 - name: mem-volume # 卷名 mountPath: /data #卷路径 volumes: # 卷 - name: mem-volume # 卷名 emptyDir: medium: Memory # 媒介类型 sizeLimit: 500Mi # 最大资源 [root@master Volume]# kubectl create -f volume-emptydir-mem.yaml pod/volume-emptydir-mem created
-
HostPath:将Pod挂载到宿主机上的文件或目录,主要用于访问宿主机上的文件系统,如日志文件、Docker引擎内部数据结构等。
在Kubernetes(k8s)中,HostPath是一种特殊的卷类型,它允许将节点(Node)上的文件或目录直接挂载到Pod中。这种挂载方式使得Pod能够访问宿主机上的文件系统,从而实现了数据的持久化存储,即使Pod被删除或重建,只要宿主机上的文件或目录仍然存在,数据就不会丢失。
-
HostPath的工作原理
HostPath卷通过Kubernetes的Volume抽象层将宿主机上的存储空间关联到容器,使容器可以直接访问宿主机上的文件系统。当Pod被调度到某个节点上时,它会将宿主机上指定的目录或文件挂载到容器内部,使得容器可以像访问本地文件系统一样访问这些资源。
-
HostPath的配置参数
在Kubernetes中配置HostPath卷时,通常需要指定以下参数:
- path:指定宿主机上的目录或文件路径,这是必选字段。
- type(可选):指定节点之上存储类型,包括以下几种:
- DirectoryOrCreate:如果给定的路径不存在,则创建一个空目录,权限设置为755。
- Directory:目录必须存在。
- FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限设置为644。
- File:文件必须存在。
- Socket:UNIX套接字,必须存在。
- CharDevice:字符设备,必须存在。
- BlockDevice:块设备,必须存在。
-
HostPath的使用场景
HostPath卷适用于以下场景:
- 需要Pod直接访问宿主机上的特定文件或目录,例如访问Docker内部机制或系统文件。
- 在某些特定场景下,如运行管理任务的系统级Pod资源,需要访问节点上的特定资源。
-
HostPath的优缺点
-
优点
直接访问宿主机文件系统:提供了较高的灵活性和便利性,允许Pod直接访问宿主机上的资源。
简单性:相比其他复杂的存储解决方案,HostPath的配置和使用相对简单。 -
缺点
安全风险:由于容器可以直接访问宿主机的文件系统,这可能导致潜在的安全风险。
可靠性问题:宿主机上的文件或目录可能被容器意外修改或删除,导致数据丢失或应用程序异常。
可移植性问题:使用HostPath卷会使应用程序依赖于特定主机的文件系统结构,增加了迁移和部署的复杂性。
不适合大规模部署:由于HostPath卷是工作节点本地的存储空间,它不适合用于大规模部署或需要高可用性和持久性的场景。
-
-
示例
以下是一个使用HostPath卷的Pod配置示例(YAML格式):
vim hostpath-example.yaml apiVersion: v1 kind: Pod metadata: name: hostpath-example spec: containers: - name: mycontainer image: nginx volumeMounts: - name: myvolume # 卷名 mountPath: /data # 卷路径 volumes: - name: myvolume hostPath: path: /path/on/host # 主机路径node节点的机器上 type: DirectoryOrCreate #模式 [root@master Volume]# kubectl create -f hostpath-example.yaml pod/hostpath-example created [root@master Volume]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostpath-example 0/1 ContainerCreating 0 13s <none> node2 <none> <none> [root@node2 ~]# cd /path/on/host/ [root@node2 host]# ls [root@node2 host]# touch test_hostpath [root@master Volume]# kubectl exec -it hostpath-example -- bash root@hostpath-example:/# cd /data/ root@hostpath-example:/data# ls test_hotpath
在这个示例中,我们创建了一个名为hostpath-example的Pod,它有一个名为mycontainer的容器,该容器使用了名为myvolume的卷,并将其挂载到容器的/data目录。myvolume卷的类型是HostPath,它指定了宿主机上的/path/on/host路径作为存储源,并且如果指定的路径不存在,则会自动创建一个空目录。
-
-
网络存储:
- NFS(Network File System):通过网络共享文件,使得不同机器、不同操作系统可以共享彼此的文件。
- CephFS、Cinder、AzureFile等:这些是基于不同云存储提供商或网络存储系统的Volume类型,提供了更高级的存储特性和更广泛的兼容性。
-
持久化存储:
- PersistentVolume(PV)和PersistentVolumeClaim(PVC):PV是集群级别的资源,用于表示由管理员配置的存储资源。PVC是用户申请的存储资源,它会被自动绑定到合适的PV上。这种机制允许用户动态地申请和使用存储资源。
-
特殊类型:
- Secret:用于向Pod传递敏感信息,如密码、私钥、证书等。这些信息存储在Secret对象中,并通过Volume挂载到Pod中,实现敏感数据的保护。
- ConfigMap:用于向Pod注入非敏感数据,如配置文件等。用户可以将配置文件存储在ConfigMap对象中,并在Pod中使用ConfigMap卷引用它。
-
三、使用流程
-
使用K8s Volume的一般流程如下:
- 创建存储卷:根据需求选择合适的Volume类型,并创建相应的存储卷资源。
- 挂载存储卷:在Pod的配置文件中指定要挂载的存储卷,并将其挂载到Pod中的容器上。
- 访问存储卷中的数据:在Pod的容器中,通过挂载路径访问存储卷中的数据。
四、注意事项
- Volume的生命周期与Pod相关,但与容器的生命周期不相关。当Pod被删除时,与其关联的Volume(除非设置为持久化存储)也会被删除。
- 在使用网络存储或持久化存储时,需要确保存储系统的稳定性和可靠性,以避免数据丢失或损坏。
对于敏感数据的存储,建议使用Secret或ConfigMap等机制来保护数据安全。
总之,K8s Volume是K8s中非常重要的一个概念,它为Pod中的容器提供了持久化存储和数据共享的能力。通过合理使用不同类型的Volume和正确的配置方法,可以确保应用程序的稳定性和可靠性。
06.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:单个节点读写模式,即卷可以被一个节点以读写方式挂载。
- ReadOnlyMany:多个节点只读模式,即卷可以被多个节点以只读方式挂载。
- ReadWriteMany:多个节点读写模式,即卷可以被多个节点以读写方式挂载。
-
-
二、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可用为止。
-
四、使用示例
示例1:
假设我们有一个Kubernetes集群,并希望为WordPress应用程序提供持久化存储。以下是使用PV和PVC的步骤:
创建PV:
apiVersion: v1
kind: PersistentVolume
metadata:
name: wordpress-pv
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/data
创建PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
创建WordPress Deployment并引用PVC:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:latest
ports:
- containerPort: 80
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wordpress-pvc
[root@master pv_pvc]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
wordpress-pvc Bound wordpress-pv 1Gi RWO <unset> 109s
[root@master pv_pvc]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
wordpress-pv 1Gi RWO Retain Bound default/wordpress-pvc <unset> 114s
[root@master pv_pvc]# kubectl get pod
NAME READY STATUS RESTARTS AGE
wordpress-55cbb948fd-j9x9w 1/1 Running 0 116s
[root@master ~]# ssh node2
Last login: Mon Aug 26 21:20:15 2024 from 10.0.17.100
[root@node2 ~]# cd /mnt/data/
[root@node2 data]# ls
index.php wp-blog-header.php wp-cron.php wp-mail.php
license.txt wp-comments-post.php wp-includes wp-settings.php
readme.html wp-config-docker.php wp-links-opml.php wp-signup.php
wp-activate.php wp-config-sample.php wp-load.php wp-trackback.php
wp-admin wp-content wp-login.php xmlrpc.php
# node2 /mnt/data 与 pod wordpress /var/www/html 关联
[root@node2 data]# echo "hellow world" >> test.html
[root@master pv_pvc]# curl 10.244.104.24/test.html
hellow world
# 无法删除时 pv/pvc edit finalizers:null
通过以上步骤,WordPress应用程序的数据将被存储在由PV提供的持久化存储中,即使Pod因为节点故障等原因被重新调度到其他节点,也能继续访问同一块存储区域中的数据。
示例2 使用nfs
1.在master node1 node2上部署nfs
yum install -y nfs-common nfs-utils rpcbind
# 使用master上面的主机作nfs磁盘分享
mkdir /nfsdata
chmod 666 /nfsdata
chown nfsnobody /nfsdata # 没有nfsnobody 使用nobody
cat /etc/exports
/nfsdata *(rw,no_root_squash,no_all_squash,sync)
[root@master script]# vim mkdirnfs.sh
#!/bin/bash
for i in {0..9};
do
#echo $i
echo "$i" > /nfsdata/$i/index.html
echo "/nfsdata/$i *(rw,no_root_squash,no_all_squash,sync)" >> /etc/exports
done
[root@master script]# ./mkdirnfs.sh
[root@master script]# tree /nfsdata/
/nfsdata/
├── 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
systemctl restart nfs-server
systemctl restart rpcbind
[root@master script]# showmount -e localhost
Export list for localhost:
/nfsdata/9 *
/nfsdata/8 *
/nfsdata/7 *
/nfsdata/6 *
/nfsdata/5 *
/nfsdata/4 *
/nfsdata/3 *
/nfsdata/2 *
/nfsdata/1 *
/nfsdata/0 *
[root@master script]# ssh node1
[root@node1 ~]# mount -t nfs master:/nfsdata/1 /mnt
[root@node1 ~]# cat /mnt/index.html
1
[root@node1 ~]# umount /mnt/
2.部署pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv0 # pv 名字
spec:
capacity: #容量
storage: 0.5Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/0 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteMany #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/1 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv2 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs1 # 存储类的名字
nfs:
path: /nfsdata/2 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv3 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Retain #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/3 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv4 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/4 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv5 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/5 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv6 # pv 名字
spec:
capacity: #容量
storage: 1.5Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/6 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv7 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/7 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv8 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/8 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv9 # pv 名字
spec:
capacity: #容量
storage: 1Gi #存储空间
accessModes: #存储模式
- ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
storageClassName: nfs # 存储类的名字
nfs:
path: /nfsdata/9 # nfs共享路径
server: 10.0.17.100 # nfs服务器地址
---
[root@master pv_pvc]# kubectl create -f nfspv.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@master pv_pvc]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 9s
nfspv1 1395864371200m RWX Recycle Available nfs <unset> 9s
nfspv2 858993459200m RWO Recycle Available nfs1 <unset> 9s
nfspv3 1Gi RWO Retain Available nfs <unset> 9s
nfspv4 1Gi RWO Recycle Available nfs <unset> 8s
nfspv5 1Gi RWO Recycle Available nfs <unset> 8s
nfspv6 1Gi RWO Recycle Available nfs <unset> 8s
nfspv7 1Gi RWO Recycle Available nfs <unset> 8s
nfspv8 1536Mi RWO Recycle Available nfs <unset> 8s
nfspv9 1Gi RWO Recycle Available nfs <unset> 8s
3.创建服务并使用pvc
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: wangyanglinux/myapp:v1.0
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@master pv_pvc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 6d13h
nginx ClusterIP None <none> 80/TCP 4m45s
# CLUSTER-IP: NONE 没有负载均衡集群
[root@master pv_pvc]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.0.0.1:443 rr
-> 10.0.17.100:6443 Masq 1 2 0
TCP 10.0.0.10:53 rr
-> 10.244.219.66:53 Masq 1 0 0
-> 10.244.219.67:53 Masq 1 0 0
TCP 10.0.0.10:9153 rr
-> 10.244.219.66:9153 Masq 1 0 0
-> 10.244.219.67:9153 Masq 1 0 0
TCP 10.15.20.146:5473 rr
-> 10.0.17.102:5473 Masq 1 0 0
UDP 10.0.0.10:53 rr
-> 10.244.219.66:53 Masq 1 0 0
-> 10.244.219.67:53 Masq 1 0 0
# 通过endpoint发现有ip 作用为写入dns记录 通过dns解析
[root@master pv_pvc]# kubectl get ep -o yaml
apiVersion: v1
items:
- apiVersion: v1
kind: Endpoints
metadata:
creationTimestamp: "2024-08-20T14:05:33Z"
labels:
endpointslice.kubernetes.io/skip-mirror: "true"
name: kubernetes
namespace: default
resourceVersion: "238357"
uid: de808267-dc60-4de8-8a0b-6aefc6f694b0
subsets:
- addresses:
- ip: 10.0.17.100
ports:
- name: https
port: 6443
protocol: TCP
- apiVersion: v1
kind: Endpoints
metadata:
annotations:
endpoints.kubernetes.io/last-change-trigger-time: "2024-08-27T03:55:47Z"
creationTimestamp: "2024-08-27T03:52:54Z"
labels:
app: nginx
service.kubernetes.io/headless: ""
name: nginx
namespace: default
resourceVersion: "263506"
uid: 098db983-ce34-44bd-b534-7610abdba05b
subsets:
- addresses:
- hostname: web-0
ip: 10.244.104.28
nodeName: node2
targetRef:
kind: Pod
name: web-0
namespace: default
uid: e3581e9b-58c9-4982-804d-0c79bceead76
- hostname: web-3
ip: 10.244.104.29
nodeName: node2
targetRef:
kind: Pod
name: web-3
namespace: default
uid: 253367fb-daaa-4083-a141-88d86741c3f6
- hostname: web-5
ip: 10.244.104.30
nodeName: node2
targetRef:
kind: Pod
name: web-5
namespace: default
uid: bf1eb577-5eed-4cd9-a072-e4782357b7de
- hostname: web-6
ip: 10.244.104.31
nodeName: node2
targetRef:
kind: Pod
name: web-6
namespace: default
uid: 479c3b44-3f35-476f-b141-43ceedd0b645
- hostname: web-1
ip: 10.244.166.161
nodeName: node1
targetRef:
kind: Pod
name: web-1
namespace: default
uid: bf436d3a-b5a4-4a03-b124-c751404958bf
- hostname: web-2
ip: 10.244.166.162
nodeName: node1
targetRef:
kind: Pod
name: web-2
namespace: default
uid: 6a2e33ce-f8fb-4684-bea3-1c612c7a2a67
- hostname: web-4
ip: 10.244.166.163
nodeName: node1
targetRef:
kind: Pod
name: web-4
namespace: default
uid: 59533be1-fe6b-4982-addf-dfe0130278ad
ports:
- name: web
port: 80
protocol: TCP
kind: List
metadata:
resourceVersion: ""
[root@master pv_pvc]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.0.0.1:443 rr
-> 10.0.17.100:6443 Masq 1 2 0
TCP 10.0.0.10:53 rr #集群负载均衡VIP
-> 10.244.219.66:53 Masq 1 0 0
-> 10.244.219.67:53 Masq 1 0 0
TCP 10.0.0.10:9153 rr
-> 10.244.219.66:9153 Masq 1 0 0
-> 10.244.219.67:9153 Masq 1 0 0
TCP 10.15.20.146:5473 rr
-> 10.0.17.102:5473 Masq 1 0 0
UDP 10.0.0.10:53 rr
-> 10.244.219.66:53 Masq 1 0 0
-> 10.244.219.67:53 Masq 1 0 1
[root@master 4]# dig -t A web-0.nginx.default.svc.cluster.local. @10.0.0.10
; <<>> DiG 9.16.23-RH <<>> -t A web-0.nginx.default.svc.cluster.local. @10.0.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33037
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 9622287aadba2217 (echoed)
;; QUESTION SECTION:
;web-0.nginx.default.svc.cluster.local. IN A
;; ANSWER SECTION:
web-0.nginx.default.svc.cluster.local. 30 IN A 10.244.166.164
;; Query time: 1 msec
;; SERVER: 10.0.0.10#53(10.0.0.10)
;; WHEN: Tue Aug 27 12:37:12 CST 2024
;; MSG SIZE rcvd: 131
[root@master pv_pvc]# dig -t A nginx.default.svc.cluster.local @10.0.0.10
; <<>> DiG 9.16.23-RH <<>> -t A nginx.default.svc.cluster.local @10.0.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60592
;; flags: qr aa rd; QUERY: 1, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 5023c3488bba032f (echoed)
;; QUESTION SECTION:
;nginx.default.svc.cluster.local. IN A
;; ANSWER SECTION:
nginx.default.svc.cluster.local. 30 IN A 10.244.104.30
nginx.default.svc.cluster.local. 30 IN A 10.244.104.29
nginx.default.svc.cluster.local. 30 IN A 10.244.166.162
nginx.default.svc.cluster.local. 30 IN A 10.244.104.28
nginx.default.svc.cluster.local. 30 IN A 10.244.166.163
nginx.default.svc.cluster.local. 30 IN A 10.244.104.31
nginx.default.svc.cluster.local. 30 IN A 10.244.166.161
;; Query time: 3 msec
;; SERVER: 10.0.0.10#53(10.0.0.10)
;; WHEN: Tue Aug 27 12:03:08 CST 2024
;; MSG SIZE rcvd: 401
# 解析出来的结果与kubectl get ep -o yaml 中的ip一致 stateFulset创建的pod的ip
[root@master pv_pvc]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 13m 10.244.104.28 node2 <none> <none>
web-1 1/1 Running 0 13m 10.244.166.161 node1 <none> <none>
web-2 1/1 Running 0 13m 10.244.166.162 node1 <none> <none>
web-3 1/1 Running 0 13m 10.244.104.29 node2 <none> <none>
web-4 1/1 Running 0 13m 10.244.166.163 node1 <none> <none>
web-5 1/1 Running 0 10m 10.244.104.30 node2 <none> <none>
web-6 1/1 Running 0 10m 10.244.104.31 node2 <none> <none>
# statefulSet控制器有序创建 有序回收
[root@master pv_pvc]# kubectl scale statefulSet web --replicas=10
statefulset.apps/web scaled
[root@master pv_pvc]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 21s
web-1 1/1 Running 0 18s
web-2 1/1 Running 0 14s
web-3 1/1 Running 0 10s
web-4 1/1 Running 0 6s
[root@master pv_pvc]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 18m
web-1 1/1 Running 0 18m
web-2 1/1 Running 0 17m
web-3 1/1 Running 0 17m
web-4 1/1 Running 0 17m
web-5 1/1 Running 0 15m
web-6 1/1 Running 0 15m
web-7 0/1 Pending 0 2m43s
# 节点pv不满足条件了无法创建pod
[root@master pv_pvc]# kubectl describe pod web-7
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m1s default-scheduler 0/3 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
#根据时间发现是一个一个创建的
[root@master pv_pvc]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-web-0 Bound nfspv3 1Gi RWO nfs <unset> 21m
www-web-1 Bound nfspv5 1Gi RWO nfs <unset> 21m
www-web-2 Bound nfspv4 1Gi RWO nfs <unset> 21m
www-web-3 Bound nfspv6 1Gi RWO nfs <unset> 21m
www-web-4 Bound nfspv9 1Gi RWO nfs <unset> 21m
www-web-5 Bound nfspv7 1Gi RWO nfs <unset> 18m
www-web-6 Bound nfspv8 1536Mi RWO nfs <unset> 18m
www-web-7 Pending nfs <unset> 6m15s
[root@master pv_pvc]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 29m
nfspv1 1395864371200m RWX Recycle Available nfs <unset> 29m
nfspv2 858993459200m RWO Recycle Available nfs1 <unset> 29m
nfspv3 1Gi RWO Retain Bound default/www-web-0 nfs <unset> 29m
nfspv4 1Gi RWO Recycle Bound default/www-web-2 nfs <unset> 29m
nfspv5 1Gi RWO Recycle Bound default/www-web-1 nfs <unset> 29m
nfspv6 1Gi RWO Recycle Bound default/www-web-3 nfs <unset> 29m
nfspv7 1Gi RWO Recycle Bound default/www-web-5 nfs <unset> 29m
nfspv8 1536Mi RWO Recycle Bound default/www-web-6 nfs <unset> 29m
nfspv9 1Gi RWO Recycle Bound default/www-web-4 nfs <unset> 29m
[root@master pv_pvc]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 24m 10.244.104.28 node2 <none> <none>
web-1 1/1 Running 0 24m 10.244.166.161 node1 <none> <none>
web-2 1/1 Running 0 24m 10.244.166.162 node1 <none> <none>
web-3 1/1 Running 0 24m 10.244.104.29 node2 <none> <none>
web-4 1/1 Running 0 23m 10.244.166.163 node1 <none> <none>
web-5 1/1 Running 0 21m 10.244.104.30 node2 <none> <none>
web-6 1/1 Running 0 21m 10.244.104.31 node2 <none> <none>
web-7 0/1 Pending 0 8m51s <none> <none> <none> <none>
[root@master pv_pvc]# curl 10.244.104.28
3
[root@master pv_pvc]# echo "Noziroh" >> /nfsdata/3/index.html
[root@master pv_pvc]# curl 10.244.104.28
3
Noziroh
[root@master pv_pvc]# kubectl delete pod web-0
pod "web-0" deleted
[root@master pv_pvc]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 10s 10.244.166.164 node1 <none> <none>
web-1 1/1 Running 0 24m 10.244.166.161 node1 <none> <none>
web-2 1/1 Running 0 24m 10.244.166.162 node1 <none> <none>
web-3 1/1 Running 0 24m 10.244.104.29 node2 <none> <none>
web-4 1/1 Running 0 24m 10.244.166.163 node1 <none> <none>
web-5 1/1 Running 0 21m 10.244.104.30 node2 <none> <none>
web-6 1/1 Running 0 21m 10.244.104.31 node2 <none> <none>
web-7 0/1 Pending 0 9m25s <none> <none> <none> <none>
[root@master pv_pvc]# curl 10.244.166.164
3
Noziroh
#更改nfs存储内数据 删除pod 重新创建的pod 读取nfs数据 更改后的数据依然存在 pod级别数据持久化
# pod内部互相访问使用域名
# 域名格式
#podName.headlessSvcName.nsName.svc,cluster.local.
#statefulSetName-num.headlessSvcName.nsName.svc,cluster.local.
[root@master 4]# kubectl exec -it pod-demo -- bash
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
3
Noziroh
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
7
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
9
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
4
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
7
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
3
Noziroh
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
8
pod-demo:~# curl http://nginx.default.svc.cluster.local./index.html
6
pod-demo:/# wget http://nginx.default.svc.cluster.local./index.html
Connecting to nginx.default.svc.cluster.local. (10.244.166.163:80)
saving to 'index.html'
index.html 100% |*********************************************| 2 0:00:00 ETA
'index.html' saved
pod-demo:/# cat index.html
9
pod-demo:~# wget http://web-0.nginx.default.svc.cluster.local./index.html
Connecting to web-0.nginx.default.svc.cluster.local. (10.244.166.164:80)
saving to 'index.html'
index.html 100% |*********************************************| 10 0:00:00 ETA
'index.html' saved
pod-demo:~# cat index.html
3
Noziroh
回收statefulSet控制器
[root@master pv_pvc]# kubectl delete statefulset --all
statefulset.apps "web" deleted
删除pvc
[root@master pv_pvc]# 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
persistentvolumeclaim "www-web-5" deleted
persistentvolumeclaim "www-web-6" deleted
persistentvolumeclaim "www-web-7" deleted
# released 表示已释放状态 无法重新绑定pvc 需要修改成活跃状态
[root@master pv_pvc]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfspv0 512Mi RWO Recycle Available nfs <unset> 58m
nfspv1 1395864371200m RWX Recycle Available nfs <unset> 58m
nfspv2 858993459200m RWO Recycle Available nfs1 <unset> 58m
nfspv3 1Gi RWO Retain Released default/www-web-0 nfs <unset> 58m
nfspv4 1Gi RWO Recycle Released default/www-web-2 nfs <unset> 58m
nfspv5 1Gi RWO Recycle Released default/www-web-1 nfs <unset> 58m
nfspv6 1Gi RWO Recycle Released default/www-web-3 nfs <unset> 58m
nfspv7 1Gi RWO Recycle Released default/www-web-5 nfs <unset> 58m
nfspv8 1536Mi RWO Recycle Released default/www-web-6 nfs <unset> 58m
nfspv9 1Gi RWO Recycle Released default/www-web-4 nfs <unset> 58m
[root@master pv_pvc]# kubectl edit pv nfspv9
#删除绑定信息
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: www-web-4
namespace: default
resourceVersion: "263192"
uid: 3568521e-386c-40ff-b623-06b77b93c446
nfspv9 1Gi RWO Recycle Available nfs <unset> 62m
07 storageClass(存储类,协助pv自动创建)
在Kubernetes(k8s)中,StorageClass 是一个非常重要的概念,它提供了一种方式用于描述存储卷的“类”(class),让管理员能够将存储的“提供者”(provisioner)的细节与使用者解耦。通过使用StorageClass,用户可以指定他们想要的存储特性(如性能、备份策略等),而不需要知道这些特性是如何在底层实现的。
-
主要作用
-
动态卷供应(Dynamic Volume Provisioning):当Pod需要存储卷,但集群中没有现成的卷可用时,StorageClass可以指示集群自动创建符合要求的存储卷。这通过配置一个或多个存储卷供应器(volume provisioner)来完成,这些供应器会根据StorageClass中定义的参数动态地创建存储卷。
-
抽象存储后端:StorageClass允许集群管理员为不同类型的存储后端(如NFS、Ceph、AWS EBS等)定义不同的类。这样,应用程序开发者或部署者就可以通过简单地指定StorageClass来请求特定类型的存储,而无需关心具体的存储后端细节。
-
-
组成部分
- Provisioner:这是负责动态创建卷的组件。它可以是Kubernetes内置的,也可以是第三方提供的。
- Parameters:这是一个键值对的集合,用于指定存储卷的具体参数,这些参数将传递给provisioner以创建存储卷。
- Reclaim Policy:定义了当存储卷不再被需要时(即,它不再被任何Pod绑定),应该如何处理它。可能的值包括Retain(保留)、Delete(删除)和Recycle(回收,但这一选项在许多环境中已不推荐使用)。
- Allow Volume Expansion:指示是否允许扩展已存在的卷的大小。
Mount Options:指定挂载选项,这些选项将在卷被挂载到Pod时使用。
-
使用示例
在Kubernetes中定义一个StorageClass的YAML文件可能如下所示:
安装nfs服务
yum install -y nfs-utils rpcbind
mkdir /nfsdata/share
chown nobody /nfsdata/share
echo "/nfsdata/share *(rw,sync,no_subtree_check)" >> /etc/exports
systemctl enable nfs-server && systemctl enable nfs-server
systemctl restart nfs-server && systemctl restart nfs-server
showmount -e master
部署nfs-client-provisioner
vim nfs-client-provisioner.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
namespace: nfs-storageclass
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
# image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
# image: k8s.dockerproxy.com/sig-storage/nfs-subdir-external-provisioner:v4.0.2
image: registry.cn-beijing.aliyuncs.com/blice_haiwai/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
# value: <YOUR NFS SERVER HOSTNAME>
value: 10.0.17.100
- name: NFS_PATH
# value: /var/nfs
value: /nfsdata/share
volumes:
- name: nfs-client-root
nfs:
# server: <YOUR NFS SERVER HOSTNAME>
server: 10.0.17.100
# share nfs path
path: /nfsdata/share
常见ca认证信息
vim RBAC.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storageclass
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
#创建名字空间
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storageclass
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storageclass
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storageclass
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-storageclass
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
创建StorageClass
vim StorageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
namespace: nfs-storageclass
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
pathPattern: ${.PVC.namespace}/${.PVC.name}
onDelete: delete #删除模式
测试pod
vim test.yaml
kind: PersistentVolumeClaim # 创建pvc
apiVersion: v1
metadata:
name: test-claim
annotations:
spec:
accessModes:
- ReadWriteMany # 多节点读写
resources:
requests:
storage: 1Mi # 请求资源大小1MB
storageClassName: nfs-client # 存储类名字
---
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: wangyanglinux/myapp:v1.0
volumeMounts:
- name: nfs-pvc
mountPath: "/usr/local/nginx/html"
restartPolicy: "Never"
volumes: # 定义卷
- name: nfs-pvc #pvc提供的卷
persistentVolumeClaim:
claimName: test-claim
创建命名空间
kubectl create ns nfs-storageclass
创建
[root@master pv_pvc]# kubectl apply -f storageclass/
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
storageclass.storage.k8s.io/nfs-client created
deployment.apps/nfs-client-provisioner created
persistentvolumeclaim/test-claim created
pod/test-pod created
[root@master storageclass]# kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 20m
[root@master storageclass]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-claim Bound pvc-582fe735-4cd2-48eb-b444-50cbf554e55b 1Mi RWX nfs-client <unset> 24m
[root@master storageclass]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-582fe735-4cd2-48eb-b444-50cbf554e55b 1Mi RWX Delete Bound default/test-claim nfs-client <unset>
[root@master storageclass]# ls /nfsdata/share/default/test-claim/
hostname.html