Docker compose : Making local development easier

Docker compose : Making local development easier

Prerequisites

  • An image is a bundled package containing your application's code, libraries, and dependencies.

  • When you run an image, it transforms into an active and isolated environment called a container, where your application executes.

  • Docker Hub is a public container registry which is also the default registry from where you pull images when using the docker pull command.

Why use docker compose?

  • Individual commands in docker can get very lengthy with environment variables, network name, image name, port mapping, volumes etc. in the same command. This keeps increasing as the complexity of your application increases.

  • To set up multiple applications/ services, you'll need to create a local network (bridge network) and set up each service/ application individually.

    (We will set up multiple containers this way to see the difference).

Docker compose enables the user to create a single docker-compose configuration YAML file which contains all of the applications/ services required along with their port mappings, image name container name, environment variables, volumes etc. and start/stop/create all of the above with a single command.

Setting up multiple containers without docker compose

Here we will run Redis, MongoDB and Mongo-Express (a web-based MongoDB interface), we will have an express application running locally (you don't need to set this up for this tutorial). You can choose to run this express app as a container as well. You can find the complete express code here for practice -https://github.com/adityabisht02/redis-prac

Firstly let's see how to set up the three containers without using docker compose.

Custom Bridge network

By default, if u run two containers separately, they run in isolation(in different local networks). A bridge network is a virtual network that exists within the host machine. Users can create custom bridge networks for their use case.

Containers in the same custom bridge network can access each other using container names(only possible for custom networks not for the default network) or IP addresses. Let's look at some commands related to bridge networks.

Create a custom bridge network

docker network create docker-demo #creates a bridge network with the name docker demo

List all networks

docker network ls # lists all custom and local networks
NETWORK ID     NAME               DRIVER    SCOPE
27e024846cb7   bridge             bridge    local
2c76366b6a7e   docker-demo        bridge    local
7060d136b742   host               host      local
80c7f8851aa9   none               null      local
8d635fa67de4   outline_default    bridge    local
b7d1fce5925e   postgres-network   bridge    local

Our docker-demo local network is listed as a bridge network as discussed above.

Adding Redis, Mongo and Mongo-Express to our network

Now that we have a local-network we can pull and run containers in the same network to make communication between them easier. Use the --network flag to add a container to the network.

  • Run the mongo container. Here we are putting the name as some-mongo, this will be useful when we want to access this container with mongo-express.
docker run --name some-mongo --network docker-demo -d mongo
  • Run the mongo-express container and access it on localhost:8081. In the below command you can observe that the mongoDB server name is some-mongo that we created earlier.
docker run --network docker-demo -e ME_CONFIG_MONGODB_SERVER=some-mongo -p 8081:8081 mongo-express
  • Run the Redis container.
docker run --name some-redis --network docker-demo -d redis
  • Listing all containers in a network
docker network inspect docker-demo
"Containers": {
            "5d2cdf2f0c342ebbd7afa5cbe0690952771761ef78adcfec8ab6e9d1cfb84504": {
                "Name": "some-redis",
                "EndpointID": "bf8eeebb0c31d4a3546538120e46b467b33eede7c59fe9b32b961332d0b57dfa",
                "MacAddress": "02:42:ac:13:00:04",
                "IPv4Address": "172.19.0.4/16",
                "IPv6Address": ""
            },
            "7bb067471f5614f977d0aae6a5ff51a3553752454f6625a4aba25bf0d7349f0c": {
                "Name": "heuristic_yalow",
                "EndpointID": "314115f5cefae77b54e3fc2ac2f48c352516e60e166a2749301e6c0ff1c006e9",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "f1e1bdf9cc8843cc0a3405154de97ca97b050f862ac3f17b756a95bf12763191": {
                "Name": "some-mongo",
                "EndpointID": "096a87b84f19f84f34d7a377ab12d88f534db698e0e8d6c12ddff377f346456f",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            }
        }

You can all the 3 containers, the mongo express container has a random name since we did not assign a name to it.

When you add a container to a network you don't have to add it every time you wish to start it. However, this process is still tedious every time a new developer wishes to set up the same project locally or everytime you create a new container. Docker compose helps simplify this.

Using Docker compose

Docker Compose creates a custom bridge network for your project by default so you don't have to create and add all services to a common network. The entire process we followed above can be done with a single command.

Creating a docker compose file

There can be multiple YAML configuration files in a project. Hence to identify the compose file docker compose will look for the docker-compose.yml/ docker-compose.yaml file by default.

Hence create a docker-compose.yml file in the root directory of your project.

Add the following contents to the file. Let's break down the contents individually as well.

version: '3.1'
services:       
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: example
      ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
    depends_on:
      - mongo
  redis:
    image: redis
    ports:
      - "6379:6379"
  • version : Defines the docker-compose version.

  • services : This section is where you define the various services that make up your multi-container application. Here we have 3 services, mongo, mongo-express (for the UI), redis.

  • image : The image to be used for the service. To be pulled from docker hub.

  • restart : In case the container stops for any reason, it restarts.

  • ports : Port mapping between host and container ports.

  • environment : This section sets environment variables if required. Instead of hard coding environment variables you can also use env_file to use a .env file instead.

  • depends_on : For every service you can specify if it depends on any other service. In that case the services it depends on will be started before. Here mongo-express requires mongo to be running to connect to it, hence we mention mongo in the depends_on section of mongo-express.

Containerize the express-app

If you wish to containerize the express app running locally as well, you will need to create a Dockerfile for it. After creating the Dockerfile you can add the following to the docker-compose file under services to build, name and run the container with the other containers.

express-app:
    build: ./
    container_name: express-app
    ports:
      - '3000:3000'

Docker compose commands

  • Creates and runs containers in the docker-compose file, also creates a network
docker compose up

Note - (Most developers prefer docker-compose with a hyphen instead of a space.)

  • Stops and removes containers as well as the network created.
docker compose down
  • Creates a network and the containers but does not start them
docker compose create
  • Starts the containers if they exist or have stopped
docker compose start
  • Stops running containers, does not remove them or the network
docker compose stop

For getting all other docker compose commands you can use the following command -

docker compose -v

That is all for this blog, hope you learnt something !!