DEV Community

Cover image for Sails development with Docker and Docker Compose
Euda for EUDA

Posted on • Updated on

Sails development with Docker and Docker Compose

Docker makes it easy to set up and run a development environment and also simplifies the deployment of your applications. In this article we will see how to setup a Sails development workflow with Docker and Docker Compose.

Whats and whys: Docker & Docker Compose

The goal of this article is to make setting up a Docker workflow for Sails as simple as possible. Docker makes it easier to set up and run a development environment and also simplifies the deployment.

First, we’ll get into how Docker and Docker Compose work and its benefits. Then a step by step guide to make our beloved Sails run inside a Docker container. Let's get started!

Alt Text

Containerization

Before talking about Docker and Sails we need to understand what is containerization and why we want it.
Containerization is a form of virtualization, through which applications are run in isolated user spaces called containers. We can think of a container as a lightweight virtual machine (but to be clear, it is not).

Alt Text

Alt TextComparing Containers and Virtual Machines [1]

But, why do we want it?

With containerization, everything an application needs to run, let's say, its binaries, libraries, configuration files and dependencies, are encapsulated and isolated in its container. This makes it easier to set up and run a development environment, and also simplifies the deployment by reducing the chances of missing or different versions of libraries, files or dependencies errors.

The container itself is abstracted away from the host OS, with only limited access to underlying resources. Isolation has its advantages, for example portability, stability and security.

So, standard, lightweight, secure… yes as developers we want containerization!

Docker and Docker Compose

The word "Docker" refers to several things including an open source project, tools and a company (Docker Inc., the company that primarily supports the project and tools).

Talking about the tools, Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.

Container orchestration automates the deployment, management, scaling, and networking of containers. It can help you deploy the same application across different environments without needing to redesign it. As an example, microservices in containers make it easy to orchestrate services, including storage, networking, and security. Docker Compose is an orchestration tool for Docker containers. [2]

Installing Docker

Mac: https://docs.docker.com/docker-for-mac/install/
Windows: https://docs.docker.com/docker-for-windows/install/
Ubuntu: https://docs.docker.com/engine/install/ubuntu/
Others: https://docs.docker.com/engine/install/

Sails containerization

Now, how do we make Sails, our beloved Node framework, run inside a container?

Sails CLI image

Our container needs an image to run. Let's create one.
To do so, create docker-sails-cli directory and a file named Dockerfile inside it with this content:

FROM node:lts

RUN npm install -g sails@1.4.2
Enter fullscreen mode Exit fullscreen mode

The FROM statement declares we are using node latest lts version as base image for ours. The syntax used is imageName:tag, if no tag is specified, the default is used. [3]

At the same time Node lts image is based on Debian Streatch. So this is our project environment, Sails 1.4.2, node 14.15.4 (at the time of writing this) running on a Debian Streatch.

Now we need to build our image.

docker build . -t sails-cli:1.4.2
Enter fullscreen mode Exit fullscreen mode

Simple, right?

Let’s see what we did.

docker build [context] -t [tag]

context: The context where we are building. As we are in the same directory as the Dockerfile we use .. And also, there’s no need to specify the file name as Dockerfile is the default one.

tag: It is optional, but we want to tag the image we just built as we are probably going to have many different images with different versions for different projects.

Creating a new Sails project

Once we have our Sails CLI image built we are going to use it to create a new Sails project.

docker run --rm -v `pwd`:/app -w /app sails-cli:1.4.0 sails new sails-docker-example --no-frontend
Enter fullscreen mode Exit fullscreen mode

docker run: Create and run a new container
--rm: Remove container after process finishes as we don’t need the container after that
-v: Mount current working directory (in our host computer) in /app path inside container. As a result we are sharing current directory with the container
-w: Set container current working directory to /app
sails-cli:1.4.0: Our previously built Sails image

sails new sails-docker-example --no-frontend: Sails CLI command we want to run in the container.

As a result, after running sails new in the container, we are going to find a new directory created with our new Sails project.

Now, let’s run our project:

cd sails-docker-example
docker run --rm -it -v `pwd`:/app -w /app -p 1337:1337 sails-cli:1.4.0 sails lift
Enter fullscreen mode Exit fullscreen mode

-i: Keep STDIN open even if not attached
-t: Allocate a pseudo-TTY
-p: Maps container 1337 to host computer so we can reach our app through localhost:1337

It's alive!

Alt Text

Running and debugging

As you may notice, although our project is running, if you modify a file you will need to stop the container and launch it again to get the changes applied.

Nodemon to the rescue!

docker run --rm -v `pwd`:/app -w /app sails-cli:1.4.0 npm install nodemon --save-dev
Enter fullscreen mode Exit fullscreen mode

Now, open package.json and in the scripts section add a new one, debug:

"scripts": {
    "debug": "nodemon --inspect=0.0.0.0 app.js",
    "start": "NODE_ENV=production node app.js",
...
Enter fullscreen mode Exit fullscreen mode

And run again, but this time instead of sails lift we are going to run with npm:

docker run --rm -v `pwd`:/app -w /app -p 1337:1337 -p 9229:9229 sails-cli:1.4.0 npm run debug
Enter fullscreen mode Exit fullscreen mode

Now, modify a file, save it and verify that the project is restarted automatically.

Finally, as we are running inside a container we need a way to get the debugger connected to the process, so we are mapping the 9559 port.

If you use VS Code you can use the following configuration in the
launch.json file:

{
    "version": "0.2.0",
    "configurations": [
        {
            "address": "localhost",
            "localRoot": "${workspaceFolder}",
            "name": "Attach to Remote",
            "port": 9229,
            "remoteRoot": "/app",
            "request": "attach",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "type": "node",
            "restart": true
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Docker compose

As we mentioned before Docker Compose is an orchestration tool. Compose allows us to define our infrastructure, let’s say, services, networks, volumes in an YAML file.

As a first step we will create a very simple file defining our Sails service on it. For this service we will set image, command, ports mapping, volume mounts, working directory, etc, as we did with Docker command line tool.

Our docker-compose.yml:

version: '3'

services:
    api:
        image: sails-cli:1.4.0 # same as with docker cli
        # Run npm install before starting sails to keep our dependencies installed and updated
        command: sh -c "npm install && npm run debug"
        working_dir: /app # same as -w with docker cli
        ports: # same as -p with docker cli
            - '1337:1337'
            - '9229:9229'
        volumes: # same as -v with docker cli
            - .:/app
Enter fullscreen mode Exit fullscreen mode

Then, we will use docker-compose command to lift our service.

docker-compose up
Enter fullscreen mode Exit fullscreen mode

The result is the same as running with Docker CLI.

In case we want to remove the created infrastructure, once stopped, run:

docker-compose down
Enter fullscreen mode Exit fullscreen mode

This will remove all the containers, networks, volumes created with docker-compose up.

Database containerization

In most cases we will require a database for our project. In this example we will be using MongoDB but it’s possible to do the same with other db engine supported by Sails. [4]

For those who don’t know, MongoDB is a free and open-source cross-platform document-oriented database program. [5]

Fortunately MongoDB has an official image that we will be using to add a database service to our project.

First, we need to add Sails MongoDB adapter to our project. As we did before, we can add and install a dependency to our project with Docker like this:

docker run --rm -v `pwd`:/app -w /app sails-cli:1.4.0 npm install sails-mongo
Enter fullscreen mode Exit fullscreen mode

Then, modify our project config and set mongo as our default adapter.

In the config/datastore.js file:

.
.
.

default: {
    adapter: require('sails-mongo'),
    url: process.env.DB_URL
},

Enter fullscreen mode Exit fullscreen mode

Next we need to add MongoDB service to our docker-compose.yml and provision Sails service with needed config.

docker-compose.yml

version: '3'

services:
    api:
        image: sails-cli:1.4.0
        command: sh -c "npm install && npm run debug"
        working_dir: /app
        ports:
            - '1337:1337'
            - '9229:9229'
        volumes:
            - .:/app
        env_file:
          - ./sails.env # set env vars using a file

    db:
        container_name: mongodb # set name to the container
        image: mongo:4.4 # as of this writing, the latest version is 4.4.3
        volumes:
            - ./mongo:/data/db
Enter fullscreen mode Exit fullscreen mode

We added a db service to our compose file, and also set an env file to Sails service. We will use that file to config our database connection.

sails.env

DB_URL=mongodb://root:@mongodb:27017/sails
Enter fullscreen mode Exit fullscreen mode

Compose allows us to connect to db using the container name as network address, that is why we can use mongodb as the address in our connection url.

Important note: we are not enabling MongoDB authentication for this example but it is a good idea to do so for production environments. [6]

Finally, use docker-compose up to run, you will see MongoDB output along with Sails.

And we are done, a fully Sails development environment running on Docker!

Alt Text

Thank you so much for reading my post, comments are welcome!

Here is the link to the writter's twitter @vidueirof for any questions you may have. 

[1] https://www.docker.com/resources/what-container
[2] https://www.docker.com/why-docker
[3] https://hub.docker.com/_/node
[4] https://sailsjs.com/documentation/concepts/extending-sails/adapters/available-adapters
[5] https://www.mongodb.com
[6] https://hub.docker.com/_/mongo

Top comments (0)