Test a Node application with Docker (while understanding Docker functions)

I was getting to know some differences between Dockerfile and docker-compose.yml and how to test it with existing node app.

skip down to action

At first I made dev-vhosts Docker to create Nginx Virtual Hosts. It has only Nginx and PHP7-fpm. And it work really well, but that’s too basic. How do you link database was the next step and everything should use Alpine Linux where possible for creating small images.

First I made a script that destroys all docker images

file: ~/bin/destroy_docker_images.sh

#!/bin/bash
# Delete all containers
docker rm -f $(docker ps -a -q)  
# Delete all images
docker rmi $(docker images -q)  
docker images | awk '{print $1 ":" $2}' | xargs docker rmi -f  

Seems a bit harsh command, but since I’m learning Docker since 3 days ago and want to build perfect images, it's all okay.

So after I made that tutorial. I want to add mysql database. I've used the normal one, then searched someone's image with alpine (and found some errors) and end up making an image for myself.

I created a folder db and put the files inside
Here is a snippet code from my docker-compose.yml

  db:
    build:
      context: ./db
      dockerfile: Dockerfile
    volumes:
      - ./db/storage:/var/lib/mysql

see context where with docker-compose command should look up for the ./db directory to look that dockerfile name.

with Dockerfile

FROM alpine

MAINTAINER Harianto van Insulinde <hariantoatwork@gmail.com>

EXPOSE 3306

RUN apk add --update mysql mysql-client && \  
    mkdir -p /var/lib/mysql && \
    mkdir -p /etc/mysql/conf.d && \
    { \
        echo '[mysqld]'; \
        echo 'expire-logs-days = 3'; \
        echo 'user = root'; \
        echo 'datadir = /var/lib/mysql'; \
        echo 'port = 3306'; \
        echo 'log-bin = /var/lib/mysql/mysql-bin'; \
        echo 'socket = /var/lib/mysql/mysql.sock'; \
        echo '!includedir /etc/mysql/conf.d/'; \
    } > /etc/mysql/my.cnf && \
    rm -rf /var/cache/apk/*

VOLUME ["/var/lib/mysql", "/etc/mysql/conf.d/"]

ENTRYPOINT ["mysqld"]

CMD ["--skip-grant-tables"]  

There I can create MySQL Alpine image now.
But it wasn't enough and sure it needs to be linked somehow.

So I need admin app for the database and thought phpmyadmin would do the trick and luckily there is Docker image. phpmyadmin/phpmyadmin

With this snippet:

  dbadmin:
    image: phpmyadmin/phpmyadmin
    links:
      - db
    ports:
      - "8080:80"

See links that points to db; the title

Then I start docker-compose up after destroy_docker_images.sh.

And I can open up the browser http://localhost:8080
and that's it.

Test in action

The next journey would be to use Node mysql admin.
I searched and I found hegdeashwin/nodemyadmin GitHub repo. But it hasn't got Dockerfile or docker-compose.yml.

Let's create Docker Compose project

So my directory structure would be like this

.\nodemyadmin
  .\app
  .\db
# make work directory and CD it
mkdir nodemyadmin && cd nodemyadmin

# clone the repo and name directory as 'app'
git clone https://github.com/hegdeashwin/nodemyadmin.git app

# create 'db' directory too
mkdir db  

From now on this guide below will points to ./nodemyadmin

So now we've got the basics. We need to know which port we should expose and probably point to the right host.

  • Searching through the files: ./app/configs/stack.conf.js we see the exposing port would be 9000. But the server host points to localhost, let’s change them to 0.0.0.0 same with address.
  • Searching through other files: ./app/config/mysql.conf.js we see MySQL host points to localhost, that’s wrong since we using Docker system we have to point them to db.
  • How does the app start? The standard node index.js or.. wait this app use npm start, got it from package.json

In short this node app should set to:

mysql:  
  host: db

server:  
  host: 0.0.0.0
  port: 9000
  address: 0.0.0.0

command: npm start  

Create file: ./docker-compose.yml

version: '2'

services:  
  db:
    build:
      context: ./db
      dockerfile: Dockerfile
    volumes:
      - ./db/storage:/var/lib/mysql

  node:
    build: .
    command: npm start
    environment:
      NODE_WORKDIR: /home/node/app
      NODE_ENV: development
    links:
      - db
    ports:
      - '9000:9000'
    volumes:
      - ./app:/home/node/app
      - /home/node/app/node_modules

Create file: ./Dockerfile

FROM mhart/alpine-node:4

ENV HOME=/home/node  
ENV NODE_WORKDIR=$HOME/app

RUN apk --update add \  
 git \
 bash \
 python \
 openssl \
 libgcc \
 make \
 libstdc++ \
 g++

RUN rm -rf /var/cache/apk/*

RUN addgroup node &&\  
  adduser -h /home/node -s /bin/false -G node -D node &&\
  npm install --global npm@3.7.5

COPY ./app/package.json $NODE_WORKDIR/  
RUN chown -R node:node $HOME 

USER node  
WORKDIR $NODE_WORKDIR  
RUN npm shrinkwrap  
RUN npm install && npm cache clean

CMD ["npm", "start"]  

See CMD as npm start to start the node app. Usually on my own projects would be: node index.js

Create file: ./db/Dockerfile

FROM alpine

MAINTAINER Harianto van Insulinde <hariantoatwork@gmail.com>

EXPOSE 3306

RUN apk add --update mysql mysql-client && \  
    mkdir -p /var/lib/mysql && \
    mkdir -p /etc/mysql/conf.d && \
    { \
        echo '[mysqld]'; \
        echo 'expire-logs-days = 3'; \
        echo 'user = root'; \
        echo 'datadir = /var/lib/mysql'; \
        echo 'port = 3306'; \
        echo 'log-bin = /var/lib/mysql/mysql-bin'; \
        echo 'socket = /var/lib/mysql/mysql.sock'; \
        echo '!includedir /etc/mysql/conf.d/'; \
    } > /etc/mysql/my.cnf && \
    rm -rf /var/cache/apk/*

VOLUME ["/var/lib/mysql", "/etc/mysql/conf.d/"]

ENTRYPOINT ["mysqld"]

CMD ["--skip-grant-tables"]  

Now run the command:

docker-compose build  
docker-compose up  

Some directories been created like: ./db/storage and a linked directory in: ./app/node_modules

It's possible to go inside docker container image and access mysql to run queries. From the command line type:
docker-compose run --rm --entrypoint="mysql -h db -u root" db.

To clear logs and enter:
PURGE BINARY LOGS BEFORE NOW();\q

End

This concludes the guide.
Thanks

comments powered by Disqus