Tech ONTAP Blogs
Tech ONTAP Blogs
Authors: Zach Ward & Diane Patton, NetApp
Exciting update for container developers and operators! If your applications need high-performance block storage, Google Cloud NetApp Volumes (GCNV) now has you covered. With the general availability of Enterprise block (iSCSI) with Flex Unified storage pools in GCNV and the latest NetApp® Trident™ CSI Provisioner (v26.02), you can now easily provision and manage iSCSI block volumes directly for your container workloads.
This article will discuss Trident, advantages of using Trident with GCNV Block storage, and how to set it up to get your stateful containerized applications up and running in no time!
NetApp Trident is an open-source dynamic storage orchestrator for Kubernetes that allows developers and application owners to provision storage on-demand from various NetApp storage systems. When paired with Google Cloud NetApp Volumes Flex Unified block storage—a type of storage ideal for applications that require low-latency, high-performance, and direct access to raw volumes (such as self-managed databases)—Trident automates the entire provisioning lifecycle, bringing enterprise-grade capabilities to containerized workloads.
Integrating Trident with your GCNV block storage provides several crucial benefits for modern container environments running applications that need block storage:
As well as others.
The combination of Trident and GCNV block storage is particularly powerful for:
Setting up Trident with GCNV block storage is very similar to Trident with file storage and you can be up and running in just a few steps!
Create a storage pool using the Flex service level and Unified type. Be sure to use a regional pool which replicates your data to another zone for disaster recovery purposes. Zonal pools will be available with Trident soon.
We will direct Trident to place all new volumes in this pool. You may also create multiple storage pools to be used by Trident, and specify the ones for Trident to use. Alternatively, you may just specify criteria and Trident will select a pool in that region that matches that criteria. The choice is yours.
The example below shows creating a regional storage pool on the console. You may also create a storage pool using the gcloud CLI or the API. We will call the pool flex-unified-pool.
Be sure to install iscsi tools on the worker nodes if not already installed by default. . Please see the Trident documentation for the exact tools and commands.
Kubernetes nodes must meet the following requirements to attach block volumes provisioned from Google Cloud NetApp Volumes:
The various methods to install Trident 26.02 are independent of your Kubernetes distribution. You have a choice of one of 3 methods.: tridentctl, trident operator or Helm. We will use Helm as an example here.
Install the helm repo:
helm repo add netapp-trident https://netapp.github.io/trident-helm-chart
Next install Trident using Helm:
helm install trident netapp-trident/trident-operator --version 100.2602.0 --create-namespace --namespace trident
Now, you’re ready to create a Trident backend for your iSCSI volumes.
First, create a secret for authentication into Google Cloud NetApp Volumes. As a quick example, you can use your service account credentials to create a secret:
apiVersion: v1
kind: Secret
metadata:
name: gcnv-sa-secret
namespace: trident
type: Opaque
stringData:
private_key_id: "..."
private_key: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
$ k apply -f gcnv-secret.yaml -n trident
Use this secret in your backend config. Be sure to use the storageDriverName google-cloud-netapp-volumes-san and apply the backend file:
apiVersion: trident.netapp.io/v1
kind: TridentBackendConfig
metadata:
name: gcnv-iscsi
namespace: trident
spec:
version: 1
storageDriverName: google-cloud-netapp-volumes-san
projectNumber: "<yourprojnumhere>"
location: <location of your pool like us-east4>
sdkTimeout: "600"
apiKey:
type: service_account
project_id: <YOUR project ID here>
private_key_id: "" # Leave empty - comes from Secret
private_key: "" # Leave empty - comes from Secret
client_email: <your client email here>
client_id: <your client id here>
auth_uri: "https://accounts.google.com/o/oauth2/auth"
token_uri: "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url: <get from your SA credentials>
client_x509_cert_url: <get from your SA credentials>
credentials:
name: <your secret name>
type: secret
storage:
- labels:
cloud: gcp
performance: flex
network: <insert vpc network here>
serviceLevel: Flex
storagePools:
- flex-unified-pool
$ k apply -f backend.yaml -n trident
Flex-unified-pool is the name of your new storage pool created in step 1.
If all is configured correctly, your backend should become bound successfully:
$ k get tbc -n trident
NAME BACKEND NAME BACKEND UUID PHASE STATUS
gcnv-iscsi gcnv-iscsi ... Bound Success
More information can be found at Configure Google Cloud NetApp Volumes for SAN workloads.
An example storage class is shown below. Be sure to use google-cloud-netapp-volumes-san as the backendType.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gcnv-san
provisioner: csi.trident.netapp.io
parameters:
backendType: "google-cloud-netapp-volumes-san"
fsType: "ext4"
allowVolumeExpansion: true
$ k apply -f storage-class.yaml -n trident
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: gcnv-san
$ kubectl apply -f mysql-pvc.yaml -n trident
$ kubectl get pvc mysql-data -n trident
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-data Bound pvc-... 101Gi RWO gcnv-san 12s
Trident returns a volume size of one GiB larger than the PVC requested. GCNV block volumes have a small amount of internal metadata overhead that reduces the kernel-visible device size compared to the provisioned capacity. Testing shows this overhead can be up to ~300 KiB on initial creation and up to ~107 MiB after a resize. To ensure the usable space on the device always meets or exceeds what the PVC requested, Trident rounds the requested size up to the next GiB to add a 1 GiB buffer. For example, a 100 GiB PVC request results in a 101 GiB volume in GCNV, guaranteeing that the usable space visible to your application is at least 100 GiB.
Below, we create the MySQL deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "my-secret-pw"
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-data
$ kubectl apply -f mysql-deployment.yaml
$ kubectl get pods -l app=mysql
NAME READY STATUS RESTARTS AGE
mysql-6c4f7b8d9f-xk2pv 1/1 Running 0 45s
$ kubectl describe pod mysql-6c4f7b8d9f-xk2pv -n trident
Name: mysql-6c4f7b8d9f-xk2pv
Namespace: default
Priority: 0
Service Account: default
Node: gke-trident-gcnv-san-fle-default-pool-3c7571ea-bwhl/10.150.0.52
Start Time: Thu, 26 Feb 2026 10:15:32 -0700
Labels: app=mysql
pod-template-hash=6c4f7b8d9f
Status: Running
IP: 10.80.0.12
Containers:
mysql:
Container ID: containerd://a3d1f8e92b4c7d6a5e0f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2
Image: mysql:8.0
Port: 3306/TCP
State: Running
Started: Thu, 26 Feb 2026 10:16:04 -0700
Ready: True
Restart Count: 0
Environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
Mounts:
/var/lib/mysql from mysql-data (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-r7m4k (ro)
Volumes:
mysql-data:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: mysql-data
ReadOnly: false
kube-api-access-r7m4k:
Type: Projected
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 38s default-scheduler Successfully assigned trident/mysql-6c4f7b8d9f-xk2pv to gke-trident-gcnv-san-fle-default-pool-3c7571ea-bwhl
Normal SuccessfulAttachVolume 31s attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-8e0dd07f-0f1f-4848-8ea9-507da0f7a1b1"
Normal Pulled 24s kubelet Container image "mysql:8.0" already present on machine
Normal Created 24s kubelet Created container: mysql
Normal Started 24s kubelet Started container app
NetApp Trident with GCNV block storage is a powerful pairing for any organization running stateful workloads on Kubernetes. It automates storage provisioning, brings GCNV’s robust data services into the container ecosystem, and simplifies the entire storage management plane. By bridging the gap between container orchestration and Google Cloud NetApp volumes, it allows you to fully harness the agility and scalability of Kubernetes without compromising on performance or data management capabilities in Google Cloud! Now you can use Trident for both file (NFS, SMB) and block (iSCSI). Try it yourself! More information can be found in the Trident documentation and Google Cloud NetApp Volumes documentation.