Dockerizing Django with Numpy and Gunicorn
Tags: docker, django, python, numpy
Published: 2021-01-14 10:19:47

---
title: Dockerizing Django with Numpy and Gunicorn
published: true
description: Dockerizing Django Web Server with Numpy
tags: #docker #django #python #numpy
cover_image: https://repository-images.githubusercontent.com/65659765/88c01100-f9bb-11e9-8b97-d356d596d0dc
---

Hi all.

Actually theres lot of tutorial about dockerizing django on web. But at the project i working on we using libraries like Numpy and Pillow and alpine linux cannot compile this because missing dependencies.

Our Stack;
- Django
- Numpy
- Pillow
- Postgresql

--- 
First of all let create basic `Dockerfile`


```Dockerfile
FROM python:3.6.4-alpine

ADD . /app

WORKDIR /app

RUN pip install -r requirements.txt

EXPOSE 8000

CMD ["manage.py", "runserver"]
```

Its basically works if you not using c compiled libraries like `Numpy`.

The thing is we need to add compilers to alpine i have big layer on there because running this proccesses on one layer is better i think.

Full Dependency install Layer :
```Dockerfile
RUN apk add --no-cache jpeg tiff-dev openjpeg-dev postgresql-dev && \
    apk add --no-cache \
    --virtual=.build-deps \
    g++ zlib-dev freetype-dev lcms2-dev \
    gcc libc-dev linux-headers tk-dev tcl-dev harfbuzz-dev \
    fribidi-dev build-base py-pip rsyslog cython file binutils \
    musl-dev python3-dev cython && \
    ln -s locale.h /usr/include/xlocale.h && \
    pip install --upgrade pip && \
    pip install -r requirements.txt --no-cache-dir && \
    rm -r /root/.cache && \
    find /usr/lib/python3.*/ -name 'tests' -exec rm -r '{}' + && \
    find /usr/lib/python3.*/site-packages/ -name '*.so' -print -exec sh -c 'file "{}" | grep -q "not stripped" && strip -s "{}"' \; && \
    rm /usr/include/xlocale.h && \
    apk --purge del .build-deps
```
---
### Compilers for Alpine
I installed some packages like `g++` and `linux-headers` virtual and cleaned them after pip install because i just need this packages for post scripts of some dependencies and size is matter on container.

```
  -t, --virtual NAME    Instead of adding all the packages to 'world', create a new 
                        virtual package with the listed dependencies and add that 
                        to 'world'; the actions of the command are easily reverted 
                        by deleting the virtual package
```

<p>What that means is when you install packages, those packages are not added to global packages. And this change can be easily reverted. So if I need gcc to compile a program, but once the program is compiled I no more need gcc. </p>
<p>
I can install gcc, and other required packages in a virtual package and all of its dependencies and everything can be removed this virtual package name. Below is an example usage
</p>

```
apk add --virtual mypacks gcc vim
apk del mypacks
```

<p>The next command will delete all 18 packages installed with the first command.</p>
---
### Postgres

Im running Postgresql on my local machine not docker. And i need to connect it. You can use `host.docker.internal` this mapping is directly routes your machines `localhost`

So i added this env variable

```Dockerfile
ENV DB_HOST=host.docker.internal
```

<p>and used this on `settings.py`</p>
---
### Gunicorn
I decided to use Gunicorn for running processed with `n` workers. So i added `gunicorn` to my requirements.txt and added this two lines for run processes

```Dockerfile
ENV WORKER_COUNT=2

CMD ["sh", "-c", "gunicorn --bind :8000 --workers ${WORKER_COUNT} project.wsgi:application"]
```
Using `WORKER_COUNT` env variable for default value. Im overriding this value everytime i need.

---

### Running
I decided to create makefile for run this container with `n` workers.
So i created Makefile with this command.

```Makefile

docker-run:
	docker run -e worker_count=${worker} -p 127.0.0.1:8000:8000 project${version}
```
---

### Full Dockerfile


```Dockerfile
FROM python:3.6.4-alpine

ADD . /app
WORKDIR /app

# Need to Update at Settings.py
ENV DB_HOST=host.docker.internal

# Default Worker Count is 2
ENV WORKER_COUNT=2

# Install dependencies layer.
RUN apk add --no-cache postgresql \
                        jpeg tiff-dev openjpeg-dev \
                        libpq postgresql-libs postgresql-dev && \
    apk add --no-cache \
    --virtual=.build-deps \
    g++ jpeg-dev zlib-dev freetype-dev lcms2-dev \
    gcc libc-dev linux-headers tk-dev tcl-dev harfbuzz-dev \
    fribidi-dev zlib-dev build-base py-pip rsyslog cython file binutils \
    musl-dev python3-dev cython && \
    ln -s locale.h /usr/include/xlocale.h && \
    pip install --upgrade pip && \
    pip install -r requirements.txt --no-cache-dir && \ 
    rm -r /root/.cache && \
    find /usr/lib/python3.*/ -name 'tests' -exec rm -r '{}' + && \
    find /usr/lib/python3.*/site-packages/ -name '*.so' -print -exec sh -c 'file "{}" | grep -q "not stripped" && strip -s "{}"' \; && \
    rm /usr/include/xlocale.h && \
    apk --purge del .build-deps  


EXPOSE 8000

CMD ["sh", "-c", "gunicorn --bind :8000 --workers ${WORKER_COUNT} project.wsgi:application"]
```
I hope you found this article useful.

Thanks.