How to deploy django app to ECS Fargate Part1
Tags: aws, docker, tutorial, webdev
Published: 2020-07-03 06:43:22

> This post was originally published on notion. Click [here](https://www.notion.so/kokospapa/How-to-deploy-django-app-to-ECS-Fargate-Part1-a1e99c19b2a3423585e67f0b1ad81cbd) if you prefer to read in notion page which has better readability.

# Introduction

This blog post illustrate development cycle using django app container. I assume readers are already somewhat familar with docker and docker-compose. Although I used django for app development, it is language-agnostic since the post is about containerized application deployment.

Walkthough is devided into three parts consisting three different environemt respectively. First part, describes the architecture of the app(api and async worker) and how they are deployed on `local` enviroment. Second part is how to deploy the docker containers on cloud using single ec2 instance with on `staging` environment. Third part, illustrate how to convert traditional ec2 deployment into ECS using fargate with github actions on `prod` environment.

You can skip to next part  if you are already familiar with docker and AWS architectures.
{% link https://dev.to/kokospapa8/how-to-deploy-django-app-to-ecs-fargate-part2-io1 %} 

# Setup

First we need to prepare application which is development ready.

This application consist 3 docker containers

- `nginx` - web server
- `app` - Api server
- `worker`- Async worker

`app` and `worker` share same base images, main difference is that `app` serves http request behind `nginx` web server using gunicorn and wsgi. Throughout this walkthough `nginx` and `app` container will share a common lifecycle. 

`worker` runs asyncronously using redis queue as broker using [Django-RQ](https://github.com/rq/django-rq).

![local docker diagram](https://dev-to-uploads.s3.amazonaws.com/i/k5fxgkowa3dmsl4t2pe7.png)

## Download source

You can download the app source code here.

[github: kokospapa8/ecs-fargate-sample-app](https://github.com/kokospapa8/ecs-fargate-sample-app)

## Docker Containers

### APP

Base app image

Since `app` and `worker` both use same base image and pip takes quite some time to build image, I have uploaded base image in the dockerhub repo.

[Docker Hub](https://hub.docker.com/r/kokospapa8/django-sample/tags)

```docker
# Creating image based on official python3 image
FROM python:3.6

# Your contacts, so people blame you afterwards
MAINTAINER Jinwook Baek <kokos.papa8@gmail.com>

# Sets dumping log messages directly to stream instead of buffering
ENV PYTHONUNBUFFERED 1

# Creating and putting configurations
RUN mkdir /config
ADD config/app /config/

# Installing all python dependencies
RUN pip install -r /config/requirements.txt
```

**APP**

```docker
FROM kokospapa8/django-sample:base

# Installing all python dependencies
RUN pip install -r /config/requirements.txt

# Open port 8000 to outside world
EXPOSE 8000

# When container starts, this script will be executed.
# Note that it is NOT executed during building
CMD ["sh", "/config/django_app.sh"]

# Creating and putting application inside container
# and setting it to working directory (meaning it is going to be default)
RUN mkdir /ecs-sample
WORKDIR /ecs-sample
ADD ecs-sample /ecs-sample/
```

**Worker**

```docker
FROM kokospapa8/django-sample:base

# Installing all python dependencies
RUN pip install -r /config/requirements.txt

# When container starts, this script will be executed.
# Note that it is NOT executed during building
CMD ["sh", "/config/django_worker.sh"]

# Creating and putting application inside container
# and setting it to working directory (meaning it is going to be default)
RUN mkdir /ecs-sample
WORKDIR /ecs-sample
ADD ecs-sample /ecs-sample/
```

### Nginx config

```
# app_local.conf
server {
  listen 80;

  # all requests proxies to app
  location / {
        proxy_pass http://app:8000;
    }

  # domain localhost
  server_name localhost;
}
```

### Enviroment

As you can see from the [source code](https://github.com/kokospapa8/ecs-fargate-sample-app/blob/master/ecs-sample/settings/secrets.py) you need to set some sensitive data into env

```python
# settings/secrets.py
SECRET_KEY = get_env_variable("SECRET_KEY")
						......
REDIS_HOST = get_env_variable("REDIS_HOST")

----

# export SECRET_KEY = '' #only SECRET_KEY is needed to be set for local dev
```

### Docker compose

docker-compose-local.yml

Just for local environment, we are using sqlite and `redis` docker container to mimic cloud enviroment. We will be using RDS-mysql and ElasticCache-redis on `staging` and `production` environment.

```yaml
# File structure version
version: '3'

services:
  # Our django application
  # Build from remote dockerfile
  # Connect local app folder with image folder, so changes will be pushed to image instantly
  # Open port 8000
  app:
    build:
      context: .
      dockerfile: config/app/Dockerfile_app
    hostname: app
    volumes:
      - ./ecs-sample:/ecs-sample
    expose:
      - "8000"
    environment:
      - SECRET_KEY
      - ENV=dev
      - DJANGO_SETTINGS_MODULE=settings.local
      - REDIS_HOST=redis
    depends_on:
      - redis
      - worker

  worker:
    build:
      context: .
      dockerfile: config/app/Dockerfile_worker
    hostname: worker
    volumes:
      - ./ecs-sample:/ecs-sample
    environment:
      - SECRET_KEY
      - ENV=dev
      - DJANGO_SETTINGS_MODULE=settings.local
      - REDIS_HOST=redis
    depends_on:
      - redis

  redis:
    image: redis:5.0.5
    expose:
      - "6379"
    restart: always

  nginx:
    image: nginx
    hostname: nginx
    ports:
      - "80:80"
    volumes:
      - ./config/nginx/app_local.conf:/etc/nginx/conf.d/app_local.conf
    depends_on:
      - app
```

Let's run the containers
```bash
$ docker-compose -f docker-compose-local.yml up --build
```

[![asciicast](https://asciinema.org/a/Wv7ogrpWRNVNRF9f6NdtPOW7G.svg)](https://asciinema.org/a/Wv7ogrpWRNVNRF9f6NdtPOW7G)

### Check result

Type in following url to see if you are getting correct response.
- http://localhost/api/healthcheck/
- http://localhost/api/v1/posts/
- http://localhost/admin/posts/
- http://localhost/django-rq/   -  to check async worker  


# Moving on
{% link https://dev.to/kokospapa8/how-to-deploy-django-app-to-ecs-fargate-part2-io1 %} 

# Reference

[Overview of Docker Compose](https://docs.docker.com/compose/)

[How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 16.04 | DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04)

[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)

[Dockerizing Django with Postgres, Gunicorn, and Nginx](https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/)