Skip to content

antony@notes:~/openshift$ cat "在-Openshift-上建立應用並驗證掛載-NFS-Storage.md"

在 Openshift 上建立應用並驗證掛載 NFS Storage

2025-07-03· openshift

在 Openshift 上建立應用並驗證掛載 NFS Storage

請在 NFS Server 的主機中執行 Step 1~4 的操作命令,Step 6 請在能夠執行 oc 命令管理 OpenShift 的主機操作

本篇文章的 NFS Server 裝在 RHEL 9.6 上

:::warning

:::spoiler {state=“open”} 目錄

[TOC]

:::

Step1: 安裝 NFS Server

sudo dnf install -y nfs-utils

Step2: 設定儲存空間

# 1. 在 VM 新增一顆硬碟專門給 NFS Server 使用

# 2. 檢查新的硬碟名稱
$ lsblk
...
NAME                MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda                   8:16   0  70G  0 disk

# 3. 將硬碟名稱設為變數
$ D="/dev/sda"

# 4. 使用 parted 建立 GPT 分割區,從 1 MiB 到磁碟尾,並標記為 LVM 用途
$ sudo parted -s ${D} mklabel gpt
$ sudo parted -s ${D} unit MiB mkpart primary 1 100%

# 5. 將剛建立的分割區初始化為 LVM PV
$ sudo pvcreate ${D}1

# 6. 建立名為 vg-nfs 的 VG
$ sudo vgcreate vg-nfs ${D}1

# 7. 從 VG 中建立一 LV,利用全部剩餘空間並命名為 lv-nfs
$ sudo lvcreate -l +100%FREE -n lv-nfs /dev/vg-nfs

# 8. 格式化 LV 為 XFS 檔案系統,以提供穩健且高效的儲存
$ sudo mkfs.xfs /dev/vg-nfs/lv-nfs

# 9. 建立 NFS Server 要共享給 Client 的目錄
$ sudo mkdir -p /data/nfs

# 10. 修改目錄權限
$ sudo chown -R 65534:65534 /data/nfs

# 11. 將目錄設為開機自動掛載 lv 裝置上
$ echo '/dev/vg-nfs/lv-nfs      /data/nfs               xfs     defaults        0 0' | sudo tee -a /etc/fstab

# 12. 將目錄掛載到 lv 裝置上
$ sudo mount -a && sudo systemctl daemon-reload

# 13. 設定共享目錄
$ echo '/data/nfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)' | sudo tee /etc/exports

# 14. 設定 NFS Server 使用 4.2 版本,以匯出分享 SELinux context。無適合的版本 client 端掛載時會出現 mount.nfs: Protocol not supported 的訊息。
$ echo  'RPCNFSDARGS="-V 4.2"' | sudo tee /etc/sysconfig/nfs

# 15. 查看目前啟動的 nfs 版本,因 server 指定使用 4.2,若出現 -4.2 表示 nfs server 沒有成功啟動。
$ sudo cat /proc/fs/nfsd/versions
+3 +4 +4.1 +4.2

Step3: 啟動 NFS Server

sudo systemctl enable --now nfs-server
sudo exportfs -arv

Step4: 設定 NFS Server 使用 fsid

# 1. 先手動 resizze vm 硬碟的儲存空間

# 2. 使用 parted 將第 1 區擴展到磁碟剩餘最大空間(自動確認、低互動)
$ sudo parted -s -a optimal /dev/sda "resizepart 1 100%"

# 3. 設定 LVM 更新 PV 已可使用空間
$ sudo pvresize /dev/sda1

# 4. 檢查目前 PV 與剩餘空間狀態
$ sudo pvs
  PV         VG     Fmt  Attr PSize   PFree
  /dev/sda1  vg-nfs lvm2 a--  <80.00g 10.00g
  /dev/sdb2  rhel   lvm2 a--  <69.00g    0

# 5. 在 vg-nfs 卷組中創建新的 5G 的 LV lv-share1
$ sudo lvcreate -L 5G -n lv-share1 /dev/vg-nfs

# 6. 顯示所有 LV 與大小資訊
$ sudo lvs
  LV        VG     Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root      rhel   -wi-ao---- <69.00g
  lv-nfs    vg-nfs -wi-ao---- <70.00g
  lv-share1 vg-nfs -wi-ao----   5.00g

# 7. 在新的 LV 上建立 XFS 檔案系統
$ sudo mkfs.xfs /dev/vg-nfs/lv-share1

# 8. 建立掛載點資料夾
$ sudo mkdir -p /data/nfs/share1

# 9. 設定資料夾所有者為 nobody:nobody (UID/GID 65534),通常對應 NFS anonymous user
$ sudo chown -R 65534:65534 /data/nfs/share1

# 10. 將新 LV 自動掛載設定加入 fstab
$ echo '/dev/vg-nfs/lv-share1   /data/nfs/share1        xfs     defaults        0 0' | sudo tee -a /etc/fstab

# 11. 掛載全部 fstab 條目,並重載 systemd daemon
$ sudo mount -a; sudo systemctl daemon-reload

# 12. 驗證 /dev/sda 與 LVM 的結構
$ lsblk /dev/sda
NAME                   MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda                      8:0    0  80G  0 disk
└─sda1                   8:1    0  80G  0 part
  ├─vg--nfs-lv--nfs    253:1    0  70G  0 lvm  /data/nfs
  └─vg--nfs-lv--share1 253:2    0   5G  0 lvm  /data/nfs/share1

# 13. 編輯 /etc/exports,NFSv4 範例設定,使用 fsid 唯一識別
$ sudo nano /etc/exports
/data/nfs       192.168.1.0/24(rw,fsid=0,sync,no_subtree_check,no_root_squash)
/data/nfs/share1 192.168.1.0/24(rw,fsid=1,sync,no_subtree_check,no_root_squash)

# 14. 重載 NFS server 配置並立即生效 export
$ sudo systemctl reload nfs-server
$ sudo exportfs -arv
exporting 192.168.1.0/24:/data/nfs/share1
exporting 192.168.1.0/24:/data/nfs

Step5: 在其他機器測試掛載

$ mkdir ~/test

$ sudo mount -t nfs -o hard,nfsvers=4.2 192.168.1.90:/ ~/test/

$ ls -l ~/test/
total 0
drwxr-xr-x. 2 nfsnobody nfsnobody 6 Jun 18 07:35 share1

$ sudo umount ~/test

Step6: 在 OCP 中建應用測試掛載

啟用 NFS Client Provisioner

# 1. 使用 NFS Client provisioner 建立動態供應儲存的 template,並指定 NFS Server 和共享目錄位置
$ oc process -f  https://raw.githubusercontent.com/openshift-examples/external-storage-nfs-client/main/openshift-template-nfs-client-provisioner.yaml \
  -p NFS_SERVER=192.168.1.90 \
  -p NFS_PATH=/  | oc apply -f -

# 2. 建立本地目錄以儲存 StorageClass 等 YAML 定義
$ mkdir ~/test-mssql

# 3. 建立一個新的 StorageClass,使用 pathPattern 區分命名空間與自訂儲存目錄路徑
$ cat <<EOF | tee ~/test-mssql/sc-nfs-share1.yaml | oc apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage-share1
provisioner: redhat-emea-ssa-team/hetzner-ocp4
parameters:
  archiveOnDelete: "false"
  pathPattern: "share1/\${.PVC.namespace}/\${.PVC.annotations.nfs.io/storage-path}"
EOF

# 4. 確認新的 StorageClass 已建立
$ oc get sc
NAME                            PROVISIONER                         RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage (default)   redhat-emea-ssa-team/hetzner-ocp4   Delete          Immediate           false                  30m
managed-nfs-storage-share1      redhat-emea-ssa-team/hetzner-ocp4   Delete          Immediate           false                  3h24m

# 5. 建立一個新的專案用來部署 MSSQL Server
$ oc new-project mssql

# 6. 建立密碼的 secret 給 MSSQL Server 的使用者
$ oc create secret generic mssql --from-literal=SA_PASSWORD="1qaz@WSX"

# 7. 建立 PersistentVolumeClaim,並使用剛建立的 NFS StorageClass
$ cat <<EOF | tee ~/test-mssql/mssql-pvc.yaml | oc apply -f -
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mssql-data
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: managed-nfs-storage-share1
EOF

# 8. 驗證 PVC 是否成功綁定 PV
$ oc get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                 VOLUMEATTRIBUTESCLASS   AGE
mssql-data   Bound    pvc-e00bad5b-9a86-4344-94d3-49c7bde4b8e1   8Gi        RWO            managed-nfs-storage-share1   <unset>                 52s

# 9. 建立 MSSQL Server 的 Deployment 與 Service,並將資料掛載到 NFS PVC
$ cat <<EOF | tee ~/test-mssql/mssql-deployment.yaml | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mssql-deployment
spec:
  selector:
    matchLabels:
      app: mssql
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mssql
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mssql
        image: mcr.microsoft.com/mssql/server:2017-CU8-ubuntu
        ports:
        - containerPort: 1433
        env:
        - name: MSSQL_PID
          value: "Developer"
        - name: ACCEPT_EULA
          value: "Y"
        - name: SA_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mssql
              key: SA_PASSWORD
        volumeMounts:
        - name: mssqldb
          mountPath: /var/opt/mssql
      volumes:
      - name: mssqldb
        persistentVolumeClaim:
          claimName: mssql-data
---
apiVersion: v1
kind: Service
metadata:
  name: mssql
spec:
  selector:
    app: mssql
  ports:
    - protocol: TCP
      port: 1433
      targetPort: 1433
EOF

# 10. 確認 Pod 是否成功建立並執行中
$ oc get pods
NAME                                READY   STATUS    RESTARTS   AGE
mssql-deployment-7578c99777-bq7dw   1/1     Running   0          3s

# 11. 檢查 NFS Server 上是否已產生 SQL Server 的儲存目錄
$ ls -l /data/nfs/share1/
total 0
drwxrwxrwx. 6 root root 59 Jun 18 15:00 mssql

# 12. 檢查 MSSQL Server 儲存目錄內部結構,包含 data/log/secrets/system 等子目錄
$ ls -al /data/nfs/share1/mssql/
total 4
drwxrwxrwx. 6 root       root     59 Jun 18 15:00 .
drwxr-xr-x. 4 nobody     nobody   34 Jun 18 14:58 ..
drwxr-xr-x. 2 1000750000 root    156 Jun 18 15:00 data
drwxr-xr-x. 2 1000750000 root   4096 Jun 18 15:00 log
drwxr-xr-x. 2 1000750000 root     25 Jun 18 15:00 secrets
drwxr-xr-x. 5 1000750000 root     74 Jun 18 15:00 .system

清理環境

$ oc delete -R -f ~/test-mssql/
$ oc delete project mssql
$ sudo rm -rf ~/test-mssql/

# execute the following command on NFS Server
$ sudo rm -rf /data/nfs/share1/mssql/

不啟用 StorageClass

# 1. 建立本機目錄以儲存 YAML 定義
$ mkdir ~/test-mssql-manually/

# 2. 建立新的專案(namespace)mssql
$ oc new-project mssql

# 3. 建立 secret,用於 MSSQL 的 SA 密碼
$ oc create secret generic mssql --from-literal=SA_PASSWORD="1qaz@WSX"

# 4. 建立 PVC 與手動對應的 PV,使用 NFS 掛載 RWX 模式
$ cat <<EOF | tee ~/test-mssql-manually/mssql-storage.yaml | oc apply -f -
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mssql-data
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: ""
  volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mssql-data-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  mountOptions:
    - hard
    - nfsvers=4.2
  nfs:
    path: /share1
    server: 192.168.1.90
  claimRef:
    namespace: mssql
    name: mssql-data
EOF

# 5. 確認 PVC 已成功綁定 PV
$ oc get pvc
NAME         STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
mssql-data   Bound    mssql-data-pv   5Gi        RWX                           <unset>                 10s

# 6. 建立 Deployment 與 Service,掛載 NFS 作為資料目錄
$ cat <<EOF | tee ~/test-mssql-manually/mssql-deployment.yaml | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mssql-deployment
spec:
  selector:
    matchLabels:
      app: mssql
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mssql
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mssql
        image: mcr.microsoft.com/mssql/server:2017-CU8-ubuntu
        ports:
        - containerPort: 1433
        env:
        - name: MSSQL_PID
          value: "Developer"
        - name: ACCEPT_EULA
          value: "Y"
        - name: SA_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mssql
              key: SA_PASSWORD
        volumeMounts:
        - name: mssqldb
          mountPath: /var/opt/mssql
      volumes:
      - name: mssqldb
        persistentVolumeClaim:
          claimName: mssql-data
---
apiVersion: v1
kind: Service
metadata:
  name: mssql
spec:
  selector:
    app: mssql
  ports:
    - protocol: TCP
      port: 1433
      targetPort: 1433
EOF

# 7. 檢查 Pod 狀態(第一次會失敗)
$ oc get pods
NAME                                READY   STATUS   RESTARTS      AGE
mssql-deployment-7578c99777-qcvgv   0/1     Error    2 (30s ago)   32s

# 8. 查看失敗原因:目錄建立失敗,權限錯誤 (Errno 13)
$ oc logs mssql-deployment-7578c99777-qcvgv
/opt/mssql/bin/sqlservr: Error: The secrets directory [var/opt/mssql/secrets/] could not be created.  Errno [13]

# 9. 確認 app container 是以哪個 UID 執行
$ oc get pods mssql-deployment-7578c99777-qcvgv -o yaml | grep -i runasuser
      runAsUser: 1000780000

# 10. 修正 NFS 路徑權限,將目錄擁有者改為指定 UID
$ sudo chown -R 1000780000 /data/nfs/share1/

# 11. 重啟 Deployment 讓 Pod 重建並應用正確的目錄權限
$ oc rollout restart deployment mssql-deployment

# 12. 確認 Pod 成功運行
$ oc get pods
NAME                               READY   STATUS    RESTARTS   AGE
mssql-deployment-9fcdd78f8-7d8r8   1/1     Running   0          3s

# 13. 驗證 NFS Server 上的目錄內容是否成功建立
$ ls -l /data/nfs/share1/
total 4
drwxr-xr-x. 2 1000780000 root  156 Jun 18 15:20 data
drwxr-xr-x. 2 1000780000 root 4096 Jun 18 15:20 log
drwxr-xr-x. 2 1000780000 root   25 Jun 18 15:20 secrets

清理環境

$ oc delete -R -f ~/test-mssql-manually/
$ oc delete project mssql
$ sudo rm -rf ~/test-mssql-manually/

# execute the following command on NFS Server
$ sudo rm -rf /data/nfs/share1/*/
$ sudo rm -rf /data/nfs/share1/.system