Docker is becoming increasingly popular and I’ve been slowly introducing it into my projects. It makes it easy to distribute your applications because regardless of where you deploy your containers to, the experience will be the same. Let’s see how this works using Docker and Node.js, which has many variations. While they generally work regardless of the web application scenario, you can’t truly be sure when using the Node.js runtime.
We’re going to see how to build a custom NoSQL container and deploy it alongside a custom web application container that makes use of Node and Docker to encapsulate the needed Couchbase functions.
What is Node.js?
Before we get to the tutorial, let’s spend a minute defining Node.js. Instead of running inside a browser, Node is an open source and fully cross-platform runtime for executing JavaScript in a server environment. With Node.js, developers can create fully cloud applications that operate invisibly to users but which provide crucial functionality in modern enterprises. Couchbase is optimized to execute applications built with Node.js. This tutorial demonstrates how to harness this capability by pairing Node.js and Docker containers.
If this is your first time being exposed to Docker containers, let’s break down what we’re hoping to accomplish. Couchbase has published an official Docker image to Docker Hub for Node.js developers, but the image is not pre-provisioned. That is not a bad thing and it is definitely what we would hope to expect in a database container. This means that we need to create a custom image based on the official image, otherwise when we deploy Couchbase, it won’t have been set up. We’re going to write an application in Node.js and “Dockerize” it into a container package. These two Couchbase and Node.js Docker containers will be able to communicate with each other.
Creating a Custom Couchbase Server Docker Image and Container
Create a directory somewhere on your computer and include the following two files:
1 2 |
Dockerfile configure.sh |
The sample dockerfile — Dockerfile — will represent our custom image, and the configure.sh file will be a runtime script for when we deploy our container.
Open the Dockerfile file and include the following:
1 2 3 4 5 |
FROM couchbase COPY configure.sh /opt/couchbase CMD ["/opt/couchbase/configure.sh"] |
Our custom image will use the official Couchbase image as the base. At build time, the configuration script will be copied to the image. When run, the script will be executed. Let’s take a look at that script.
Open the configure.sh file and include the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
set -m /entrypoint.sh couchbase-server & sleep 15 curl -v -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=512 -d indexMemoryQuota=512 curl -v http://127.0.0.1:8091/node/controller/setupServices -d services=kv%2cn1ql%2Cindex curl -v http://127.0.0.1:8091/settings/web -d port=8091 -d username=$COUCHBASE_ADMINISTRATOR_USERNAME -d password=$COUCHBASE_ADMINISTRATOR_PASSWORD curl -i -u $COUCHBASE_ADMINISTRATOR_USERNAME:$COUCHBASE_ADMINISTRATOR_PASSWORD -X POST http://127.0.0.1:8091/settings/indexes -d 'storageMode=memory_optimized' curl -v -u $COUCHBASE_ADMINISTRATOR_USERNAME:$COUCHBASE_ADMINISTRATOR_PASSWORD -X POST http://127.0.0.1:8091/pools/default/buckets -d name=$COUCHBASE_BUCKET -d bucketType=couchbase -d ramQuotaMB=128 -d authType=sasl -d saslPassword=$COUCHBASE_BUCKET_PASSWORD sleep 15 curl -v http://127.0.0.1:8093/query/service -d "statement=CREATE PRIMARY INDEX ON `$COUCHBASE_BUCKET`" fg 1 |
Again, the point of the configure.sh script is to provision the server after it is launched. To do this, we can make use of the Couchbase RESTful API. Configuration includes creating a cluster, enabling Couchbase services, defining administration credentials, creating a Bucket, and creating a N1QL index on the bucket.
In the configuration script you’ll notice several environment variables being used, for example the $COUCHBASE_ADMINISTRATOR_USERNAME
variable. This will save us from hard-coding potentially sensitive information into the image. Instead we can define these variables at deployment.
Now let’s build the custom Couchbase image for our project. From the Docker Shell, execute the following:
1 |
docker build -t couchbase-custom /path/to/directory/with/dockerfile |
In the above command, the couchbase-custom
text is the name of our image, while the path is the path to our Dockerfile and configure.sh files.
With the custom image created for Couchbase, execute the following command to run it:
1 2 3 4 5 6 7 8 9 |
docker run -d \ -p 8091-8093:8091-8093 \ -e COUCHBASE_ADMINISTRATOR_USERNAME=Administrator \ -e COUCHBASE_ADMINISTRATOR_PASSWORD=password \ -e COUCHBASE_BUCKET=default \ -e COUCHBASE_BUCKET_PASSWORD= \ --network="docker_default" \ --name couchbase \ couchbase-custom |
The above command says we want to deploy a container, mapping each of the necessary Couchbase ports. We pass in the environment variables that are found in our configure.sh script and we define which network we want the container to run on. The container will be named couchbase
, which is also going to be the container’s host name.
Creating a Node.js RESTful API Web Application
With Couchbase up and running, we can develop a simple Node.js application. Create a directory somewhere on your computer to represent our project. Within that project, create an app.js file. We’re going to be installing private packages with NPM in a Docker container. From the Command Prompt or Terminal, execute the following:
1 |
npm init --y |
The above command will create a very basic package.json file which will be the foundation of our Node.js application. There are a few dependencies that must be installed, so let’s execute the following to obtain them:
1 |
npm install express couchbase body-parser uuid --save |
We’ll be using express
for building our API, couchbase
as our SDK, body-parser
for working with POST bodies, and uuid
for generating unique id values that will represent document keys.
With the application foundation in place, we can start developing the application. Open the project’s app.js file and include the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
var Couchbase = require("couchbase"); var Express = require("express"); var BodyParser = require("body-parser"); var UUID = require("uuid"); var app = Express(); var N1qlQuery = Couchbase.N1qlQuery; var bucket = (new Couchbase.Cluster("couchbase://" + process.env.COUCHBASE_HOST)).openBucket(process.env.COUCHBASE_BUCKET, process.env.COUCHBASE_BUCKET_PASSWORD); app.use(BodyParser.json()); app.get("/", function(request, response) { response.send("Try using the `/get` or `/save` endpoints!"); }); app.get("/get", function(request, response) { var query = N1qlQuery.fromString("SELECT `" + bucket._name + "`.* FROM `" + bucket._name + "`"); bucket.query(query, function(error, result) { if(error) { return response.status(500).send(error); } response.send(result); }); }); app.post("/save", function(request, response) { bucket.insert(UUID.v4(), request.body, function(error, result) { if(error) { return response.status(500).send(error); } response.send(result); }); }); var server = app.listen(process.env.APPLICATION_PORT || 3000, function() { console.log("Listening on port " + server.address().port + "..."); }); |
So what is happening in the above code?
First we import all the downloaded dependencies and then we establish a connection to Couchbase. You’ll notice that we are using process.env
throughout the code. This allows us to read environment variables. We’re going to be using a similar approach to what we saw in the Couchbase image.
The API has two endpoints that actually do anything. The /save
endpoint will insert whatever is in the request body and the /get
endpoint will query for whatever is saved in Couchbase.
Containerizing the Node.js Web Application
With the application built, we need to containerize it so it can be deployed easily with Docker. In the same directory as your package.json file, add the following two files:
1 2 |
Dockerfile .dockerignore |
The plan is to create a custom image based on the official Node.js Docker image. Within the Dockerfile file, include the following:
1 2 3 4 5 6 7 8 |
FROM node:6-alpine COPY . /srv/ WORKDIR /srv RUN /usr/local/bin/npm install CMD /usr/local/bin/node app.js |
During the image build time, we are going to copy our project into the image filesystem and change the working directory. Then we are going to install all the dependencies found in the package.json file. When the container is deployed, the app.js file will be ran.
You might be wondering why we are doing an install when we build the image. We are doing this because we don’t want to copy the dependencies into the image as there could be OS and architecture incompatibilities. To exclude the node_modules directory from our image, include the following in the .dockerignore file:
1 |
node_modules |
Anything you want excluded from the image can go in that file.
To build this image, execute the following from the Docker Shell:
1 |
docker build -t nodejs-custom /path/to/directory/with/dockerfile |
The image name for our Node.js application will be called nodejs-custom
and it will be based on the directory that contains the Dockerfile file. More information on building custom Docker images can be found in a previous article that I wrote.
With the image available, we need to run it. From the Docker Shell, execute the following:
1 2 3 4 5 6 7 8 9 |
docker run -d \ -p 3000:3000 \ -e COUCHBASE_HOST=couchbase \ -e COUCHBASE_BUCKET=default \ -e COUCHBASE_BUCKET_PASSWORD= \ -e APPLICATION_PORT=3000 \ --network="docker_default" \ --name nodejs \ nodejs-custom |
The above command should look familiar to what we saw in the Couchbase deployment. We are defining the port mapping of our application, passing in the environment variables that are used in the app.js file, defining the container network and the image name.
If you go to your web browser and navigate to any of the endpoints of the web application, it should work fine.
Using a Compose File for Deploying Docker Containers
Having to remember all the environment variables and everything in the command for deploying the containers can be painful. Creating a Compose file can make things a lot simpler.
After you’ve created your two custom images, create a docker-compose.yml file somewhere on your computer. This file should contain the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
version: '2' services: couchbase: image: couchbase-custom ports: - 8091:8091 - 8092:8092 - 8093:8093 environment: - COUCHBASE_ADMINISTRATOR_USERNAME=Administrator - COUCHBASE_ADMINISTRATOR_PASSWORD=password - COUCHBASE_BUCKET=default - COUCHBASE_BUCKET_PASSWORD= nodejs: image: nodejs-custom ports: - 3000:3000 environment: - COUCHBASE_HOST=couchbase - COUCHBASE_BUCKET=default - COUCHBASE_BUCKET_PASSWORD= - APPLICATION_PORT=3000 restart: always |
When using Compose, you don’t have to worry about defining the network as everything in the file will be on the same network. To launch either of the containers, you can execute the following:
1 |
docker-compose run -d --service-ports --name couchbase couchbase |
With Couchbase, you can’t spin everything up together because Couchbase doesn’t have a method of telling you when it is ready. Because of this, the Node.js application might try to connect before Couchbase is ready to accept connections.
1 |
docker-compose up -d |
The above command usually spins up all containers in the Compose file, but we can’t use it in this scenario.
Conclusion
If you’re wondering how you can deploy your Node.js with Couchbase web application, Docker is an easy solution. After building images of either Couchbase or Node.js, you can trust that it will work the same on any server or computer running the Docker Engine.
For more information on using the Couchbase Node.js SDK, check out the Couchbase Developer Portal.
[…] too long ago I wrote about containerizing a Node.js RESTful API and Couchbase Server to demonstrate how easy it is to deploy web applications in a quick and reliable fashion. In that […]
[…] If you’re interested in deploying a containerized Node.js web application that uses Couchbase containers, check out this article that I had previously written called, Deploy a Node.js with Couchbase Web Application as Docker Containers. […]
[…] following posts from Nic Raboy are very helpful to get going with Docker and […]