disclaimer: I am new to Docker so please take this with a grain of salt. at the time of writing, this setup worked on my computer.
also please let me know in the comments if I got something wrong
the files
These files assumes that your Dockerfile lives in the root of your Django project (i.e. in the same level as manage.py
).
Dockerfile
FROM python:3.9-slim-buster
# set environment variables
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# don't write .pyc files
ENV PYTHONDONTWRITEBYTECODE 1
# prevent Docker from buffering stdout
ENV PYTHONUNBUFFERED 1
# set working directory
WORKDIR /code
RUN apt-get update && apt-get install -y \
curl \
gnupg2 \
unzip \
wget
# chrome
RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& apt-get update \
&& apt-get install google-chrome-stable -y
# chromedriver
RUN CHROME_VERSION="$(google-chrome --version)" \
&& export CHROMEDRIVER_RELEASE="$(echo $CHROME_VERSION | sed 's/^Google Chrome //')" \
&& export CHROMEDRIVER_RELEASE=${CHROMEDRIVER_RELEASE%%.*} \
&& CHROMEDRIVER_VERSION=$(curl http://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROMEDRIVER_RELEASE}) \
&& curl --output /tmp/chromedriver_linux64.zip "http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip" \
&& cd /tmp \
&& unzip chromedriver_linux64.zip \
&& rm -rf chromedriver_linux64.zip \
&& mv chromedriver /usr/local/bin/chromedriver \
&& chmod +x /usr/local/bin/chromedriver \
&& cd /code
COPY ./requirements.txt .
# install dependencies
RUN apt-get update && apt-get install -y \
gcc \
less \
libmagickwand-dev \
libpq-dev \
vim \
&& rm -rf /var/lib/apt/lists/* \
&& pip install -r requirements.txt
# copy project
COPY . .
docker-compose.yml
version: "3.9"
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
environment:
- CHROME_DRIVER_PATH=/usr/local/bin/chromedriver
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- "POSTGRES_HOST_AUTH_METHOD=trust"
redis:
image: redis:alpine
celery:
build:
context: .
command: celery -A rises_ops worker -l INFO
environment:
- CHROME_DRIVER_PATH=/usr/local/bin/chromedriver
volumes:
# same volume as web
- .:/code
depends_on:
- db
- redis
- web
volumes:
postgres_data:
migration script
after running docker compose up --build
, run the migration from your terminal
$ docker-compose exec web python manage.py migrate
(there is probably a smarter way to do this, let me know in the comments)
references
blogs that actually helped
https://learndjango.com/tutorials/django-docker-and-postgresql-tutorial
https://learndjango.com/tutorials/django-docker-and-postgresql-tutorial
debugging tips
# build image with no cache
$ docker build . --no-cache
# output build process to your terminal
$ docker build . --progress=plain
# re-build your services
$ docker compose up --build
# clean cache
$ docker system prune
$ docker image prune
note that the pg_data
volume is actually saved in Host, so the DB data will persist even after you delete your conatiners. to clear even the DB,
# identify your volume
# format is project_db-data e.g. myproject_db-data
$ docker volume ls
# remove
$ docker volume rm <VOLUME ID>
ref: https://torajirousan.hatenadiary.jp/entry/2020/07/08/094325
docker-compose.yml
the docker-compose.yml
is pretty straightforward. we have 4 services: web
db
redis
celery
.
- web: the Django werver
- db: PostgreSQL DB
- redis: Redis used in the Celery workers
- celery: worker process for Django
the commands written in the command:
section of the file should be similar to what you execute in your local terminal.
one thing to note is that, web
and celery
share the same volume, since both of them need access to the Django code
Dockerfile
the bulk of this file is to install chrome and chromedriver. both of them are not in the default apt-get
repository, so you have to
- add the repository for
google-chrome-stable
, and - download the zip file for
chromedriver
in my past experience, Ubuntu has chromium
and chromium-driver
in the default repository, so this might be a better option if image size is not a problem for you.
chrome
to explain the process for installing Google Chrome,
# add the repository to the list
RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' \
# add key for signed packages
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
# don't forget to update the list after adding the repo
&& apt-get update \
# finally install the package
&& apt-get install google-chrome-stable -y
chromedriver
getting chromedriver is even more complex.
this is because
- chrome and chromedriver needs to have compatible versions
- BUT chrome and chromedriver versions are not always the same
to get the compatible versions, you have to first download the chrome, and get the compatible version from a web page.
let's go through this with an example.
at the time of writing (Sep. 2022), the latest version of google-chrome
is 105.0.5195.102
.
$ google-chrome --version
# Google Chrome 105.0.5195.102
to get the corresponding chromedriver version, go to
https://chromedriver.storage.googleapis.com/LATEST_RELEASE_105
(if your chrome version was 80.x.x.x
, you would instead go to https://chromedriver.storage.googleapis.com/LATEST_RELEASE_80 )
the page would show you that the corresponding chromedriver version is 105.0.5195.52
Dockerfile
does this in the script by first getting the 105
part from the chrome version 105.0.5195.102
.
# get chrome version
RUN CHROME_VERSION="$(google-chrome --version)" \
# remove string "Google Chrome " from the output
&& export CHROMEDRIVER_RELEASE="$(echo $CHROME_VERSION | sed 's/^Google Chrome //')" \
# get the major version
# `%%` removes the longest match of the following pattern `.*`
# so it removes `.0.5195.52` and stores `105` in the variable
&& export CHROMEDRIVER_RELEASE=${CHROMEDRIVER_RELEASE%%.*} \
next, it accesses the LATEST_RELEASE_
page, and stores the response in the variable CHROMEDRIVER_VERSION
.
&& CHROMEDRIVER_VERSION=$(curl http://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROMEDRIVER_RELEASE}) \
# 105.0.5195.52
# coincindentally, chrome & chromedriver versions match
then, download the zip via curl --output
, and unzip in the /tmp
folder
# specify output location to /tmp/chromedriver_linux64.zip
&& curl --output /tmp/chromedriver_linux64.zip "http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip" \
# go to `/tmp`
&& cd /tmp \
# unzip in `/tmp`
&& unzip chromedriver_linux64.zip \
finally, we move chromedriver to /usr/local/bin
and make sure chromedriver is executable
# remove zip file because we don't need it anymore
&& rm -rf chromedriver_linux64.zip \
# move executable to /usr/local/bin
&& mv chromedriver /usr/local/bin/chromedriver \
# add permission
&& chmod +x /usr/local/bin/chromedriver \
as a clean up step, we move back to the WORKDIR
&& cd /code
put together, we have
RUN CHROME_VERSION="$(google-chrome --version)" \
&& export CHROMEDRIVER_RELEASE="$(echo $CHROME_VERSION | sed 's/^Google Chrome //')" \
&& export CHROMEDRIVER_RELEASE=${CHROMEDRIVER_RELEASE%%.*} \
&& CHROMEDRIVER_VERSION=$(curl http://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROMEDRIVER_RELEASE}) \
&& curl --output /tmp/chromedriver_linux64.zip "http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip" \
&& cd /tmp \
&& unzip chromedriver_linux64.zip \
&& rm -rf chromedriver_linux64.zip \
&& mv chromedriver /usr/local/bin/chromedriver \
&& chmod +x /usr/local/bin/chromedriver \
&& cd /code
(same result as docker-compose.yml
in the first section)
Top comments (0)