diff --git a/README.md b/README.md index 879b2b2cf..16a5252af 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ production-ready Django projects quickly. - Optional custom static build using Gulp or Webpack - Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable) - Media storage using Amazon S3, Google Cloud Storage, Azure Storage or nginx -- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support) +- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Angie](https://angie.software/) with [LetsEncrypt](https://letsencrypt.org/) support) - [Procfile](https://devcenter.heroku.com/articles/procfile) for deploying to Heroku - Instructions for deploying to [PythonAnywhere](https://www.pythonanywhere.com/) - Run tests with unittest or pytest diff --git a/{{cookiecutter.project_slug}}/compose/production/angie/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/angie/Dockerfile new file mode 100644 index 000000000..126ad6c14 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/angie/Dockerfile @@ -0,0 +1,5 @@ +FROM docker.angie.software/angie:minimal +RUN rm /etc/angie/angie.conf && apk add --no-cache angie-console-light +COPY ./compose/production/angie/default.conf /etc/angie/angie.conf + +CMD ["angie", "-g", "daemon off;"] diff --git a/{{cookiecutter.project_slug}}/compose/production/angie/default.conf b/{{cookiecutter.project_slug}}/compose/production/angie/default.conf new file mode 100644 index 000000000..13f0c7b05 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/production/angie/default.conf @@ -0,0 +1,93 @@ +events { + worker_connections 1024; +} +http { + include /etc/angie/mime.types; + + resolver 127.0.0.11; + acme_client {{ cookiecutter.project_slug }} https://acme-v02.api.letsencrypt.org/directory email={{ cookiecutter.email }}; + acme_client_path /var/lib/angie/acme/domains; + + upstream backend { + server django:5000; + } + {%- if cookiecutter.use_celery == 'y' %} + upstream flower { + server flower:5555; + } + {%- endif %} + + + + server { + listen 80; + listen 443 ssl; + {%- if cookiecutter.domain_name.count('.') == 1 %} + server_name {{ cookiecutter.domain_name }} www.{{ cookiecutter.domain_name }}; + {%- else %} + server_name {{ cookiecutter.domain_name }}; + {%- endif %} + acme {{ cookiecutter.project_slug }}; + ssl_certificate $acme_cert_{{ cookiecutter.project_slug }}; + ssl_certificate_key $acme_cert_key_{{ cookiecutter.project_slug }}; + + location / { + proxy_pass http://backend; + 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 $scheme; + + } + {%- if cookiecutter.cloud_provider == 'None' %} + location /media/ { + root /usr/share/angie; + } + location /static/ { + root /usr/share/angie; + } + location = /favicon.ico { + root /usr/share/angie/static/images/favicons; + } + {% endif %} + } + {%- if cookiecutter.use_celery == 'y' %} + server { + listen 5555 ssl; + server_name {{ cookiecutter.domain_name }}; + acme {{ cookiecutter.project_slug }}; + ssl_certificate $acme_cert_{{ cookiecutter.project_slug }}; + ssl_certificate_key $acme_cert_key_{{ cookiecutter.project_slug }}; + + location / { + proxy_pass http://flower; + 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 $scheme; + + } + } + {%- endif %} + server { + listen 5558 ssl; + server_name {{ cookiecutter.domain_name }}; + acme {{ cookiecutter.project_slug }}; + ssl_certificate $acme_cert_{{ cookiecutter.project_slug }}; + ssl_certificate_key $acme_cert_key_{{ cookiecutter.project_slug }}; + + location /console/ { + + auto_redirect on; + + alias /usr/share/angie-console-light/html/; + index index.html; + + location /console/api/ { + api /status/; + } + } + } + + +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/compose/production/nginx/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/nginx/Dockerfile deleted file mode 100644 index ec2ad35cb..000000000 --- a/{{cookiecutter.project_slug}}/compose/production/nginx/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM docker.io/nginx:1.17.8-alpine -COPY ./compose/production/nginx/default.conf /etc/nginx/conf.d/default.conf diff --git a/{{cookiecutter.project_slug}}/compose/production/nginx/default.conf b/{{cookiecutter.project_slug}}/compose/production/nginx/default.conf deleted file mode 100644 index 562dba86c..000000000 --- a/{{cookiecutter.project_slug}}/compose/production/nginx/default.conf +++ /dev/null @@ -1,7 +0,0 @@ -server { - listen 80; - server_name localhost; - location /media/ { - alias /usr/share/nginx/media/; - } -} diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile deleted file mode 100644 index d54bf27ca..000000000 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM docker.io/traefik:2.11.2 -RUN mkdir -p /etc/traefik/acme \ - && touch /etc/traefik/acme/acme.json \ - && chmod 600 /etc/traefik/acme/acme.json -COPY ./compose/production/traefik/traefik.yml /etc/traefik diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml b/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml deleted file mode 100644 index f5d9e52fc..000000000 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/traefik.yml +++ /dev/null @@ -1,108 +0,0 @@ -log: - level: INFO - -entryPoints: - web: - # http - address: ':80' - http: - # https://doc.traefik.io/traefik/routing/entrypoints/#entrypoint - redirections: - entryPoint: - to: web-secure - - web-secure: - # https - address: ':443' - {%- if cookiecutter.use_celery == 'y' %} - - flower: - address: ':5555' - {%- endif %} - -certificatesResolvers: - letsencrypt: - # https://doc.traefik.io/traefik/https/acme/#lets-encrypt - acme: - email: '{{ cookiecutter.email }}' - storage: /etc/traefik/acme/acme.json - # https://doc.traefik.io/traefik/https/acme/#httpchallenge - httpChallenge: - entryPoint: web - -http: - routers: - web-secure-router: - {%- if cookiecutter.domain_name.count('.') == 1 %} - rule: 'Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)' - {%- else %} - rule: 'Host(`{{ cookiecutter.domain_name }}`)' - {%- endif %} - entryPoints: - - web-secure - middlewares: - - csrf - service: django - tls: - # https://doc.traefik.io/traefik/routing/routers/#certresolver - certResolver: letsencrypt - {%- if cookiecutter.use_celery == 'y' %} - - flower-secure-router: - rule: 'Host(`{{ cookiecutter.domain_name }}`)' - entryPoints: - - flower - service: flower - tls: - # https://doc.traefik.io/traefik/master/routing/routers/#certresolver - certResolver: letsencrypt - {%- endif %} - {%- if cookiecutter.cloud_provider == 'None' %} - - web-media-router: - {%- if cookiecutter.domain_name.count('.') == 1 %} - rule: '(Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)) && PathPrefix(`/media/`)' - {%- else %} - rule: 'Host(`{{ cookiecutter.domain_name }}`) && PathPrefix(`/media/`)' - {%- endif %} - entryPoints: - - web-secure - middlewares: - - csrf - service: django-media - tls: - certResolver: letsencrypt - {%- endif %} - - middlewares: - csrf: - # https://doc.traefik.io/traefik/master/middlewares/http/headers/#hostsproxyheaders - # https://docs.djangoproject.com/en/dev/ref/csrf/#ajax - headers: - hostsProxyHeaders: ['X-CSRFToken'] - - services: - django: - loadBalancer: - servers: - - url: http://django:5000 - {%- if cookiecutter.use_celery == 'y' %} - - flower: - loadBalancer: - servers: - - url: http://flower:5555 - {%- endif %} - {%- if cookiecutter.cloud_provider == 'None' %} - - django-media: - loadBalancer: - servers: - - url: http://nginx:80 - {%- endif %} - -providers: - # https://doc.traefik.io/traefik/master/providers/file/ - file: - filename: /etc/traefik/traefik.yml - watch: true diff --git a/{{cookiecutter.project_slug}}/docker-compose.production.yml b/{{cookiecutter.project_slug}}/docker-compose.production.yml index d0d06338d..b6cae938e 100644 --- a/{{cookiecutter.project_slug}}/docker-compose.production.yml +++ b/{{cookiecutter.project_slug}}/docker-compose.production.yml @@ -1,9 +1,10 @@ volumes: production_postgres_data: {} production_postgres_data_backups: {} - production_traefik: {} + production_angie: {} {%- if cookiecutter.cloud_provider == 'None' %} production_django_media: {} + production_django_static: {} {%- endif %} {% if cookiecutter.use_celery == 'y' %} production_redis_data: {} @@ -32,6 +33,7 @@ services: {%- if cookiecutter.cloud_provider == 'None' %} volumes: - production_django_media:/app/{{ cookiecutter.project_slug }}/media + - production_django_static:/app/staticfiles {%- endif %} depends_on: - postgres @@ -52,22 +54,6 @@ services: env_file: - ./.envs/.production/.postgres - traefik: - build: - context: . - dockerfile: ./compose/production/traefik/Dockerfile - image: {{ cookiecutter.project_slug }}_production_traefik - depends_on: - - django - volumes: - - production_traefik:/etc/traefik/acme - ports: - - '0.0.0.0:80:80' - - '0.0.0.0:443:443' - {%- if cookiecutter.use_celery == 'y' %} - - '0.0.0.0:5555:5555' - {%- endif %} - redis: image: docker.io/redis:6 {% if cookiecutter.use_celery == 'y' %} @@ -104,15 +90,24 @@ services: volumes: - production_postgres_data_backups:/backups:z {%- endif %} - {%- if cookiecutter.cloud_provider == 'None' %} - nginx: + angie: build: context: . - dockerfile: ./compose/production/nginx/Dockerfile - image: {{ cookiecutter.project_slug }}_production_nginx + dockerfile: ./compose/production/angie/Dockerfile + image: {{ cookiecutter.project_slug }}_production_angie depends_on: - django volumes: - - production_django_media:/usr/share/nginx/media:ro - {%- endif %} + {%- if cookiecutter.cloud_provider == 'None' %} + - production_django_media:/usr/share/angie/media:ro + - production_django_static:/usr/share/angie/static:ro + {%- endif %} + - production_angie:/var/lib/angie/acme + ports: + - '0.0.0.0:80:80' + - '0.0.0.0:443:443' + - '0.0.0.0:5558:5558' + {%- if cookiecutter.use_celery == 'y' %} + - '0.0.0.0:5555:5555' + {%- endif %} \ No newline at end of file