Skip to content

alioualarbi/cicd-with-cloudbuild-flask-app

Repository files navigation

CI/CD With cloudbuild

In this example show and explains how to create a continuous integration and delivery (CI/CD) pipeline on Google Cloud using only hosted products and the popular GitOps methodology.

img

At the end you create a CI/CD pipeline that automatically builds a container image from committed code, stores the image in Container Registry, updates a Kubernetes manifest in a Git repository, and deploys the application to Google Kubernetes Engine (GKE) using that manifest.

img

If the gcloud config get-value project command does not return the ID of the project you selected, configure Cloud Shell to use your project. gcloud config set project [PROJECT_ID]

Enable the required APIs.

gcloud services enable container.googleapis.com \
    cloudbuild.googleapis.com \
    sourcerepo.googleapis.com \
    containeranalysis.googleapis.com

Create a GKE cluster that you will use to deploy the sample application.

gcloud container clusters create hello-cloudbuild \
    --num-nodes 1 --zone us-central1-b

Use Git, configure it with your name and email address. Git will use those to identify you as the author of the commits

git config --global user.email "[YOUR_EMAIL_ADDRESS]"
git config --global user.name "[YOUR_NAME]"

Run the gcloud credential helper. This will connect your Google Cloud user account to Cloud Source Repositories.

git config --global credential.helper gcloud.sh

Creating the Git repositories in Cloud Source Repositories

Create the two Git repositories (app and env), and initialize the app one with some sample code. Create the two Git repositories.

gcloud source repos create hello-cloudbuild-app
gcloud source repos create hello-cloudbuild-env

Clone the sample code from GitHub.

cd ~
git clone https://github.com/alioualarbi/cicd-with-cloudbuild \
    hello-cloudbuild-app

Configure Cloud Source Repositories as a remote.

cd ~/hello-cloudbuild-app
PROJECT_ID=$(gcloud config get-value project)
git remote add google \
    "https://source.developers.google.com/p/${PROJECT_ID}/r/hello-cloudbuild-app"

app.py file

from flask import Flask
app = Flask('hello-cloudbuild')

@app.route('/')
def hello():
  return "Hello World!\n"

if __name__ == '__main__':
  app.run(host = '0.0.0.0', port = 8080)

Dockerfile

FROM python:3.7-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

With this Dockerfile, you can create a container image with Cloud Build and store it in Container Registry.

Create a Cloud Build build based on the latest commit with the following command.

cd ~/hello-cloudbuild-app
COMMIT_ID="$(git rev-parse --short=7 HEAD)"
gcloud builds submit --tag="gcr.io/${PROJECT_ID}/hello-cloudbuild:${COMMIT_ID}" .
Cloud Build streams the logs generated by the creation of the container image to your terminal when you execute this command.

Verify that your new container image is available in Container Registry.

Creating the continuous integration pipeline

You configure Cloud Build to automatically run a small unit test, build the container image, and then push it to Container Registry. Pushing a new commit to Cloud Source Repositories automatically triggers this pipeline. The *cloudbuild.yaml* file included in the code is the pipeline's configuration.

cloudbuild.yaml

steps:
# This step runs the unit tests on the app
- name: 'python:3.7-slim'
  id: Test
  entrypoint: /bin/sh
  args:
  - -c
  - 'pip install flask && python test_app.py -v'

# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Container Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'

Create Cloud Build Triggers

Open the Cloud Build Triggers page > Triggers > Create trigger. Fill out the following options as below:

  • Name field, type hello-cloudbuild.
  • Event, select Push to a branch.
  • Source, select hello-cloudbuild-app as your Repository and ^master$ as your Branch.
  • Build configuration, select Cloud Build configuration file.
  • Cloud Build configuration file location field, type cloudbuild.yaml after the /.

Click Create to save your build trigger.

Push the application code to Cloud Source Repositories to trigger the CI pipeline in Cloud Build.

cd ~/hello-cloudbuild-app
git push google master

Go to Cloud Build console > Cloud Build > Click the build to follow its execution and examine its logs.

Creating the continuous delivery pipeline

Granting Cloud Build access to GKE

Set the Project number environement and grant container.develope role to the Cloud build service account.

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT_NUMBER} \
    --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
    --role=roles/container.developer

Initializing the hello-cloudbuild-env repository

You need to initialize the hello-cloudbuild-env repository with two branches (production and candidate) and a Cloud Build configuration file describing the deployment process. Clone the hello-cloudbuild-env repository and create the production branch.

cd ~
gcloud source repos clone hello-cloudbuild-env
cd ~/hello-cloudbuild-env
git checkout -b production

Copy the cloudbuild-delivery.yaml file available in the hello-cloudbuild-app repository and commit the change.

cd ~/hello-cloudbuild-env
cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/hello-cloudbuild-env/cloudbuild.yaml
git add .
git commit -m "Create cloudbuild.yaml for deployment"

The cloudbuild-delivery.yaml file describes the deployment process to be run in Cloud Build. It has two steps: Cloud Build applies the manifest on the GKE cluster. If successful, Cloud Build copies the manifest on the production branch.

steps:
# This step deploys the new version of our container image
# in the hello-cloudbuild Kubernetes Engine cluster.
- name: 'gcr.io/cloud-builders/kubectl'
  id: Deploy
  args:
  - 'apply'
  - '-f'
  - 'kubernetes.yaml'
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=us-central1-b'
  - 'CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild'

# This step copies the applied manifest to the production branch
# The COMMIT_SHA variable is automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/git'
  id: Copy to production branch
  entrypoint: /bin/sh
  args:
  - '-c'
  - |
    set -x && \
    # Configure Git to create commits with Cloud Build's service account
    git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)') && \
    # Switch to the production branch and copy the kubernetes.yaml file from the candidate branch
    git fetch origin production && git checkout production && \
    git checkout $COMMIT_SHA kubernetes.yaml && \
    # Commit the kubernetes.yaml file with a descriptive commit message
    git commit -m "Manifest from commit $COMMIT_SHA
    $(git log --format=%B -n 1 $COMMIT_SHA)" && \
    # Push the changes back to Cloud Source Repository
    git push origin production
 

Create a candidate branch and push both branches for them to be available in Cloud Source Repositories.

git checkout -b candidate
git push origin production
git push origin candidate

Grant the Source Repository Writer IAM role to the Cloud Build service account for the hello-cloudbuild-env repository.

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
    --format='get(projectNumber)')"
cat >/tmp/hello-cloudbuild-env-policy.yaml <<EOF
bindings:
- members:
  - serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
  role: roles/source.writer
EOF
gcloud source repos set-iam-policy \
    hello-cloudbuild-env /tmp/hello-cloudbuild-env-policy.yaml

Creating the trigger for the continuous delivery pipeline

Configure the Cloud Build to be triggered by a push to the candidate branch of the hello-cloudbuild-env repository. Open the Triggers page of Cloud Build > Triggers > Create trigger.

Fill out the following options:

  • Name field, type hello-cloudbuild-deploy.
  • Event, select Push to a branch.
  • Source, select hello-cloudbuild-env as your Repository and ^candidate$ as your Branch.
  • Build configuration, select Cloud Build configuration file (yaml or json).
  • Cloud Build configuration file location location field, type cloudbuild.yaml after the /.

Click Create.

Modifying the continuous integration pipeline to trigger the continuous delivery pipeline

Add some steps to the continuous integration pipeline that generates a new version of the Kubernetes manifest and push it to the hello-cloudbuild-env repository to trigger the continuous delivery pipeline.

Replace the cloudbuild.yaml file with the extended example in the cloudbuild-trigger-cd.yaml file.

cd ~/hello-cloudbuild-app
cp cloudbuild-trigger-cd.yaml cloudbuild.yaml

The cloudbuild-trigger-cd.yaml is an extended version of the cloudbuild.yaml file. It adds steps to generate the new Kubernetes manifest and trigger the continuous delivery pipeline.

# This step clones the hello-cloudbuild-env repository
- name: 'gcr.io/cloud-builders/gcloud'
  id: Clone env repository
  entrypoint: /bin/sh
  args:
  - '-c'
  - |
    gcloud source repos clone hello-cloudbuild-env && \
    cd hello-cloudbuild-env && \
    git checkout candidate && \
    git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)')

# This step generates the new manifest
- name: 'gcr.io/cloud-builders/gcloud'
  id: Generate manifest
  entrypoint: /bin/sh
  args:
  - '-c'
  - |
     sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_ID}/g" kubernetes.yaml.tpl | \
     sed "s/COMMIT_SHA/${SHORT_SHA}/g" > hello-cloudbuild-env/kubernetes.yaml

# This step pushes the manifest back to hello-cloudbuild-env
- name: 'gcr.io/cloud-builders/gcloud'
  id: Push manifest
  entrypoint: /bin/sh
  args:
  - '-c'
  - |
    set -x && \
    cd hello-cloudbuild-env && \
    git add kubernetes.yaml && \
    git commit -m "Deploying image gcr.io/${PROJECT_ID}/hello-cloudbuild:${SHORT_SHA}
    Built from commit ${COMMIT_SHA} of repository hello-cloudbuild-app
    Author: $(git log --format='%an <%ae>' -n 1 HEAD)" && \
    git push origin candidate

Commit the modifications and push them to Cloud Source Repositories.

cd ~/hello-cloudbuild-app
git add cloudbuild.yaml
git commit -m "Trigger CD pipeline"
git push google master

This triggers the continuous integration pipeline in Cloud Build.

Examine the continuous integration build.

Go to Cloud Build, your recently run and finished builds for the hello-cloudbuild-app repository appear. You can click on a build to follow its execution and examine its logs. The last step of this pipeline pushes the new manifest to the hello-cloudbuild-env repository, which triggers the continuous delivery pipeline.

Examine the continuous delivery build.

Go to Cloud Build,your recently run and finished builds for the hello-cloudbuild-env repository appear. You can click on a build to follow its execution and examine its logs.

Testing the complete pipeline

The complete CI/CD pipeline is now configured. You test it from end to end. Go to the GKE Services page > Go to Google Kubernetes Engine Services The list contains a single service called hello-cloudbuild created by the recently completed continuous delivery build.

Click on the endpoint for the hello-cloudbuild service. "Hello World!" appears. If there is no endpoint, or if you see a load balancer error, you might have to wait a few minutes for the load balancer to be completely initialized.

Replace "Hello World" by "Hello Cloud Build", both in the application and in the unit test.

cd ~/hello-cloudbuild-app
sed -i 's/Hello World/Hello Cloud Build/g' app.py
sed -i 's/Hello World/Hello Cloud Build/g' test_app.py

Commit and push the change to Cloud Source Repositories.

git add app.py test_app.py
git commit -m "Hello Cloud Build"
git push google master

This triggers the full CI/CD pipeline. After a few minutes, reload the application in your browser. "Hello Cloud Build!" appears.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published