Add Dockerfile, docker-compose.yml, S3 uploaded media settings (configurable via the environment), Docker instructions, and a .travis.yml to build/test the Docker image.

pull/45/head
Tobias McNulty 2017-02-17 20:43:58 -05:00
rodzic 6eefd5f991
commit 6652c42d09
14 zmienionych plików z 249 dodań i 60 usunięć

4
.dockerignore 100644
Wyświetl plik

@ -0,0 +1,4 @@
Dockerfile
docker-compose.yml
Procfile
Vagrantfile

1
.gitignore vendored
Wyświetl plik

@ -12,7 +12,6 @@ bakerydemo/media/*
bakerydemo/settings/local.py
bakerydemodb
__pycache__
.*
.vagrant/
/.vagrant/
/Vagrantfile.local

23
.travis.yml 100644
Wyświetl plik

@ -0,0 +1,23 @@
sudo: required
services:
- docker
env:
global:
- GREP_TIMEOUT=360
before_install:
- sudo apt-get update
- sudo apt-get install -qy -o Dpkg::Options::="--force-confold" docker-engine coreutils
script:
# Bring up the postgres, redis, and app containers
- docker-compose up --build -d
- timeout $GREP_TIMEOUT grep -m 1 'Running migrations' <(docker-compose logs --follow app 2>&1)
- timeout $GREP_TIMEOUT grep -m 1 'spawned uWSGI http 1' <(docker-compose logs --follow app 2>&1)
- docker-compose run app /venv/bin/python /code/manage.py check
after_script:
- docker-compose logs
- docker images

44
Dockerfile 100644
Wyświetl plik

@ -0,0 +1,44 @@
FROM python:3.5-alpine
ADD requirements/ /requirements/
RUN set -ex \
&& apk add --no-cache --virtual .build-deps \
gcc \
g++ \
make \
libc-dev \
musl-dev \
linux-headers \
pcre-dev \
postgresql-dev \
libjpeg-turbo-dev \
&& pyvenv /venv \
&& /venv/bin/pip install -U pip \
&& LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "/venv/bin/pip install -r /requirements/production.txt" \
&& runDeps="$( \
scanelf --needed --nobanner --recursive /venv \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --virtual .python-rundeps $runDeps \
&& apk del .build-deps
RUN apk add --no-cache postgresql-client
RUN mkdir /code/
WORKDIR /code/
ADD . /code/
EXPOSE 8000
# Add custom environment variables needed by Django or your settings file here:
ENV DJANGO_SETTINGS_MODULE=bakerydemo.settings.production DJANGO_DEBUG=off
# uWSGI configuration (customize as needed):
ENV UWSGI_VIRTUALENV=/venv UWSGI_WSGI_FILE=bakerydemo/wsgi_production.py UWSGI_HTTP=:8000 UWSGI_MASTER=1 UWSGI_WORKERS=2 UWSGI_THREADS=8 UWSGI_UID=1000 UWSGI_GID=2000
# Call collectstatic with dummy environment variables:
RUN DATABASE_URL=postgres://none REDIS_URL=none /venv/bin/python manage.py collectstatic --noinput
# start uWSGI, using a wrapper script to allow us to easily add more commands to container startup:
ENTRYPOINT ["/code/docker-entrypoint.sh"]
CMD ["/venv/bin/uwsgi", "--http-auto-chunked", "--http-keepalive"]

Wyświetl plik

@ -1,2 +1,2 @@
release: yes "yes" | python manage.py migrate
web: gunicorn bakerydemo.heroku_wsgi --log-file -
web: uwsgi --http-socket=:$PORT --master --workers=2 --threads=8 --die-on-term --wsgi-file=bakerydemo/wsgi_production.py

Wyświetl plik

@ -4,7 +4,9 @@
"repository": "https://github.com/wagtail/bakerydemo",
"keywords": ["wagtail", "django"],
"env": {
"DJANGO_SETTINGS_MODULE": "bakerydemo.settings.heroku"
"DJANGO_DEBUG": "off",
"DJANGO_SETTINGS_MODULE": "bakerydemo.settings.production",
"DJANGO_SECURE_SSL_REDIRECT": "on"
},
"scripts": {
"postdeploy": "django-admin.py migrate && django-admin.py load_initial_data && echo 'from wagtail.wagtailimages.models import Rendition; Rendition.objects.all().delete()' | django-admin.py shell"

Wyświetl plik

@ -1,22 +0,0 @@
import dj_database_url
from .base import *
# Accept all hostnames, since we don't know in advance which hostname will be used for any given Heroku instance.
# IMPORTANT: Set this to a real hostname when using this in production!
# See https://docs.djangoproject.com/en/1.10/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ['*', ]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# BASE_URL required for notification emails
BASE_URL = 'http://localhost:8000'
db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES['default'].update(db_from_env)
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'

Wyświetl plik

@ -0,0 +1,68 @@
import os
import dj_database_url
import random
import string
from .base import *
DEBUG = os.getenv('DJANGO_DEBUG', 'off') == 'on'
# DJANGO_SECRET_KEY *should* be specified in the environment. If it's not, generate an ephemeral key.
if 'DJANGO_SECRET_KEY' in os.environ:
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
else:
# Use if/else rather than a default value to avoid calculating this if we don't need it
print("WARNING: DJANGO_SECRET_KEY not found in os.environ. Generating ephemeral SECRET_KEY.")
SECRET_KEY = ''.join([random.SystemRandom().choice(string.printable) for i in range(50)])
# Make sure Django can detect a secure connection properly on Heroku:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Redirect all requests to HTTPS
SECURE_SSL_REDIRECT = os.getenv('DJANGO_SECURE_SSL_REDIRECT', 'off') == 'on'
# Accept all hostnames, since we don't know in advance which hostname will be used for any given Heroku instance.
# IMPORTANT: Set this to a real hostname when using this in production!
# See https://docs.djangoproject.com/en/1.10/ref/settings/#allowed-hosts
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(';')
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# BASE_URL required for notification emails
BASE_URL = 'http://localhost:8000'
db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES['default'].update(db_from_env)
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
MIDDLEWARE.append('whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
if 'AWS_STORAGE_BUCKET_NAME' in os.environ:
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID', '')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY', '')
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_AUTO_CREATE_BUCKET = True
INSTALLED_APPS.append('storages')
MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}

33
docker-compose.yml 100644
Wyświetl plik

@ -0,0 +1,33 @@
version: '2'
services:
db:
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app_user
POSTGRES_PASSWORD: changeme
restart: always
image: postgres:9.6
expose:
- "5432"
redis:
restart: always
image: redis:3.0
expose:
- "6379"
app:
environment:
DJANGO_SECRET_KEY: changeme
DATABASE_URL: postgres://app_user:changeme@db/app_db
REDIS_URL: redis://redis
build:
context: .
dockerfile: ./Dockerfile
links:
- db:db
- redis:redis
ports:
- "8000:8000"
depends_on:
- db
- redis

Wyświetl plik

@ -0,0 +1,19 @@
#!/bin/sh
set -e
until psql $DATABASE_URL -c '\l'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 1
done
>&2 echo "Postgres is up - continuing"
if [ "$1" = '/venv/bin/uwsgi' ]; then
/venv/bin/python manage.py migrate --noinput
fi
if [ "x$DJANGO_LOAD_INITIAL_DATA" = 'xon' ]; then
/venv/bin/python manage.py load_initial_data
fi
exec "$@"

Wyświetl plik

@ -26,7 +26,7 @@ Run the following commands:
```bash
git clone git@github.com:wagtail/bakerydemo.git
cd wagtaildemo
cd bakerydemo
vagrant up
vagrant ssh
# then, within the SSH session:
@ -38,9 +38,39 @@ interface at [http://localhost:8000/admin/](http://localhost:8000/admin/).
Log into the admin with the credentials ``admin / changeme``.
Setup without Vagrant
-----
Don't want to set up a whole VM to try out Wagtail? No problem.
Setup with Docker
-----------------
### Dependencies
* [Docker](https://docs.docker.com/engine/installation/)
### Installation
Run the following commands:
```bash
git clone git@github.com:wagtail/bakerydemo.git
cd bakerydemo
docker-compose up --build -d
docker-compose run app /venv/bin/python manage.py load_initial_data
```
The demo site will now be accessible at [http://localhost:8000/](http://localhost:8000/) and the Wagtail admin
interface at [http://localhost:8000/admin/](http://localhost:8000/admin/).
Log into the admin with the credentials ``admin / changeme``.
**Important:** This `docker-compose.yml` is configured for local testing only, and is not intended for production use.
### Debugging
To tail the logs from the Docker containers in realtime, run:
```bash
docker-compose logs -f
```
Local Setup
-----------
Don't want to set up a whole VM nor use Docker to try out Wagtail? No problem.
### Dependencies
* [PIP](https://github.com/pypa/pip)
@ -89,53 +119,40 @@ update in the browser. Once finished, click `View` to see the public site.
Log into the admin with the credentials ``admin / changeme``.
To prevent the demo site from regenerating a new Django `SECRET_KEY` each time Heroku restarts your site, you should set
a `DJANGO_SECRET_KEY` environment variable in Heroku using the web interace or the [CLI](https://devcenter.heroku.com/articles/heroku-cli). If using the CLI, you can set a `SECRET_KEY` like so:
heroku config:set DJANGO_SECRET_KEY=changeme
To learn more about Heroku, read [Deploying Python and Django Apps on Heroku](https://devcenter.heroku.com/articles/deploying-python).
### Storing Wagtail Media Files on AWS S3
If you have deployed the demo site to Heroku, you may want to perform some additional setup. Heroku uses an
[ephemeral filesystem](https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem). In laymen's terms, this means
that uploaded images will disappear at a minimum of once per day, and on each application deployment. To mitigate this,
you can host your media on S3.
If you have deployed the demo site to Heroku or via Docker, you may want to perform some additional setup. Heroku uses an
[ephemeral filesystem](https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem), and Docker-based hosting
environments typically work in the same manner. In laymen's terms, this means that uploaded images will disappear at a
minimum of once per day, and on each application deployment. To mitigate this, you can host your media on S3.
This documentation assumes that you have an AWS account, an IAM user, and a properly configured S3 bucket. These topics
are outside of the scope of this documentation; the following [blog post](https://wagtail.io/blog/amazon-s3-for-media-files/)
will walk you through those steps.
Next, you will need to add `django-storages` and `boto3` to `requirements/heroku.txt`.
Then you will need to edit `settings/heroku.py`:
INSTALLED_APPS.append('storages')
You will also need to add the S3 bucket and access credentials for the IAM user that you created.
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME', 'changeme')
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID', 'changeme')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY', 'changeme')
AWS_S3_CUSTOM_DOMAIN = '{}.s3.amazonaws.com'.format(AWS_STORAGE_BUCKET_NAME)
Next, you will need to set these values in the Heroku environment. The next steps assume that you have
the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) installed and configured. You will
execute the following commands to set the aforementioned environment variables:
This demo site comes preconfigured with a production settings file that will enable S3 for uploaded media storage if
``AWS_STORAGE_BUCKET_NAME`` is defined in the shell environment. All you need to do is set the following environment
variables. If using Heroku, you will first need to install and configure the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). Then, execute the following commands to set the aforementioned environment variables:
heroku config:set AWS_STORAGE_BUCKET_NAME=changeme
heroku config:set AWS_ACCESS_KEY_ID=changeme
heroku config:set AWS_SECRET_ACCESS_KEY=changeme
Do not forget to replace the `changeme` with the actual values for your AWS account.
Do not forget to replace the `changeme` with the actual values for your AWS account. If you're using a different hosting
environment, set the same environment variables there using the method appropriate for your environment.
Finally, we need to configure the `MEDIA_URL` as well as inform Django that we want to use `boto3` for the storage
backend:
MEDIA_URL = 'https://{}/'.format(AWS_S3_CUSTOM_DOMAIN)'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
Commit these changes and push to Heroku and you should now have persistent media storage!
Once Heroku restarts your application or your Docker container is refreshed, you should have persistent media storage!
### Sending email from the contact form
The following setting in `base.py` and `heroku.py` ensures that live email is not sent by the demo contact form.
The following setting in `base.py` and `production.py` ensures that live email is not sent by the demo contact form.
`EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'`

Wyświetl plik

@ -1 +1 @@
-r requirements/heroku.txt
-r requirements/production.txt

Wyświetl plik

@ -1,6 +1,8 @@
-r base.txt
# Additional dependencies for Heroku deployment
dj-database-url==0.4.1
gunicorn==19.6.0
uwsgi==2.0.14
psycopg2==2.6.2
whitenoise==3.2.2
boto==2.45.0
django-storages==1.5.2