Skip to content

Instantly share code, notes, and snippets.

@stephane
Last active September 7, 2021 20:47
Show Gist options
  • Save stephane/9069bf72f61d0eaa15d78dd5275376c8 to your computer and use it in GitHub Desktop.
Save stephane/9069bf72f61d0eaa15d78dd5275376c8 to your computer and use it in GitHub Desktop.
Docker file for Poetry
.git
__pycache__
app/static
# syntax=docker/dockerfile:1
FROM python:3.9.7-slim-bullseye as base
# Required for psycopg2 client in the final application
RUN apt-get update && apt-get install --no-install-recommends -y libpq5
# The installation in the final layer is done with pip and don't use a Debian gunicorn
RUN pip install -U pip gunicorn
# Builder layer
FROM base AS builder
ENV PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_CREATE=false \
PATH="$PATH:/runtime/bin" \
PYTHONPATH="$PYTHONPATH:/runtime/lib/python3.9/site-packages" \
# It's recommanded to lock Poetry version
POETRY_VERSION=1.1.8
# System dependencies
RUN apt-get update && apt-get install --no-install-recommends -y \
build-essential \
unzip \
wget \
python-dev \
libpq-dev \
libffi-dev
RUN pip install "poetry==$POETRY_VERSION"
WORKDIR /src
# Generate requirements and install *all* dependencies.
COPY pyproject.toml poetry.lock /src/
RUN poetry export --dev --without-hashes --no-interaction --no-ansi -f requirements.txt -o requirements.txt
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
COPY . /src
FROM base AS runtime
COPY --from=builder /runtime /usr/local
WORKDIR /code
# Explicit copy of required files for the runtime of my application
# I also set a list of subdirectories to not include in .dockerignore (static assets, etc).
COPY alembic alembic
COPY alembic.ini .
COPY app app
COPY docker/gunicorn_conf.py gunicorn_conf.py
# Could be the right place to copy not secret production settings
CMD ["alembic", "upgrade", "head"]
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "gunicorn_conf.py", "app.main:app"]
# From https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/docker-images/gunicorn_conf.py
import json
import multiprocessing
import os
workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
max_workers_str = os.getenv("MAX_WORKERS")
use_max_workers = None
if max_workers_str:
use_max_workers = int(max_workers_str)
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
host = os.getenv("HOST", "0.0.0.0")
port = os.getenv("PORT", "8080")
bind_env = os.getenv("BIND", None)
use_loglevel = os.getenv("LOG_LEVEL", "info")
if bind_env:
use_bind = bind_env
else:
use_bind = f"{host}:{port}"
cores = multiprocessing.cpu_count()
workers_per_core = float(workers_per_core_str)
default_web_concurrency = workers_per_core * cores
if web_concurrency_str:
web_concurrency = int(web_concurrency_str)
assert web_concurrency > 0
else:
web_concurrency = max(int(default_web_concurrency), 2)
if use_max_workers:
web_concurrency = min(web_concurrency, use_max_workers)
accesslog_var = os.getenv("ACCESS_LOG", "-")
use_accesslog = accesslog_var or None
errorlog_var = os.getenv("ERROR_LOG", "-")
use_errorlog = errorlog_var or None
graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120")
timeout_str = os.getenv("TIMEOUT", "120")
keepalive_str = os.getenv("KEEP_ALIVE", "5")
# Gunicorn config variables
loglevel = use_loglevel
workers = web_concurrency
bind = use_bind
errorlog = use_errorlog
worker_tmp_dir = "/dev/shm"
accesslog = use_accesslog
graceful_timeout = int(graceful_timeout_str)
timeout = int(timeout_str)
keepalive = int(keepalive_str)
# For debugging and testing
log_data = {
"loglevel": loglevel,
"workers": workers,
"bind": bind,
"graceful_timeout": graceful_timeout,
"timeout": timeout,
"keepalive": keepalive,
"errorlog": errorlog,
"accesslog": accesslog,
# Additional, non-gunicorn variables
"workers_per_core": workers_per_core,
"use_max_workers": use_max_workers,
"host": host,
"port": port,
}
print(json.dumps(log_data))
docker-build:
DOCKER_BUILDKIT=1 docker build --tag yourapp --file docker/Dockerfile .
docker-run:
docker run -d -p 8001:8000 -e "DB_HOST=123" -e "DB_USER=123" yourapp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment