Deploying an app to Google Cloud

I believe that throughout the internet it is surprisingly little information about how to do deployments to various services. It is not complicated but the documentation just isn’t there at all times which lead me to make a post about it, explaining with simple terms. I am now using Google Cloud App Engine and using Windows 10 as a operative system, and focusing on Angular and React. This is meant to be guiding you through the build and deployment stage if you have a small angular or react app ready for a basic deployment!

So for your Angular or React application that is ready for deployment, you will be using Cloud SDK (gcloud command) in the terminal.

Here is a good place to install Cloud SDK to your computer so you can use the gcloud if you already don’t have it installed: https://cloud.google.com/sdk/docs/quickstart-windows

The Cloud SDK is a set of tools for Google Cloud. It contains gcloud, gsutil, and bq, which you can use to access Compute Engine, Cloud Storage, BigQuery, and other products and services from the command line. You can run these tools interactively or in your automated scripts.

cloud.google

It is an awesome tool as you can see that can do a lot.
Go to your Google Cloud Console, make sure you have a project ready for your application deploy. If you have one, navigate to it and pay attention to the Project ID name, see image below. You might need to activate billing and the build api if not already done so before.

My project ID inside my Demo project

At this point your should have installed Google Cloud SDK.
Now open up your terminal window in your working directory of your app and make sure you are on your Cloud project you want to deploy to, achieve this by the command below, using your project ID as you’ve found (in the image) and type in the following:

gcloud config set project deep-wares-123456

My project ID is called deep-wares-123456, so change that to yours.
You will be greeted with the following message: Updated property [core/project]
If you are curious where the settings are stored, take a look at: C:\Users\yourwindowsusername\AppData\Roaming\gcloud\configurations

To make sure you’ve selected the right project you can type

gcloud config get-value project

Build your app

Make sure you are in the workspace directory. Highlighting some of the importance of what is said in the documentations.

Angular
We want to use the ng build.

ng build compiles an Angular app into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.

The application builder uses the webpack build tool, with default configuration options specified in the workspace configuration file (angular.json) or with a named alternative configuration. A “production” configuration is created by default when you use the CLI to create the project, and you can use that configuration by specifying the –configuration=”production” or the –prod=”true” option.

angular.io/cli/build
ng build --prod

React / Nodejs back-end application
We can use npm build.

npm run build creates a build directory with a production build of your app. Inside the build/static directory will be your JavaScript and CSS files. Each filename inside of build/static will contain a unique hash of the file contents. This hash in the file name enables long term caching techniques.

create-react-app.dev/docs/production-build/
npm build --prod

Build complete and time for deployment

To deploy to Google Cloud AppEngine we need usually need an yaml file or the gcloud app deploy command will not work. Create a app.yaml file in your working directory. Here is some information about the yaml file if you care to read a bunch of settings without much of clear explanation.

There is a lot of information about which environment to use and when, you can read up on all of that here.

First off, what is the required settings? Well actually: runtime is the only one! However, depending on how your app look and how much you care about the server performance and cost, you might need to declare some things. Depending on what app we are deploying, the yaml configuration might look different, let’s go through some different types.

Backend nodejs application

app.yaml configuration

You can honestly just go with the runtime setting in your application.

runtime: nodejs12

This is what Google Cloud supports and not (see quote). Note that Node.js keeps updating and so does the services of the Cloud, so try stay updated!

The Node.js runtime supports Node.js 12, Node.js 10, and Node.js 8 (deprecated), and it uses the latest stable release of the version you choose. App Engine automatically updates to new release versions upon app deployment, but it will not automatically update the minor version.

Google Cloud Documentation

Angular app

Suppose you have an basic angular app created through the ng new myapp command.
Using Angular version 10.0.4.

app.yaml configuration

runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /
  static_files: dist/myapp/index.html
  upload: dist/myapp/index.html
- url: /
  static_dir: dist/myapp

React app

Suppose you have a basic react app created through the create-react-app command.

app.yaml configuration

For react app we need to declare handlers

runtime: nodejs12
handlers:
  # Serve all static files with url ending with a file extension
  - url: /(.*\..+)$
    static_files: build/\1
    upload: build/(.*\..+)$
  # Catch all handler to index.html
  - url: /.*
    static_files: build/index.html
    upload: build/index.html

PHP application

Suppose you have a PHP 5 application, then we need to specify this in runtime. The sample will serve files with extension of gif, png, or jpg as static resources. The files have been configured to be readable by the application code at runtime.

app.yaml configuration

runtime: php55
api_version: 1

handlers:
# Serve images as static resources.
- url: /(.+\.(gif|png|jpg))$
  static_files: \1
  upload: .+\.(gif|png|jpg)$
  application_readable: true

# Serve php scripts.
- url: /(.+\.php)$
  script: \1

Firebase angular app

Suppose you have a firebase angular app, created by the following commands: ng new myapp and ng add @angular/fire. Then you will get a firebase.json file which is used for deployment. The app.yaml file is not needed.

Read more about how to quick-start Firebase to your Angular app here. Note that you will type firebase deploy to deploy. Let’s take a look at the file. In this case, my firebase.json file looks like this.

firebase.json configuration

{
  "hosting": {
    "public": "dist/myapp",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}

Now you are ready for deployment to the Google Cloud by entering

gcloud app deploy

Add a domain to your Google Cloud Storage API

This is the process of adding a domain to your Google Cloud Storage.

I am assuming at this point you have a domain ready to use for your buckets in your Google Cloud project. Good reference website how to go about this is here: https://cloud.google.com/storage/docs/hosting-static-website.

Selecting Storage -> Browser in Google Cloud

Like always, let’s first login to our Google Cloud Console at https://console.cloud.google.com. Be in the correct Project of where you are going to add you domain to your storage.

Not sure about you but I have a bucket filled with images I want to connect a domain to already, so what is described below the case when you already have a bucket that you want to connect a domain to.

Renaming a bucket in Google Cloud

What we wish to do is rename the bucket we already have with a proper name of the desired domain we want to use such as yourdomain.com or img.yourdomain.com depending on how you want your custom domain connected.

Interesting enough for some reason, someone thought it was a good idea to not allow bucket renaming. So since it is impossible to rename a bucket, what we will do is just copy the one we already have to another newly-created one.

Create a new bucket, with your domain. My is going to be called, img.mydomain.com. The bucket settings are mostly up to you but remember to select Uniform for Access control.

At the list of your buckets click in on Cloud Shell in the right corner to open up the terminal. (see image to the left). Now enter the command to verify that you are in the right place and can see your bucket(s): gsutil ls.

The Cloud Shell Button

Do you see your old and newly bucket in the list? Great, then go for the command written below with your own bucket names with source-bucket as the old one, and dest-bucket as the newly created bucket we just made, which is in my case img.mydomain.com. Note you can use a root domain too, you don’t need to go with a subdomain.

gsutil -m rsync -r -d -p gs://source-bucket gs://dest-bucket

Files will start to copy into the new bucket. Now when this is done open up your new bucket and select the Permissions tab and select  Add members and type in allUsers. In the Select a role drop down, select the Cloud Storage sub-menu, and click the Storage Object Viewer option.

To serve your website through HTTPS, you need an SSL certificate. This section shows you how to add your bucket to a load balancer’s backend and how to add a new Google-managed SSL certificate to the load balancer’s front end.

console.cloud.google.com

Okay what Google tells us here is that we need a SSL certificate if we want to use HTTPS when loading our images, it’s not as complicated as it sounds since they will make the certificate for us.

Go to Network services, under Create load balancer, and click start configuration under HTTP(S) Load Balancing. Go with From Internet to my VMs. Go with some neat short name, mydom-lib for example, then in the Create or select backend services & backend buckets dropdown, go to the Backend buckets sub-menu, and click the Create a backend bucket option, make a bucket with any name you would like, then at Cloud Storage bucket select your bucket from before. Check Enable Cloud CDN check box.

Host and path rules should already have the bucket you created for the balancer earlier.

For the front end configuration, check HTTPS, port 443, click create IP adress, and go with the same name as before, mydom-lib for example

When all of this is done (see image to left), last step is to click on Select a certificate and Create a new certificate, again, I go with mydom-lib just to keep it easy, and select Create Google-managed certificate. Add your domains img.mydomain.com and www.img.mydomain.com and click Create.

Click on Create as review and finalize is optional.

Click on your load balancer in the list which you’ve arrived after creating one, it should look like something similar to the picture above. See the IP Address there? That’s what we want to use for our DNS in the domain.

Use the Google Cloud Balancer IP Address for your domain

Personally I have Godaddy, so let’s open Godaddy. Find your domain and open up the DNS settings where you can add records. https://dcc.godaddy.com/manage/mydomain.com/dns This is the Godaddy DNS location, note you must change mydomain.com to your domain.

Record example of two records of www and @ with data/value to 30.90.80.100 of A type

If you are having your whole domain connected to the storage, go with as shown in the picture above, add two records www and @ with the type of A, with the IP Address you have in your load balancer.

In my case, I use a subdomain, so I will simply just add one record of A type, with NAME of img, and with IP to the one of my balancer.

We can check the certificate status and see it is still not activated as it says PROVISIONING

It can take hours before the DNS for the domain has been updated. But if we are lucky we can see the results quite fast. Before then, you can get all kinds of errors, like ERR_SSL_VERSION_OR_CIPHER_MISMATCH. Just need some patience! Also if you forgot something this is pretty forgiving and it is easy to change the settings after saved various things.

See the update in action

Waited long enough and ready for some balance action? Let’s try loading an image over the domain.

Go to one of your pictures that you have on your bucket of images that has the name of this domain you created in the beginning.

An image object inside the bucket of img.mydomain.com

Now you can go ahead and copy the public url of that photo, mine looks like this:
https://storage.googleapis.com/img.mydomain.com/033e560c-1a6e-4a58-a143-7acb1e04a329/image1.jpg

Now try replacing storage.googleapis.com/img.mydomain.com with your domain, like this:
https://img.japanesehelp.com/033e560c-1a6e-4a58-a143-7acb1e04a329/image1.jpg
If you can see the photo then it is confirmed it is working as expected.

Note that http does not work, you must use https. This is what the documentation says about this.

Note: To redirect traffic from HTTP to HTTPS, you need to set up an additional HTTP load balancer with a redirect setting in the URL map. For instructions, see Setting up HTTP-to-HTTPS redirect for external HTTP(S) load balancers.

Google Cloud Documentation

Make http redirect to https

Go back to the load balance page. https://console.cloud.google.com/networking/loadbalancing/add?_ga=2.21780475.1310845519.1595928318-1531761783.1542900100 and click Start configuration under HTTP(S) Load Balancing. Choose From Internet to my VMs. Name it to something similar to what you did before but that it indicates http, so I’ll go with: dom-lib-http. Skip Backend configuration and jump straight to Host and path rules. Check Advanced host and path rule (URL redirect, URL rewrite). Action, select Redirect the client to different host/path. Under Path redirect, select Prefix redirect. Under Redirect response code, select 301 – Moved Permanently. Under HTTPS redirect, select Enable, then done.

New balancer for http redirect

For Configure the frontend, enter http-content-rule. Keep Protocol to HTTP. Name it http-content-rule or something you’d prefer. Choose the IP address you created before from when you did this for HTTPS. Keep it at port 80.

Create! Then you should be done and have a http redirection to the https balancer one you created from earlier. Note that this also takes a while before it updates and you can visit links of your storage with your custom domain on http.