Continuous Delivery Pipelines with Spinnaker and Google Kubernetes Engine

This tutorial shows you how to create a continuous delivery pipeline using Google Kubernetes Engine (GKE), Cloud Source Repositories, Cloud Build, and Spinnaker. After you create a sample app, you configure these services to automatically build, test, and deploy it. When you modify the app code, the changes trigger the continuous delivery pipeline to automatically rebuild, retest, and redeploy the new version.

Pipeline architecture

The following diagram illustrates the architecture of the continuous delivery pipeline.

To continuously deliver app updates to your users, you need an automated process that reliably builds, tests, and updates your software. Code changes should automatically flow through a pipeline that includes artifact creation, unit testing, functional testing, and production rollout. In some cases, you want a code update to apply to only a subset of your users, so that it is exercised realistically before you push it to your entire user base. If one of these canary releases proves unsatisfactory, your automated procedure must be able to quickly roll back the software changes.

With GKE and Spinnaker, you can create a robust continuous delivery flow that helps to ensure your software is shipped as quickly as it is developed and validated. Although rapid iteration is your end goal, you must first ensure that each app revision passes through a series of automated validations before becoming a candidate for production rollout. When a given change has been vetted through automation, you can also validate the app manually and conduct further prerelease testing.

After your team decides the app is ready for production, one of your team members can approve it for production deployment.

App delivery pipeline

In this tutorial, you build the continuous delivery pipeline shown in the following diagram.

Architecture of the app delivery pipeline.

The high-level steps of this pipeline are as follows:

  1. A developer changes code and pushes it to a repository.
  2. Cloud Build detects the changes, builds the Docker image, tests the image, and pushes the image to Spinnaker.
  3. Spinnaker detecks the image, deploys image to Canary, and tests the Canary deployment. After a manual approval, Spinnaker deploys the image to production.


  • Set up your environment by launching Cloud Shell, creating a GKE cluster, and configuring your identity and user management scheme.
  • Download a sample app, create a Git repository, and upload it to a Cloud Source Repositories.
  • Deploy Spinnaker to GKE using Helm.
  • Build your Docker image.
  • Create triggers to create Docker images when your app changes.
  • Configure a Spinnaker pipeline to reliably and continuously deploy your app to GKE.
  • Deploy a code change, triggering the pipeline, and watch it roll out to production.


This tutorial uses billable components of Google Cloud Platform (GCP), including:

  • GKE
  • Cloud Load Balancing
  • Cloud Build

Use the Pricing Calculator to generate a cost estimate based on your projected usage.

New GCP users might be eligible for a free trial.

Before you begin

  1. Select or create a GCP project.

Note : If you don’t plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

2. Make sure that billing is enabled for your Google Cloud Platform project.LEARN HOW TO ENABLE BILLING
3. Enable the GKE, Cloud Build, and Cloud Source Repositories APIs.ENABLE THE APIS

Set up your environment

In this section, you configure the infrastructure and identities required to complete the tutorial.

Start a Cloud Shell instance and create a GKE cluster

You run all the terminal commands in this tutorial from Cloud Shell.

  1. Open Cloud Shell:OPEN CLOUD SHELL
  2. Create a GKE cluster to deploy Spinnaker and the sample app with the following commands:

gcloud config set compute/zone us-central1-f

gcloud container clusters create spinnaker-tutorial \ --machine-type=n1-standard-2

Configure identity and access management

You create a Cloud Identity and Access Management (Cloud IAM) service account to delegate permissions to Spinnaker, allowing it to store data in Cloud Storage. Spinnaker stores its pipeline data in Cloud Storage to ensure reliability and resiliency. If your Spinnaker deployment unexpectedly fails, you can create an identical deployment in minutes with access to the same pipeline data as the original.

  1. Create the service account:

gcloud iam service-accounts create spinnaker-account \ --display-name spinnaker-account

  1. Store the service account email address and your current project ID in environment variables for use in later commands:

export SA_EMAIL=$(gcloud iam service-accounts list \ --filter=“displayName:spinnaker-account” \ --format=‘value(email)’) export PROJECT=$(gcloud info --format=‘value(config.project)’)

  1. Bind the storage.admin role to your service account:

gcloud projects add-iam-policy-binding \ $PROJECT --role roles/storage.admin --member serviceAccount:$SA_EMAIL

  1. Download the service account key. You need this key later when you install Spinnaker and upload the key to GKE.

gcloud iam service-accounts keys create spinnaker-sa.json --iam-account $SA_EMAIL

Set up Cloud Pub/Sub to trigger Spinnaker pipelines

  1. Create the Cloud Pub/Sub topic for notifications from Container Registry. This command might fail with the error “Resource already exists in the project”, which means that the topic has already been created for you.

gcloud beta pubsub topics create projects/$PROJECT/topics/gcr

  1. Create a subscription that Spinnaker can read from to receive notifications of images being pushed.

gcloud beta pubsub subscriptions create gcr-triggers \ --topic projects/${PROJECT}/topics/gcr

  1. Give Spinnaker’s service account permissions to read from the gcr-triggers subscription.

export SA_EMAIL=$(gcloud iam service-accounts list \ --filter=“displayName:spinnaker-account” \ --format=‘value(email)’) gcloud beta pubsub subscriptions add-iam-policy-binding gcr-triggers \ --role roles/pubsub.subscriber --member serviceAccount:$SA_EMAIL

Deploying Spinnaker using Helm

In this section, you use Helm to deploy Spinnaker from the Charts repository. Helm is a package manager you can use to configure and deploy Kubernetes apps.

Install Helm

  1. Download and install the helm binary:
  1. Unzip the file to your local system:
tar zxfv helm-v2.10.0-linux-amd64.tar.gz

cp linux-amd64/helm .
  1. Grant Tiller, the server side of Helm, the cluster-admin role in your cluster:
kubectl create clusterrolebinding user-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account) kubectl create serviceaccount tiller --namespace kube-system kubectl create clusterrolebinding tiller-admin-binding --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
  1. Grant Spinnaker the cluster-admin role so it can deploy resources across all namespaces:
kubectl create clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:default spinnaker-admin
  1. Initialize Helm to install Tiller in your cluster:
./helm init --service-account=tiller

./helm update
  1. Ensure that Helm is properly installed by running the following command. If Helm is correctly installed, v2.10.0 appears for both client and server.
./helm version

Client: &version.Version{SemVer:“v2.10.0”, GitCommit:“9ad53aac42165a5fadc6c87be0dea6b115f93090”, GitTreeState:“clean”} Server: &version.Version{SemVer:“v2.10.0”, GitCommit:“9ad53aac42165a5fadc6c87be0dea6b115f93090”, GitTreeState:“clean”}

Configure Spinnaker

  1. Create a bucket for Spinnaker to store its pipeline configuration:
export PROJECT=$(gcloud info \ --format='value(config.project)') export BUCKET=$PROJECT-spinnaker-config gsutil mb -c regional -l us-central1 gs://$BUCKET
  1. Create the file ( spinnaker-config.yaml ) describing the configuration for how Spinnaker should be installed:
export SA_JSON=$(cat spinnaker-sa.json)export PROJECT=$(gcloud info --format='value(config.project)')export BUCKET=$PROJECT-spinnaker-configcat > spinnaker-config.yaml <<EOFgcs:  enabled: true  bucket: $BUCKET  project: $PROJECT  jsonKey: '$SA_JSON'dockerRegistries:- name: gcr  address:  username: _json_key  password: '$SA_JSON'  email: [email protected]# Disable minio as the default storage backendminio:  enabled: false# Configure Spinnaker to enable GCP serviceshalyard:  spinnakerVersion: 1.10.2  image:    tag: 1.12.0  additionalScripts:    create: true    data: |-        \$HAL_COMMAND config artifact gcs account add gcs-$PROJECT --json-path /opt/gcs/key.json        \$HAL_COMMAND config artifact gcs enable |-        \$HAL_COMMAND config pubsub google enable        \$HAL_COMMAND config pubsub google subscription add gcr-triggers \          --subscription-name gcr-triggers \          --json-path /opt/gcs/key.json \          --project $PROJECT \          --message-format GCREOF

Deploy the Spinnaker chart

  1. Use the Helm command-line interface to deploy the chart with your configuration set. This command typically takes five to ten minutes to complete.
./helm install -n cd stable/spinnaker -f spinnaker-config.yaml --timeout 600 \ --version 1.1.6 --wait
  1. After the command completes, run the following command to set up port forwarding to the Spinnaker UI from Cloud Shell:
export DECK_POD=$(kubectl get pods --namespace default -l "cluster=spin-deck" \ -o jsonpath="{.items[0]}") kubectl port-forward --namespace default $DECK_POD 8080:9000 >> /dev/null &

Note: This command can take several minutes to complete. Be sure to wait until you see that it has succeeded before proceeding.

  1. To open the Spinnaker user interface, click Web Preview in Cloud Shell and click Preview on port 8080 .Web preview drop-down list with preview selected.
  2. You see the welcome screen, followed by the Spinnaker UI:

Building the Docker image

In this section, you configure Cloud Build to detect changes to your app source code, build a Docker image, and then push it to Container Registry.

Create your source code repository

  1. In Cloud Shell, download the sample source code:


  1. Unpack the source code:

tar xzfv sample-app-v2.tgz

  1. Change directories to the source code:

cd sample-app

  1. Set the username and email address for your Git commits in this repository. Replace [EMAIL_ADDRESS] with your Git email address, and replace [USERNAME] with your Git username.

git config --global “[EMAIL_ADDRESS]” git config --global “[USERNAME]”

  1. Make the initial commit to your source code repository:

git init git add . git commit -m “Initial commit”

  1. Create a repository to host your code:

gcloud source repos create sample-app git config credential.helper

  1. Add your newly created repository as remote:

export PROJECT=$(gcloud info --format=‘value(config.project)’) git remote add origin$PROJECT/r/sample-app

  1. Push your code to the new repository’s master branch:

git push origin master

  1. Check that you can see your source code in the console:GO TO THE SOURCE CODE PAGE

Configure your build triggers

The following diagram illustrates the trigger that you build in this section.

Spinnaker workflow

You configure Cloud Build to build and push your Docker images every time you push Git tags to your source repository. Cloud Build automatically checks out your source code, builds the Docker image from the Dockerfile in your repository, and pushes that image to Container Registry.

  1. In the GCP Console, in the Cloud Build section, click Build Triggers .GO TO THE BUILD TRIGGERS PAGE
  2. Select Cloud Source Repository and click Continue .
  3. Select your newly created sample-app repository from the list, and click Continue .
  4. Set the following trigger settings:
  • Name : sample-app-tags
  • Trigger type : Tag
  • Tag (regex) : v.*
  • Build configuration : cloudbuild.yaml
  • cloudbuild.yaml location : cloudbuild.yaml
  1. Click Create trigger .

From now on, whenever you push a Git tag prefixed with the letter “v” to your source code repository, Cloud Build automatically builds and pushes your app as a Docker image to Container Registry.

Prepare your Kubernetes Manifests for use in Spinnaker

Spinnaker needs access to your Kubernetes manifests in order to deploy them to your clusters. This section creates a Cloud Storage bucket that will be populated with your manifests during the CI process in Cloud Build. After your manifests are in Cloud Storage, Spinnaker can download and apply them during your pipeline’s execution.

  1. Create the bucket.
export PROJECT=$(gcloud info --format='value(config.project)') gsutil mb -l us-central1 gs://$PROJECT-kubernetes-manifests
  1. Enable versioning on the bucket so that you have a history of your manifests.
gsutil versioning set on gs://$PROJECT-kubernetes-manifests
  1. Set the correct GCP project ID in your Kubernetes deployment manifests:
sed -i s/PROJECT/$PROJECT/g k8s/deployments/*
  1. Commit the changes to the repository:
git commit -a -m "Set project ID"

Build your image

Push your first image using the following steps:

  1. Go to your source code folder in Cloud Shell.
  2. Create a Git tag:
git tag v1.0.0
  1. Push the tag:
git push --tags
  1. In Cloud Build , click Build History to check that the build has been triggered. If not, verify the trigger was configured properly in the previous section.GO TO BUILD HISTORY

Configuring your deployment pipelines

Now that your images are building automatically, you need to deploy them to the Kubernetes cluster.

The following diagram illustrates the deployment pipeline steps.

Configure deployment pipeline.

You deploy to a scaled-down environment for integration testing. After the integration tests pass, you must manually approve the changes to deploy the code to production services.

Install the spin CLI for managing Spinnaker

Spin is a command-line utility for managing Spinnaker’s applications and pipelines.

  1. Download the latest version of spin .
curl -LO
  1. Make spin executable.
chmod +x spin

Create the deployment pipeline

  1. Use spin to create an app in Spinnaker.
./spin application save --application-name sample \ --owner-email [email protected] \ --cloud-providers kubernetes \ --gate-endpoint http://localhost:8080/gate

Next, you create the continuous delivery pipeline. In this tutorial, the pipeline is configured to detect when a Docker image with a tag prefixed with “v” has arrived in your Container Registry.

  1. In a new tab of Cloud Shell, run the following command in the source code directory to upload an example pipeline to your Spinnaker instance:
export PROJECT=$(gcloud info --format='value(config.project)') sed s/PROJECT/$PROJECT/g spinnaker/pipeline-deploy.json > pipeline.json ./spin pipeline save --gate-endpoint http://localhost:8080/gate -f pipeline.json

View your pipeline execution

The configuration you just created uses notifications of newly tagged images being pushed to trigger a Spinnaker pipeline. In a previous step, you pushed a tag to the Cloud Source Repositories which triggered Cloud Build to build and push your image to Container Registry. You can now check on the pipeline that was triggered.

  1. Return to the Pipelines page by clicking Pipelines .
  2. Click Details to see more information about the pipeline’s progress. This section shows the status of the deployment pipeline and its steps. Steps in blue are currently running, green ones have completed successfully, and red ones have failed. Click a stage to see details about it.After 3 to 5 minutes the integration test phase completes and the pipeline requires manual approval to continue the deployment.
  3. Hold the pointer over Push to production , and then click Continue .push to productionYour rollout continues to the production frontend and backend deployments. It completes after a few minutes.
  4. To view the app, select Infrastructure > Load Balancers in the top of the Spinnaker UI.Infrastructure drop-down list with Load Balancers selected.
  5. Scroll down the list of load balancers and click Default , under sample-frontend-production .
  6. Scroll down the details pane on the right and copy your app’s IP address by clicking the clipboard button on the Ingress IP. The ingress IP link from the Spinnaker UI uses HTTPS by default, but the application is configured to use HTTP.Details pane with your app's IP address.
  7. Paste the address into your browser to view the production version of the app.Production version of the app.You have now manually triggered the pipeline to build, test, and deploy your app.

Triggering your pipeline from code changes

In this section, you test the pipeline end to end by making a code change, pushing a Git tag, and watching the pipeline run in response. By pushing a Git tag that starts with “v”, you trigger Cloud Build to build a new Docker image and push it to Container Registry. Spinnaker detects that the new image tag begins with “v” and triggers a pipeline to deploy the image to canaries, run tests, and roll out the same image to all pods in the deployment.

  1. Chnge the color of the app from orange to blue:
sed -i 's/orange/blue/g' cmd/gke-info/common-service.go
  1. Tag your change and push it to the source code repository:
git commit -a -m "Change color to blue" git tag v1.0.1 git push --tags
  1. See the new build appear in the Cloud Build Build History.
  2. Click Pipelines to watch the pipeline start to deploy the image.
  3. Observe the canary deployments. When the deployment is paused, waiting to roll out to production, start refreshing the tab that contains your app. Four of your backends are running the previous version of your app, while only one backend is running the canary. You should see the new, blue version of your app appear about every tenth time you refresh.
  4. After testing completes, return to the Spinnaker tab and approve the deployment.
  5. When the pipeline completes, your app looks like the following screenshot. The Version field now reads v1.0.1 .You have now successfully rolled out your app to your entire production environment!
  6. Optionally, you can roll back this change by reverting your previous commit. Rolling back adds a new tag (v1.0.2 ), and pushes the tag back through the same pipeline you used to deploy v1.0.1 :
git revert v1.0.1 git tag v1.0.2 git push --tags

Cleaning up

To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial:

  1. Delete the Spinnaker installation:
../helm delete --purge cd
  1. Delete the sample app services:
kubectl delete -f k8s/services
  1. Remove the service account IAM bindings:
export SA_EMAIL=$(gcloud iam service-accounts list \ --filter="displayName:spinnaker-account" --format='value(email)') export PROJECT=$(gcloud info --format='value(config.project)') gcloud projects remove-iam-policy-binding $PROJECT --role roles/storage.admin --member serviceAccount:$SA_EMAIL
  1. Delete the service account:
export SA_EMAIL=$(gcloud iam service-accounts list \ --filter="displayName:spinnaker-account" --format='value(email)') gcloud iam service-accounts delete $SA_EMAIL
  1. Delete the GKE cluster:
gcloud container clusters delete spinnaker-tutorial --zone=us-central1-f
  1. Delete the repository:
gcloud source repos delete sample-app
  1. Delete the bucket:
export PROJECT=$(gcloud info --format='value(config.project)') export BUCKET=$PROJECT-spinnaker-config gsutil -m rm -r gs://$BUCKET
  1. Delete your container images:
export PROJECT=$(gcloud info --format='value(config.project)') gcloud container images delete$PROJECT/sample-app:v1.0.0 gcloud container images delete$PROJECT/sample-app:v1.0.1
  1. If you created v1.0.2 in the optional rollback step above, delete that container image:
gcloud container images delete$PROJECT/sample-app:v1.0.2