A manual build and deploy of App Engine using Cloud Build
One of our customers uses Google App Engine to host an Angular app with Angular Universal (server side rendering or SSR) enabled. SSR was enabled to overcome SEO issues.
Typically the deployment is straightforward. The package.json
contains the following scripts:
{
"name": "angular-appengine-ssr-app",
"version": "1.0.0",
"scripts": {
"deploy": "npm run build:ssr && gcloud app deploy",
"build:ssr": "npm run build:client-and-server-bundles",
"build:client-and-server-bundles": "ng build --prod --aot && ng run ssr-app:server:production"
}
}
So all they have to do is run this on the command line:
$ npm run deploy
Essentially, after building, the CLI command gcloud app deploy
will deploy it to the App Engine. It can be a long wait, as a lot happens in the background:
- The CLI pushes the code to Google Container Registry.
- Launches Cloud Build to compile the image.
- Deploys image to App Engine.
New Node.js version in Cloud Build
Since August 2022, the customer started experiencing problems with deploying the app. The CLI keeps returning tons of error messages and deployments failed. The customer could not figure out the problem and finally asked my team for help.
I dug into the Cloud Build history to invest the logs. After hours of investigation and experimenting, I concluded that it was a Node.js version problem. The Angular app has been running well with node v16.15.0. In Cloud Build, it started using v16.16.0 in August 2022. This conflicted with a lot of packages:
The first solution was to try upgrading to v16.16.0. This created a lot more problems as many of the packages had dependencies that simply could not be upgraded. I wasn't keen to go down the rabbit hole of upgrading every single package.
A manual submit to Cloud Build
My next solution was to force Cloud Build to use v16.15.0. This was not simple unfortunately. Passing in the version number via parameters to the gcloud app deploy
command doesn't work.
I eventually found that I can use gcloud builds submit
instead and pass in the version number in the parameters. I also found that the build URL is always the latest and can be specified by the latest
tag:
gcloud builds submit --pack=env=GOOGLE_RUNTIME_VERSION=16.15.0,image=${URL}
I thought this would work but it still failed when I tried gcloud app deploy
after the build. I was finally rewarded with the final piece of the puzzle after reading through the documentation again. The --image-url
parameter can only be used for the App Engine flex environment and it was simple change in the app.yaml
from standard environment to flexible environment:
runtime: nodejs
env: flex
The updated deployment script is now:
{
"name": "angular-appengine-ssr-app",
"version": "1.0.1",
"config": {
"gae_img": "asia.gcr.io/project-id/app-engine/app/default/ttl-18h:latest"
},
"scripts": {
"deploy:nodejs16-15-0": "npm run build:ssr && npm run gcloud:builds-submit && npm run gcloud:app-deploy",
"build:ssr": "npm run build:client-and-server-bundles",
"build:client-and-server-bundles": "ng build --prod --aot && ng run ssr-app:server:production",
"gcloud:builds-submit": "gcloud builds submit --pack=env=GOOGLE_RUNTIME_VERSION=16.15.0,image=${npm_package_config_gae_img}",
"gcloud:app-deploy": "gcloud app deploy app-flex.yaml --image-url=${npm_package_config_gae_img}"
}
}
UPDATE: as of November 2022, Cloud Build is using v16.18.0 but I can see that this method is still working!
Subscribe to my newsletter
Read articles from Schiff Heimlich directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by