From b312d516a422b08c299b57cdd3515a235f82b03e Mon Sep 17 00:00:00 2001 From: Demetris Date: Tue, 19 Mar 2019 11:32:45 +0200 Subject: [PATCH] Replace Caddy with Traefik (#1714) ## Description Replace Caddy with Traefik ## Rationale There is some trouble with the Caddy license (https://github.com/pydanny/cookiecutter-django/pull/1282#issuecomment-329617536) @drdaeman suggested using Traefik (https://github.com/pydanny/cookiecutter-django/pull/1282#issuecomment-353655273) which supports ACME and also plays very nice with Docker. ## Comments I am currently using the proposed setup on a live site and it working great so far. If this PR is of interest to the maintainers, then I could commit more changes and take care of the documentation. Of course, any suggestions by the more experienced people around here, are welcome! --- CONTRIBUTORS.rst | 2 + README.rst | 4 +- docs/deployment-with-docker.rst | 10 ++--- docs/developing-locally-docker.rst | 3 +- .../.envs/.production/.caddy | 3 -- .../compose/production/caddy/Caddyfile | 15 ------- .../compose/production/caddy/Dockerfile | 3 -- .../compose/production/traefik/Dockerfile | 5 +++ .../compose/production/traefik/traefik.toml | 41 +++++++++++++++++++ .../merge_production_dotenvs_in_dotenv.py | 1 - {{cookiecutter.project_slug}}/production.yml | 12 +++--- 11 files changed, 61 insertions(+), 38 deletions(-) delete mode 100644 {{cookiecutter.project_slug}}/.envs/.production/.caddy delete mode 100644 {{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile delete mode 100644 {{cookiecutter.project_slug}}/compose/production/caddy/Dockerfile create mode 100644 {{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile create mode 100644 {{cookiecutter.project_slug}}/compose/production/traefik/traefik.toml diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index dc41463f..d8d312ab 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -93,6 +93,7 @@ Listed in alphabetical order. Davit Tovmasyan `@davitovmasyan`_ Davur Clementsen `@dsclementsen`_ @davur Delio Castillo `@jangeador`_ @jangeador + Demetris Stavrou `@demestav`_ Denis Orehovsky `@apirobot`_ Dónal Adams `@epileptic-fish`_ Diane Chen `@purplediane`_ @purplediane88 @@ -216,6 +217,7 @@ Listed in alphabetical order. .. _@Collederas: https://github.com/Collederas .. _@davitovmasyan: https://github.com/davitovmasyan .. _@ddiazpinto: https://github.com/ddiazpinto +.. _@demestav: https://github.com/demestav .. _@dezoito: https://github.com/dezoito .. _@dhepper: https://github.com/dhepper .. _@dot2dotseurat: https://github.com/dot2dotseurat diff --git a/README.rst b/README.rst index 37f36204..2c037e51 100644 --- a/README.rst +++ b/README.rst @@ -48,7 +48,7 @@ Features * Optional custom static build using Gulp and livereload * Send emails via Anymail_ (using Mailgun_ by default, but switchable) * Media storage using Amazon S3 -* Docker support using docker-compose_ for development and production (using Caddy_ with LetsEncrypt_ support) +* Docker support using docker-compose_ for development and production (using Traefik_ with LetsEncrypt_ support) * Procfile_ for deploying to Heroku * Instructions for deploying to PythonAnywhere_ * Run tests with unittest or py.test @@ -82,7 +82,7 @@ Optional Integrations .. _Sentry: https://sentry.io/welcome/ .. _docker-compose: https://github.com/docker/compose .. _PythonAnywhere: https://www.pythonanywhere.com/ -.. _Caddy: https://caddyserver.com/ +.. _Traefik: https://traefik.io/ .. _LetsEncrypt: https://letsencrypt.org/ Constraints diff --git a/docs/deployment-with-docker.rst b/docs/deployment-with-docker.rst index f6e21e82..904947e3 100644 --- a/docs/deployment-with-docker.rst +++ b/docs/deployment-with-docker.rst @@ -19,7 +19,7 @@ Before you begin, check out the ``production.yml`` file in the root of this proj * ``django``: your application running behind ``Gunicorn``; * ``postgres``: PostgreSQL database with the application's relational data; * ``redis``: Redis instance for caching; -* ``caddy``: Caddy web server with HTTPS on by default. +* ``traefik``: Traefik reverse proxy with HTTPS on by default. Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there are three more services: @@ -63,11 +63,11 @@ It is always better to deploy a site behind HTTPS and will become crucial as the * Access to the Django admin is set up by default to require HTTPS in production or once *live*. -The Caddy web server used in the default configuration will get you a valid certificate from Lets Encrypt and update it automatically. All you need to do to enable this is to make sure that your DNS records are pointing to the server Caddy runs on. +The Traefik reverse proxy used in the default configuration will get you a valid certificate from Lets Encrypt and update it automatically. All you need to do to enable this is to make sure that your DNS records are pointing to the server Traefik runs on. -You can read more about this here at `Automatic HTTPS`_ in the Caddy docs. +You can read more about this feature and how to configure it, at `Automatic HTTPS`_ in the Traefik docs. -.. _Automatic HTTPS: https://caddyserver.com/docs/automatic-https +.. _Automatic HTTPS: https://docs.traefik.io/configuration/acme/ (Optional) Postgres Data Volume Modifications @@ -112,7 +112,7 @@ If you want to scale your application, run:: docker-compose -f production.yml scale django=4 docker-compose -f production.yml scale celeryworker=2 -.. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``caddy``. +.. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``traefik``. To see how your containers are doing run:: diff --git a/docs/developing-locally-docker.rst b/docs/developing-locally-docker.rst index 895140f9..10815419 100644 --- a/docs/developing-locally-docker.rst +++ b/docs/developing-locally-docker.rst @@ -105,7 +105,6 @@ The most important thing for us here now is ``env_file`` section enlisting ``./. │   ├── .django │   └── .postgres └── .production - ├── .caddy ├── .django └── .postgres @@ -120,7 +119,7 @@ Consider the aforementioned ``.envs/.local/.postgres``: :: POSTGRES_USER=XgOWtQtJecsAbaIyslwGvFvPawftNaqO POSTGRES_PASSWORD=jSljDz4whHuwO3aJIgVBrqEml5Ycbghorep4uVJ4xjDYQu0LfuTZdctj7y0YcCLu -The three envs we are presented with here are ``POSTGRES_DB``, ``POSTGRES_USER``, and ``POSTGRES_PASSWORD`` (by the way, their values have also been generated for you). You might have figured out already where these definitions will end up; it's all the same with ``django`` and ``caddy`` service container envs. +The three envs we are presented with here are ``POSTGRES_DB``, ``POSTGRES_USER``, and ``POSTGRES_PASSWORD`` (by the way, their values have also been generated for you). You might have figured out already where these definitions will end up; it's all the same with ``django`` service container envs. One final touch: should you ever need to merge ``.envs/production/*`` in a single ``.env`` run the ``merge_production_dotenvs_in_dotenv.py``: :: diff --git a/{{cookiecutter.project_slug}}/.envs/.production/.caddy b/{{cookiecutter.project_slug}}/.envs/.production/.caddy deleted file mode 100644 index 83d7fc7a..00000000 --- a/{{cookiecutter.project_slug}}/.envs/.production/.caddy +++ /dev/null @@ -1,3 +0,0 @@ -# Caddy -# ------------------------------------------------------------------------------ -DOMAIN_NAME={{ cookiecutter.domain_name }} diff --git a/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile b/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile deleted file mode 100644 index 323e4392..00000000 --- a/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile +++ /dev/null @@ -1,15 +0,0 @@ -www.{% raw %}{$DOMAIN_NAME}{% endraw %} { - redir https://{% raw %}{$DOMAIN_NAME}{% endraw %} -} - -{% raw %}{$DOMAIN_NAME}{% endraw %} { - proxy / django:5000 { - header_upstream Host {host} - header_upstream X-Real-IP {remote} - header_upstream X-Forwarded-Proto {scheme} - header_upstream X-CSRFToken {~csrftoken} - } - log stdout - errors stdout - gzip -} diff --git a/{{cookiecutter.project_slug}}/compose/production/caddy/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/caddy/Dockerfile deleted file mode 100644 index c32efb3e..00000000 --- a/{{cookiecutter.project_slug}}/compose/production/caddy/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM abiosoft/caddy:0.11.0 - -COPY ./compose/production/caddy/Caddyfile /etc/Caddyfile diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile new file mode 100644 index 00000000..7088e6fe --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile @@ -0,0 +1,5 @@ +FROM traefik:alpine +RUN mkdir -p /etc/traefik/acme +RUN touch /etc/traefik/acme/acme.json +RUN chmod 600 /etc/traefik/acme/acme.json +COPY ./compose/production/traefik/traefik.toml /etc/traefik diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.toml b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.toml new file mode 100644 index 00000000..ad1f20e9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.toml @@ -0,0 +1,41 @@ +logLevel = "INFO" +defaultEntryPoints = ["http", "https"] + +# Entrypoints, http and https +[entryPoints] + # http should be redirected to https + [entryPoints.http] + address = ":80" + [entryPoints.http.redirect] + entryPoint = "https" + # https is the default + [entryPoints.https] + address = ":443" + [entryPoints.https.tls] + +# Enable ACME (Let's Encrypt): automatic SSL +[acme] +# Email address used for registration +email = "{{ cookiecutter.email }}" +storageFile = "/etc/traefik/acme/acme.json" +entryPoint = "https" +onDemand = false +OnHostRule = true + # Use a HTTP-01 acme challenge rather than TLS-SNI-01 challenge + [acme.httpChallenge] + entryPoint = "http" + +[file] +[backends] + [backends.django] + [backends.django.servers.server1] + url = "http://django:5000" + +[frontends] + [frontends.django] + backend = "django" + passHostHeader = true + [frontends.django.headers] + HostsProxyHeaders = ['X-CSRFToken'] + [frontends.django.routes.dr1] + rule = "Host:{{ cookiecutter.domain_name }}" diff --git a/{{cookiecutter.project_slug}}/merge_production_dotenvs_in_dotenv.py b/{{cookiecutter.project_slug}}/merge_production_dotenvs_in_dotenv.py index 5c3027a4..4e70e2ad 100644 --- a/{{cookiecutter.project_slug}}/merge_production_dotenvs_in_dotenv.py +++ b/{{cookiecutter.project_slug}}/merge_production_dotenvs_in_dotenv.py @@ -8,7 +8,6 @@ PRODUCTION_DOTENVS_DIR_PATH = os.path.join(ROOT_DIR_PATH, ".envs", ".production" PRODUCTION_DOTENV_FILE_PATHS = [ os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".django"), os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".postgres"), - os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".caddy"), ] DOTENV_FILE_PATH = os.path.join(ROOT_DIR_PATH, ".env") diff --git a/{{cookiecutter.project_slug}}/production.yml b/{{cookiecutter.project_slug}}/production.yml index fd8388ac..88cf8773 100644 --- a/{{cookiecutter.project_slug}}/production.yml +++ b/{{cookiecutter.project_slug}}/production.yml @@ -3,7 +3,7 @@ version: '3' volumes: production_postgres_data: {} production_postgres_data_backups: {} - production_caddy: {} + production_traefik: {} services: django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %} @@ -30,17 +30,15 @@ services: env_file: - ./.envs/.production/.postgres - caddy: + traefik: build: context: . - dockerfile: ./compose/production/caddy/Dockerfile - image: {{ cookiecutter.project_slug }}_production_caddy + dockerfile: ./compose/production/traefik/Dockerfile + image: {{ cookiecutter.project_slug }}_production_traefik depends_on: - django volumes: - - production_caddy:/root/.caddy - env_file: - - ./.envs/.production/.caddy + - production_traefik:/etc/traefik/acme ports: - "0.0.0.0:80:80" - "0.0.0.0:443:443"