《OpenShift 4.x HOL教程汇总》
说明:本文已经在OpenShift 4.10环境中验证
文章目录
Kubernetes 的 emptyDir、hostPath、local 类型 PV 使用的都是 Node 节点的存储,但是功能和使用场景不同。
- emptyDir 类型的 PV 是临时性的,因为 Pod 一旦重建,则原有的内容则被清空。
- hostPath 类型的 PV 锁定到 Node 节点的本地存储,因此不适合集群中有多个 Node 节点的情况。因为重建的 Pod 可能会调度到其它 Node 节点上,导致无法访问存储。
- local 类型的 PV 类似 hostPath 类型 PV,但是它具备亲和性,比较适合在集群中为容器化数据库提供存储。
emptyDir 类型 pv
emptyDir 类型的 PV 是在 Pod 内部,可以在一个 Pod 中的多个 Container 之间共享存储。emptyDir 类型的 PV 在 pod 分配到Node 上时被创建,Kubernetes 会在 Node 上自动分配一个目录,无需指定宿主机 Node 上对应的目录文件。 这个目录的初始内容为空,当 Pod 从 Node 上移除时,emptyDir 中的数据会被永久删除。
- 执行以下命令,运行 2 个使用了 emptydir 的 Pod,其中每个 Pod 中运行了 2 个 Container,而 2 个 Container 分别在 /tmp/demo1 和 /tmp/demo2 挂载了 emptydir 类型的 PV。
$ cat << EOF | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-emptydir
labels:
app: demo-emptydir
spec:
replicas: 2
selector:
matchLabels:
app: demo-emptydir
template:
metadata:
labels:
app: demo-emptydir
spec:
containers:
- name: demo-1
image: quay.io/rhdevelopers/myboot:v4
volumeMounts:
- mountPath: /tmp/demo1
name: emptydir-volume
- name: demo-2
image: quay.io/rhdevelopers/myboot:v4
env:
- name: SERVER_PORT
value: "8090"
volumeMounts:
- mountPath: /tmp/demo2
name: emptydir-volume
volumes:
- name: emptydir-volume
emptyDir: {}
EOF
- 查看有 2 个 Pod 已经运行。
$ oc get pod -l app=demo-emptydir
NAME READY STATUS RESTARTS AGE
demo-emptydir-7bcf66487c-r77pz 2/2 Running 0 7s
demo-emptydir-7bcf66487c-zrs2x 2/2 Running 0 7s
- 执行命令,进入第 1 个 Pod 中名为 demo-1 的 Container。
$ oc rsh -c demo-1 pod/demo-emptydir-7bcf66487c-r77pz
- 执行以下命令,在 demo1 容器中的 /tmp/demo1 目录写入文件 hello,然后从 demo1 容器中退出来。
$ echo hello > /tmp/demo1/hello
$ cat /tmp/demo1/hello
$ exit
- 执行命令,进入第 1 个 Pod 中名为 demo-2 的 Container。确认有 /tmp/demo2/hello 文件,且文件内容是上一步创建的内容。
$ oc rsh -c demo-2 pod/demo-emptydir-7bcf66487c-r77pz
$ cat /tmp/demo2/hello
$ exit
- 针对第 2 个 Pod 进入其中的 2 个 Container,然后确认在 /tmp/demo1 和 /tmp/demo2 中没有名为 hello 的文件。
hostPath 类型 pv
一个 hostPath 类型的 PV 将从主机节点的文件或目录挂载到 pod,因此 hostPath 类型的 PV 只能跨多个 Pod 而不能跨主机访问。hostPath PV 通常只在单节点集群中使用,因为如果 Pod 被调度到其他节点后,它将访问不到原有节点的 hostPath 了。如果要在多节点集群中使用,一定要配合 NodeSelector 部署运行 Pod。此外,hostPath 类型 pv 必须通过静态方式声明,而不能用 PVC 方式动态创建。
示例1
本示例使用手动创建的 PV/PVC 访问主机节点 hostpath。
- 查找当前状态为 Available 的 PV,然后自行执行命令删除他们。
$ oc get pv pv-mysql
- 查看当前集群中只有一个 Node 节点。
$ oc get node
NAME STATUS ROLES AGE VERSION
crc-dzk9v-master-0 Ready master,worker 88d v1.22.0-rc.0+894a78b
$ NODE_NAME=crc-dzk9v-master-0
- 进入 OpenShift 集群中的 “${NODE_NAME}” 节点,先创建目录 /mnt/pv-data/mysql-data,然后修改其访问权限,最后退出 Node 节点。
$ oc debug node/${NODE_NAME}
# chroot /host
# mkdir -p /mnt/pv-data/mysql-data
# chmod -R 770 /mnt/pv-data/mysql-data
# exit
- 执行命令,基于主机节点上的 /mnt/pv-data/mysql-data 创建 PV。
$ cat << EOF | oc apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-mysql
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
- ReadOnlyMany
persistentVolumeReclaimPolicy: Recycle
volumeMode: Filesystem
hostPath:
path: "/mnt/pv-data/mysql-data"
type: ""
EOF
- 执行命令,在 mysql 项目中根据 mysql-persistent 模板创建应用。
$ oc new-project mysql
$ oc new-app --template=mysql-persistent
$ oc get pod mysql
NAME READY STATUS RESTARTS AGE
mysql-1-5j4vh 1/1 Running 0 30s
mysql-1-deploy 0/1 Completed 0 37s
- 进入运行 mysql 的 Pod,然后查看 “/var/lib/mysql/data/” 下的文件,然后退出 Pod。
$ oc rsh dc/mysql
sh-4.4$ ls -al /var/lib/mysql/data/
sh-4.4$ exit
- 参照以上(3)的操作再次进入 Node 节点,然后查看 “/mnt/pv-data/mysql-data” 目录下的文件,确认和(6)看到的完全一样。
$ oc debug node/${NODE_NAME}
# chroot /host
# ls -al /mnt/pv-data/mysql-data
示例2
本示例在 Pod 中直接使用主机节点的 hostpath,而不是通过 PV/PVC 访问主机节点的 hostpath,因此此方法无需指定存储大小,设置访问存储安全特性。
- 执行命令创建一个 Pod,它将节点的 /mnt/my-hostpath 目录挂到 Pod 的 /tmp/demo 目录。其中容器使用了 “privileged: true” 特权后才能在 Pod 中访问主机 hostpath 中的内容。
$ cat << EOF | oc apply -f -
apiVersion: v1
kind: Pod
metadata:
name: my-hostpath
spec:
containers:
- image: quay.io/rhdevelopers/myboot:v4
name: test-container
securityContext:
privileged: true
volumeMounts:
- mountPath: /tmp/demo
name: hostpath-pv
volumes:
- name: hostpath-pv
hostPath:
path: /mnt/my-hostpath
EOF
注意:如果要使用非集群管理员执行以上命令,例如 developer,则需要先使用集群管理员给 developer 赋予名为 privileged 的 SCC 安全上下文。
$ oc adm policy add-scc-to-user privileged developer
- 确认此时在节点的 /mnt/my-hostpath 目录中没有文件。
$ oc debug node/${NODE_NAME}
# chroot /host
# ls -al /mnt/my-hostpath
- 执行命令,确认已经在 Pod 的 /tmp/demo 目录中生成 greeting.txt 文件。
$ oc rsh pod/my-hostpath curl localhost:8080/appendgreetingfile
$ oc rsh pod/my-hostpath ls -al /tmp/demo
total 4
drwxr-xr-x. 2 root root 26 Sep 17 14:21 .
drwxrwxrwt. 1 root root 126 Sep 17 14:07 ..
-rw-r--r--. 1 root root 5 Sep 17 14:21 greeting.txt
- 确认此时在节点的 /mnt/my-hostpath 目录中已经有 greeting.txt 文件了。
$ oc debug node/${NODE_NAME}
# chroot /host
# ls -al /mnt/my-hostpath
# exit
- 确认示例 2 不会创建新的 PV 和 PVC,这说明可以在 Pod 中通过 hostpath 直接使用节点的存储。
$ oc get pv
$ oc get pvc
local 类型 pv
local pv 虽然使用的也是节点本地存储,但它可以解决 hostPath 类型 PV 必须手动使用 NodeSelector 将 Pod 调度到特定主机节点的缺陷。
local pv 可以无需手动调度 pod 到节点的情况下使用,这是因为 Kubernetes 的 PV Controller 和 Scheduler 会对 local PV 做特殊的逻辑处理,以实现 Pod 使用本地存储时发生 re-schedule 的情况下能再次调度到 local volume 所在的节点。
local pv 使用的节点本地存储,通常可以利用本地的 SSD 高速存储来提升数据访问性能,例如分布式存储,这时单节点故障不会造成数据丢失。另外在需要频繁访问存储的 AI 数据训练场景中也可以使用 local pv。
Kubernetes 和 OpenShift 只能静态创建 local pv 。
方法1:不使用 Local Storage Operator
创建 PV
- 查看 OpenShift 集群中的节点。
$ oc get node
NAME STATUS ROLES AGE VERSION
crc-dzk9v-master-0 Ready master,worker 88d v1.22.0-rc.0+894a78b
$ NODE_NAME=crc-dzk9v-master-0
- 进入 OpenShift 集群中的 “${NODE_NAME}” 节点,然后创建在后面步骤的 my-local-pv 对象中用到的 “path” 目录。最后退出集群节点。
$ oc debug node/${NODE_NAME}
# chroot /host
# DIR_NAME="vol1"
# mkdir -p /mnt/disk/${DIR_NAME}
# chcon -Rt svirt_sandbox_file_t /mnt/disk/${DIR_NAME}
# chmod 777 /mnt/disk/${DIR_NAME}
# exit
- 执行命令,在集群中创建一个 StorageClass。
$ cat << EOF | oc apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: my-local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
EOF
- 执行命令,基于上面创建的 StorageClass 对象在 $NODE_NAME 节点上创建一个 “local” 类型的 PersistentVolume。
注意:
“/mnt/disk/vol1” 是以上步骤创建的 $NODE_NAME 节点本地目录。
“nodeAffinity” 保证了 “local” 类型的 PersistentVolume 和指定节点的亲和性。
$ cat << EOF | oc apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-local-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: my-local-storage
local:
path: /mnt/disk/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- ${NODE_NAME}
EOF
$ oc get pv my-local-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-local-pv 5Gi RWO Retain Available my-local-storage 44s
使用 local pv
- 执行命令,在测试项目中通过 “my-local-storage” 的 storageClassName 申请使用 Local pv。
$ oc new-project local-pv-test
$ cat << EOF | oc apply -f -
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-local-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: my-local-storage
resources:
requests:
storage: 5Gi
EOF
- 使用 my-local-pvc 运行 Pod。
$ cat << EOF | oc apply -f -
apiVersion: v1
kind: Pod
metadata:
name: www
labels:
name: www
spec:
containers:
- name: www
image: nginx:alpine
ports:
- containerPort: 80
name: www
volumeMounts:
- name: www-persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: www-persistent-storage
persistentVolumeClaim:
claimName: my-local-pvc
EOF
- 查看运行的测试 Pod, 记录 Pod 的 IP。
$ oc get pod www -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
www 1/1 Running 0 32s 10.217.0.82 crc-dzk9v-master-0 <none> <none>
- 确认 “my-local-pv” 已经中已经有 “my-local-pvc”。
$ oc get pv my-local-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-local-pv 5Gi RWO Retain Bound local-pv-test/my-local-pvc my-local-storage 3m33s
- 再次进入 “NODE_NAME”。先向该 Node 节点的 “** /mnt/disk/vol1/**” 目录写入文件,然后访问测试 Pod 的 IP 地址,确认返回结果。
$ oc debug node/${NODE_NAME}
# chroot /host
# echo "Hello local persistent volume" > /mnt/disk/vol1/index.html
# url 10.217.0.82
Hello local persistent volume
方法2:使用 Local Storage Operator
为了能使用 Local Storage Operator 配置 Local Volume,需要有一个满足以下条件的本地磁盘:它附加到一个节点,并且尚未挂载,同时不包含分区。 Local Storage Operator 安装和 Local Volume 配置方法可参见:
https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.10/html/storage/persistent-storage-using-local-volume
https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.10/html/storage/local-volume-cr_persistent-storage-local
参考
https://redhat-scholars.github.io/kubernetes-tutorial/kubernetes-tutorial/volumes-persistentvolumes.html
https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.10/html/storage/persistent-storage-hostpath-pod_persistent-storage-hostpath
https://vocon-it.com/2018/12/20/kubernetes-local-persistent-volumes/
https://speakerdeck.com/msau42/2018-kubecon-eu-kubernetes-local-persistent-volumes-in-production
https://bswen.com/2020/12/others-local-vs-hostpath-k8s.html
https://cloud.tencent.com/developer/article/1195068
https://itnext.io/local-storage-for-openshift-121224daf2d9