How to Deploy a Node.js app on OpenShift
I write often about Java, but I don’t feel like I’ve given enough love to the JavaScript crew lately.
Are you a back-end JavaScript developer? This article is for you!
Do you want to know how to build a Docker image for your Node API, and then deploy it in OpenShift? Then read on.
In this tutorial I’ll go through deploying a Node.js hello world application onto OpenShift.
By the way, you’ll need the oc
tool installed for this.
What we’re going to build
In this article we’re going to deploy a JavaScript application to OpenShift.
When you deploy an app into OpenShift or Kubernetes, you’re basically creating a bunch of objects, that usually look like JSON or YAML files.
A typical application in OpenShift consists of a BuildConfig, a Deployment, a Service and a Route.
Object | What it does |
---|---|
BuildConfig | Builds a Docker image inside OpenShift from our source code, and push it to OpenShift’s internal Docker registry. In this tutorial, we’re going to use the Source-to-Image (S2I) method for building an image. |
Deployment | Deploys the image. It creates a set of Pods to run the image. (see some Deployment examples) |
Service | This load-balances traffic across the Pods. |
Route | Exposes the app outside the OpenShift cluster. |
The BuildConfig will build our Docker image, and then the other objects are responsible for deploying it, and making it available to the outside world.
First, we’ll need an app to deploy
First, we need an application to deploy.
If you just want to skip creating an app, and use one I made earlier, then use this one. This is using Node.js/Express:
https://github.com/monodot/container-up/tree/master/node-hello-world
If you’ve already got your own app, make sure you’ve configured Node to listen on all interfaces (0.0.0.0
) and listen on port 8080.
But what about if you want to create an app, from scratch? I’ll show you how I created one:
I’m no JavaScript coder, by any stretch of the imagination. So, to keep me on the safe side, I’m going to keep things as basic as possible.
I decided to use Express JS, and started out by creating a new Express application, using the Express Generator:

Use the Express Generator to create a new ExpressJS app
If you use this generator to create your Express app, then make sure you add the --no-view
option. This is because by default, the generator sets up Jade as your HTML rendering engine, and it’s apparently already deprecated and full of critical bugs (!!!). So either swap to the pug engine, or add --no-view
.
Here’s the command that I used, in case you want to do the same as me:
npx express-generator node-hello-world --no-view
Now you should have a basic Express app.
cd
into the directory, run npm install
and npm start
and you should get an API.
Got your Node.js application ready to deploy? Read on, in the next section we’ll look at building an image from your code.
Build and deploy with oc new-app
So now we’ve got our Node.js application ready.
The next thing to do is create the necessary objects in OpenShift: a BuildConfig, a Deployment, a Service and a Route.
The oc new-app
command will create all of those objects for you. Just pass it your Git repo URL, and optionally a context-dir
(if your code is in a subfolder), and a name
:
oc new-app https://github.com/monodot/container-up
--context-dir=node-hello-world
--name=node-app
--strategy=source
(My example repo contains a Dockerfile, so I’m using --strategy=source
to force OpenShift to use an S2I build, instead of a Docker build.)
OpenShift will detect the language of the application (Node), and try to pick an appropriate S2I builder image, from the image streams that are installed in your cluster.
If your cluster is missing the node S2I builder images, or you just want to use a different S2I builder image, like the community CentOS NodeJS image, you can give the full image URL with a tilde, like this:
oc new-app quay.io/centos7/nodejs-12-centos7~https://github.com/monodot/container-up \
--name=node-app
--context-dir=node-hello-world
--strategy=source
And you’ll get output like this:
$ oc new-app https://github.com/monodot/container-up --name=node-app --context-dir=node-hello-world
--> Found container image 995ff80 (7 days old) from Docker Hub for "node:14"
* An image stream tag will be created as "node:14" that will track the source image
* A Docker build using source code from https://github.com/monodot/container-up will be created
* The resulting image will be pushed to image stream tag "node-app:latest"
* Every time "node:14" changes a new build will be triggered
* This image will be deployed in deployment config "node-app"
* Port 8080/tcp will be load balanced by service "node-app"
* Other containers can access this service through the hostname "node-app"
* WARNING: Image "node:14" runs as the 'root' user which may not be permitted by your cluster administrator
--> Creating resources ...
imagestream.image.openshift.io "node" created
imagestream.image.openshift.io "node-app" created
buildconfig.build.openshift.io "node-app" created
deploymentconfig.apps.openshift.io "node-app" created
service "node-app" created
--> Success
Build scheduled, use 'oc logs -f bc/node-app' to track its progress.
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose svc/node-app'
Run 'oc status' to view your app.
Which port and host?
It’s really important that your Node app is serving on the correct host and port. Otherwise, OpenShift will not be able to route requests to it.
For oc new-app
to work, your app should run on the same port that is exposed by the S2I image. (A Docker image usually has an EXPOSE
instruction, which says which ports the container should expose)
The Red Hat and CentOS S2I images generally expose port 8080. This means that your Node app also needs to run on port 8080.
You also need to make sure that your app is listening on all interfaces. Set your server listen address to 0.0.0.0
, not “localhost” or “127.0.0.1”.
Behind the scenes, OpenShift will start a Build to build your app and create a Docker image. You can follow the build logs:
oc logs -f bc/node-app
Finally, the last step is to expose your app by creating a Route (or you could create an Ingress object on OpenShift if you prefer):
oc expose svc/node-app
And now our app should be up and available! Super.
Accessing the app
We can test it out:
Get the URL to the app by using oc get route
. The Route is the thing you created at the end of the last section:
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
node-app node-app-toms-project.apps.example.com node-app 8080-tcp None
If you’ve deployed my example app, then you can hit the endpoint with curl
, adding /greeting
to the end:
curl http://node-app-toms-project.apps.example.com/greeting
which will give you:
{"greeting":"Hello, world!"}
Success
So there you have it. A Node JS Express application built and deployed onto OpenShift.
We used oc new-app
to detect the language of our app, and create a BuildConfig. This ran a Source-to-Image (S2I) build, to create a Docker image.
oc new-app
also created a Deployment and a Service to run the container in Pods. Finally, we created a Route so we could access the API outside the cluster.
And a reminder of the main gotchas:
-
Make sure your app is running on the same port as the S2I builder image you use. e.g. port 8080.
-
Make sure your app is listening on all addresses (
0.0.0.0
) -
If
oc new-app
can’t find any suitable S2I builder images in the cluster, you can always specify the image you want to use, withoc new-app <image url>~<git url>
Next steps….
-
Try updating your code in Git and run a new build, from the web console.
-
See how to run Docker builds in OpenShift, if you prefer that instead of S2I.
Thanks for reading!