Continuous integration and continuous delivery (CI/CD) keep cloud-native applications releasable at all times. Google Cloud Build handles the build and deploy steps, while Google Kubernetes Engine (GKE) provides a managed environment for running your workloads. This post walks through a production-grade pipeline based on lessons from the Hana Server repository, illustrating how Cloud Build pushes code into a GKE cluster.
1. Containerizing the Application
The pipeline begins with a reproducible Docker image. A multi-stage Dockerfile keeps the runtime image lean while still compiling TypeScript and installing dependencies during the build stage:
1# Stage 1: build
2FROM node:20-alpine AS build
3WORKDIR /usr/src/app
4COPY package*.json ./
5RUN npm install
6COPY . .
7ENV NODE_OPTIONS="--max-old-space-size=8192"
8RUN npm run build
9
10# Stage 2: production image
11FROM node:20-alpine
12WORKDIR /usr/src/app
13COPY /usr/src/app/dist ./dist
14COPY package*.json ./
15RUN npm install --only=production
16ENV NODE_ENV=production
17ENV NODE_OPTIONS="--max-old-space-size=8192"
18CMD ["node", "dist/main.js"]The first stage compiles the application and prepares the dist/ directory. The second stage copies only the compiled code and production dependencies, yielding a smaller image that boots faster and reduces the attack surface.
2. Automating Builds with Cloud Build
Cloud Build coordinates the steps required to build, push and deploy the image. The configuration below performs three actions: build the image, push it to Artifact Registry, and update the running deployment in GKE.
1# cloudbuild.yaml
2steps:
3 # Step 1: Build image
4 - name: 'gcr.io/cloud-builders/docker'
5 args: ['build', '-t', '${_CONTAINER_REGISTRY}/$PROJECT_ID/${_REPO_NAME}/${_APP_NAME}:$SHORT_SHA', '.']
6
7 # Step 2: Push image
8 - name: 'gcr.io/cloud-builders/docker'
9 args: ['push', '${_CONTAINER_REGISTRY}/$PROJECT_ID/${_REPO_NAME}/${_APP_NAME}:$SHORT_SHA']
10
11 # Step 3: Deploy to GKE
12 - name: 'gcr.io/cloud-builders/kubectl'
13 args:
14 - 'set'
15 - 'image'
16 - 'deployment/${_APP_NAME}'
17 - '${_APP_NAME}=${_CONTAINER_REGISTRY}/$PROJECT_ID/${_REPO_NAME}/${_APP_NAME}:$SHORT_SHA'
18 - '--namespace=${_NAMESPACE_NAME}'
19 env:
20 - 'CLOUDSDK_COMPUTE_ZONE=${_CLUSTER_ZONE}'
21 - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME}'
22
23images:
24 - '${_CONTAINER_REGISTRY}/$PROJECT_ID/${_REPO_NAME}/${_APP_NAME}:$SHORT_SHA'
25
26timeout: '1600s'
27serviceAccount: 'projects/$PROJECT_ID/serviceAccounts/${_BUILD_SERVICE_ACCOUNT}'
28options:
29 logging: CLOUD_LOGGING_ONLY3. Defining Kubernetes Resources
The deployment manifest describes how the application runs in the cluster. It sets resource requests/limits, a liveness probe and a rolling update strategy.
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 namespace: app-namespace
5 name: app-service
6 labels:
7 app: app-service
8spec:
9 replicas: 1
10 revisionHistoryLimit: 10
11 selector:
12 matchLabels:
13 app: app-service
14 strategy:
15 type: RollingUpdate
16 rollingUpdate:
17 maxSurge: 25%
18 maxUnavailable: 25%
19 template:
20 metadata:
21 labels:
22 app: app-service
23 spec:
24 terminationGracePeriodSeconds: 60
25 automountServiceAccountToken: false
26 containers:
27 - name: app-service
28 image: asia-south1-docker.pkg.dev/example-project/example-repo/app-service:latest
29 imagePullPolicy: IfNotPresent
30 ports:
31 - name: http
32 containerPort: 8080
33 resources:
34 limits:
35 cpu: 100m
36 memory: 500Mi
37 requests:
38 cpu: 100m
39 memory: 500Mi
40 livenessProbe:
41 httpGet:
42 path: /health
43 port: 8080
44 initialDelaySeconds: 30
45 periodSeconds: 104. Putting It All Together
- Commit code — Developers push to the repository.
- Cloud Build trigger — The trigger executes cloudbuild.yaml, building and deploying the container.
- Artifact Registry — Images are stored with immutable tags ($SHORT_SHA).
- GKE deployment — kubectl set image ensures a rolling update with zero downtime.
- Monitoring — Liveness probes and resource constraints help maintain cluster health; integrate Cloud Logging and Cloud Monitoring for observability.
Tips for Production-Ready Pipelines
- Separate environments: Use multiple Cloud Build configs or substitution variables to target staging, UAT and production clusters.
- Secrets management: Rely on Google Secret Manager and Kubernetes Secret objects; never embed secrets directly in manifests or Cloud Build files.
- Rollbacks: Leverage deployment revision history and Cloud Build's ability to rebuild previous commits.
- Security: Use Workload Identity or dedicated service accounts with least privilege.
- Observability: Export metrics and logs to centralised dashboards. Kubernetes PodMonitoring or Prometheus operators can scrape application metrics.
By combining Cloud Build's declarative build steps with Kubernetes manifests, you can achieve a robust CI/CD pipeline that automatically tests, builds and deploys your application. The sample configuration shows a scalable approach: containerised builds, artifact management, and zero-downtime deployments to GKE. Adapt these concepts to your own services and you'll have a repeatable path from commit to production.