I’ve been following Arm for a while, so was glad that the C4A VM was GA’d earlier this year. These machines run Google silicon Axion chip. It’s also great to see broad geographical availability including regions like Tokyo. Feels like Arm is finally ready for prime time.
C4D running 5th Gen AMD EPYC processors (Turin) was even more recently released. Let’s try them both out!
Arm on GKE
Here’s how to deploy an Arm workload on GKE using Compute Class. I’ll be using Autopilot mode.
First, define the Compute Class. Why Compute Class? Among other reasons, it’s a convenient place to deploy our compute priorities and other configurations giving the workload a single name to reference. If in the future a new Arm VM was added, we can replace it in the Compute Class without updating every workload. It’s also possible to request the machine-family directly.
Here’s a simple Compute Class requesting C4A. I included “minCores: 4” out of habit since GKE adds a bit of overhead on the node, anything less than 4 cores is not a great overhead-to-allocatable ratio.
apiVersion: cloud.google.com/v1
kind: ComputeClass
metadata:
name: c4a
spec:
priorities:
- machineFamily: c4a
minCores: 4
To request this in an Arm workload, we actually need to do two things. First reference the compute class, and second request an Arm node. This may seem redundant since this compute class only offers Arm nodes (and you might see a warning like “Key ‘kubernetes.io/arch’ is not recommended with node selector; Consider using Custom Compute Classes mechanisms, simultaneous use of both may lead to unexpected behavior, use with caution.”), but GKE requires Arm workloads to have this selector or the workload won’t be scheduled.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
pod: nginx-pod
template:
metadata:
labels:
pod: nginx-pod
spec:
nodeSelector:
kubernetes.io/arch: arm64
cloud.google.com/compute-class: c4a
containers:
- name: nginx-container
image: nginx
resources:
requests:
cpu: 40m
memory: 40Mi
ephemeral-storage: 100Mi
That’s it! Create both objects and you should now have a workload running on GKE Autopilot using Arm C4A.
kubectl create -f https://raw.githubusercontent.com/WilliamDenniss/autopilot-examples/refs/heads/master/custom-compute-class/c4a/c4a-cc.yaml
kubectl create -f https://raw.githubusercontent.com/WilliamDenniss/autopilot-examples/refs/heads/master/custom-compute-class/c4a/deploy.yaml
Remember, you can ignore the warning in this instance.
computeclass.cloud.google.com/c4a created
Warning: Key 'kubernetes.io/arch' is not recommended with node selector; Consider using Custom Compute Classes mechanisms, simultaneous use of both may lead to unexpected behavior, use with caution.
deployment.apps/nginx created
Another thing to notice is that when you run using node-based compute classes (instead of the Pod-based container-optimized compute platform), you have the full VM at your disposal and there are no Pod minimums. Just make sure you fill the VM for efficiency, as you’re paying for the whole thing regardless.
Taking a look at the created, we can see an Arm C4A machine in this abridged output:
$ kubectl describe node gk3-autopilot-tokyo-nap-1j2padwp-97ecb77b-rp6h
Name: gk3-autopilot-tokyo-nap-1j2padwp-97ecb77b-rp6h
Roles: <none>
Labels: addon.gke.io/node-local-dns-ds-ready=true
cloud.google.com/compute-class=c4a
kubernetes.io/arch=arm64
topology.kubernetes.io/region=asia-northeast1
topology.kubernetes.io/zone=asia-northeast1-b
And the Pod we can see is running as a Burstable QoS with 40mCPU requested (allowing it to burst outside this when resources are available).
$ kubectl describe pod
Name: nginx-574cbc959d-79w2m
Namespace: default
Controlled By: ReplicaSet/nginx-574cbc959d
Containers:
nginx-container:
Container ID: containerd://3a8b8ad97a828f589068f3aa7e59167a8e71af3a0fdbd634ac492e6b0208a7cd
Image: nginx
Image ID: docker.io/library/nginx@sha256:33e0bbc7ca9ecf108140af6288c7c9d1ecc77548cbfd3952fd8466a75edefe57
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 17 Aug 2025 19:31:02 +0000
Ready: True
Restart Count: 0
Requests:
cpu: 40m
ephemeral-storage: 100Mi
memory: 40Mi
QoS Class: Burstable
Node-Selectors: cloud.google.com/compute-class=c4a
kubernetes.io/arch=arm64
Tolerations: cloud.google.com/compute-class=c4a:NoSchedule
kubernetes.io/arch=arm64:NoSchedule
node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
C4D on GKE
Here’s the same setup for the very capable C4D machine. Again, I’ll use Compute Class as a convenient way to declare my machine configuration in a single place, and request a min-4 core machine (C4D goes as low as 2 cores, but there’s a bit of system overhead that makes them less useful).
Here’s the compute class:
apiVersion: cloud.google.com/v1
kind: ComputeClass
metadata:
name: c4d
spec:
priorities:
- machineFamily: c4d
minCores: 4
And the workload:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-x86
spec:
replicas: 1
selector:
matchLabels:
pod: nginx-x86-pod
template:
metadata:
labels:
pod: nginx-x86-pod
spec:
nodeSelector:
cloud.google.com/compute-class: c4d
containers:
- name: nginx-container
image: nginx
resources:
requests:
cpu: 40m
memory: 40Mi
ephemeral-storage: 100Mi
Create both:
kubectl create -f https://raw.githubusercontent.com/WilliamDenniss/autopilot-examples/refs/heads/master/custom-compute-class/c4d/c4d-cc.yaml
kubectl create -f https://raw.githubusercontent.com/WilliamDenniss/autopilot-examples/refs/heads/master/custom-compute-class/c4d/deploy.yaml
As with the earlier example, no minimums apply when using node-based compute classes, our workload is using 40mCPU of the allocatable space, so it’s our responsibility to fully utilize this node.
$ kubectl describe pod nginx-x86-688f977fbc-77v69
Name: nginx-x86-688f977fbc-77v69
Namespace: default
Containers:
nginx-container:
Container ID: containerd://e2d87da8bf2b540ba367627a27a15a9b4a24748b6ba0c31ec8a2b70137058b83
Image: nginx
Image ID: docker.io/library/nginx@sha256:33e0bbc7ca9ecf108140af6288c7c9d1ecc77548cbfd3952fd8466a75edefe57
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 17 Aug 2025 19:57:08 +0000
Ready: True
Restart Count: 0
Requests:
cpu: 40m
ephemeral-storage: 100Mi
memory: 40Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pt8zc (ro)
QoS Class: Burstable
Node-Selectors: cloud.google.com/compute-class=c4d
Tolerations: cloud.google.com/compute-class=c4d:NoSchedule
node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s