Create a CI/CD Pipeline With Google App Engine and GitHub Actions.
Introduction.
Google App Engine (GAE) is a cloud computing platform as a service for developing and hosting web applications and mobile backends, it allows developers to build scalable web and mobile backends in any programing language. Deploying an app to GAE is a simple matter of having a Google cloud account and, Google cloud SDK installed and configured in your local machine and an app.yaml configuration file in your project directory. With these things in place deploying to GAE is just one command; gcloud app deploy. While the above process is easy and simple to use for one-man projects, when working on larger projects involving multiple contributors a continuous integration and development pipeline with automated testing and deployment is necessary for peace of mind and sanity of all involved.
GitHub Actions is an event-driven CI/CD pipeline that runs automated tasks in your software development life cycle based on specified events such as pull requests and pushes. Automating tasks with GitHub actions is achieved with the combination of GitHub components such as workflows, events, actions, jobs, steps, runners, secrets among others. To see these things in action we are going to deploy a simple express.js app to GAE standard environment. You can pull a sample app from my repo using this link https://github.com/Ichinga-Samuel/Book-Lovers.git or build a simpler app.
Setting Up the App.
To go along with the sample app you will need to either set up a MonogDB server in your local machine or go to https://cloud.mongodb.com and create a free tier cloud Atlas database, whichever one you choose copy the connection URI string and create a configuration file in the config folder. Name this file gae.env, it will contain our environment variables and app secrets, assign the connection string to the MONGO_URI variable.
You can try out google cloud platform offerings for 90 days and with $300 to spend on cloud resources all you need is a Gmail account that has a payment method in other to enable billing. Following instructions from Google cloud docs start a new project then create a service account and, a cloud storage bucket. In other to deploy an app to GAE using GitHub actions your service account will need to be granted certain roles as listed below.
· App Engine Admin(appengine.appAdmin) to manage all App Engine Resources
· Service Account User (roles/iam.serviceAccountUser): to deploy as the service account
· Storage Admin (roles/compute.storageAdmin): to upload files
· Cloud Build Editor (cloudbuild.builds.editor): to build the application
After enabling these roles for the service account create and download the JSON key file and store it in the config folder of the project.
In the gae.env configuration file add the following variables.
MONGO_URI=mongo connection string
GCS_BUCKET=bucketname
GCLOUD_PROJECT=project_id
GCS_KEYFILE=path to the service account key file eg. config/key.json
PORT=3000
NODE_ENV=production
The app is now ready to run in the development environment, we can now turn our focus on making it ready for deployment via GitHub actions. We start by creating the app.yaml configuration file at the root of our project. To use the necessary actions for deployment we have to create some secrets in our repo. These secrets contain the credentials needed for authentication when deploying via GitHub Actions, they are the project ID and the service accounts key file. To add a secret to a repository go to the secrets tab under repository settings click on the New repository secret button on the top right section and add a name and value for the secret you wish to add. Our project ID will be stored as GCP_PROJECT and the key file as GCP_SA_KEY.
Since we can not commit our .env and service account key files to our repo we need a way to inject them back into the project directory before deployment as they are needed for our app to run in the production environment. There are a few different ways of doing this but one simple method is to encode these files as base64 strings and store them as secrets in the repo. We can then decode them during the deployment workflow and write them back to the appropriate directory. To do this run either of these commands below for bash or PowerShell copy the output and save it as a secret named GAE_CONFIG.
For Bash
base64 config/gae.env
For Windows Powershell
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($(cat config/gae.env )))
Set up The Workflow.
The final step is to create a workflow yaml file in the .github/workflow folder this file contains the customizations required for carrying out our tasks.
name: Book Lovers Deployment
on:
push:
branches: [production]
jobs:
Deploy_to_GAE:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Load Config Files
run: |
echo "${{secrets.GAE_CONFIG}}" | base64 --decode > config/gae.env
echo "${{secrets.GCP_SA_KEY}}" > config/booklovers-v1-5606eddf4394.json
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16.x
- run: npm install
- run: npm run test
- name: Setup Google Cloud SDK
uses: google-github-actions/setup-gcloud@v0.2.0
with:
project_id: ${{secrets.GCP_PROJECT}}
service_account_key: ${{secrets.GCP_SA_KEY}}
- name: Deploy
uses: google-github-actions/deploy-appengine@v0.2.0
with:
deliverables: app.yaml
project_id: ${{ secrets.GCP_PROJECT }}
credentials: ${{ secrets.GCP_SA_KEY }}
promote: true
version: v1
Workflow files are written in a declarative style. They consist of jobs which in turn consist of steps and actions. Jobs are usually run in parallel but can be made to run in sequence if need be. Let us walk through the workflow file above.
It will be triggered to run by a push event to the production branch as specified by the on key.
It has only one job which is to deploy the app to GAE. This job will run on an Ubuntu Virtual machine.
The first step in carrying out this job is to copy files from our repository to the virtual machine using the checkout action. We then load our config files from the secrets since they are not available in our repo.
Use the setup-node action to install Node.js in the virtual machine. Run npm install to install project dependencies then carry out tests.
Finally, set up Google cloud SDK and run the deployment actions using the credentials stored in secrets.
To trigger this workflow create a production branch and push to it. Then go to the Actions tab of your repository to see if it was successful or not.
Conclusion
GitHub Actions is easy and simple to use once you get a hang of some of its quirks, coupled with cloud serverless cloud offerings creating a CI/CD workflow is easy and sweet.