1042 字
5 分鐘
試玩GKE的GCS FUSE CSI
2023-05-05
無標籤

昨天(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/v1
kind: Deployment
metadata:
name: gcs-fuse-csi-example
spec:
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: v1
kind: Service
metadata:
name: gcs-fuse-csi-example
spec:
type: LoadBalancer
selector:
app: gcs-fuse-csi-example
ports:
- name: http
port: 80
targetPort: 80

也可以透過PV、PVC達到一樣的功能,設定如下

apiVersion: v1
kind: PersistentVolume
metadata:
name: gcs-fuse-csi-pv-test
spec:
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: v1
kind: PersistentVolumeClaim
metadata:
name: gcs-fuse-csi-pvc-test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
volumeName: gcs-fuse-csi-pv-test
storageClassName: dummy-storage-class
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcs-fuse-csi-use-pvc-example
spec:
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: v1
kind: Service
metadata:
name: gcs-fuse-csi-use-pvc-example
spec:
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的問題,我會試試看一些服務上這功能,有發現什麼問題會再分享