[K8S] Phần 3 - Cài đặt Storage cho K8S dùng NFS
Lời tựa
Mình sẽ không dành thời gian giới thiệu lại về Storage trên K8S mà sẽ tập trung vào hướng dẫn cài đặt nó. Các bạn có thể tìm hiểu kỹ hơn ở đây: https://kubernetes.io/docs/concepts/storage/storage-classes/ Trong nội dung bài viết này mình sẽ hướng dẫn các bài cài đặt cấu hình storage class sử dụng NFS.
Update 2/2025
- Mình mới update lại hướng dẫn do helmchart cũ đã bị deprecated
- Update lại hướng dẫn cài NFS server trên Ubuntu (trước là Centos)
- Do lab cũ khi viết hướng dẫn này đã ko còn nên mình sẽ dựng lab mới (thông tin IP có thể thay đổi) nhưng cách làm vẫn vậy
- Bổ sung Video chi tiết: https://www.youtube.com/watch?v=649jULKPl_s
- Các file config các bạn có thể tham khảo ở repo sau: https://github.com/rockman88v/vietdevops/tree/master/platform_apps/storage
Giới thiệu
Như đã trình bày trong các phần trước, mình sẽ cài NFS-Server lên node vtq-rancher (để đỡ phải tạo một node riêng cho NFS vì node rancher tải khá thấp). Và mình cũng đã tạo sẵn phân vùng /data2 để dành cho NFS-Server rồi.
Thêm một ý nữa, về reclaim policy có 2 loại là delete và retain, hiểu đơn giản đó là cấu hình chính sách xử lý các phân vùng lưu trữ khi xóa PVC.
- "delete": Khi bạn xóa một Persistent Volume Claim (PVC) trên K8S thì hệ thống cũng tự động xóa Persistent Volume (PV) tương ứng và đồng thời hỗ trợ xóa luôn phân vùng lưu trên thiết bị lưu trữ mà gán với PV đó
- "retain": Khi bạn xóa PVC trên K8S thì phân vùng lữu trữ trên thiết bị lưu trữ sẽ không tự động bị xóa đi.
Các bước cài đặt trong bài lab này có tóm tắt lại như sau:
- Cài đặt NFS Server (trên node vtq-rancher)
- Cài đặt NFS Client (trên các Worker Node)
- Cài đặt kubect/helm3 (trên node vtq-rancher)
- Cài đặt storage class
- Tạo PVC để test
Cài đặt NFS storage cho K8S
Trong môi trường production thì một số loại storage đã hỗ trợ sẵn NFS Server, nghĩa là có thể output ra cho bạn một phân vùng share để sử dụng. Việc quản lý lỗi, quản lý tính sẵn sàng sẽ được thực hiện trên thiết bị Storage này. Nhưng nếu bạn cài đặt NFS Server để share cho K8S sử dụng thì lúc đó nó sẽ là một node chạy single sẽ được coi là điểm chết (dead-point) vì khi NFS Server này down thì gây ảnh hưởng dịch vụ. Trong phạm vi bài lab này do không có thiết bị Storage chuyên dụng do đó mình sẽ cài một NFS Server để sử dụng.
Setup
Bạn cần setup server chạy NFS và quy hoạch phân vùng ổ cứng dùng để share qua NFS. Mình sử dụng phân vùng là /data
, các bước sau sẽ tạo thư mục con từ phân vùng này.
Cài đặt NFS-Server
Việc cài đặt này khá đơn giản, đầu tiên cần tạo thư mục để share và cài NFS Server:
Cài trên NFS trên Centos:
#NFS Server installation
sudo -s
yum install nfs-utils -y
#Create shared folder
mkdir -p /data/delete
mkdir -p /data/retain
#Change folder
chmod -R 755 /data2
chown -R nfsnobody:nfsnobody /data
systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap
systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap
#stat service
systemctl restart nfs-server
Cài NFS trên Ubuntu:
#NFS Server installation
sudo -s
apt update -y
#yum install nfs-utils -y
sudo apt install nfs-kernel-server -y
#Create shared folder
mkdir -p /data/delete
mkdir -p /data/retain
#Change folder
chmod -R 755 /data
#chown -R nfsnobody:nfsnobody /data
chown -R nobody:nogroup /data
systemctl restart nfs-server
Cấu hình file /etc/exports
để share quyền cho các node theo format sau mục đích là để cho phép các node trong dải ip 192.168.10.0/24
có quyền vào 2 thư mục /data/delete
và /data/retain
:
/data/retain 192.168.10.0/24(rw,sync,no_root_squash,no_all_squash)
/data/delete 192.168.10.0/24(rw,sync,no_root_squash,no_all_squash)
Thông tin dải IP của các node cần cập nhật theo môi trường thực tế triển khai của các bạn nhé!
Restart lại NFS server để update cấu hình mới:
systemctl restart nfs-server
Kiểm tra lại xem 2 thư mục trên đã được share bằng lệnh sau,t rong đó 192.168.10.19
là IP của NFS server:
[root@vtq-rancher sysadmin]# showmount -e 192.168.10.19
Export list for 192.168.10.19:
/data/delete 192.168.10.0/24
/data/retain 192.168.10.0/24
Cài đặt NFS Client trên K8S Node
Cần phải cài đặt NFS Client trên tất cả các worker node để khi tạo Pod trên node đó có sử dụng NFS Storage Class thì node đó có thể mount được phân vùng NFS đã được share bởi NFS Server.
Cài NFS Client như sau:
#Centos
sudo yum install nfs-utils -y
#Ubuntu
sudo apt install nfs-common -y
Sau đó cũng check lại từ node này đã thấy được các folder được share chưa:
[sysadmin@vtq-master1 ~]$ showmount -e 192.168.10.19
Export list for 192.168.10.19:
/data/delete 192.168.10.0/24
/data/retain 192.168.10.0/24
Cài đặt NFS Storage Class trên K8S
Chỗ này có một chú ý là ban đầu mình đã đứng từ node vtq-cicd để cài đặt Kubernetes Cluster bằng Kubespray. Toàn bộ phần cài đặt khác cho cụm K8S mình sẽ thực hiện từ node này để quản lý phần cấu hình cài đặt dễ dàng hơn (quản lý tập trung). Mình sẽ cài đặt NFS Storage Class qua helm chart, do đó cần phải cài helm lên node này trước.
Cài đặt kubectl và helm
Update: Bản chất của việc chạy lệnh kubect/helm từ đâu cũng dc miễn là nó có cấu hình kubeconfig trỏ tới cluster của bạn, nên đừng quá máy móc ở việc bạn chạy lệnh này từ node nào nhé!
Do phiên bản Kubernetes đang cài trong series LAB này là v1.20.7 nên mình cũng sẽ cài kubectl cùng phiên bản:
curl -LO https://dl.k8s.io/release/v1.20.7/bin/linux/amd64/kubectl
curl -LO https://dl.k8s.io/release/v1.30.4/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
Update: bạn có thể cài phiên bản kubectl cụ thể tương ứng với version k8s của bạn, ví dụ mình mới cài bản 1.30.4 thì cài kubectl tương ứng:
curl -LO https://dl.k8s.io/release/v1.30.4/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
Kết quả:
sysadmin@master:~$ ./kubectl version --client
Client Version: v1.30.4
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Cấu hình kubectl để kết nối tới cụm K8S của mình:
mkdir -p $HOME/.kube
scp viettq-master1:~/.kube/config $HOME/.kube/
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Sửa file config, tham số "server: https://127.0.0.1:6443
" thành "server: https://192.168.10.11:6443
" và lưu lại. Trong đó 192.168.10.11:6443
là thông tin tới master node (hoặc external API LB của bạn)
Giờ thử kiếm tra kết nối bằng lệnh kubect get node xem đã kết nối ok hay chưa:
[sysadmin@vtq-cicd .kube]$ kubectl get node
NAME STATUS ROLES AGE VERSION
viettq-master1 Ready control-plane,master 19h v1.20.7
viettq-master2 Ready control-plane,master 19h v1.20.7
viettq-master3 Ready control-plane,master 19h v1.20.7
viettq-worker1 Ready <none> 19h v1.20.7
viettq-worker2 Ready <none> 19h v1.20.7
viettq-worker3 Ready <none> 19h v1.20.7
Update version hiện tại (ở lab mới):
sysadmin@master:~$ kubectl get node -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane 97d v1.30.4 192.168.10.11 <none> Ubuntu 24.04.1 LTS 6.8.0-51-generic containerd://1.7.21
worker1 Ready <none> 97d v1.30.4 192.168.10.12 <none> Ubuntu 24.04.1 LTS 6.8.0-51-generic containerd://1.7.21
worker2 Ready <none> 97d v1.30.4 192.168.10.13 <none> Ubuntu 24.04.1 LTS 6.8.0-51-generic containerd://1.7.21
OK như này là mượt rồi. Giờ tiêp tục cài helm thôi.
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
sudo chmod 700 get_helm.sh
./get_helm.sh
Do helm sẽ mặc định dùng chung config của kubectl nếu có, nên ở bước này không cần cấu hình gì thêm cả. Giờ chạy thử xem đã thông chưa nào:
[sysadmin@vtq-cicd k8s_tools]$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
Kết quả như trên là helm kết nối K8S ok rồi nhé!
Cài đặt NFS Storage
Tạo thư mục cài đặt để lưu helm-chart và các file config sau này:
cd /home/sysadmin/kubernetes_installation
mkdir nfs-storage
cd nfs-storage
Update 2025: Sử dụng helmchart mới là nfs-subdir-external-provisioner
Download helm chart về để cài offline:
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm pull nfs-subdir-external-provisioner/nfs-subdir-external-provisioner
tar -xzf nfs-subdir-external-provisioner-4.0.18.tgz
Trước khi cài đặt cần thay đổi tham số mặc định của helm chart này. Mình sẽ tạo 2 storage class khác nhau tương ứng với reclaim policy là delete
và retain
--> Cần 2 file value tương ứng để cài đặt 2 storage class này.
Tạo file value cho storage class có reclaim policy là "delete" và "retain":
touch values-nfs-delete.yaml
touch values-nfs-retain.yaml
Thay đổi các tham số trong file values-nfs-delete.yaml như sau:
replicaCount: 1
nfs:
server: 192.168.10.19
path: /data/delete
volumeName: nfs-subdir-external-provisioner-root
reclaimPolicy: Delete
# For creating the StorageClass automatically:
storageClass:
create: true
provisionerName: viettq-nfs-storage-delete-provisioner
defaultClass: false
name: nfs-subdir-delete
allowVolumeExpansion: true
reclaimPolicy: Delete
archiveOnDelete: false
accessModes: ReadWriteOnce
volumeBindingMode: Immediate
Thay đổi các tham số trong file values-nfs-retain.yaml như sau:
replicaCount: 1
nfs:
server: 192.168.10.19
path: /data/retain
reclaimPolicy: Retain
storageClass:
create: true
provisionerName: viettq-nfs-storage-retain-provisioner
defaultClass: true
name: nfs-subdir-retain
allowVolumeExpansion: true
reclaimPolicy: Retain
archiveOnDelete: true
accessModes: ReadWriteOnce
volumeBindingMode: Immediate
Giờ thì cài đặt 2 storage class này thôi, nhưng nhớ tạo một namespace riêng cho phần storage để dễ bề quản lý nhé:
kubectl create namespace "storage"
namespace/storage created
helm install nfs-subdir-delete -f values-nfs-delete.yaml nfs-subdir-external-provisioner
helm install nfs-subdir-retain -f values-nfs-retain.yaml nfs-subdir-external-provisioner
Kết quả cài đặt sẽ như thế này:
[sysadmin@vtq-cicd nfs-storage]$ kubectl get pods -n storage
NAME READY STATUS RESTARTS AGE
sysadmin@master:~/nfs-storage$ kubectl get pods -n storage
NAME READY STATUS RESTARTS AGE
nfs-subdir-delete-nfs-subdir-external-provisioner-b5748889lrnwh 1/1 Running 0 14s
nfs-subdir-retain-nfs-subdir-external-provisioner-8f4454f8z72zn 1/1 Running 0 10s
[sysadmin@vtq-cicd nfs-storage]$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-subdir-delete viettq-nfs-storage-delete-provisioner Delete Immediate true 29s
nfs-subdir-retain (default) viettq-nfs-storage-retain-provisioner Retain Immediate true 25s
Kiểm tra nfs-storageclass bằng cách tạo thử pvc
Giờ tạo một PVC xem thằng storageclass nó có tự động sinh ra PV cho mình không nhé!
Tạo file config cho PVC có reclaim policy là delete như sau, lưu ý tham số storageClassName: nfs-subdir-delete được gán đúng với tên storage class mình đã tạo ở bước trước bằng cách tạo file test-pod-pvc-delete.yaml
:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-pvc-delete
spec:
storageClassName: nfs-subdir-delete
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-with-pvc-delete
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: log-volume
mountPath: /var/log/nginx # Đây là nơi nginx sẽ lưu trữ log
volumes:
- name: log-volume
persistentVolumeClaim:
claimName: test-pvc-delete
Tạo PVC và Pod bằng lệnh kubectl:
[sysadmin@vtq-cicd nfs-storage]$ kubectl apply -f test-pod-pvc-delete.yaml
persistentvolumeclaim/test-pvc-delete created
pod/nginx-pod-with-pvc-delete created
sysadmin@master:~/nfs-storage$ k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-pvc-delete Bound pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951 10Mi RWO nfs-subdir-delete <unset> 8h
Rồi tới đây nếu PVC ở trạng thái Pending mãi thì là bắt đầu có vấn đề, mọi thứ đã làm đúng hết thì thằng PVC này phải được gán PV cho nó chứ, tức là phải ở trạng thái "Bound" chứ sao lại pending mãi vậy??
Update 2025: Phiên bản k8s mới mình cài là 1.30.4 đã không còn bị lỗi cũ như bên dưới nữa, nên khi test tạo PVC mà nó ở trạng thái Bound thì bạn bỏ qua phần hướng dẫn xử lý như bên dưới nhé!
Đây là một known-issue của Kubernetes phiên bản v1.20.7 này, và để khỏi mất thời gian của các bạn thì mình hướng dẫn luôn cách xử lý nhé. Thực hiện update lại file /etc/kubernetes/manifests/kube-apiserver.yaml trên tất cả các Master Node, thêm một config như sau - --feature-gates=RemoveSelfLink=false, nhìn nó sẽ kiểu ntn:
- --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key
#fix nfs-storageclass issue
- --feature-gates=RemoveSelfLink=false
image: k8s.gcr.io/kube-apiserver:v1.20.7
imagePullPolicy: IfNotPresent
Sau đó thì ngồi chơi xơi nước cho hạ hỏa trong lúc chờ thằng kube-api nó tự động restart để update config mới:
[sysadmin@vtq-master1 kubernetes]$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-657959df74-929nv 1/1 Running 0 21h
kube-system coredns-657959df74-v5fns 1/1 Running 0 21h
kube-system dns-autoscaler-b5c786945-wlnl2 1/1 Running 0 21h
kube-system kube-apiserver-viettq-master1 1/1 Running 0 88s
kube-system kube-apiserver-viettq-master2 1/1 Running 0 68s
kube-system kube-apiserver-viettq-master3 1/1 Running 0 48s
Rồi tụi nó lên lại rồi đấy. Giờ thì check lại thằng PVC đã được gán PV chưa nhé:
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-pvc-delete Bound pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951 10Mi RWO nfs-subdir-delete <unset> 8h
Tiếp tục tạo PVC sử dụng storageclass nfs-subdir-retain
xem sự khác biệt là gì nhé!
Tạo file test-pod-pvc-retain.yaml
có nội dung:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-pvc-retain
spec:
storageClassName: nfs-subdir-retain
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-with-pvc-retain
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: log-volume
mountPath: /var/log/nginx
volumes:
- name: log-volume
persistentVolumeClaim:
claimName: test-pvc-retain
Tạo PVC và Pod bằng lệnh kubectl:
$ kubectl apply -f test-pod-pvc-retain.yaml
persistentvolumeclaim/test-pvc-retain created
pod/nginx-pod-with-pvc-retain created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-pvc-delete Bound pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951 10Mi RWO nfs-subdir-delete <unset> 8h
test-pvc-retain Bound pvc-8b1de563-9869-494b-9c91-af9e6b37d8dd 10Mi RWO nfs-subdir-retain <unset> 8h
Sự khác biệt lớn nhất khi sử dụng 2 storageclass nfs-subdir-delete
và nfs-subdir-retain
đó là chúng được set RECLAIMPOLICY
khác nhau. Hiểu nôm na là khi xóa PVC thì tùy RECLAIMPOLICY mà nó sẽ xóa hay giữ lại phân dùng lưu trữ trên NFS server.
Hiện tại khi tạo ra 2 PVC như trên, ta xem kết quả phía NFS server sẽ thấy các thư mục được tạo ra như sau:
root@nfs-server:/data# tree /data/ -L 2
/data/
├── delete
│ └── storage-test-pvc-delete-pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951
└── retain
└── storage-test-pvc-retain-pvc-8b1de563-9869-494b-9c91-af9e6b37d8dd
5 directories, 0 files
Tên thư mục có thể nhận ra format là [namespace]-[pvc-name]-[pv-name]
.
Bây giờ nếu ta xóa PVC test-pvc-delete
thì thư mục storage-test-pvc-delete-pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951
trên NFS server cũng sẽ bị xóa.
Ngược lại nếu ta xóa PVC test-pvc-retain
thì thư mục storage-test-pvc-retain-pvc-8b1de563-9869-494b-9c91-af9e6b37d8dd
trên NFS server sẽ không bị xóa. Hãy cùng kiểm chứng như sau:
- Xóa 2 PVC/Pod đã tạo trước đó
$ kubectl delete -f test-pod-pvc-delete.yaml
persistentvolumeclaim "test-pvc-delete" deleted
pod "nginx-pod-with-pvc-delete" deleted
$ kubectl delete -f test-pod-pvc-retain.yaml
persistentvolumeclaim "test-pvc-retain" deleted
pod "nginx-pod-with-pvc-retain" deleted
$ kubectl get pvc
No resources found in storage namespace.
- Kiểm tra kết quả trên NFS server
root@nfs-server:/data# tree /data/ -L 2
/data/
├── delete
└── retain
└── storage-test-pvc-retain-pvc-8b1de563-9869-494b-9c91-af9e6b37d8dd
4 directories, 0 files
Rõ ràng ta thấy thư mục storage-test-pvc-delete-pvc-6c862ec3-9b98-4d29-8fb9-90604b3d4951
đã bị xóa còn thư mục storage-test-pvc-retain-pvc-8b1de563-9869-494b-9c91-af9e6b37d8dd
thì vẫn còn trên NFS.
Như vậy qua bài viết này các bạn có thể tự setup một NFS server để cung cấp storage cho các ứng dụng cài đặt trên K8S mà cần sử dụng PersistentVolume rồi. Chúc các bạn thành công!
Cài đặt thằng nfs storage cho Kubernetes Cluster tưởng đơn giản nhưng cũng mất nhiều bước. Trong bài sau mình sẽ tiếp tục hướng dẫn cài đặt longhorn storage cho K8S. Các bạn chú ý theo dõi nhé! Thank you!
All rights reserved