Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Deploy a Node.js With Couchbase Web Application as Docker Containers

DZone's Guide to

Deploy a Node.js With Couchbase Web Application as Docker Containers

In this post, we'll look at how to use Node.js, Couchbase, and Docker to create, launch, and containerize a web application.

· Web Dev Zone
Free Resource

Tips, tricks and tools for creating your own data-driven app, brought to you in partnership with Qlik.

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, the experience will be the same. Take Node.js for example. There are many versions of Node.js and while they generally work regardless of the web application scenario, you can’t truly be sure.

We’re going to see how to build a custom Couchbase NoSQL container and deploy it alongside a custom Node.js web application container that makes use of the Couchbase container.

If this is your first time being exposed to Docker, let’s break down what we’re hoping to accomplish. Couchbase has published an official Docker image to Docker Hub, 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. When it comes to Node.js, we’re going to create an application and package it into a Docker container. 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:

Dockerfile
configure.sh

The Dockerfile file 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:

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:

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. The configuration includes creating a cluster, enabling Couchbase services, defining administration credentials, creating a Bucket, and creating an 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:

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:

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. First, create a directory somewhere on your computer to represent our project. Within that project, create an app.js file. From the Command Prompt or Terminal, execute the following:

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:

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:

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 can 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:

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:

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 run.

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:

node_modules

Anything you want to be excluded from the image can go in that file.

To build this image, execute the following from the Docker Shell:

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:

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, and 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:

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:

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.

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 a 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.

Explore data-driven apps with less coding and query writing, brought to you in partnership with Qlik.

Topics:
node.js ,couchbase ,docker ,web app development ,web dev

Published at DZone with permission of Nic Raboy, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}