DEV Community

Chris Muir
Chris Muir

Posted on


Dockerizing a basic Slack app

Alt Text

In a previous blog I wrote about creating a basic Slack app using NodeJS, Slack's Bolt JavaScript framework, and ngrok. In this blog I will extend this to include deploying the application to Docker.

Like the previous blog, this blog is just capturing my notes on how to do this so I don't have to remember all the steps.

The main inspiration for the Dockerfile in this post comes from Kathleen Juell's post How to Build a Node.js Application with Docker. All credit goes to Kathleen for summarizing these steps so neatly.

Adding the Dockerfile

On the assumption we already have Docker installed on our local machine, within the application we create a Docker configuration file literally called the Dockerfile.

(1) In the previous application's root directory, in our favourite text editor, create a new file Dockerfile.
(2) Copy in the following code:

FROM node:14.7.0
USER node
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/ScratchSlackApp
COPY package*.json ./
RUN npm install
COPY --chown=node:node . .
CMD [ "node", "app.js" ]
Enter fullscreen mode Exit fullscreen mode

What this does:

  • FROM node:14.7.0 - defines the version of Node to run in the Docker container. The tag 14.7.0 represents the current NodeJS LTS version at the time of writing. Other supported version can be found via the Docker Official Node Images page.
  • USER node - allows us to create a new user besides root to run the application
  • RUN mkdir && chown - creates the application directory structure and gives the node user the appropriate ownership on the files
  • WORKDIR - sets the default directory for the container to start executing the code from. This is linked to the CMD call explained below
  • COPY package*.json ./ - copies the package.json file into the container
  • RUN npm install - within in the container downloads the necessary dependencies/libraries defined in the package.json file
  • COPY --chown-node:node . . - the two full stops say to copy the code from the base directory of the source, to the base directory of the container. The chown statement makes the owner the node user we defined earlier in USER.
  • EXPOSE 5000 - defines which port the application running in the container will be listening on
  • CMD - defines how the application will be started in the container

Of special note from the Node Bolt application example in my previous blog, remember that the application runs on port 5000. This is why I've set EXPOSE to 5000 in the Dockerfile.

(4) When building the container we want it to download the node_modules fresh. To avoid the existing node_modules being copied over, we create a .dockerignore file and add the following entries:

Enter fullscreen mode Exit fullscreen mode

Build the Docker image

With the Dockerfile in place we can then build the first Docker application image using the following command from the application's source base directory:

(5) docker build -t scratchslackapp .

  • The -t flag represents the image name to build in Docker. + The image name must be in lowercase.
  • The final full-stop implies the image is built from the current directory

Docker images can be listed by executing docker images after the build.

Create and run a Docker container

With the image in place, we can now create a running container based on the image:

(6) docker run --name scratchslackapp -p 5000:5000 -d scratchslackapp

  • The --name flag defines the container name. I've made this the same as the image, but it can be different.
  • The -p flag defines the host post mapped to the container port we defined earlier in the Dockerfile. I've kept this to 5000 in all cases to keep things simple.
  • The -d flag runs the container in the background

Some useful additional Docker commands to know:

  • docker ps - lists the running Docker containers, which includes a unique generated container-id per instance, used in the following commands.

  • docker stop <container-id - stops a container

  • docker rm <container-name> - deletes a container

  • docker logs --follow <container-id> - tails the container's STDOUT & STDERR output to the screen.

If the code needs to be updated, stop and remove the previous container, then build and run via the docker commands described above.

Accessing the Docker container via Slack

From the above, assuming the Docker container is now running, from the previous blog post, remember if we've restarted ngrok, that the ngrok port must align with the Docker EXPOSE port, and, if we're using the free version of ngrok that rotates the URL, we must also update the URL in the Slack manifest file. Look to the previous blog post for how that was done.

Top comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.