【K8S学习笔记-006】Volume详解(NFS,Ceph,Secret,ConfigMap, Downward API,PV/PVC)
Volume NFS,Ceph,Secret,ConfigMap, Downward API,PV/PVC
【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
部署过程
-
准备一台 NFS 服务器(略)
-
修改 apiserver 启动参数,增加
--feature-gates=RemoveSelfLink=false
选项(1.20版本需要,其它版本未测)
-
创建并运行 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 起来即可
- 测试
运行起来后我们可以创建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 块存储的用法:
-
Ceph集群
创建image -
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 的用法:
- Ceph集群
创建MDS
创建存储池(data, metadata) - 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,只需要简单的两步即可。
- 定义PVC,声明想要的 Volume 的属性:
在这个 PVC 对象里,不需要任何关于 Volume 细节的字段,只有描述性的属性和定义。
storageClassName: 表示需求的 PV 的类, 能够满足这个需求的 PV 也需要指定为此类
storage: 1Gi: 表示需求的 Volume 大小至少是 1 GiB;
accessModes: ReadWriteOnce: 表示这个 Volume 的挂载方式是可读写,并且只能被挂载在一个节点上而非被多个节点共享。
- 在应用的 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
-
《深入剖析Kubernetes 》张磊,容器编排和Kubernetes作业管理
-
Kubernetes官方文档
更多推荐
所有评论(0)