Introduction
We just created a simple Vue application deployed in Firebase in the previous article. Like I mentioned many times in this blog, regular feedback is key to build a successful application.
Let's enhance the Continuous Deployment pipeline we created in Continuous Deployment pipeline with Cloud Build on Cloud Run to add automatic deployment of the frontend part of our application.
To remind you, we previously created a nice Vue application communicating with a Cloud Run backend application. The application was deployed on Firebase to have an international audience with no effort. Let's automate this deployment.
Here is the Github project you can clone to follow the article.
Create the Continuous Deployment pipeline for a Firebase deployed application
I won't cover all the subjects like I did in Continuous Deployment pipeline with Cloud Build on Cloud Run. If you started from here and you wish to configure your GCP project to use Cloud Build, please check previous articles.
Create a Firebase deployment image
To deploy using Firebase, you need to create an image containing the Firebase CLI, making it easy to deploy your application. Here is the official link to create a Firebase image to deploy your application. At the time of the article, here are the commands:
git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/firebase
gcloud builds submit .
cd ../..
rm -rf cloud-builders-community/
If these steps do not work for you, check the official link to make sure it didn't change since the article was written (which is very likely)
The steps above create and push a Firebase ready image on your Container Registry which can be used in a Cloud Build pipeline.
To make sure the image is in your container, try executing this command:
gcloud container images list --filter="name:firebase"
NAME
gcr.io/truaro-resources/firebase
Cloud Build pipeline for Vue application in Firebase
Now you have a Firebase image, let's add new steps to deploy our Vue application. As a reminder, you should have the following project structure:
* gcpapplication/
|--- gcpcloudrunback/
|--- gcpfirebasefront/
|--- cloudbuild.yaml
The content of cloudbuild.yaml
after the first pipeline is:
steps:
- id: 'dockerize-project'
name: gcr.io/cloud-builders/docker
dir: gcpcloudrunback
args: ['build',
'-t', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA',
'-t', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest',
'.']
- id: 'push-to-cloud-registry'
name: gcr.io/cloud-builders/docker
args: ['push', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA']
- id: 'deploy-cloud-run'
name: gcr.io/cloud-builders/gcloud
dir: gcpcloudrunback
entrypoint: bash
args:
- '-c'
- |
apt-get update
apt-get install -qq -y gettext
export PROJECT_ID=$PROJECT_ID
export IMAGE_VERSION=$SHORT_SHA
export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}
envsubst < gcp-cloudrun-back.yaml > gcp-cloudrun-back_with_env.yaml
gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \
--platform=managed --region=europe-west1
gcloud run services add-iam-policy-binding gcp-cloudrun-back \
--platform=managed --region=europe-west1 \
--member="allUsers" --role="roles/run.invoker"
images:
- 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'
- 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'
It is deploying only the backend part of our application.
Let's add the Frontend part now.
Configuring Cloud Build to deploy on Firebase
To deploy on Firebase, your Cloud Build service account must have the role Firebase Admin. Grant the role to your service account with:
gcloud projects add-iam-policy-binding ${YOUR_PROJECT_ID} \
--member=serviceAccount:XXXX@cloudbuild.gserviceaccount.com \
--role=roles/firebase.admin
- Replace
${YOUR_PROJECT_ID}
andXXXX
by your actual valueroles/firebase.admin
: To allow the Cloud Build service account to deploy on Firebase
Or, if you follow this, you can also manually enable the Firebase Admin role.
Adding steps to deploy Vue in Firebase
Just add the following 3 steps between the step deploy-cloud-run
and the images
part:
- id: 'install-yarn'
waitFor: ['-']
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: ['install', '--silent']
- id: 'build-front'
waitFor: [ 'install-yarn' ]
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: [ 'build' ]
- id: 'deploy-firebase'
waitFor: [ 'build-front' ]
name: gcr.io/${PROJECT_ID}/firebase
args: [ 'deploy', '--project=$PROJECT_ID', '--only', 'hosting' ]
dir: gcpfirebasefront
As always, let's review the important part.
Install the yarn dependencies
- id: 'install-yarn'
waitFor: ['-']
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: ['install', '--silent']
waitFor: [ '-' ]
: Run this step in parallel with the others. Indeed, we don't need to wait for the backend to be deployed in order to deploy the front. It is a performance improvement of your CD pipeline.name: node
: We use the officialnode
image from the Docker hub registry.entrypoint: yarn
:yarn
is the package manager we use to build the application.dir: gcpfirebasefront
: We change the directory to go in thegcpfirebasefront
.args: [ 'install', '--silent' ]
: Just run theyarn install --silent
command, to package our application for production. Not mandatory, but I findyarn install
to be quite verbose.
Build the Vue application
- id: 'build-front'
waitFor: [ 'install-yarn' ]
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: [ 'build' ]
waitFor: [ 'install-yarn' ]
:yarn install
needs to be done before moving furtherargs: [ 'build' ]
: Just run theyarn build
command, to package our application for production
As you can see, we do not create any Docker image. We simply build the application using yarn build
. The dist
folder is located under /workspace
, the shared volumes of Cloud Build. The next step will be able to deploy on Firebase what has been built at this step.
Deploy on Firebase
- id: 'deploy-firebase'
waitFor: [ 'build-front' ]
name: gcr.io/$PROJECT_ID/firebase
args: [ 'deploy', '--project=$PROJECT_ID', '--only', 'hosting' ]
dir: gcpfirebasefront
waitFor: [ 'build-front' ]
: Just wait for the previous step, not the backend steps.name: gcr.io/$PROJECT_ID/firebase
: Step based on the image we created earlier to use the Firebase CLIargs: [ 'deploy', '--project=$PROJECT_ID', '--only', 'hosting' ]
: The args to be passed tofirebase
command$PROJECT_ID
: As always, a substitution variable provided by Cloud Build
This step simply executes the command firebase deploy --project=${PROJECT_ID} --only hosting
. If you remember well, it was the same command we used in the previous article from our local machine.
Once again, do not go too fast, start slow with easy steps. You can only create a Continuous Deployment pipeline if you have something to automate...
Review the Cloud Build deployment file
The final file looks like:
steps:
- id: 'dockerize-project'
name: gcr.io/cloud-builders/docker
dir: gcpcloudrunback
args: [ 'build',
'-t', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA',
'-t', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest',
'.' ]
- id: 'push-to-cloud-registry'
name: gcr.io/cloud-builders/docker
args: [ 'push', 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA' ]
- id: 'deploy-cloud-run'
name: gcr.io/cloud-builders/gcloud
dir: gcpcloudrunback
entrypoint: bash
args:
- '-c'
- |
apt-get update
apt-get install -qq -y gettext
export PROJECT_ID=$PROJECT_ID
export IMAGE_VERSION=$SHORT_SHA
export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}
envsubst < gcp-cloudrun-back.yaml > gcp-cloudrun-back_with_env.yaml
gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \
--platform=managed --region=europe-west1
gcloud run services add-iam-policy-binding gcp-cloudrun-back \
--platform=managed --region=europe-west1 \
--member="allUsers" --role="roles/run.invoker"
- id: 'install-yarn'
waitFor: ['-']
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: ['install', '--silent']
- id: 'build-front'
waitFor: [ 'install-yarn' ]
name: node
entrypoint: yarn
dir: gcpfirebasefront
args: [ 'build' ]
- id: 'deploy-firebase'
waitFor: [ 'build-front' ]
name: gcr.io/$PROJECT_ID/firebase
args: [ 'deploy', '--project=$PROJECT_ID', '--only', 'hosting' ]
dir: gcpfirebasefront
images:
- 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'
- 'gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'
Change the Vue application to see a change
Just change the gcpfirebasefront/src/HelloWorld.vue
file. Update the <h1>
to see the Frontend has been deployed:
<template>
<div class="hello">
<h1>From firebase: {{ msg }}</h1>
As we already have the Cloud Build triggers created in a previous article, simply push your application and everything will be automatically deployed.
To follow up the build execution, go on the GCP console, you will have the logs. Or just use this command to see if the deployment is still ongoing:
gcloud builds list --ongoing
46e2992d-24f4-4df5-8190-a6c132a4827b 2020-12-05T12:37:43+00:00 github_truar_try-gcp-deploy@80c5b6d1eb9bc5425e2abec092535ff2a30ee5b6 - WORKING
Once the build is finished (i.e the command output is empty), just access your firebase web application page. Here is my result:
Summary
In this article, we covered:
- Creating a Firebase image to use the
firebase
CLI in a Cloud Build step - Configuring the Cloud Build service account to deploy applications on Firebase
- Configuring Cloud Build steps to build and deploy a static frontend application on Firebase
With all the previous articles, we are starting to have a nice view of the tools and services we can lever to easily deploy an application on the Cloud. You might have noticed, the pricing is very appealing for an application that is available 24/7 everywhere in the world. This is one of the main advantages of GCP platform, you can build international applications for a very attractive price.
What's next
To go further, we could:
- Configure a cache to improve build speed to avoid downloading frontend dependencies at each build
- Use the multi-site Firebase feature to have a staging and production website
- Use the new preview channel feature
- Split the
cloudbuild.yaml
file to deploy independently the frontend and the backend