In my previous post, I explained how to locally Dockerize a Node application. When we ended, we were able to build a Docker image of a Next.js app for production and run it locally. We're going to pick up where we left off and build and push our Docker images in Google Cloud Build. If you are not familiar with Google Cloud Build, read up on it. It's a powerful CI/CD tool from Google that allows us to run builds by defining steps using yaml syntax. Under the hood each step is a Docker container of its own. You can even make your own steps, which I'll do in a future post.
If you have not used Google Cloud yet with your project, be sure visit the Google Cloud Build page in the Developer console. If you have been using Google App Engine, you may have a build history already.
Next write a simple build script to kick off a build. Put the following into ./ci/example.cloudbuild.yaml
####################################
# Cloud Build Example File #
####################################
steps:
- name: 'gcr.io/cloud-builders/gsutil'
entrypoint: 'bash'
args:
- '-c'
- |
echo "Hello World - this is step 1"
- name: 'gcr.io/cloud-builders/gsutil'
entrypoint: 'bash'
args:
- '-c'
- |
echo "Hello World - this is step 2"
Next switch into your ./ci directory and run the build. Doing so will prevent having to upload your project to cloud build.
gcloud builds submit --config=./example.cloudbuild.yaml --project=blaine-garrett
This should generate something like:
Creating temporary tarball archive of 4 file(s) totalling 1.1 KiB before compression.
Uploading tarball of [.] to [gs://blaine-garrett_cloudbuild/source/1572186587.89-fd136181801341c08dcec59579f2539b.tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/blaine-garrett/builds/6d63f77e-361e-4ddf-82ab-f97f53f98ae3].
Logs are available at [https://console.cloud.google.com/gcr/builds/6d63f77e-361e-4ddf-82ab-f97f53f98ae3?project=1069402597832].
---------------------------------------------------------------------------------- REMOTE BUILD OUTPUT ----------------------------------------------------------------------------------
starting build "6d63f77e-361e-4ddf-82ab-f97f53f98ae3"
FETCHSOURCE
Fetching storage object: gs://blaine-garrett_cloudbuild/source/1572186587.89-fd136181801341c08dcec59579f2539b.tgz#1572186589870838
Copying gs://blaine-garrett_cloudbuild/source/1572186587.89-fd136181801341c08dcec59579f2539b.tgz#1572186589870838...
/ [1 files][ 706.0 B/ 706.0 B]
Operation completed over 1 objects/706.0 B.
BUILD
Starting Step #0
Step #0: Already have image (with digest): gcr.io/cloud-builders/gsutil
Step #0: Hello World - this is step 1
Finished Step #0
Starting Step #1
Step #1: Already have image (with digest): gcr.io/cloud-builders/gsutil
Step #1: Hello World - this is step 2
Finished Step #1
PUSH
DONE
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ID CREATE_TIME DURATION SOURCE IMAGES STATUS
6d63f77e-361e-4ddf-82ab-f97f53f98ae3 2019-10-27T14:29:50+00:00 7S gs://blaine-garrett_cloudbuild/source/1572186587.89-fd136181801341c08dcec59579f2539b.tgz - SUCCESS
Next, go to the Google Cloud Build history for your project and you should see something like the following:
This is a suprisingly quick step.
Firstly, check out what (if any) containers are already in your Google Container Registry. If you have been using Google App Engine, you might have some containers preasent.
Next, similar to our .dockerignore file, we want to add a Cloud Ignore file so things like our .git and node_modules directories do not get uploaded to Cloud Builder.
Add the following to ./ci/build.gcloudignore:
# This file specifies files that are *not* uploaded to Google Cloud Platform
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
# Node.js dependencies:
node_modules/
*.hot-update.js*
# Other config and Files
README.md
.eslintignore
.eslintrc
Finally, add the following to ./ci/build.production.cloudbuild.yaml:
####################################
# Build to Generate Docker Image #
####################################
steps:
- name: 'gcr.io/cloud-builders/docker'
args:
[
'build',
'-t',
'gcr.io/$PROJECT_ID/gae-node-next-demo:prod',
'-f',
'./ci/build.production.Dockerfile',
'.',
]
images: ['gcr.io/$PROJECT_ID/gae-node-next-demo']
Then run in the project root directory, run:
gcloud builds submit --ignore-file=./ci/build.gcloudignore --config=./ci/build.production.cloudbuild.yaml --project=blaine-garrett
Note: $PROJECT_ID is a Environent Variable Cloud Buiild passes to your build. It is set to your GCloud project id (eg. blaine-garrett). Read more on variable substitution in builds.
Once the above command, reload the Google Container Registry page and see your image. You should have a folder with the name of your image (eg. gae-node-next-demo) and it it should be a Docker container with the tag "prod" such as below:
If you see the above, you are done. Celebrate.
We can now use Google Cloud Build to build a Docker image of our production Node.js application and push it to our Google Container Registry. The next steps are: