+60

[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

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.

image.png

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/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 policydeleteretain --> 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-deletenfs-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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí