DEV Community

loading...

Working with Multiple Containers Using Docker Compose on Linux with Django and NGINX

pmutua profile image Philip Mutua ・4 min read

In this article we’re going to explore how to segment our app into a small network of multiple Docker containers, each with their own images.

Single containers are easy enough to build imperatively in the command line, but doing anything more complicated can quickly get out of hand. Instead we’re going to use a new special type of config file called docker-compose.yml. This declarative approach will allow us to quickly define our images in each container and setup the networking between them.

In this example we’re going to be setting up an NGINX server, a Django server.

Prerequisites

It would be helpful to know how to build images with Dockerfile, which you can brush up on here, but that will mostly be taken care of in the starter.

Make sure the following are installed in your local machine

  1. virtualenv - for Python local environment
  2. Docker
  3. Docker Compose
  4. Python - Should be installed in your system to run virtualenv.

Project Structure

├── botservice/
├── core
|   ├── core/
│   │    ├── __init__.py
│   │    ├── asgi.py
│   │    ├── settings.py
│   │    ├── urls.py
│   │    ├── wsgi.py
│   │ 
│   └── Dockerfile
│   │ 
│   └── db.sqlite3
│   ├── manage.py
│   └── requirements.txt
├── nginx
│   ├── default.conf
│   └── Dockerfile
├── env/
├──docker-compose.yml

Starter Setup

  1. Create a directory in your workspace eg. botserver navigate to your directory cd botserver using the terminal.

  2. Activate virtual environment - run virtualenv -p python env. Then to activate virtual environment run
    source env/bin/activate

  3. Install Django into the virtual environment by running pip install Django it will install the latest version of Django framework.

  4. Create Django project run django-admin startproject core core is the name of our project. Inside the core folder where manage.py is located create a file called requirements.txt this is where we add our Python dependencies they will be installed when docker runs builds, in the requirements.txt file add the dependencies as shown below:

Django==3.1.1
gunicorn==20.0.4

Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX.

NGINX Setup

The NGINX server is different than the other containers. NGINX will act as the router for the server, directing requests to the server container.

In a special configuration file, default.conf, we’ll use upstream to tell NGINX on what server port each the container is running.

  1. Create a folder called nginx in the botservice folder, inside botservice folder in the terminal run mkdir nginx

  2. Navigate inside the newly created folder nginx and create a file called default.conf and add the following code.

upstream botservice {
    server botservice:7000;
}


server {

  listen 80;

  location /{
    proxy_pass http://botservice/;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_redirect off;
    proxy_connect_timeout       300;
    proxy_send_timeout          300;
    proxy_read_timeout          300;
    send_timeout                300;
  }

}

Now we just need docker to put this configuration somewhere more useful. The NGINX container will already have an empty default.conf file, so copying ours to its location will override the old one.

Create a Dockerfile inside the nginx folder add the following to the Dockerfile


FROM nginx

RUN rm -rf /usr/share/nginx/html/*


COPY  ./default.conf  /etc/nginx/conf.d/default.conf

We also need to add a Dockerfile for the Django server
inside the core directory where manage.py file is located add a file called Dockerfile then add the following.


FROM python:3.8-slim-buster

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1


WORKDIR /src


COPY requirements.txt /src/

RUN pip install -r requirements.txt


COPY . .

Docker Compose

Create a file called docker-compose.yml inside the botservice folder and add the following:


version: '3'

services:
  botservice:
    container_name: botservice
    build: 
      context: ./core

    command: sh -c "gunicorn --bind 0.0.0.0:7000 core.wsgi:application"
    restart: always
    volumes:
      - "./core/:/src/"
      - static_volume:/src/static

    expose:
     - "7000"


  nginx:
    container_name: nginx
    restart: always
    build: ./nginx
    ports:
      - "80:80"
    volumes:
      - static_volume:/src/static 

    depends_on: 
      - botservice

volumes:
  static_volume:

Let’s go over exactly what this is trying to do:

  1. service - Declares each container with its particular configuration, which we can name however we like.
  2. build - Tells how we want our container built, in this case which file to use and where it is with dockerfile and context.
  3. restart - Tells Docker what to do if a container fails during runtime, in this case we always want it to attempt to restart.
  4. ports - Remaps whatever port we want to the default port, just like the -p flag when working in the terminal.
  5. volumes - Are the persistent data connected to each container. We’re duplicating parts of our container and its dependencies in a way that when we throw the container away and start a new one it’ll have that cache to avoid the time of reinstalling everything.
  6. depends_on - Express dependency between services, which has two effects:
  7. container_name - Specify a custom container name, rather than a generated default name.
  8. expose - Expose ports without publishing them to the host machine - they’ll only be accessible to linked services. Only the internal port can be specified.
  9. command - Override the default command.

More info about Docker Compose file.

Finally we can create our services and attach our containers together using the docker-compose up command and the --build flag to build out our Dockerfiles.

Navigate to where the docker-compose.yml file is located via terminal and run:

docker-compose up --build

Closing Thoughts

This may have been a very simple use case, but Docker Compose is definitely one of the major tools you’ll be using with almost all of your Docker projects. You can check out a demo here to see how I did it when developing some chatbot.

Discussion (1)

pic
Editor guide
Collapse
maksam07 profile image
maksam07

we have nginx listening on port 80 ("80:80"). It turns out that I cannot create a second project on port 80 so that another project will open on another homer?