[TOC]
`pod` 中的文件在磁盘上是临时存放的,这给容器中运行的较重要的应用程序带来一些问题。
1、当容器崩溃时文件丢失。kubelet 会重新启动容器, 但容器会以干净的状态重启。
2、在同一个 `pod` 中运行多个容器并需要共享文件。
> kubernetes解决上面的方法,提出volumes的抽象概念来解决。
`Kubernetes` 支持很多类型的卷。 `Pod` 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 `Pod` 相同,但持久卷可以比 `Pod` 的存活期长。 当 `Pod` 不再存在时,`Kubernetes` 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。对于给定 `Pod` 中任何类型的卷,在容器重启期间数据都不会丢失。
该文章围绕kubernetes如何使用NFS存储。
> 注意:应该每个k8s节点都安装nfs客户端。
> CentOS发行版:`nfs-utils`,验证 `rpm -qa nfs-utils`
## 安装nfs
1. 下载 `nfs-utils`
```shell
$ yum install nfs-utils -y
```
2. 修改配置文件
```shll
$ cat > /etc/exports <<-EOF
/data/nfs 192.168.200.0/24(rw,root_squash,all_squash,sync)
EOF
$ mkdir -p /data/nfs
$ chown nfsnobody.nfsnobody /data/nfs
```
> **参数说明:**
> - rw: 读写
> - root_squash:客户端使用root用户映射成nobody用户
> - all_squash:客户端使用普通用户映射成nobody用户
> - sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性;
3. 启动nfs
```shell
$ systemctl start nfs
```
如果有开防火墙的话,请将 `nfs` 的相关端口放通。通过 `rpcinfo -p` 查看当前使用的端口。
> 启动NFS会开启如下端口:
1)portmapper 端口:111 udp/tcp;
2)nfs/nfs_acl 端口:2049 udp/tcp;
3)mountd 端口:"32768--65535" udp/tcp
4)nlockmgr 端口:"32768--65535" udp/tcp
固定上面的随机端口
```shell
$ cat >> /etc/sysconfig/nfs <<-EOF
RQUOTAD_PORT=4001
LOCKD_TCPPORT=4002
LOCKD_UDPPORT=4002
MOUNTD_PORT=4003
STATD_PORT=4004
EOF
$ systemctl restart nfs
```
放通 `iptables` 规则
```shell
iptables -I INPUT -p tcp -m multiport --dports 111,2049,4001,4002,4003,4004 -m comment --comment "nfs tcp ports" -j ACCEPT
iptables -I INPUT -p udp -m multiport --dports 111,2049,4001,4002,4003,4004 -m comment --comment "nfs udp ports" -j ACCEPT
```
## volumes
`volumes` 的核心是一个目录,其中可能存有数据,`Pod` 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放 的内容。
使用卷时, 在 `.spec.volumes` 字段中设置为 `Pod` 提供的卷,并在 `.spec.containers[*].volumeMounts` 字段中声明卷在容器中的挂载位置。 容器中的进程看到的是由它们的 Docker 镜像和卷组成的文件系统视图。 Docker镜像位于文件系统层次结构的根部。各个卷则挂载在镜像内的指定路径上。 卷不能挂载到其他卷之上,也不能与其他卷有硬链接。 `Pod` 配置中的每个容器必须独立指定各个卷的挂载位置。
1.部署以下模板,创建pod
```yaml
apiVersion: v1
kind: Pod
metadata:
name: test-volume
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28.1
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- sleep 3600
volumeMounts:
- name: nfs
mountPath: /nfs
volumes:
- name: nfs
nfs:
server: 192.168.31.136
path: /data/nfs
```
2.部署pod
```shell
$ kubectl apply -f test-volume.yml
pod/test-volume created
```
## Persistent Volume
存储的管理是一个与计算实例的管理完全不同的问题。`PersistentVolume` 子系统为用户 和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:`PersistentVolume` 和 `PersistentVolumeClaim`。
持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者 使用存储类(Storage Class)来动态供应。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 `Volume` 一样,也是使用 卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 `NFS、iSCSI` 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上与 `Pod` 类似。 `Pod` 会耗用节点资源,而 `PVC` 申领会耗用 `PV` 资源。`Pod` 可以请求特定数量的资源(CPU 和内存);同样 `PVC` 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 `ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany` 模式之一来挂载,参见访问模式)。
尽管 `PersistentVolumeClaim` 允许用户消耗抽象的存储资源,常见的情况是针对不同的 问题用户需要的是具有不同属性(如,性能)的 `PersistentVolume` 卷。 集群管理员需要能够提供不同性质的 `PersistentVolume`,并且这些 PV 卷之间的差别不 仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了 存储类(StorageClass) 资源。
PV 卷是集群中的资源。PVC 申领是对这些资源的请求,也被用来执行对资源的申领检查。PV 卷的供应有两种方式:静态供应或动态供应。
### 静态供应
1.PersistentVolume 对象
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv001
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /data/nfs
server: 192.168.31.136
```
2.创建pv
```shell
$ kubectl apply -f pv.yml
persistentvolume/nfs-pv001 created
```
3.查看pv状态
```shell
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv001 5Gi RWX Recycle Available nfs 23s
```
> 每个卷会处于以下阶段(Phase)之一:
> Available(可用)-- 卷是一个空闲资源,尚未绑定到任何申领;
> Bound(已绑定)-- 该卷已经绑定到pvc;
> Released(已释放)-- 所绑定的pvc已被删除,但是资源尚未被集群回收;
> Failed(失败)-- 卷的自动回收操作失败。
4.PersistentVolumeClaims对象
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-001
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 3Gi
storageClassName: nfs
```
> 注意:
> 1、`accessModes` 和 `storageClassName` 与 `PersistentVolume` 资源清单必须一致。
> 2、 `resources.requests.storage` 小于 `PersistentVolume` 资源清单的 `capacity.storage`
5.创建PVC
```shell
$ kubectl apply -f pvc.yml
persistentvolumeclaim/pvc-001 created
```
6.查看pvc状态
```shell
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-001 Bound nfs-pv001 5Gi RWX nfs 3m
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv001 5Gi RWX Recycle Bound default/pvc-001 nfs 14m
```
7.pod使用pvc资源
```yaml
apiVersion: v1
kind: Pod
metadata:
name: test-volume
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28.1
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- sleep 3600
volumeMounts:
- name: nfs
mountPath: /nfs
volumes:
- name: nfs
nfs:
server: 192.168.31.136
path: /data/nfs
```
8.创建pod
```shell
$ kubectl apply -f test-pvc.yml
pod/test-pvc created
```
9.查看pod
```shell
$ kubectl get pod test-volume
NAME READY STATUS RESTARTS AGE
test-volume 1/1 Running 1 70m
$ kubectl exec test-volume -- df -h
Filesystem Size Used Available Use% Mounted on
overlay 40.0G 9.9G 30.0G 25% /
192.168.31.136:/data/nfs
40.0G 21.2G 18.8G 53% /nfs
/dev/sda3 40.0G 9.9G 30.0G 25% /dev/termination-log
/dev/sda3 40.0G 9.9G 30.0G 25% /etc/localtime
/dev/sda3 40.0G 9.9G 30.0G 25% /etc/resolv.conf
/dev/sda3 40.0G 9.9G 30.0G 25% /etc/hostname
/dev/sda3 40.0G 9.9G 30.0G 25% /etc/hosts
...
```
### 动态供应(storageclasses)
`StorageClass` 为管理员提供了描述存储 "类" 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。
每个 `StorageClass` 都有一个提供商(Provisioner),用来决定使用哪个卷插件创建PV。 这个使用的 nfs 存储介质。所以安装 nfs-Provisioner 。
#### 安装nfs-provisioner
1.rbac权限
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-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-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io
```
2.deployment清单
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-provisioner
labels:
app: nfs-provisioner
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-provisioner
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccountName: nfs-provisioner
containers:
- name: nfs-provisioner
image: jiaxzeng/nfs-subdir-external-provisioner:v4.0.2
env:
# 可以自定义名称
- name: PROVISIONER_NAME
value: k8s/nfs-provisioner
- name: NFS_SERVER
value: 192.168.31.136
- name: NFS_PATH
value: /data/nfs
volumeMounts:
- name: nfs-provisioner-root
mountPath: /persistentvolumes
volumes:
- name: nfs-provisioner-root
nfs:
server: 192.168.31.136
path: /data/nfs
```
3.创建nfs-provisioner
```shell
$ kubectl apply -f rbac.yaml
serviceaccount/nfs-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created
$ kubectl apply -f deploy.yml
deployment.apps/nfs-provisioner created
```
#### 创建StorageClass
1.StorageClass清单
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: k8s/nfs-provisioner # must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "false"
```
2.创建StorageClass
```shell
$ kubectl apply -f class.yml
storageclass.storage.k8s.io/managed-nfs-storage created
```
#### 测试
1.创建pvc清单
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
spec:
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1024Mi
```
2.创建pod清单
```yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox:1.28.1
command:
- "/bin/sh"
args:
- "-c"
- "sleep 3600"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim
```
3.创建pvc和deployment
```shell
$ kubectl apply -f test-claim.yaml
persistentvolumeclaim/test-claim created
$ kubectl apply -f test-pod.yml
pod/test-pod created
```
4.查看pv和pvc状态
```shell
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-470a0959-0313-4d4a-8e1c-3543fa79e737 1Gi RWX Delete Bound default/test-claim managed-nfs-storage 84s
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim Bound pvc-470a0959-0313-4d4a-8e1c-3543fa79e737 1Gi RWX managed-nfs-storage 96s
```
5.查看pod挂载情况
```shell
$ kubectl exec test-pod -- df -h
Filesystem Size Used Available Use% Mounted on
overlay 40.0G 10.8G 29.2G 27% /
192.168.31.136:/data/nfs/default-test-claim-pvc-470a0959-0313-4d4a-8e1c-3543fa79e737
40.0G 10.1G 29.9G 25% /mnt
/dev/sda3 40.0G 10.8G 29.2G 27% /dev/termination-log
/dev/sda3 40.0G 10.8G 29.2G 27% /etc/localtime
/dev/sda3 40.0G 10.8G 29.2G 27% /etc/resolv.conf
/dev/sda3 40.0G 10.8G 29.2G 27% /etc/hostname
/dev/sda3 40.0G 10.8G 29.2G 27% /etc/hosts
```
如上述截图,pod上挂载点有 `/mnt` 的话,那就没问题。大功告成
## 参考文档
> volume的模式:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#access-modes
> 安装nfs-provisioner:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
- 前言
- 架构
- 部署
- kubeadm部署
- kubeadm扩容节点
- 二进制安装基础组件
- 添加master节点
- 添加工作节点
- 选装插件安装
- Kubernetes使用
- k8s与dockerfile启动参数
- hostPort与hostNetwork异同
- 应用上下线最佳实践
- 进入容器命名空间
- 主机与pod之间拷贝
- events排序问题
- k8s会话保持
- 容器root特权
- CNI插件
- calico
- calicoctl安装
- calico网络通信
- calico更改pod地址范围
- 新增节点网卡名不一致
- 修改calico模式
- calico数据存储迁移
- 启用 kubectl 来管理 Calico
- calico卸载
- cilium
- cilium架构
- cilium/hubble安装
- cilium网络路由
- IP地址管理(IPAM)
- Cilium替换KubeProxy
- NodePort运行DSR模式
- IP地址伪装
- ingress使用
- nginx-ingress
- ingress安装
- ingress高可用
- helm方式安装
- 基本使用
- Rewrite配置
- tls安全路由
- ingress发布管理
- 代理k8s集群外的web应用
- ingress自定义日志
- ingress记录真实IP地址
- 自定义参数
- traefik-ingress
- traefik名词概念
- traefik安装
- traefik初次使用
- traefik路由(IngressRoute)
- traefik中间件(middlewares)
- traefik记录真实IP地址
- cert-manager
- 安装教程
- 颁布者CA
- 创建证书
- 外部存储
- 对接NFS
- 对接ceph-rbd
- 对接cephfs
- 监控平台
- Prometheus
- Prometheus安装
- grafana安装
- Prometheus配置文件
- node_exporter安装
- kube-state-metrics安装
- Prometheus黑盒监控
- Prometheus告警
- grafana仪表盘设置
- 常用监控配置文件
- thanos
- Prometheus
- Sidecar组件
- Store Gateway组件
- Querier组件
- Compactor组件
- Prometheus监控项
- grafana
- Querier对接grafana
- alertmanager
- Prometheus对接alertmanager
- 日志中心
- filebeat安装
- kafka安装
- logstash安装
- elasticsearch安装
- elasticsearch索引生命周期管理
- kibana安装
- event事件收集
- 资源预留
- 节点资源预留
- imagefs与nodefs验证
- 资源预留 vs 驱逐 vs OOM
- scheduler调度原理
- Helm
- Helm安装
- Helm基本使用
- 安全
- apiserver审计日志
- RBAC鉴权
- namespace资源限制
- 加密Secret数据
- 服务网格
- 备份恢复
- Velero安装
- 备份与恢复
- 常用维护操作
- container runtime
- 拉取私有仓库镜像配置
- 拉取公网镜像加速配置
- runtime网络代理
- overlay2目录占用过大
- 更改Docker的数据目录
- Harbor
- 重置Harbor密码
- 问题处理
- 关闭或开启Harbor的认证
- 固定harbor的IP地址范围
- ETCD
- ETCD扩缩容
- ETCD常用命令
- ETCD数据空间压缩清理
- ingress
- ingress-nginx header配置
- kubernetes
- 验证yaml合法性
- 切换KubeProxy模式
- 容器解析域名
- 删除节点
- 修改镜像仓库
- 修改node名称
- 升级k8s集群
- 切换容器运行时
- apiserver接口
- 其他
- 升级内核
- k8s组件性能分析
- ETCD
- calico
- calico健康检查失败
- Harbor
- harbor同步失败
- Kubernetes
- 资源Terminating状态
- 启动容器报错