[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狀態
- 啟動容器報錯