How to link multiple Docker containers and encapsulate the result?

I have a Node.js web-application that connects to a Neo4j database. I would like to encapsulate these in a single Docker image (using also a Neo4j Docker container), but I’m a docker novice and can’t seem to figure this out. What’s the recommended way to do it in the latest Docker versions?

My intuition would be to run the Neo4j container nested inside the app container. But from what I’ve read, I think the supported / recommended approach is to link the containers together. What I need is pretty well illustrated in this image. But the article where the image comes from isn’t clear to me. Anyway, it’s using the soon-to-be-deprecated legacy container linking, while networking is recommended these days. A tutorial or explanation would be much appreciated.

  • Docker user executing a process cannot be removed
  • Could not install gems on docker container
  • Jenkins and docker-compose
  • How to start Docker for Mac daemon on boot?
  • How to monitor Nodejs app running on Kubernetes
  • Jenkins: running build does not provision docker slave
  • Also, how does docker-compose fit into all this?

  • Map public container port for few private in Rancher
  • Docker issue in sharing folder on windows host through VBoxManage command
  • what triggers Elastic Beanstalk to pull in an updated Docker image
  • org.apache.http.conn.UnsupportedSchemeException: unix protocol is not supported
  • Where does docker ps Command look for current containers?
  • Run docker in ubuntu live disk
  • One Solution collect form web for “How to link multiple Docker containers and encapsulate the result?”

    Running a container within another container would imply to run a Docker engine within a Docker container. This is referenced as dind for Docker-in-Docker and I would strongly advise against it. You can search ‘dind’ online and discover why in most cases it is a bad idea, but as it is not the main object of your question I won’t extend this subject any further.

    Running both a node.js process and a neo4j process in the same container

    While most people will tell you to refrain yourself from running more than one process within a Docker container, nothing prevents you from doing so. If you want to follow this path, take a look at the Using Supervisor with Docker from the Docker documentation website, or at the Phusion baseimage Docker image.

    Just be aware that this way of doing things will make your Docker image more and more difficult to maintain over time.

    Linking containers

    As you found out, keeping Docker images as simple as you can (i.e: running one and only one app within a Docker container) will make your life easier on the long term.

    Linking containers together is trivial when both containers run on the same Docker engine. It is just a matter of:

    • having your neo4j container expose the port its service listens on
    • running your node.js container with the --link <neo4j container name>:<alias> option
    • within the node.js application configuration, set the neo4j host to the <alias> hostname, docker will take care of forwarding that connection to the IP it assigned to the neo4j container

    When you want to run those two containers on different hosts, things get more difficult.

    With Docker Compose, you have to use the link: key to define your links

    The new Docker network feature

    You also discovered that linking containers won’t be supported in the future and that the new way of making multiple Docker containers communicate is to create a virtual network and attach those 2 containers to that network.

    Here’s how to proceed:

    docker network create mynet
    docker run --detach --name myneo4j --net mynet neo4j
    docker run --detach --name mynodejs --net mynet <your nodejs image>
    

    Your node application configuration should then use myneo4j as the host to connect to.

    To tell Docker Compose to use the new network feature, you would have to use the --x-networking option. Also you would not use the links: key.

    Using the new networking feature also means that you won’t be able to define any alias for the db. As a result you have to use the container name. Beware that unless you use the container_name: key in your docker-compose.yml file, Compose will create container names based on the directory which contains your docker-compose.yml file, the service name as found in the yml file and a number.

    For instance, the following docker-compose.yml file, if within a directory named “foo” would create two containers named foo_web_1 and foo_db_1:

    web:
      build: .
      ports:
        - "8000:8000"
    db:
      image: postgres
    

    when started with docker-compose --x-networking up, the web app configuration should then use foo_db_1 as the db hostname.

    While if you use container_name:

    web:
      build: .
      ports:
        - "8000:8000"
    db:
      image: postgres
      container_name: mydb
    

    when started with docker-compose --x-networking up, the web app configuration should then use mydb as the db hostname.


    Example of using Docker Compose to run a web app using nodeJS and neo4j

    In this example, I will show how to dockerize the example app from github project aseemk/node-neo4j-template which uses nodejs and neo4j.

    I assume you already have Docker 1.9.0+ and Docker Compose 1.5+ installed.

    This project will use 2 docker containers, one to run the neo4j database and one to run the nodeJS web app.

    Dockerizing the web app

    We need to build a Docker image from which Docker compose will run a container. For that, we will write a Dockerfile.

    Create a file named Dockerfile (mind the capital D) with the following content:

    FROM node
    RUN git clone https://github.com/aseemk/node-neo4j-template.git
    WORKDIR /node-neo4j-template
    RUN npm install
    # ugly 20s sleep to wait for neo4j to initialize
    CMD sleep 20s && node app.js
    

    This Dockerfile describes the steps the Docker engine will have to follow to build a docker image for our web app. This docker image will:

    • be based on the official node docker image
    • clone the nodeJS example project from Github
    • change the working directory to the directory containing the git clone
    • run the npm install command to download and install the nodeJS app dependencies
    • instruct docker which command to use when running a container of that image

    A quick review of the nodeJS code reveals that the author allows us to configure the URL to use to connect to the neo4j database using the NEO4J_URL environment variable.

    Dockerizing the neo4j database

    Well people took care of that for us already. We will use the official Docker image for neo4j which can be found on the Docker Hub.

    A quick review of the readme tells us to use the NEO4J_AUTH environment variable to change the neo4j password. And setting this variable to none will disable the authentication all together.

    Setting up Docker Compose

    In the same directory as the one containing our Dockerfile, create a docker-compose.yml file with the following content:

    db:
      container_name: my-neo4j-db
      image: neo4j
      environment:
        NEO4J_AUTH: none
    
    web:
      build: .
      environment:
        NEO4J_URL: http://my-neo4j-db:7474
      ports:
        - 80:3000
    

    This Compose configuration file describes 2 services: db and web.

    The db service will produce a container named my-neo4j-db from the official neo4j docker image and will start that container setting up the NEO4J_AUTH environment variable to none.

    The web service will produce a container named at docker compose discretion using a docker image built from the Dockerfile found in the current directory (build: .). It will start that container setting up the environment variable NEO4J_URL to http://my-neo4j-db:7474 (note how we use here the name of the neo4j container my-neo4j-db). Furthermore, docker compose will instruct the Docker engine to expose the web container’s port 3000 on the docker host port 80.

    Firing it up

    Make sure you are in the directory that contains the docker-compose.yml file and type: docker-compose --x-networking up.

    Docker compose will read the docker-compose.yml file, figure out it has to first build a docker image for the web service, then create and start both containers and finally will provide you with the logs from both containers.

    Once the log shows web_1 | Express server listening at: http://localhost:3000/, everything is cooked and you can direct your Internet navigator to http://<ip of the docker host>/.

    To stop the application, hit Ctrl+C.

    If you want to start the app in the background, use docker-compose --x-networking up -d instead. Then in order to display the logs, run docker-compose logs.

    To stop the service: docker-compose stop

    To delete the containers: docker-compose rm

    Making neo4j storage persistent

    The official neo4j docker image readme says the container persists its data on a volume at /data. We then need to instruct Docker Compose to mount that volume to a directory on the docker host.

    Change the docker-compose.yml file with the following content:

    db:
      container_name: my-neo4j-db
      image: neo4j
      environment:
        NEO4J_AUTH: none
      volumes:
        - ./neo4j-data:/data
    
    web:
      build: .
      environment:
        NEO4J_URL: http://my-neo4j-db:7474
      ports:
        - 80:3000
    

    With that config file, when you will run docker-compose --x-networking up, docker compose will create a neo4j-data directory and mount it into the container at location /data.

    Starting a 2nd instance of the application

    Create a new directory and copy over the Dockerfile and docker-compose.yml files.

    We then need to edit the docker-compose.yml file to avoid name conflict for the neo4j container and the port conflict on the docker host.

    Change its content to:

    db:
      container_name: my-neo4j-db2
      image: neo4j
      environment:
        NEO4J_AUTH: none
      volumes:
        - ./neo4j-data:/data
    
    web:
      build: .
      environment:
        NEO4J_URL: http://my-neo4j-db2:7474
      ports:
        - 81:3000
    

    Now it is ready for the docker-compose --x-networking up command. Note that you must be in the directory with that new docker-compose.yml file to start the 2nd instance up.

    Docker will be the best open platform for developers and sysadmins to build, ship, and run distributed applications.