Skip to content

Commit b7f792f

Browse files
committed
lp(gke-multi-arch-axion): add gke-build-push page; update _index, cloud-build, gke-deploy, multi-arch-images, project-setup
Signed-off-by: ranimandepudi <ranichowdary.mandepudi@arm.com>
1 parent aa036bf commit b7f792f

6 files changed

Lines changed: 322 additions & 266 deletions

File tree

content/learning-paths/servers-and-cloud-computing/gke-multi-arch-axion/_index.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ cascade:
66

77
minutes_to_complete: 90
88

9-
who_is_this_for: This learning path is for cloud, platform and SRE engineers operating Kubernetes on Google Cloud who need a prescriptive path to build multi‑arch images and migrate services from x86 to Arm (Google Axion) using production‑grade practices.
9+
who_is_this_for: This learning path is for cloud, platform, and SRE engineers operating Kubernetes on Google Cloud who need a prescriptive path to build multi‑architecture images and migrate services from x86 to Arm (Google Axion) using production‑grade practices.
1010

1111
learning_objectives:
12-
13-
- Build and publish Docker images that support both amd64 and arm64 with Docker Buildx and Artifact Registry.
14-
- Create a GKE Standard cluster with x86 (amd64) nodes and add an Arm (arm64) node pool using Axion-based C4A machine types.
15-
- Deploy to amd64 first, then migrate to arm64 using Kustomize overlays for safe, incremental rollout.
16-
- Optionally automate builds and rollouts end-to-end with Cloud Build and Skaffold.
12+
- Prepare Dockerfiles for multi-architecture builds (minimal, safe edits so services compile and run on amd64 & arm64).
13+
- Create a dual-architecture GKE Standard cluster with two node pools, amd64 and arm64 (Axion-based C4A).
14+
- Build and publish multi-architecture images to Artifact Registry using Docker Buildx (Kubernetes driver) - BuildKit pods run natively on both pools (no QEMU or extra build VMs).
15+
- Deploy to amd64 first, then migrate to arm64 using Kustomize overlays and progressive rollout.
16+
- Optionally automate builds and rollouts end-to-end with Cloud Build and Skaffold.
1717

1818
prerequisites:
19-
- A [Google Cloud account](https://console.cloud.google.com/)with billing enabled.
20-
- Cloud Shell access (recommended) to run all steps in the browser; no local setup required.
19+
- A [Google Cloud account](https://console.cloud.google.com/) with billing enabled.
20+
- Cloud Shell access (used as the control plane, includes gcloud, kubectl, and Docker Buildx).
2121
- (Optional if not using Cloud Shell) A Linux/macOS workstation with Docker (Buildx enabled), kubectl, the Google Cloud CLI (gcloud), and Git.
2222
- Basic familiarity with Docker, Kubernetes, and gcloud.
2323

@@ -34,6 +34,9 @@ operatingsystems:
3434
- Linux
3535
tools_software_languages:
3636
- Kubernetes
37+
- GKE
38+
- Skaffold
39+
- Cloud Build
3740

3841

3942
further_reading:
@@ -42,7 +45,7 @@ further_reading:
4245
link: https://cloud.google.com/kubernetes-engine/docs
4346
type: documentation
4447
- resource:
45-
title: Create Arm based clusters and node pools
48+
title: Create Arm-based clusters and node pools
4649
link: https://cloud.google.com/kubernetes-engine/docs/how-to/create-arm-clusters-nodes
4750
type: documentation
4851

content/learning-paths/servers-and-cloud-computing/gke-multi-arch-axion/cloud-build.md

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Automate builds and rollout with Cloud Build & Skaffold
3-
weight: 5
3+
weight: 6
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
@@ -28,28 +28,38 @@ Cloud Build runs as a per-project service account: `<PROJECT_NUMBER>@cloudbuild.
2828
PROJECT_NUMBER="$(gcloud projects describe "${PROJECT_ID}" --format='value(projectNumber)')"
2929
CLOUD_BUILD_SA="${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com"
3030

31-
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
32-
--member="serviceAccount:${CLOUD_BUILD_SA}" \
33-
--role="roles/cloudbuild.builds.builder"
31+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${CLOUD_BUILD_SA}" --role="roles/cloudbuild.builds.builder" --condition=None --quiet
3432

35-
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
36-
--member="serviceAccount:${CLOUD_BUILD_SA}" \
37-
--role="roles/container.developer"
33+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${CLOUD_BUILD_SA}" --role="roles/container.developer" --condition=None --quiet
3834

39-
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
40-
--member="serviceAccount:${CLOUD_BUILD_SA}" \
41-
--role="roles/artifactregistry.writer"
35+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${CLOUD_BUILD_SA}" --role="roles/artifactregistry.writer" --condition=None --quiet
4236

43-
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
44-
--member="serviceAccount:${CLOUD_BUILD_SA}" \
45-
--role="roles/logging.logWriter"
37+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${CLOUD_BUILD_SA}" --role="roles/logging.logWriter" --condition=None --quiet
4638
```
4739

4840
## Update skaffold.yaml for deploy-only
4941

5042
This will let Cloud Build handle image builds and use Skaffold only to apply the Kustomize overlays.
5143

5244
```yaml
45+
# From the repo root (microservices-demo)
46+
[ -f skaffold.yaml ] && cp skaffold.yaml "skaffold.yaml.bak.$(date +%s)"
47+
cat > skaffold.yaml <<'YAML'
48+
49+
# Copyright 2021 Google LLC
50+
#
51+
# Licensed under the Apache License, Version 2.0 (the "License");
52+
# you may not use this file except in compliance with the License.
53+
# You may obtain a copy of the License at
54+
#
55+
# http://www.apache.org/licenses/LICENSE-2.0
56+
#
57+
# Unless required by applicable law or agreed to in writing, software
58+
# distributed under the License is distributed on an "AS IS" BASIS,
59+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
60+
# See the License for the specific language governing permissions and
61+
# limitations under the License.
62+
5363
apiVersion: skaffold/v3
5464
kind: Config
5565
metadata:
@@ -83,6 +93,7 @@ manifests:
8393
- ./kubernetes-manifests/loadgenerator.yaml
8494
deploy:
8595
kubectl: {}
96+
YAML
8697

8798
```
8899

@@ -91,6 +102,35 @@ deploy:
91102
This pipeline installs `Docker + Buildx` in the runner, enables QEMU, builds two services as examples (extend as desired), connects to your cluster, deploys to amd64, verifies, migrates to arm64, verifies, and prints the external IP. 
92103

93104
```yaml
105+
cat > cloudbuild.yaml <<'YAML'
106+
107+
# Copyright 2020 Google LLC
108+
#
109+
# Licensed under the Apache License, Version 2.0 (the "License");
110+
# you may not use this file except in compliance with the License.
111+
# You may obtain a copy of the License at
112+
#
113+
# http://www.apache.org/licenses/LICENSE-2.0
114+
#
115+
# Unless required by applicable law or agreed to in writing, software
116+
# distributed under the License is distributed on an "AS IS" BASIS,
117+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
118+
# See the License for the specific language governing permissions and
119+
# limitations under the License.
120+
121+
# [START cloudbuild_microservice_demo_cloudbuild]
122+
123+
# This configuration file is used to build and deploy the app into a
124+
# GKE cluster using Google Cloud Build.
125+
#
126+
# PREREQUISITES:
127+
# - Cloud Build service account must have role: "Kubernetes Engine Developer"
128+
129+
# USAGE:
130+
# GCP zone and GKE target cluster must be specified as substitutions
131+
# Example invocation:
132+
# `gcloud builds submit --config=cloudbuild.yaml --substitutions=_ZONE=us-central1-b,_CLUSTER=demo-app-staging .`
133+
94134
substitutions:
95135
_REGION: ${REGION}
96136
_CLUSTER: ${CLUSTER_NAME}
@@ -142,7 +182,7 @@ steps:
142182
143183
docker buildx build --platform linux/amd64,linux/arm64 \
144184
-t "${_REPO}/cartservice:v1" \
145-
src/cartservice --push
185+
src/cartservice/src --push
146186
147187
# 3) Connect kubectl to the target cluster
148188
- name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
@@ -189,7 +229,8 @@ steps:
189229
kubectl get pods -o wide
190230
echo "Fetching external IP for the frontend service..."
191231
IP=$(kubectl get svc frontend-external -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')
192-
echo "Open http://${IP} in your browser."
232+
echo "Open http://$${IP} in your browser."
233+
YAML
193234
```
194235

195236
{{% notice Note %}}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
title: Provision a Dual-Arch GKE Cluster and Publish Multi-Arch Images
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
Now create a **GKE cluster** with **two node pools** (amd64 & arm64), then build and push multi-arch images natively on those node pools. Each architecture uses its own BuildKit pod, and no QEMU emulation is involved.
10+
11+
#### Networking (VPC-native / IP aliasing)
12+
13+
GKE uses **VPC-native (IP aliasing)** and requires **two secondary ranges** on the chosen subnet: one for **Pods** and one for **Services**.
14+
- **Default VPC:** Skip this step. GKE will create the secondary ranges automatically.
15+
- **Custom VPC/subnet:** Set variables and add/verify secondary ranges:
16+
17+
```bash
18+
# Set/confirm network variables (adjust to your environment)
19+
export REGION="${REGION:-us-central1}"
20+
export NETWORK="dev-eco-nw-pb" # your VPC
21+
export SUBNET="dev-eco-nw-subnet" # your subnet
22+
export POD_RANGE_NAME="gke-boutique-pods"
23+
export SVC_RANGE_NAME="gke-boutique-svcs"
24+
25+
# Inspect the subnet and existing ranges
26+
gcloud compute networks subnets list --network "${NETWORK}" --regions "${REGION}" \
27+
--format="table(name,region,ipCidrRange,secondaryIpRanges.list())"
28+
29+
# If missing, add two secondary ranges (example CIDRs; ensure no overlap)
30+
gcloud compute networks subnets update "${SUBNET}" --region "${REGION}" --add-secondary-ranges ${POD_RANGE_NAME}=10.8.0.0/14,${SVC_RANGE_NAME}=10.4.0.0/20
31+
```
32+
This avoids users on default VPC accidentally setting NETWORK/SUBNET and passing the wrong flags later.
33+
34+
### Create the GKE cluster
35+
36+
Create a GKE Standard cluster with VPC-native (IP aliasing) enabled and no default node pool (you'll add amd64 and arm64 pools next). The command below works for both default and custom VPCs: if NETWORK, SUBNET, and the secondary range variables are unset, GKE uses the default VPC and manages ranges automatically.
37+
38+
Create the cluster with no default node pool and add node pools explicitly.
39+
40+
```bash
41+
# Cluster vars (reuses earlier PROJECT_ID/REGION/ZONE)
42+
export CLUSTER_NAME="${CLUSTER_NAME:-gke-multi-arch-cluster}"
43+
44+
# If using the default VPC, you can omit --network/--subnetwork.
45+
# If using a custom VPC, include them and pass the secondary range names you set above.
46+
gcloud container clusters create "${CLUSTER_NAME}" --region "${REGION}" --enable-ip-alias --num-nodes "1" --machine-type "e2-standard-2" ${NETWORK:+--network "${NETWORK}"} ${SUBNET:+--subnetwork "${SUBNET}"} ${POD_RANGE_NAME:+--cluster-secondary-range-name "${POD_RANGE_NAME}"} ${SVC_RANGE_NAME:+--services-secondary-range-name "${SVC_RANGE_NAME}"}
47+
```
48+
49+
Create an x86 (amd64) pool and an Arm (arm64) pool. Use machine types available in your region (e.g., c4-standard-* for x86 and c4a-standard-* for Axion).
50+
51+
```bash
52+
# amd64 pool (x86)
53+
gcloud container node-pools create amd64-pool --cluster="${CLUSTER_NAME}" --region="${REGION}" --machine-type="c4-standard-16" --num-nodes="1" --image-type="COS_CONTAINERD" --quiet
54+
55+
# arm64 pool (Axion)
56+
gcloud container node-pools create arm64-pool --cluster="${CLUSTER_NAME}" --region="${REGION}" --machine-type="c4a-standard-16" --num-nodes="1" --image-type="COS_CONTAINERD" --quiet
57+
58+
# delete the tiny default pool
59+
gcloud container node-pools delete default-pool --cluster="${CLUSTER_NAME}" --region="${REGION}" --quiet
60+
```
61+
62+
Connect kubectl and confirm node architectures:
63+
64+
```bash
65+
gcloud container clusters get-credentials "${CLUSTER_NAME}" --region "${REGION}"
66+
kubectl config current-context
67+
kubectl get nodes -o wide
68+
kubectl get nodes -L kubernetes.io/arch
69+
```
70+
You should see nodes for both architectures. In zonal clusters (or when a pool has --num-nodes=1 in a single zone), expect one amd64 and one arm64 node. In regional clusters, --num-nodes is per zone, with three zones you'll see three amd64 and three arm64 nodes.
71+
72+
### Create the Buildx builder on GKE (native, one pod per arch)
73+
74+
Now run a BuildKit pod on an amd64 node and another on an arm64 node. Buildx will route each platform's build to the matching pod - native builds, no emulation.
75+
76+
```bash
77+
78+
# Namespace for BuildKit pods
79+
kubectl create ns buildkit --dry-run=client -o yaml | kubectl apply -f -
80+
81+
# Create the builder (amd64 node)
82+
docker buildx create --driver kubernetes --name gke-native --use --driver-opt namespace=buildkit,replicas=1,loadbalance=sticky,nodeselector=kubernetes.io/arch=amd64 --platform linux/amd64
83+
84+
# Append an arm64 node to the same builder
85+
docker buildx create --driver kubernetes --append --name gke-native --driver-opt namespace=buildkit,replicas=1,loadbalance=sticky,nodeselector=kubernetes.io/arch=arm64 --platform linux/arm64
86+
87+
# Bootstrap and verify pods
88+
docker buildx inspect gke-native --bootstrap
89+
kubectl -n buildkit get pods -o wide
90+
91+
```
92+
{{% notice Note %}}
93+
You will now have a multi-node Buildx builder named gke-native. Each BuildKit pod is pinned to a specific CPU arch via nodeselector.
94+
{{% /notice %}}
95+
96+
### Build & push all services (multi-arch manifest lists)
97+
Build every service for linux/amd64 and linux/arm64 using the GKE-backed builder:
98+
```bash
99+
cat << 'EOF' > build-all-multiarch.sh
100+
#!/usr/bin/env bash
101+
set -euo pipefail
102+
103+
: "${GAR:?Set GAR like REGION-docker.pkg.dev/PROJECT/REPO first}"
104+
105+
services=(
106+
adservice
107+
cartservice # special context below
108+
checkoutservice
109+
currencyservice
110+
emailservice
111+
frontend
112+
paymentservice
113+
productcatalogservice
114+
recommendationservice
115+
shippingservice
116+
loadgenerator
117+
)
118+
119+
for svc in "${services[@]}"; do
120+
# cartservice Dockerfile path differs
121+
if [ "$svc" = "cartservice" ] && [ -d "src/cartservice/src" ]; then
122+
ctx="src/cartservice/src"
123+
else
124+
ctx="src/${svc}"
125+
fi
126+
127+
echo ">>> Building ${svc} for amd64+arm64..."
128+
docker buildx build --builder gke-native --platform linux/amd64,linux/arm64 --provenance=false -t "${GAR}/${svc}:v1" "${ctx}" --push
129+
done
130+
EOF
131+
132+
chmod +x build-all-multiarch.sh
133+
./build-all-multiarch.sh
134+
```
135+
Each :v1 you push is a manifest list that points to two images (one per arch).
136+
137+
### Verify manifest lists and per-arch pulls
138+
139+
List pushed images:
140+
141+
```bash
142+
gcloud artifacts docker images list "${GAR}"
143+
```
144+
145+
Inspect one tag (should show both platforms):
146+
```bash
147+
docker buildx imagetools inspect "${GAR}/adservice:v1"
148+
```
149+
150+
Expected Output:
151+
```
152+
Platform: linux/amd64
153+
Platform: linux/arm64
154+
```

0 commit comments

Comments
 (0)