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数据库中,而真实数据则存储在分配的持久卷上。

总结
在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
  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值