Upload
danielle-madeley
View
3.565
Download
7
Tags:
Embed Size (px)
DESCRIPTION
An introduction to Pallet and Forklift, a standard and tool for deploying and developing web applications on top of Docker.
Citation preview
Running Djangoon Dockera workflow and code
Alexey Kotlyarov • Danielle Madeley
The problem
A diverse world ofapplications
Reproducibledeployments
Base OS (Debian bootstrap)
App dependencies(Python, libs)
Built application
Transient runtime
Imm
utab
leExternalStorage
But there's nostandards!
12factor.net is a must read
PalletAn interface forDocker containers
github.com/infoxchange/pallet
What's in deploy?database migrationsloading fixturesinstall static content to static web server(CDN, Ceph, nginx, etc.)
What's in serve?Start app serverStarting supporting services, e.g. Celery
Keep it lean
$ docker build .
DockerfileFROM debian/ubuntu/fedora/etc.RUN apt-get -qq update && apt-get -qq install \ git mercurial \ python python-virtualenv python-pip \ ...
RUN useradd -d /app -r appWORKDIR /app
ADD requirements.txt /app/requirements.txtRUN virtualenv python_env && \ . python_env/bin/activate && \ pip install -r requirements.txt
ADD . /app
VOLUME ["/static", "/storage"]
RUN mkdir -p /static /storage && \ chown -R app /static /storage
RUN echo "__version__ = 'git describe'" \ > myapp/__version__.py
RUN ./invoke.sh install
ENTRYPOINT ["./invoke.sh"]
EXPOSE 8000
invoke.sh#!/bin/sh
# default parameters: ${APP_USER:=app}: ${WEB_CONCURRENCY:=1}export WEB_CONCURRENCY
if [ "x$(whoami)" != "x$APP_USER" ]; then # ensure we own our storage chown -R "$APP_USER" /static /storage
# Call back into ourselves as the app user exec sudo -sE -u "$APP_USER" -- "$0" "$@"else
else . ./startenv case "$1" in deploy) shift 1 # consume command from $@ ./manage.py migrate "$@" ;;
serve) gunicorn -w "$WEB_CONCURRENCY" \ -b 0.0.0.0:8000 "${APP}.wsgi:application" ;;
*) ./manage.py "$@" ;; esacfi
Django settings.py
from dj_database_url import parseDATABASES = { 'default': parse(os.environ['DB_DEFAULT_URL']),}
# Logging is complexLOGGING['handlers']['logstash'] = { 'level': 'DEBUG' if DEBUG else 'INFO', 'class': 'logging.handlers.SysLogHandler', 'address': (os.environ['SYSLOG_SERVER'], int(os.environ['SYSLOG_PORT'])) 'socktype': socket.SOCK_STREAM \ if os.environ['SYSLOG_PROTO'] == else socket.SOCK_DGRAM,}
# Trust our nginx serverUSE_X_FORWARDED_HOST = TrueSECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', MY_SITE_DOMAIN = os.environ.get('SITE_DOMAIN')if MY_SITE_DOMAIN: ALLOWED_HOSTS = (MY_SITE_DOMAIN,)
IXDjangopallet configuration forDjangopip install IXDjango
from ixdjango.docker_settings import *
github.com/infoxchange/ixdjango
An example(in Flask)
github.com/danni/linux-conf-au-flask-tute/tree/dockerify
Running thecontainer
docker run \ -p 8000:8000 \ -e DB_DEFAULT_URL=postgres://user:pass@db3:5432/myapp \ -e SITE_DOMAIN=myapp-staging.company.com \ -e SITE_PROTO=https \ -e ENVIRONMENT=staging \ -e ELASTICSEARCH_URLS=http://elastic-1:9200/myapp \ -v /mnt/docker-storage/myapp:/storage \ -h WHY_ARE_YOU_STILL_READING_THIS \ myapp \ serve
Urgh!
Forklifta tool for loading pallets
github.com/infoxchange/docker-forklift
myapp/forklift.yamlservices:- postgres- elasticsearch
$ forklift myapp serve
Developing withForklift
$ forklift ./invoke.sh serve$ forklift ./manage.py test
Poking aroundinside containers
(aka troubleshooting)
$ forklift --mount-root/tmp/myapp myapp sshd
Extending Forklift
forklift.services.memcache@register('memcache')class Memcache(Service):
providers = ('localhost', 'container')
DEFAULT_PORT = 11211
def __init__(self, key_prefix='', hosts=None):
self.key_prefix = key_prefix self.hosts = hosts or []
def environment(self):
return { 'MEMCACHE_HOSTS': '|'.join(self.hosts), 'MEMCACHE_PREFIX': self.key_prefix, }
def available(self): """ Check whether memcache is available """
...
@classmethod def localhost(cls, application_id): """The default memcached provider"""
return cls( key_prefix=application_id, hosts=['localhost:{0}'.format(cls.DEFAULT_PORT)])
@classmethod @transient_provider def container(cls, application_id): """Memcached provided by a container."""
container = ensure_container( image='fedora/memcached', port=cls.DEFAULT_PORT, application_id=application_id, )
return cls( key_prefix=application_id, hosts=['localhost:{0}'.format(container.port)], )
Continuousintegration
forklift --cleanroom myapp test
Legacyapplications
Fin ;-PQuestions?
github.com/infoxchange/palletgithub.com/infoxchange/docker-forklift