Google Cloud has recently added several new features to their platform that enable us to easily build and deploy .NET Core applications. Of particular note are:
- Cloud Run: Fully managed Serverless with containers
- Cloud Build: Automated build and deployment, with full support for .NET Core
- GitHub App triggers: Automatically invoke builds on Git pushes and pull requests
In this blog post I will go through the steps required to set up a Google Cloud automated build and deployment pipeline, for an ASP.NET Core web application stored in GitHub. This is also known as a Continuous Deployment setup.
1. Prerequisites
A GitHub account and a Google account is required.
Sign up for Google Cloud (using your Google account credentials): https://cloud.google.com/
Please note that, although Google Cloud has a free tier, we will be using paid services in this tutorial. As of time of writing (August 2020), if you’re a new user of Google Cloud you will receive a 12-month free trial with US$300 credit which should be more than sufficient.
Using Microsoft’s sample application eShopOnWeb as an example, and with the source code stored in GitHub, we will now proceed to set up a Continuous Deployment pipeline to Google Cloud.
Fork the eShopOnWeb repository which will give you a copy of the repository in your own GitHub account: https://github.com/dotnet-architecture/eShopOnWeb
Rename the forked repository in your own GitHub account to an all lowercase name like eshoponweb, as there is currently an issue with using mixed case repository names.
2. Create a project
1. Click the "Select a project" dropdown in the top menu to open the "Select a project" screen
Select a project dropdown
Select a project screen
2. Click "New project"
Google Cloud has the concept of “Enabling APIs for a project”. Next we will be doing this for the required APIs.
For readers who are familiar with Microsoft Azure, you can think of "Projects" as the equivalent of "Resource groups" in Azure, while "Enabling APIs for a project" would be the equivalent of adding assets to a Resource group in Azure.
3. Create a Cloud Run Service
1. Open Cloud Run.
2. Click Create Service. This will automatically enable the Cloud Run API for this project.
Deployment platform: “Cloud run (fully managed)” (default)
Region: australia-southeast1 (Sydney) in our case
Enter a service name: eshop-on-web in our case (Service name may only start with a letter and contain up to 63 lowercase letters, numbers or hyphens)
Authentication: Require authentication
Cloud Run screen
Create service - Service settings screen
3. Click Next.
4. Select “Continuously deploy new revisions from a source code repository”.
Create service - configure the service's first revision screen
5. Click “Set up with Cloud Build”.
6. Click “Enable Cloud Source Repositories API”, “Enable Cloud Build API” and “Enable Container Analysis API”.
Repository Provider: GitHub
Click “Authenticate”.
Check “I consent to Google collecting and storing my authentication token in order to provide the connected repository service.”
Click “Continue”. A popup window will appear and request you to log into GitHub, if necessary.
Repository: eShopOnWeb repository
Source repository screen
Set up with Cloud Build - Source repository screen
7. Click Next.
Branch: ^master$ (default)
Build type: Go, Node.js, Python, Java or .NET Core via Google Cloud Buildpacks
NOTE: There is already a ready made Dockerfile present in the repository, but we’ll be using the buildpacks approach in this tutorial.
Set up with Cloud Build - Build Configuration screen
8. Click Save.
Create service - configure the service's first revision screen
9. Click Create. A Cloud Build trigger will be created and a build will be started.
This first build will fail as we need to adapt the trigger slightly to fit the structure of the eShopOnWeb repository.
Cloud Run Service details screen - build in progress
Cloud Run Service details screen - build completed
4. Continuous Deployment
With the default trigger configuration, Cloud Build expects to find exactly one project file in the repository. Since the eShopOnWeb repository contains multiple projects we will have to tell Cloud Build a bit more about the project structure in order to produce a successful build.
1. Open Cloud Build.
2. Click Triggers, then click Edit (on the right hand side).
Cloud Build menu
Cloud Build trigger
3. On the “Edit trigger” page, scroll down to Build Configuration then copy the inline build configuration. Save this inline build configuration as cloudbuild.yaml in the root of the repository.
Cloud Build - Edit trigger screen
4. In the cloudbuild.yaml file, in the first step, gcr.io/k8s-skaffold/pack, add the following two lines to the args section:
- "--env=GOOGLE_BUILDABLE=src/Web" - "--env=GOOGLE_ENTRYPOINT=cd bin && dotnet Web.dll"
5. Commit and push the changes to GitHub.
6. On the “Edit trigger” page, change Build Configuration from “Inline” to “Cloud Build configuration file (yaml or json)”.
Cloud Build - Edit trigger screen
7. Click Save.
8. Open Cloud Run.
9. Click the Cloud Run service that was just created (“eshop-on-web”).
Cloud Run - Services screen
10. Click Edit & Deploy New Revision
Container / Memory Allocated: 128 MB (the minimum value)
Container / Autoscaling / Maximum number of instances: 1
Cloud Run - Service details screen
Cloud Run - Deploy new revision screen - Container settings
Variables / Environment variables:
ASPNETCORE_ENVIRONMENT | Development |
ASPNETCORE_URLS | http://*:8080 |
Cloud Run - Deploy new revision screen - Environment variables
11. Click Deploy. NOTE: This will not result in a functioning deployment, but it is necessary in order to persist the two environment variables.
12. Open Cloud Build.
13. Click Triggers then Run trigger.
14. Click Dashboard, then after 5 to 10 minutes the build should finish and turn green. In the unlikely event that the build results in a timeout, add timeout: 1800s at the end of the cloudbuild.yaml file, which will increase the Cloud Build timeout from 10 minutes to 30 minutes.
5. Test the deployed website
We have now completed the deployment pipeline using Cloud Build.
Once the build and automatic Cloud Run deployment is complete, navigate to the URL of the Cloud Run service. It will be in a format similar to https://eshop-on-web-..........run.app
As we chose Require authentication when we created the Cloud Run service the web page will return HTTP 403 Forbidden.
We will have to pass an “Authorization” HTTP header in order to access the website.
In Chrome, Edge, and Safari this is hard to accomplish, as extensions are not allowed to set the “Authorization” HTTP header.
In Firefox, however, it is possible to set this header to the custom value required by Google Cloud.
We will need the gcloud command line tools for Google Cloud, and we will also need the Firefox web browser.
1. Install the Google Cloud SDK Shell from here: https://cloud.google.com/sdk/docs/downloads-interactive
2. Run the command gcloud auth print-identity-token which will output a JSON Web Token (JWT) representing your Google user account. The token will be valid for exactly 1 hour only. JWT tokens are typically of the format ey………...
3. Download and install Firefox, then install the add-on Modify Header Value (HTTP Headers) from here: https://addons.mozilla.org/en-US/firefox/addon/modify-header-value/
4. In Firefox, open the Options page for the Modify Header Value (HTTP Headers) add-on/extension, and use the following values:
URL: The URL from the Cloud Run service, on the format https://eshop-on-web-..........run.app
Header name: Authorization
Header value: Bearer ey………… (Bearer + JWT token from the step above)
5. Click Add (+)
Modify Header Value (HTTP Headers) add-on/extension settings screen
6. Open the URL in a new tab in Firefox. The page should now load successfully.
eShopOnWeb web site as it appears in Firefox
Conclusion
We now have a fully functional Continuous Deployment setup where Google Cloud will automatically build, deploy and publish any updates pushed to the master branch of your GitHub repository containing an ASP.NET Core web application.