【K8S学习笔记-006】Volume详解(NFS,Ceph,Secret,ConfigMap, Downward API,PV/PVC)

Volume

Volume (存储卷) 是 Pod 中能够被多个容器访问的共享目录。
Volume 定义在 Pod 上,可以被一个 Pod 里的多个容器挂载,以保证当容器终止或者重启时,Volume 中的数据也不会丢失。
Kubernetes 支持多种类型的 Volume,例如 GlusterFS、Ceph 等分布式文件系统。
除此之外,Kubernetes 还提供了容器配置文件集中化定义与管理,通过ConfigMap对象来实现。
在此,我们将学习以下类型的volume:

  • emptyDir
  • hostPath
  • NFS
  • Secret
  • ConfigMap
  • Downward API

其它volume
• iscsi:使用 iSCSI 存储设备上的目录挂载到 Pod 中。
• flocker:使用 Flocker 来管理存储卷。
• glusterfs:使用 GlusterFS 网络文件系统的目录挂载到 Pod 中。
• rbd:使用 Ceph 块设备共享存储(Rados Block Device)挂载到Pod中。
• gitRepo:通过挂载一个空目录,并从 GIT 库 clone 一个 git repository 以供 Pod 使用。
• gcePersistentDisk: 谷歌云存储
• awsElasticBlockStore:亚马逊云存储
• …


wordpress官方例子:
https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/

emptyDir

emptyDir 是在 Pod 分配到 Node 时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件。
emptyDir 是Kubernetes 自动分配的一个目录,当 Pod 从 Node 上移除时,emptyDir 中的数据也会被永久删除。
emptyDir的用途如下:
- 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保存。
- 长时间任务的中间过程 CheckPoint (检查点)的临时保存目录。
- 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。

下面是一个emptyDir的例子:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
    - key: tier
      operator: In
      values: ["frontend"]
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 0.1
          limits:
            cpu: 0.2
        volumeMounts:
        - mountPath: /mydata-data
          name: datavol
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 0.1
          limits:
            cpu: 0.2
        volumeMounts:
        - mountPath: /mydata-data
          name: datavol
      volumes:
      - name: datavol
        emptyDir: {}

注意:
1.需要使用内存作为emptyDir的可用存储资源也是可以的,只需要在创建emptyDir时增加一个emptyDir.medium字段的定义,并赋值为"Memory"即可;
2.在使用tmpfs文件系统作为emptyDir的存储后端时,如果遇到node节点重启,则emptyDir中的数据也会全部丢失。同时,你编写的任何文件也都将计入Container的内存使用限制。

hostPath

使用 hostPath 挂载宿主机上的文件或目录,使容器可以使用宿主机的文件系统持久化存储数据。
使用 hostPath 时,需要注意以下几点:
- 在不同的 Node 上的 Pod 挂载的是本地宿主机的目录,如果要想让不同的 Node 挂载相同的目录,需要使用网络存储或分布式文件存储。
- 如果使用了资源配额管理,则 Kubernetes 无法将其在宿主机上使用的资源纳入管理。

下面是一个hostPath的例子:

...
  volumes:
  - name: "persistent-storage"
    hostPath:
      path: "/data"
...

注意:
1. 不提供pod的亲和性,如: hostPath映射的目录在node1,如果pod被调度到node2,将导致原来在node1的数据不存在;
2. 能够提供pv/pvc/storage class的方式使用;
3. 数据能持久化。

NFS

使用 NFS 网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个 NFS Server。
NFS可以解决hostPath的多个node之间的数据共享问题。
下面是一个NFS的例子

...
  volumes:
  - name: nfs-volume
    nfs:
      server: 192.168.10.20      # NFS服务器的地址
      path: "/nfs"               # NFS服务器共享的目录
...

NFS-PVC

参考:https://www.yisu.com/zixun/15552.html

在一个大规模的Kubernetes集群里,可能有成千上万个PVC,根据项目的需要,可能会不断有新的PVC被提交
如此一来,运维人员就需要不断的添加满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。

另外,不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等
为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass
通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等
用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。

NFS-Client Provisioner

NFS-Client Provisioner 可以根据PVC的需求自动创建符合要求的PV。
NFS-client-provisioner是一个automatic provisioner,使用NFS作为存储,自动创建PV和对应的PVC,本身不提供NFS存储,需要外部先有一套NFS存储服务。
• PV以 ${namespace}-${pvcName}-${pvName}的命名格式提供(在NFS服务器上)
• PV回收的时候以 archieved- n a m e s p a c e − {namespace}- namespace{pvcName}-${pvName} 的命名格式(在NFS服务器上)

注意: 在所有需要运行Pod的节点上安装nfs-utils
在这里插入图片描述
部署过程

  1. 准备一台 NFS 服务器(略)

  2. 修改 apiserver 启动参数,增加 --feature-gates=RemoveSelfLink=false 选项(1.20版本需要,其它版本未测)
    在这里插入图片描述

  3. 创建并运行 NFS-Client Provisioner
    创建 RBAC 文件

[root@k8s-master nfs]# vim rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - 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: default
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: default
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
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

创建 deployment 文件

[root@k8s-master nfs]# vim deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: qf.com/nfs                # 自定义
            - name: NFS_SERVER
              value: 192.168.10.100       # 根据实际情况修改
            - name: NFS_PATH
              value: /netshare                 # 根据实际情况修改
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.10.100          # 根据实际情况修改
            path: /netshare                       # 根据实际情况修改

创建 storageclass 文件

[root@k8s-master nfs]# vim storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: qf.com/nfs # 必须和 deployment 的 env 中的 PROVISIONER_NAME 定义的一致
parameters:
  archiveOnDelete: "false"

定义好以后将它们 apply 起来即可

  1. 测试
    运行起来后我们可以创建pvc测试一下
[root@k8s-master yaml]# vim test-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  # annotations:
    # volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  storageClassName: managed-nfs-storage      # 与 storage-class.yaml 中 metadata.name 保持一致
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Mi

执行后我们可以看到很快就跟一个pv进行了绑定:
在这里插入图片描述

在NFS服务器上也能看到创建了一个目录:
在这里插入图片描述

接下来创建一个deployment测试一下:

[root@k8s-master yaml]# vim test-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-web
spec:
  selector:
    matchLabels:
      app: nfs-web
  replicas: 2
  template:
    metadata:
      labels:
        app: nfs-web
    spec:
      containers:
      - name: nginx
        image: nginx:1.20
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
      volumes:
      - name: www
        persistentVolumeClaim:
          claimName: nfs-pvc

运行起来后可以看到

在这里插入图片描述

Ceph

使用 ceph 时,我们需要事先部署一个 ceph 集群。
ceph可提供块设备存储(rbd)和文件存储(cephFS)
ceph 块存储的用法:

  1. Ceph集群
    创建image

  2. k8s节点
    安装ceph
    拷贝秘钥文件
    编写yaml文件

...
  volumes:
  - name: rbd-volume
    rbd
      monitors: string array
      pool: string [ default: rbd ] #存储池名称
      image: string   # image名称
      fsType: string  # 想要格式化的文件系统
      readOnly: boolean [ default: false ]
      keyring: string [ default: /etc/ceph/keyring ]  # 秘钥文件的路径
      user: string [ default: admin ]
...

cephFS 的用法:

  1. Ceph集群
    创建MDS
    创建存储池(data, metadata)
  2. k8s节点
    安装ceph
    拷贝秘钥文件
    编写yaml文件
...
  volumes:
  - name: cephfs
    cephfs
      monitors: string array # monitor的IP:port                                                 
      path: string [ default: / ]
      readOnly: boolean [ default: false ]
      secretFile: string [ default: /etc/ceph/user.secret ]
      user: string [ default: admin ]
...

Secret

Secret简介

Secret 的作用是把 Pod 想要访问的加密数据,存放到 Etcd 中
然后你就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了。
Secret是用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。
这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。

Secret有三种类型:
• Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。
• kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息。
• kubernetes.io/service-account-token:用于被 ServiceAccount 引用。ServiceAccout 创建时 Kubernetes 会默认创建对应的 secret。
Pod 如果使用了 SA,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。

创建Secret

两种创建方式:
2.1 使用命令行工具从文件创建

[root@master ~]# echo -n "jerry" > user.txt 
[root@master ~]# echo -n "jerry_pass" > pass.txt
[root@master ~]# kubectl create secret generic jerry-secret --from-file=./user.txt --from-file=./pass.txt
secret/jerry-secret created

2.2 使用yaml文件创建
2.2.1 使用base64对数据进行转码

[root@master ~]# echo -n "tom" | base64
dG9t
[root@master ~]# echo -n "tom_pass" | base64
dG9tX3Bhc3M=

2.2.2 创建api文件

# filename: tom-secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: tom-secret
  namespace: default
type: Opaque
data:
  username: dG9t
  password: dG9tX3Bhc3M=

2.2.3 创建Secret

[root@master ~]# kubectl apply -f tom-secret.yml 
secret/tom-secret created

3 查看Secret

[root@master ~]# kubectl get secret
NAME                     TYPE                                  DATA   AGE
default-token-9zvr2      kubernetes.io/service-account-token   3      7d23h
jerry-secret             Opaque                                2      8m
tom-secret               Opaque                                2      99s
[root@master ~]# kubectl get secret jerry-secret -o yaml
apiVersion: v1
data:
  pass.txt: amVycnlfcGFzcw==
  user.txt: amVycnk=
kind: Secret
metadata:
  creationTimestamp: "2020-03-10T12:06:37Z"
  name: jerry-secret
  namespace: default
  resourceVersion: "365148"
  selfLink: /api/v1/namespaces/default/secrets/jerry-secret
  uid: 97ddb2ab-62c7-11ea-bc13-000c29af1f49
type: Opaque

使用Secret

创建好Secret之后,可以通过两种方式使用:
• 以Volume方式挂载到容器中使用
• 以环境变量方式使用

4.1 以volume方式挂载到容器中

# filename: pod-secret.yml
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
spec:
  containers:
  - name: nginx
    image: registry.cn-shenzhen.aliyuncs.com/leedon/nginx:1.12
    volumeMounts:
    - name: jerrys
      mountPath: /etc/jerry        # 将名称为jerrys的volume挂载到/etc/jerry中
      readOnly: true
    - name: toms
      mountPath: /etc/tom
      readOnly: true
  volumes:
  - name: jerrys                   # 定义名称为jerrys的volume
    secret:
      secretName: jerry-secret
  - name: toms
    secret:
      secretName: tom-secret

运行起来后我们可以进入到容器中查看

root@pod-secret:/# ls /etc/jerry/
pass.txt  user.txt                        # 这两个文件中分别记录的是jerry-secret的数据

root@pod-secret:/# ls /etc/tom/
password  username                        # 这两个文件中分别记录的是tom-secret的数据

4.2 将Secret设置为容器中的环境变量

---
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret-env
spec:
  containers:
  - name: nginx
    image: registry.cn-shenzhen.aliyuncs.com/leedon/nginx:1.12
    env:
    - name: user1
      valueFrom:
        secretKeyRef: 
          name: tom-secret
          key: username
    - name: user2
      valueFrom:
        secretKeyRef:
          name: jerry-secret
          key: user.txt

运行起来后进入容器中查看:

root@pod-secret-env:/# env | grep "user"
user2=jerry
user1=tom

5 Secret注意事项
• 被挂载到Pod中的 secret 需要提前创建,否则会导致Pod创建失败;
• secret是有命名空间属性的,只有在相同namespace的Pod才能引用它;
• 通过secretKeyRef引用一个不存在的 secret key 会导致pod创建失败;
• 如果修改一个Secret的内容,那么挂载了该 Secret 的容器中也将会取到更新后的值;
• 环境变量读取 Secret 很方便,但无法自动更新.

ConfigMap

ConfigMap简介

很多应用程序的配置需要通过配置文件,命令行参数和环境变量的组合配置来完成。
这些配置应该从image内容中解耦,以此来保持容器化应用程序的便携性。
ConfigMap API资源提供了将配置数据注入容器的方式。
ConfigMap可以用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。

在一个pod里面使用ConfigMap大致有三种方式:

• 命令行参数
• 环境变量
• 数据卷文件

创建ConfigMap

创建configmap有多种方式. 最佳实践是通过yaml文件创建,这样能够达到svc、rc、configmap创建的统一.
如果是文件,我们可以先通过命令行创建configmap,然后通过 “kubectl get configmap XXX -o yaml” 导出yaml文件。

通过目录创建

准备目录及文件

[root@master ~]# mkdir configmap
[root@master ~]# echo CreateFrom=Directory > configmap/file1
[root@master ~]# echo "Listen 80" > configmap/file2

从目录创建ConfigMap

[root@master ~]# kubectl create cm  cmfromdir --from-file=./configmap/
configmap/cmfromdir created

查看

[root@master ~]# kubectl describe cm cmfromdir
Name:         cmfromdir
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
file1:                      # Key1
----
CreateFrom=Directory        # Value1
                                          
file2:                      # Key2
----
Listen 80                   # Value2
                                                  
Events:  <none>

说明:
- 当–from-file指向目录的时候,目录下所有文件会被用在ConfigMap里面创建键值对,键为文件名,值为文件内容
- 可通过多个 --from-file 创建多个键值对

通过文件创建

准备文件

[root@master ~]# head -3 /etc/passwd > /tmp/pass


从文件创建configmap

[root@master ~]# kubectl create cm  cmfromfile --from-file=/tmp/pass 
configmap/cmfromfile created


查看

[root@master ~]# kubectl describe cm cmfromfile
Name:         cmfromfile
Namespace:    default
Labels:       <none>
Annotations:  <none>
​                                      
Data
====
pass:                                   # Key
----
root:x:0:0:root:/root:/bin/bash         # Value
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
​                                                                       
Events:  <none>

说明:
- 从文件创建与从目录创建类似, 以文件名为键, 以文件内容为值. 可通过多个 --from-file 创建多个键值对
- 也可以使用 --from-file=“KEY=FILE” 自定义键名, 值同样为文件内容

通过文字创建

从命令行通过文件创建configmap

[root@master ~]# kubectl create cm  cmfromliteral --from-literal="bind=bind 0.0.0.0" 
configmap/cmfromliteral created


查看

[root@master ~]# kubectl describe cm  cmfromliteral 
Name:         cmfromliteral
Namespace:    default
Labels:       <none>
Annotations:  <none>
​                                      
Data
====
bind:
----
bind 0.0.0.0
Events:  <none>


说明:
- 直接在命令行通过文字创建, 键名和值都是自定义.
- 可通过多个 --from-literal 创建多个键值对

通过YAML文件创建

编写yaml文件

# filename: configmap-mysql.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cmfromyaml
  namespace: default
data:
  application: MySQL
  master.cnf: |
    server_id = 0
    log_bin = binlog
    port = 3306
  slave.cnf: |
    server_id = 1
    port = 3306

通过yaml文件创建configmap
创建

[root@master ~]# kubectl apply -f configmap-mysql.yml 
configmap/cmfromyaml created


查看

[root@master ~]# kubectl describe cm cmfromyaml
Name:         cmfromyaml
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"application":"MySQL","master.cnf":"server_id = 0\nlog_bin = binlog\nport = 3306\n","slave.cnf":"server_id = 1\...
​            
Data
====
application:
----
MySQL
master.cnf:
----
server_id = 0
log_bin = binlog
port = 3306
​                
slave.cnf:
----
server_id = 1
port = 3306
​                        
Events:  <none>

使用ConfigMap

创建好ConfigMap之后,可以通过两种方式使用:
• 以Volume方式挂载到容器中使用使用
• 以环境变量方式使用
A 以volume方式挂载使用ConfigMap

# filename: pod-cm.yml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm
spec:
  containers:
  - name: nginx
    image: registry.cn-shenzhen.aliyuncs.com/leedon/nginx:1.12
    volumeMounts:
    - name: master
      mountPath: /etc/my.cnf.d            # 挂载到什么目录
  volumes:
  - name: master
    configMap:
      name: cmfromyaml
      items:
      - key: master.cnf                   # 键名, 其值将作为下面的文件内容
        path: mymaster.cnf                # 文件名称

Pod运行起来后我们进入容器查看

root@pod-cm:/# cat /etc/my.cnf.d/mymaster.cnf
server_id = 0
log_bin = binlog
port = 3306

B 生成为容器内的环境变量

# filename: pod-cm.yml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-env
spec:
  nodeName: 192.168.10.11
  containers:
  - name: nginx
    image: registry.cn-shenzhen.aliyuncs.com/leedon/nginx:1.12
    env:
    - name: VAR_NAME1
      valueFrom:
        configMapKeyRef:
          name: cmfromdir
          key: file1
    - name: VAR_NAME2
      valueFrom:
        configMapKeyRef:
          name: cmfromfile
          key: passPod

运行起来后进入容器查看

root@pod-cm-env:/# echo $VAR_NAME1

root@pod-cm-env:/# echo $VAR_NAME2

C 设置容器启动命令的启动参数
同上面的环境变量 ,设置 env , 在启动命令中引用环境变量 (略)

使用ConfigMap的限制

• ConfigMap 必须在 Pod 之前创建
• ConfigMap 受 NameSpace 限制,只有处于相同 Namespace 中的 pod 才可以引用他
• ConfigMap 的配额管理还未实现
• kubelet 只支持可以被 API Server 管理的 Pod 使用 ConfigMap。 静态 Pod 将无法引用 ConfigMap

Downward API

官方文档

Downward API 用于在容器中获取 Pod 的基本信息,kubernetes原生支持 。
Downward API 提供了两种方式用于将 Pod 的信息注入到容器内部:
• 以Volume方式挂载到容器中
• 以环境变量方式使用

这两种 将Pod和Container的信息暴露给运行中的容器的方法就叫做Downward API。

volume方式
下面是一个volume方式的示例:

apiVersion: v1
kind: Pod
metadata:
  name: downwardapi-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
  - name: client-container
    image: registry.cn-shenzhen.aliyuncs.com/leedon/alpine
    command: ["sleep","10000"]
    resources:
      requests:
        cpu: 0.1
        memory: "32Mi"
      limits:
        cpu: 0.2
        memory: "64Mi"
    volumeMounts:
    - name: podinfo
      mountPath: /etc/podinfo
    - name: containerinfo
      mountPath: /etc/containerinfo
  volumes:
  - name: podinfo
    downwardAPI:
       items:
       - path: "labels"
         fieldRef:
           fieldPath: metadata.labels
  - name: containerinfo
    downwardAPI:
      items:
      - path: "cpu_limit"
        resourceFieldRef:
          containerName: client-container
          resource: limits.cpu
      - path: "mem_limit"
        resourceFieldRef:
          containerName: client-container
          resource: limits.memory

Pod运行起来后可以进入查看:

[root@master ~]# kubectl exec -it downwardapi-volume -- sh
/ # cat /etc/podinfo/labels 
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast" 
/ # cat /etc/containerinfo/cpu_limit 
1
/ # cat /etc/containerinfo/mem_limit 
67108864

这个例子把 metadata.labels 以文件的形式挂到了容器的 /etc/podinfo 中,把 limits.cpu 、limits.memory 以文件的形式挂到容器的 /etc/containerinfo 中,在容器里查看相应目录中的相应文件,可以拿到 Pod 或 Container 的相关信息,达到了downware API 的目的。
这些文件实际是指向临时文件的链接,这样在Pod信息更新时,可以通过rename更新文件内容。
注意:如果使用了 subPath 的方式挂载,将不能及时收到数据更新。

环境变量方式
下面是一个环境变量的示例

apiVersion: v1
kind: Pod
metadata:
  name: downwardapi-env
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
  - name: client-container
    image: registry.cn-shenzhen.aliyuncs.com/leedon/alpine
    command: ["sleep","10000"]
    resources:
      requests:
        cpu: "100m"
        memory: "32Mi"
      limits:
        cpu: "200m"
        memory: "64Mi"
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: CPU_LIMIT
      valueFrom:
        resourceFieldRef:
          resource: limits.cpu

说明:
POD 的 name 或 namespace 属于元数据,是在 POD 创建之前就已经定下来了的,所以使用 metadata 获取就可以了;
但是 POD 的 IP 则不一样,因为POD IP 是不固定的,POD 重建了就变了,它属于状态数据,所以使用 status 去获取。
如果不太确定,可使用 kubectl get po POD_NAME -o yaml 命令查看。

Pod运行起来后可以进入查看:

[root@master ~]# kubectl exec -it downwardapi-env -- sh
/ # echo $POD_IP
10.244.2.10
/ # echo $POD_NAME
downwardapi-env
/ # echo $CPU_LIMIT
1

Downward API的功能
环境变量和downwardAPI卷都支持传递如下信息 :

# 通过 fieldRef:
- metadata.name                     # Pod名称
- metadata.namespace                # Pod命名空间
- metadata.uid                      # Pod的uid
- metadata.labels['KEY']               # Pod标签中的某个键,如:metadata.labels['zone']
- metadata.annotations['KEY']          # Pod注解中的某个键

# 通过 resourceFieldRef :
- limits.cpu                     # 容器CPU使用上限
- requests.cpu                   # 容器请求的CPU- limits.memory                  # 容器使用memory上限
- requests.memory                # 容器请求的memory
- A Container’s ephemeral-storage limit, available since v1.8.0-beta.0        # 待研究
- A Container’s ephemeral-storage request, available since v1.8.0-beta.0      # 待研究

除了上面的信息之外:
• downwardAPI 卷通过 fieldRef 还可以传递如下信息:

  • metadata.labels # Pod所有标签
  • metadata.annotations # Pod所有注解

• 环境变量还可以传递如下信息:

  • status.podIP # Pod的IP地址
  • spec.serviceAccountName # Pod的ServiceAccount名称
  • spec.nodeName # Pod所在节点名称
  • status.hostIP # Pod所在节点的IP地址

PV/PVC

参考文档: https://www.kubernetes.org.cn/pvpvcstorageclass
https://www.cnblogs.com/rexcheny/p/10925464.html
要在一个 Pod 里声明 Volume,只要在 Pod 里加上 spec.volumes 字段,然后在这个字段里定义一个具体类型的 Volume 即可, 如:
在这里插入图片描述

可是,如果你并不知道有哪些 Volume 类型可以用,要怎么办呢? 当然,这种场景一般是针对开发人员。
开发人员可能对持久化存储项目(比如 Ceph、GlusterFS 等)一窍不通,自然不会编写它们对应的 Volume 定义文件。
所谓“术业有专攻”,这些关于 Volume 的管理和远程持久化存储的知识,不仅超越了开发者的知识储备,还会有暴露公司基础设施秘密的风险。
比如,下面这个例子,就是一个声明了 Ceph RBD 类型 Volume 的 Pod:
在这里插入图片描述

1). 一般开发不懂得 Ceph RBD 的使用方法,那么这个 Pod 里 Volumes 字段,他们十有八九完全看不懂
2). 存储服务器的地址、用户名、授权文件的位置,将被轻易地暴露给了全公司的所有开发人员,信息被“过度暴露”, 带来一定的风险

为此, Kubernetes 引入了 Persistent Volume Claim(PVC)和 Persistent Volume(PV),大大降低了用户声明和使用持久化 Volume 的门槛。

有了 PVC 之后,一个开发人员想要使用一个 Volume,只需要简单的两步即可。

  1. 定义PVC,声明想要的 Volume 的属性:
    在这里插入图片描述

在这个 PVC 对象里,不需要任何关于 Volume 细节的字段,只有描述性的属性和定义。

storageClassName: 表示需求的 PV 的类, 能够满足这个需求的 PV 也需要指定为此类
storage: 1Gi: 表示需求的 Volume 大小至少是 1 GiB;
accessModes: ReadWriteOnce: 表示这个 Volume 的挂载方式是可读写,并且只能被挂载在一个节点上而非被多个节点共享。
  1. 在应用的 Pod 中,声明使用这个 PVC:
    在这里插入图片描述

在这个 Pod 的 Volumes 定义中,只需要声明它的类型是 persistentVolumeClaim,然后指定 PVC 的名字,完全不必关心 Volume 本身的定义。
只要创建这个 PVC 对象,Kubernetes 就会自动为它绑定一个符合条件的 Volume。

这就解决了 “信息过度暴露” 的问题, 可是还有一个问题, volume 从哪来呢?
来自运维人员的 PV(Persistent Volume) 对象, 下面是一个 PV 的例子:
在这里插入图片描述

storageClassName: 指定 PV 的类, 特定类的PV只能绑定到请求该类的PVC。无此字段的PV没有类,只能绑定到不需要特定类的PVC。
capacity.storage: 指定 PV 的容量
accessModes: 指定 PV 的访问模式

我们创建这个PV和PVC:

[root@node2 ~]# kubectl apply -f pv.yaml  -f pvc.yaml

查看:
在这里插入图片描述

发现该它们已经绑定上了

Kubernetes 中 PVC 和 PV 的设计,实际上类似于“接口”和“实现”的思想。
开发者只要知道并会使用“接口”,即:PVC;而运维人员则负责给“接口”绑定具体的实现,即:PV。
这种解耦,就避免了因为向开发者暴露过多的存储系统细节而带来的隐患。
此外,这种职责的分离,往往也意味着出现事故时可以更容易定位问题和明确责任,从而避免“扯皮”现象的出现。

在使用时,我们需要先在node机器上安装好 ceph 及传递好密钥环文件, 并创建出 kube 这个存储池及 vol-2G 这个镜像, 再创建出相应的 Pod 即可

[root@node2 ~]# kubectl apply -f pvpod.yaml
[root@node2 ~]# kubectl describe po pv-pod

在这里插入图片描述

Reference

  1. 《深入剖析Kubernetes 》张磊,容器编排和Kubernetes作业管理

  2. Kubernetes官方文档

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值