-
Notifications
You must be signed in to change notification settings - Fork 0
Google Cloud Build
Documentation is here
It's nice to have two configurations - cloudbuild.yaml
which is invoked automatically on push. This configuration represents development pipeline, therefore it should build everything in development mode and deploy to staging environment.
Second configuration is called cloudbuild-prod.yaml
and is invoked manually from gcloud console. This pipeline builds components in production mode and deploys application to production environment.
Following example shows build of microservice project with caching of Go in Google Cloud Storage. Output artifact of this build is Docker image, that is build and pushed in last two steps.
steps:
# Cache cloning with the following priority: branch cache -> master cache -> no cache
- name: gcr.io/cloud-builders/gsutil
entrypoint: '/bin/bash'
args:
[
'-c',
'gsutil -m -q cp gs://flowup-golang-cache/${_PROJECT}-$BRANCH_NAME.${_EXTENSION} /workspace || gsutil -m -q cp gs://flowup-golang-cache/${_PROJECT}-${_MASTER_BRANCH}.${_EXTENSION} /workspace || exit 0',
]
# Cache extraction
- name: ubuntu
entrypoint: '/bin/bash'
args:
[
'-c',
'tar -xf ${_PROJECT}-$BRANCH_NAME.${_EXTENSION} || tar -xf ${_PROJECT}-${_MASTER_BRANCH}.${_EXTENSION} || exit 0',
]
# Creating binaries folder
- name: ubuntu
entrypoint: '/bin/bash'
args: ['-c', 'mkdir bin']
# Go install with dependencies download, saving binaries to previously created bin folder
- name: golang:1.11
entrypoint: 'go'
args: ['install', '-installsuffix', 'cgo', './...']
env:
- 'GOCACHE=/workspace/.cache'
- 'GOBIN=/workspace/bin'
- 'CGO_ENABLED=0'
- 'GOOS=linux'
# Cache compression with upload to bucket
- name: ubuntu
entrypoint: tar
args: ['-cf', '${_PROJECT}-$BRANCH_NAME.${_EXTENSION}', '.cache']
- name: gcr.io/cloud-builders/gsutil
args: ['-m', '-q', 'cp', '/workspace/${_PROJECT}-$BRANCH_NAME.${_EXTENSION}', 'gs://flowup-golang-cache']
# Building backend dockerfile
- name: gcr.io/cloud-builders/docker
args: ['build', '-t', 'gcr.io/$PROJECT_ID/project-name:$BRANCH_NAME-$COMMIT_SHA', '-f', 'ci.Dockerfile', '.']
- id: backend-push
name: gcr.io/cloud-builders/docker
args: ['push', 'gcr.io/$PROJECT_ID/project-name:$BRANCH_NAME-$COMMIT_SHA']
substitutions:
_PROJECT: project-name
_EXTENSION: tar.gz
_MASTER_BRANCH: master
options:
substitution_option: 'ALLOW_LOOSE'
Dockerfile is in the root of project and is named ci.Dockerfile
:
FROM alpine:latest
WORKDIR /root/
RUN apk --no-cache add ca-certificates
COPY /bin .
cloudbuild.yaml
can also have one more step that tests the code.
Most of the project use monorepo, therefore, frontend is stored in repository in directory ui
.
Frontend can be hosted by multiple ways: from bucket or from container (in Kubernetes).
# Build frontend
- id: build-frontend
name: gcr.io/cloud-builders/npm:node-10.10.0
entrypoint: '/bin/bash'
dir: 'ui'
args:
- '-c'
- 'npm ci && npm run lint && npm run build -- --progress=false --base-href /$BRANCH_NAME/'
env:
- 'BRANCH_NAME=$BRANCH_NAME'
- 'COMMIT_SHA=$COMMIT_SHA'
- 'BUILD_ID=$BUILD_ID'
- 'TAG_NAME=$TAG_NAME'
# Deploy frontend
- id: deploy-frontend
name: gcr.io/cloud-builders/gsutil
dir: 'ui'
args: ['-m', '-h', 'Cache-Control:public,max-age=60', 'rsync', '-d', '-r', '-a', 'public-read', 'dist', 'gs://some-bucket/project-name/$BRANCH_NAME']
waitFor: ['build-frontend']
substitutions:
_PROJECT: project-name
_EXTENSION: tar.gz
_MASTER_BRANCH: master
options:
substitution_option: 'ALLOW_LOOSE'
When frontend is server from container, it needs to have Docker image build and pushed to repository. Deployment is similar to deployment of backend and it is described in next section
cloudbuilder.yaml
:
steps:
- id: build-docker-frontend
name: gcr.io/cloud-builders/docker
dir: ui
args: ['build', '-t', 'gcr.io/$PROJECT_ID/project-name-fe:$BRANCH_NAME-$COMMIT_SHA', '.']
- id: push-docker-frontend
name: gcr.io/cloud-builders/docker
args: ['push', 'gcr.io/$PROJECT_ID/project-name-fe:$BRANCH_NAME-$COMMIT_SHA']
waitFor:
- build-docker-frontend
substitutions:
_PROJECT: project-name
_EXTENSION: tar.gz
_MASTER_BRANCH: master
options:
substitution_option: 'ALLOW_LOOSE'
and ui/Dockerfile
contains following:
FROM node:10
WORKDIR /app
COPY . .
RUN npm ci --silent
RUN npm run lint
RUN npm run build -- --progress=false --base-href "/"
# prod build
FROM flowup/noginx
ENV BASE_HREF /
ENV STATIC_SITE_PATH /static
COPY --from=0 /app/dist /static
Steps in Dockerfile are equivalent to steps in cloudbuilder.yaml for building frontend served from bucket.
Application is deployed using Helm, which has special cloud builder step, which runs only on master
branch. Helm step can look as following:
steps:
- id: deploy-backend
name: gcr.io/$PROJECT_ID/helm
args:
- 'upgrade'
- '--install'
- 'project-name'
- '--namespace'
- 'project-name'
- '--set'
- 'global.image.tag=$BRANCH_NAME-$COMMIT_SHA'
- '--set'
- 'frontend.image.tag=$BRANCH_NAME-$COMMIT_SHA'
- './helm/dashly'
env:
- CLOUDSDK_COMPUTE_ZONE=europe-west1-d # Cluster's zone
- CLOUDSDK_CONTAINER_CLUSTER=staging-cluster # Cluster's name
- BRANCH_NAME=$BRANCH_NAME
waitFor:
- push-backend
- push-docker-frontend
Why are not images pushed by providing list of images to push?
Because images are pushed after all steps are successful. Despite that, application cannot be deployed correctly in one of the steps, because deployment depends on pushing images to Docker registry.
Some steps need specific permission to access components of Google Cloud. These permission can be added to Cloud Builder service account in IAM. Name of the account is in form id@cloudbuild.gserviceaccount.com
.
Table of permission:
Step | Permissions |
---|---|
Deploy of frontend to bucket |
Storage Object Admin + correct ACL's in bucket |
Deploy of application to Kubernetes cluster (Helm) | Kubernetes Engine Admin |
-
FE / Angular
-
Golang
-
DevOps
-
Agile process and SCRUM
-
Internship
-
Bootcamp