Skip to content

Instantly share code, notes, and snippets.

@arifwn
Last active December 23, 2024 05:57
Show Gist options
  • Save arifwn/2b9d5eec6c93665f30bfe8b558b8b6bd to your computer and use it in GitHub Desktop.
Save arifwn/2b9d5eec6c93665f30bfe8b558b8b6bd to your computer and use it in GitHub Desktop.
(Relatively) Simple Dockerfile that bundles Django, Nginx, Gunicorn, Supervisord, Celery, Redis, Memcached, wkhtmltopdf, nodejs and React frontend build in a single container
version: '2' # sample docker-compose to bring up the whole thing
services:
nginx: # the "load balancer" to terminate https connections, configure this to proxy all request to "appserver"
image: nginx:1.15-alpine
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
ports:
- "80:80"
- "443:443"
volumes:
- ~/tmp/app/conf:/etc/nginx/conf.d:cached
depends_on:
- mls
postgres:
image: postgres
volumes:
- ~/tmp/app/postgres:/var/lib/postgresql/data:cached
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: replaceme
appserver:
image: me/myapp
volumes:
- ~/tmp/app/media:/var/media:cached
- ~/tmp/app/logs:/var/logs:cached
- ~/tmp/app/build:/var/app/ui/build
- ~/tmp/app/node_modules:/var/app/ui/node_modules
depends_on:
- postgres
environment:
HOST: app.localhost # make sure to update your settings.py to allow specifying these config via env variables
SECRET_KEY: replaceme
PRODUCTION: production
BUILD_UI: "NO" # set to "YES" to run `yarn run build` on ui directory
DATABASE_BACKEND: django.db.backends.postgresql_psycopg2
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_NAME: postgres
DATABASE_USER: postgres
DATABASE_PASSWORD: replaceme
MEDIA_ROOT: /var/media/media
STATIC_ROOT: /var/media/static
WORKER_COUNT: 2
FROM python:3.8
ENV PYTHONUNBUFFERED 1
# Install node prereqs, nodejs and yarn
# Ref: https://deb.nodesource.com/setup_10.x
# Ref: https://yarnpkg.com/en/docs/install
RUN \
apt-get update && \
apt-get install -yqq apt-transport-https xfonts-base xfonts-75dpi libsasl2-dev
RUN \
echo "deb https://deb.nodesource.com/node_10.x stretch main" > /etc/apt/sources.list.d/nodesource.list && \
wget -qO- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list && \
wget -qO- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
apt-get update && \
apt-get install -yqq nodejs yarn && \
rm -rf /var/lib/apt/lists/*
# install redis
RUN groupadd -r redis && useradd -r -g redis redis
ENV REDIS_VERSION 5.0.4
ENV REDIS_DOWNLOAD_URL http://download.redis.io/releases/redis-5.0.4.tar.gz
ENV REDIS_DOWNLOAD_SHA 3ce9ceff5a23f60913e1573f6dfcd4aa53b42d4a2789e28fa53ec2bd28c987dd
# for redis-sentinel see: http://redis.io/topics/sentinel
RUN set -ex; \
wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL"; \
echo "$REDIS_DOWNLOAD_SHA *redis.tar.gz" | sha256sum -c -; \
mkdir -p /usr/src/redis; \
tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1; \
rm redis.tar.gz; \
grep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 1$' /usr/src/redis/src/server.h; \
sed -ri 's!^(#define CONFIG_DEFAULT_PROTECTED_MODE) 1$!\1 0!' /usr/src/redis/src/server.h; \
grep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 0$' /usr/src/redis/src/server.h; \
make -C /usr/src/redis -j "$(nproc)"; \
make -C /usr/src/redis install; \
rm -r /usr/src/redis;
RUN mkdir /var/redis && chown redis:redis /var/redis
# install memcached
RUN groupadd --system --gid 11211 memcache && useradd --system --gid memcache --uid 11211 memcache
ENV MEMCACHED_VERSION 1.5.14
ENV MEMCACHED_SHA1 94592688ae685caf53ab90a821c99f1503bb1018
RUN set -x \
&& wget -O memcached.tar.gz "https://memcached.org/files/memcached-$MEMCACHED_VERSION.tar.gz" \
&& echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \
&& mkdir -p /usr/src/memcached \
&& tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \
&& rm memcached.tar.gz \
&& cd /usr/src/memcached \
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
&& ./configure \
--build="$gnuArch" \
--enable-sasl \
&& make -j "$(nproc)" \
&& make install \
&& cd / && rm -rf /usr/src/memcached \
&& memcached -V
RUN mkdir /var/memcache && chown memcache:memcache /var/memcache
# install nginx
ENV NGINX_VERSION 1.15.12-1~stretch
ENV NJS_VERSION 1.15.12.0.3.1-1~stretch
RUN set -x \
&& \
NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
found=''; \
for server in \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
done; \
test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture)" \
&& nginxPackages=" \
nginx=${NGINX_VERSION} \
nginx-module-xslt=${NGINX_VERSION} \
nginx-module-geoip=${NGINX_VERSION} \
nginx-module-image-filter=${NGINX_VERSION} \
nginx-module-njs=${NJS_VERSION} \
" \
&& echo "deb https://nginx.org/packages/mainline/debian/ stretch nginx" >> /etc/apt/sources.list.d/nginx.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
$nginxPackages \
gettext-base \
&& rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list
# install wkhtmltopdf
RUN \
cd /tmp/ && \
wget --quiet https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb && \
dpkg -i wkhtmltox_0.12.5-1.stretch_amd64.deb && \
rm wkhtmltox_0.12.5-1.stretch_amd64.deb
# install app
RUN mkdir /var/app && chown www-data:www-data /var/app
WORKDIR /var/app
COPY ./requirements.txt /var/app/
RUN pip install -r requirements.txt
COPY . /var/app/
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
VOLUME /var/media
VOLUME /var/app/ui/build
VOLUME /var/app/ui/node_modules
VOLUME /var/logs
RUN chown www-data:www-data /var/media
ENTRYPOINT ["./entrypoint.sh"]
CMD ["run"]
#!/bin/bash
set -e
if [ "$1" = 'run' ]; then
export | sed 's/declare\ \-x\ /export\ /' > env.sh
python ./manage.py migrate --noinput
if [ "$BUILD_UI" = 'YES' ]; then
cd ./ui
yarn install
yarn build
cd ../
fi
python ./manage.py collectstatic --noinput
exec supervisord -n -c ./supervisord.conf
else
exec "$@"
fi
server {
listen 80 default_server;
error_log /dev/stdout info;
access_log /dev/stdout;
client_max_body_size 100M;
location /static {
root /var/app/ui/build;
}
location /site-static {
root /var;
}
location /media {
root /var;
}
location / {
root /var/app/ui/build; # try react build directory first, if file doesn't exist, route requests to django app
try_files $uri $uri/index.html $uri.html @app;
}
location @app {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https"; # assumes https already terminated by the load balancer in front of us
proxy_pass http://127.0.0.1:8000;
proxy_read_timeout 300;
proxy_buffering off;
}
}
celery==4.4.2
Django==3.0.5
django-celery-beat==2.0.0
gunicorn==20.0.4
psycopg2==2.8.5
python-memcached==1.59
redis==3.4.1
supervisor==4.1.0
[supervisord]
loglevel=info
logfile=../logs/supervisord.log ; main log file; default $CWD/supervisord.log
logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
loglevel=info ; log level; default info; others: debug,warn,trace
pidfile=../logs/supervisord.pid
[supervisorctl]
serverurl=unix://./supervisor.sock
[program:myapp]
user=www-data
command=gunicorn --workers=%(ENV_WORKER_COUNT)s --bind 0.0.0.0:8000 myapp_project.wsgi:application
directory=./
autostart=true
autorestart=true
stdout_logfile = ../logs/myapp.log
stderr_logfile = ../logs/myapp.err.log
[program:celery_worker]
user=www-data
command=celery -A myapp worker -c2 -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
directory=./
autostart=true
autorestart=true
stdout_logfile = ../logs/celery-worker.log
stderr_logfile = ../logs/celery-worker.err.log
[program:celery_beat]
user=www-data
command=celery -A myapp beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
directory=./
autostart=true
autorestart=true
stdout_logfile = ../logs/celery-beat.log
stderr_logfile = ../logs/celery-beat.err.log
[program:redis]
user=redis
command=/usr/local/bin/redis-server
directory=/var/redis/
autostart=true
autorestart=true
stdout_logfile = ../logs/redis.log
stderr_logfile = ../logs/redis.err.log
[program:memcache]
user=memcache
command=/usr/local/bin/memcached
directory=/var/memcache
autostart=true
autorestart=true
stdout_logfile = ../logs/memcache.log
stderr_logfile = ../logs/memcache.err.log
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
directory=/var/app
autostart=true
autorestart=true
stdout_logfile = ../logs/nginx.log
stderr_logfile = ../logs/nginx.err.log
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment