昨天(2023/05/04)看到ithome文章講到「Google發布雲端儲存FUSE CSI驅動程式,簡化GKE機器學習工作負載資料存取」,內心大喜,這不就是我上個月問Google顧問什麼時候會release的功能,上個月還在unstable,沒想到這麼快就可以用了。
GCS FUSE
GCS FUSE CSI可以讓我們使用gcsfuse的衍生功能,兩者都是開源專案,可以將Google Cloud Stroage掛在到Kubernetes Container內的路徑,把它當作一般的檔案系統使用。
過去我們要在GKE使用GCS的時候有兩個做法,第一個方法是程式開發的時候就按造標準的S3 API與GCS溝通;第二個方法是將GCS透過FUSE掛載成volume,把他當檔案系統操作。我們如果把舊有的專案移到GKE,不修改程式的話,第二個方法比較簡單,但就會衍生出一些問題。
首先,過去我們需要先將gcsfuse這支程式包進去container image,在entrypoint啟動的時候,將gcsfuse掛載起來。再來,如果要在container內使用fuse需要開啟privileged,詳見本Github Issue。
GKE有兩個版本,分別為自動調度Node的Autopilot與傳統固定Node的Standard,慘的是,Autopilot不支援設定privileged,所以在過去是沒辦法在Autopilot使用GCS FUSE。現在我們就可以透過GCS FUSE CSI在Autopilot中使用。
GKE Version
GCS FUSE CSI支援GKE的Autopilot、Standrad,且版本要1.26以上。目前的穩定版是在1.24、1.25,需要先升級。
安裝說明
Filestore、PD可以直接透過StroageClass自動建立儲存區,目前是提供CSIDriver,要先自己開GCS Bucket。
整體安裝流程參考官方文件。大致流程說明如下:
- 先啟用GKE的GcsFuseCsiDriver功能
- 再來需要建立一個GCP ServiceAccount能夠讀寫GCS,我建議STORAGE_ROLE設定在roles/storage.admin,因為該程式至少會用到storage.buckets.get的權限,權限不足將會失敗。
- 接著將GCP ServiceAccount的權限bind到K8s ServiceAccount,我們需要記住這個K8s ServiceAccount,後面會用到。
- 最後開好一個要用的GCS Bucket
安裝完會發現csidriver多一個gcsfuse.csi.storage.gke.io。

範例
我提供一個範例,這是一個php的網站,首頁是一個上傳檔案的功能,上傳後的檔案會放到/var/www/html/uploads。透過網址/uploads/檔名 可以下載檔案。如下圖所示:

上傳檔案後,就會放到GCS,在GCP可以看到上傳的檔案。

範例的Kubernetes YML如下,adrianlzt/docker-nginx-php-upload是我在Github上找的範例,就借來用一下。
使用GCSFUSE有幾個要注意的設定:
- 將Pod中
metadata需要設定gke-gcsfuse/volumes: “true” - volume設定中設定mountOptions解決寫入權限問題,本範例要給nginx寫入權限,因此將volume權限設定在100:101
- serviceAccountName填入上面安裝流程中建立的K8s ServiceAccount,他擁有讀寫GCS的權限
apiVersion: apps/v1kind: Deploymentmetadata: name: gcs-fuse-csi-examplespec: replicas: 3 selector: matchLabels: app: gcs-fuse-csi-example template: metadata: labels: app: gcs-fuse-csi-example annotations: gke-gcsfuse/volumes: "true" spec: containers: - name: nginx image: adrianlzt/docker-nginx-php-upload ports: - containerPort: 80 volumeMounts: - name: gcs-fuse-csi-example mountPath: /var/www/html/uploads serviceAccountName: gcs-fuse-csi volumes: - name: gcs-fuse-csi-example csi: driver: gcsfuse.csi.storage.gke.io volumeAttributes: bucketName: moda-csi-test mountOptions: "uid=100,gid=101,debug_fuse"
---apiVersion: v1kind: Servicemetadata: name: gcs-fuse-csi-examplespec: type: LoadBalancer selector: app: gcs-fuse-csi-example ports: - name: http port: 80 targetPort: 80也可以透過PV、PVC達到一樣的功能,設定如下
apiVersion: v1kind: PersistentVolumemetadata: name: gcs-fuse-csi-pv-testspec: accessModes: - ReadWriteMany capacity: storage: 5Gi storageClassName: dummy-storage-class claimRef: namespace: default name: gcs-fuse-csi-pvc-test mountOptions: - uid=100 - gid=101 - debug_fuse csi: driver: gcsfuse.csi.storage.gke.io volumeHandle: moda-csi-test
---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: gcs-fuse-csi-pvc-testspec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi volumeName: gcs-fuse-csi-pv-test storageClassName: dummy-storage-class
---apiVersion: apps/v1kind: Deploymentmetadata: name: gcs-fuse-csi-use-pvc-examplespec: replicas: 3 selector: matchLabels: app: gcs-fuse-csi-example template: metadata: labels: app: gcs-fuse-csi-example annotations: gke-gcsfuse/volumes: "true" spec: containers: - name: nginx image: adrianlzt/docker-nginx-php-upload ports: - containerPort: 80 volumeMounts: - name: gcs-fuse-csi-example3 mountPath: /var/www/html/uploads serviceAccountName: gcs-fuse-csi volumes: - name: gcs-fuse-csi-example3 persistentVolumeClaim: claimName: gcs-fuse-csi-pvc-test
---apiVersion: v1kind: Servicemetadata: name: gcs-fuse-csi-use-pvc-examplespec: type: LoadBalancer selector: app: gcs-fuse-csi-example ports: - name: http port: 80 targetPort: 80原理說明
你會發現所有的Pod,都會多一個Container。

這個Container作為Sidecar,提供GCS FUSE mount用。

結語
GCS FUSE CSI這功能真的滿讚的,很早以前就期待這功能,因為Filestore太貴、硬碟不支援ReadWriteMany,也解決了原本Autopilot無法使用GCS FUSE的問題,我會試試看一些服務上這功能,有發現什麼問題會再分享