├── .gitignore ├── ops ├── base │ ├── requirements.txt │ └── Dockerfile ├── local │ └── Dockerfile └── deploy │ ├── Dockerfile │ ├── supervisord.conf │ └── nginx.conf ├── app ├── .gitignore └── app.py ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /ops/base/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | gunicorn 3 | requests 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | .installed.cfg 2 | bin 3 | develop-eggs 4 | dist 5 | downloads 6 | eggs 7 | parts 8 | src/*.egg-info 9 | lib 10 | lib64 11 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | 5 | @app.route("/") 6 | def hello(): 7 | return "Hello World!" 8 | 9 | 10 | if __name__ == "__main__": 11 | app.run(debug=True, host='0.0.0.0') 12 | -------------------------------------------------------------------------------- /ops/local/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rehabstudio/python-base 2 | MAINTAINER Peter McConnell 3 | 4 | # start supervisor to run our wsgi server 5 | CMD cd /opt/app/ && /opt/venv/bin/python app.py 6 | 7 | EXPOSE 5000 8 | -------------------------------------------------------------------------------- /ops/deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rehabstudio/python-base 2 | MAINTAINER Peter McConnell 3 | 4 | RUN pip install supervisor-stdout 5 | 6 | # file management, everything after an ADD is uncached, so we do it as late as 7 | # possible in the process. 8 | ADD ./supervisord.conf /etc/supervisord.conf 9 | ADD ./nginx.conf /etc/nginx/nginx.conf 10 | 11 | # restart nginx to load the config 12 | RUN service nginx stop 13 | 14 | # start supervisor to run our wsgi server 15 | CMD supervisord -c /etc/supervisord.conf -n 16 | -------------------------------------------------------------------------------- /ops/deploy/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon = true 3 | 4 | [program:nginx] 5 | command = /usr/sbin/nginx 6 | startsecs = 5 7 | stdout_events_enabled = true 8 | stderr_events_enabled = true 9 | 10 | [program:app-gunicorn] 11 | command = /opt/venv/bin/gunicorn app:app -w 4 -b 127.0.0.1:5000 --log-level=debug --chdir=/opt/app 12 | stdout_events_enabled = true 13 | stderr_events_enabled = true 14 | 15 | [eventlistener:stdout] 16 | command = supervisor_stdout 17 | buffer_size = 100 18 | events = PROCESS_LOG 19 | result_handler = supervisor_stdout:event_handler 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "build-local - Build container for local development" 3 | @echo "build-deploy - Build container in production mode" 4 | @echo "run-local - Run container for local development" 5 | @echo "run-deploy - Run container for in production mode" 6 | 7 | build-base: 8 | cd ops/base/; docker build -t="rehabstudio/python-base" . 9 | 10 | build-local: build-base 11 | cd ops/local/; docker build -t="rehabstudio/python-local" . 12 | 13 | build-deploy: build-base 14 | cd ops/deploy/; docker build -t="rehabstudio/python-deploy" . 15 | 16 | run-local: build-local 17 | docker run -P -t -i -v $(CURDIR)/app:/opt/app rehabstudio/python-local 18 | 19 | run-deploy: build-deploy 20 | docker run -P -t -i -v $(CURDIR)/app:/opt/app rehabstudio/python-deploy 21 | -------------------------------------------------------------------------------- /ops/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Peter McConnell 3 | 4 | # keep upstart quiet 5 | RUN dpkg-divert --local --rename --add /sbin/initctl 6 | RUN ln -sf /bin/true /sbin/initctl 7 | 8 | # no tty 9 | ENV DEBIAN_FRONTEND noninteractive 10 | 11 | # get up to date 12 | RUN apt-get update --fix-missing 13 | 14 | # global installs [applies to all envs!] 15 | RUN apt-get install -y build-essential git 16 | RUN apt-get install -y python python-dev python-setuptools 17 | RUN apt-get install -y python-pip python-virtualenv 18 | RUN apt-get install -y nginx supervisor 19 | 20 | # stop supervisor service as we'll run it manually 21 | RUN service supervisor stop 22 | 23 | # build dependencies for postgres and image bindings 24 | RUN apt-get build-dep -y python-imaging python-psycopg2 25 | 26 | # create a virtual environment and install all depsendecies from pypi 27 | RUN virtualenv /opt/venv 28 | ADD ./requirements.txt /opt/venv/requirements.txt 29 | RUN /opt/venv/bin/pip install -r /opt/venv/requirements.txt 30 | 31 | # expose port(s) 32 | EXPOSE 80 33 | -------------------------------------------------------------------------------- /ops/deploy/nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; 2 | error_log /dev/stdout info; 3 | worker_processes 1; 4 | 5 | # user nobody nogroup; 6 | pid /tmp/nginx.pid; 7 | 8 | events { 9 | worker_connections 1024; 10 | accept_mutex off; 11 | } 12 | 13 | http { 14 | include mime.types; 15 | default_type application/octet-stream; 16 | access_log /dev/stdout combined; 17 | sendfile on; 18 | 19 | upstream app_server { 20 | # For a TCP configuration: 21 | server 127.0.0.1:5000 fail_timeout=0; 22 | } 23 | 24 | server { 25 | listen 80 default; 26 | client_max_body_size 4G; 27 | server_name _; 28 | 29 | keepalive_timeout 5; 30 | 31 | # path for static files 32 | root /opt/app/static; 33 | 34 | location / { 35 | # checks for static file, if not found proxy to app 36 | try_files $uri @proxy_to_app; 37 | } 38 | 39 | location @proxy_to_app { 40 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 41 | proxy_set_header Host $http_host; 42 | proxy_redirect off; 43 | 44 | proxy_pass http://app_server; 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker Python Base 2 | ================== 3 | 4 | A bare bones python Docker setup to support local and production development from a single ops base. A super simple Flask app is contained in the `app/` directory for demo purposes, but this can easily be switched for any WSGI compatible application. 5 | 6 | 7 | ## Steps to run 8 | 9 | For local development simply run `make run-local`. Once built, the container will run the Flask demo app on `http://:5000`. 10 | 11 | To run the deploy in a production container, run `make run-deploy`. Once built, the container will run the Flask demo app on `http://`. 12 | 13 | In addition to the `run` commands, `make build-local` and `make build-deploy` are available to allow building containers without running them. 14 | 15 | 16 | ## Useful Docker Commands (use with care) 17 | 18 | - View docker images 19 | ``` 20 | docker images 21 | ``` 22 | - List actively running images (add -l to include stopped containers) 23 | ``` 24 | docker ps 25 | ``` 26 | - View container logs 27 | ``` 28 | docker logs -f 29 | ``` 30 | - Stop container 31 | ``` 32 | docker stop 33 | ``` 34 | - Delete dead images 35 | ``` 36 | for i in `docker images|grep \|awk '{print $3}'`;do docker rmi $i;done 37 | ``` 38 | - Delete containers 39 | ``` 40 | docker rm -f `docker ps --no-trunc -a -q` 41 | ``` 42 | --------------------------------------------------------------------------------