├── .dockerignore ├── .gitignore ├── docker-compose.yml ├── flask ├── Dockerfile ├── app │ ├── __init__.py │ ├── config.py │ ├── models.py │ ├── static │ │ ├── css │ │ │ └── style.css │ │ └── img │ │ │ ├── bootstrap-logo.png │ │ │ ├── docker-logo.png │ │ │ ├── flask-logo.png │ │ │ ├── mongodb-logo.jpg │ │ │ ├── nginx-logo.png │ │ │ ├── python-logo.png │ │ │ ├── redis-logo.png │ │ │ └── uwsgi-logo.png │ ├── templates │ │ ├── guestbook.html │ │ ├── index.html │ │ └── templates │ │ │ └── public_template.html │ └── views.py ├── requirements.txt ├── wsgi.ini └── wsgi.py ├── nginx ├── Dockerfile └── nginx.conf └── readme.md /.dockerignore: -------------------------------------------------------------------------------- 1 | env 2 | 3 | __pycache__ 4 | 5 | .vscode 6 | 7 | .git 8 | 9 | .gitignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | 117 | .vscode 118 | .idea -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | 5 | mongo: 6 | image: mongo 7 | container_name: mongo 8 | restart: always 9 | # Username and password user for local development. Change to something suitable for production 10 | # Be sure to update __init__.py located in /flask/app when you change the username & password 11 | environment: 12 | MONGO_INITDB_ROOT_USERNAME: root 13 | MONGO_INITDB_ROOT_PASSWORD: example 14 | # Specify a local volume to store the db data & logging info 15 | volumes: 16 | - "~/container_volumes/mongo/data/db:/data/db" 17 | - "~/container_logs/mongo/log:/var/log/mongodb" 18 | # If using a GCP VM. Log to stackdriver 19 | # logging: 20 | # driver: gcplogs 21 | 22 | redis: 23 | image: redis 24 | # Change password `example` to something suitable 25 | # Be sure to update __init__.py located in /flask/app when you change the password 26 | command: redis-server --requirepass example 27 | container_name: redis 28 | restart: always 29 | # If using a GCP VM. Log to stackdriver 30 | # logging: 31 | # driver: gcplogs 32 | 33 | nginx: 34 | build: ./nginx 35 | container_name: nginx 36 | restart: always 37 | ports: 38 | - "80:80" 39 | # Specify a local volume to store logging info 40 | volumes: 41 | - "~/container_logs/nginx/log:/var/log/nginx" 42 | # If using a GCP VM. Log to stackdriver 43 | # logging: 44 | # driver: gcplogs 45 | 46 | flask: 47 | build: ./flask 48 | container_name: flask 49 | restart: always 50 | # Specify a local volume to store logging info 51 | volumes: 52 | - "~/container_logs/flask/log:/var/log/wsgi" 53 | # If using a GCP VM. Log to stackdriver 54 | # logging: 55 | # driver: gcplogs 56 | -------------------------------------------------------------------------------- /flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.1-stretch 2 | 3 | # Environment variables 4 | 5 | ENV REDIS_PW = "root" 6 | 7 | ENV MONGO_USERNAME = "root" 8 | ENV MONGO_PASSWORD = "example" 9 | 10 | ADD . /code 11 | 12 | WORKDIR /code 13 | 14 | RUN pip install -r requirements.txt 15 | 16 | CMD ["uwsgi", "wsgi.ini"] -------------------------------------------------------------------------------- /flask/app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | import redis 3 | from mongoengine import connect 4 | 5 | import os 6 | 7 | app = Flask(__name__) 8 | 9 | r = redis.Redis( 10 | host='redis', 11 | port=6379, 12 | db=0, 13 | password="example" 14 | ) 15 | 16 | connect( 17 | db="flask-db", 18 | host="mongo", 19 | port=27017, 20 | username="root", 21 | password="example", 22 | authentication_source="admin", 23 | connect=False 24 | ) 25 | 26 | 27 | from app import views 28 | from app import models -------------------------------------------------------------------------------- /flask/app/config.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | SECRET_KEY = "kueghfo734yfo8g387" 3 | 4 | class ProductionConfig(Config): 5 | DEBUG = False 6 | 7 | class DevelopmentConfig(Config): 8 | DEBUG = True -------------------------------------------------------------------------------- /flask/app/models.py: -------------------------------------------------------------------------------- 1 | from mongoengine import Document, StringField, DateTimeField 2 | import datetime 3 | 4 | 5 | class Message(Document): 6 | name = StringField() 7 | message = StringField() 8 | date = DateTimeField(default=datetime.datetime.utcnow) 9 | 10 | meta = { 11 | "ordering": [ 12 | "-date" 13 | ] 14 | } -------------------------------------------------------------------------------- /flask/app/static/css/style.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | margin-bottom: 25px; 3 | } 4 | 5 | body { 6 | background-color: #fafafa; 7 | } 8 | 9 | .card-text { 10 | color: #272727; 11 | font-size: 0.9em; 12 | } 13 | 14 | .card-img-top { 15 | padding: 10px 10px 16 | } -------------------------------------------------------------------------------- /flask/app/static/img/bootstrap-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/bootstrap-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/docker-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/docker-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/flask-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/flask-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/mongodb-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/mongodb-logo.jpg -------------------------------------------------------------------------------- /flask/app/static/img/nginx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/nginx-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/python-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/python-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/redis-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/redis-logo.png -------------------------------------------------------------------------------- /flask/app/static/img/uwsgi-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Julian-Nash/docker-flask-nginx-redis-mongo/f3701a720e5b36565b1394052fc7af8832dcc8df/flask/app/static/img/uwsgi-logo.png -------------------------------------------------------------------------------- /flask/app/templates/guestbook.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/public_template.html" %} 2 | 3 | {% block title %}Guestbook{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

Sign the guestbook

11 |

Leave your name and message below to see the database in action

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 | 37 | {% if not messages|length > 0 %} 38 | 39 |
40 |
41 |
42 |
Messages
43 |
44 |

No messages! Go ahead and write one

45 |
46 |
47 |
48 | 49 | {% else %} 50 | 51 |
52 |
53 |
54 |
Messages
55 |
56 | {% for message in messages %} 57 |
58 |
59 | {{ message.name }} wrote on {{ message.date.strftime('%d-%m-%y at %H:%M') }} 60 |
61 |
62 | {{message.message}} 63 |
64 |
65 | {% endfor %} 66 |
67 |
68 |
69 | 70 | {% endif %} 71 | 72 | {% endblock %} -------------------------------------------------------------------------------- /flask/app/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/public_template.html" %} 2 | 3 | {% block title %}Index{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

Hello again, world!

11 |

This simple application is powered by the open source technology you see below...

12 | {{ hits }} hits from Redis cache 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
Docker CE
26 |

27 | Docker is a tool designed to make it easier to create, deploy, and run applications 28 | by using containers. Containers allow a developer to package up an application with 29 | all of the parts it needs, such as libraries and other dependencies, and ship it 30 | all out as one package. 31 |

32 |
33 | 38 |
39 |
40 | 41 |
42 |
Python
43 |

44 | Python is a programming language that lets you work more quickly and integrate your 45 | systems more effectively. 46 | You can learn to use Python and see almost immediate gains in productivity and 47 | lower maintenance costs. 48 |

49 |
50 | 55 |
56 |
57 | 58 |
59 |
Flask
60 |

61 | Flask is a micro web framework written in Python. It is classified as a 62 | microframework because it does not require particular tools or libraries.It has 63 | no database abstraction layer, form validation, or any other components where 64 | pre-existing third-party libraries provide common functions. 65 |

66 |
67 | 72 |
73 |
74 | 75 |
76 |
Nginx
77 |

78 | NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as 79 | well as an IMAP/POP3 proxy server. NGINX is known for its high performance, 80 | stability, rich feature set, simple configuration, and low resource consumption. 81 | NGINX is one of a handful of servers written to address the C10K problem. 82 |

83 |
84 | 89 |
90 |
91 | 92 |
93 |
Redis
94 |

95 | Redis is an open source (BSD licensed), in-memory data structure store, used as a 96 | database, cache and message broker. It supports data structures such as strings, 97 | hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, 98 | geospatial indexes with radius queries and streams. Redis has built-in replication, 99 | Lua scripting, LRU eviction, transactions and different levels of on-disk 100 | persistence, and provides high availability via Redis Sentinel and automatic 101 | partitioning with Redis Cluster. 102 |

103 |
104 | 109 |
110 |
111 | 112 |
113 |
MongoDB
114 |

115 | MongoDB is an open source, document-oriented database designed with both 116 | scalability and developer agility in mind. Instead of storing your data in tables 117 | and rows as you would with a relational database, in MongoDB you store JSON-like 118 | documents with dynamic schemas. 119 |

120 |
121 | 126 |
127 |
128 | 129 |
130 |
uWSGI
131 |

132 | The uWSGI project aims at developing a full stack for building hosting services. 133 | 134 | Application servers (for various programming languages and protocols), proxies, 135 | process managers and monitors are all implemented using a common api and a common 136 | configuration style. 137 | 138 | The “WSGI” part in the name is a tribute to the namesake Python standard, as it has 139 | been the first developed plugin for the project. 140 | 141 | Versatility, performance, low-resource usage and reliability are the strengths of 142 | the project (and the only rules followed). 143 |

144 |
145 | 150 |
151 |
152 | 153 |
154 |
Bootstrap
155 |

156 | Bootstrap is a free and open-source front-end framework for designing websites and 157 | web applications. It contains HTML- and CSS-based design templates for typography, 158 | forms, buttons, navigation and other interface components, as well as optional 159 | JavaScript extensions. Unlike many earlier web frameworks, it concerns itself with 160 | front-end development only. 161 | 162 | Bootstrap is the second most-starred project on GitHub, with more than 129,000 163 | stars. 164 |

165 |
166 | 171 |
172 |
173 |
174 |
175 |
176 | 177 | {% endblock %} -------------------------------------------------------------------------------- /flask/app/templates/templates/public_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | {% block title %}{% endblock %} 17 | 18 | 19 | 20 | 21 | 35 | 36 |
37 | 38 | {% block main %}{% endblock %} 39 | 40 |
41 | 42 | 43 | 44 | 46 | 48 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /flask/app/views.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from app import r 3 | from app.models import Message 4 | 5 | from flask import render_template, request, redirect, url_for 6 | 7 | 8 | @app.route("/") 9 | def index(): 10 | 11 | r.incr("hits") 12 | hits = int(r.get("hits")) 13 | 14 | return render_template("index.html", hits=hits) 15 | 16 | 17 | @app.route("/guestbook", methods=["GET", "POST"]) 18 | def guestbook(): 19 | 20 | messages = Message.objects() 21 | 22 | if request.method == "POST": 23 | 24 | Message( 25 | name=request.form.get("name"), 26 | message=request.form.get("message") 27 | ).save() 28 | 29 | return redirect(request.url) 30 | 31 | return render_template("guestbook.html", messages=messages) -------------------------------------------------------------------------------- /flask/requirements.txt: -------------------------------------------------------------------------------- 1 | Click==7.0 2 | Flask==1.0.2 3 | itsdangerous==1.1.0 4 | Jinja2==2.10 5 | MarkupSafe==1.1.0 6 | mongoengine==0.16.2 7 | pymongo==3.7.2 8 | redis==3.0.1 9 | six==1.11.0 10 | uWSGI==2.0.17.1 11 | Werkzeug==0.14.1 12 | -------------------------------------------------------------------------------- /flask/wsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | http = 0.0.0.0:3030 3 | wsgi-file = wsgi.py 4 | callable = app 5 | processes = 4 6 | threads = 2 7 | logto = /var/log/wsgi -------------------------------------------------------------------------------- /flask/wsgi.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | if __name__ == "__main__": 4 | app.run(host="0.0.0.0") -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | RUN rm /etc/nginx/conf.d/default.conf 4 | 5 | COPY nginx.conf /etc/nginx/conf.d/ -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | 5 | location / { 6 | include uwsgi_params; 7 | proxy_pass http://flask:3030; 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Simple dockerised Flask application 2 | 3 | *Note - Early work in progress. Day 1 of Docker* 4 | 5 | #### Flask app comprised of 4 containers 6 | 7 | ``` 8 | flask: Contains the Flask application and uWSGI application server 9 | nginx: Contains the Nginx web server 10 | redis: Contains the Redis cache 11 | mongo: Contains the MongoDB database 12 | ``` 13 | 14 | #### Some info on the stack 15 | 16 | ``` 17 | Python version: 3.7.1 18 | Web framework: Flask 19 | Database: MongoDB (Using the Mongoengine driver) 20 | Cache: Redis 21 | App server: uWSGI 22 | Web server: Nginx 23 | Front end: Bootsrap4 24 | ``` 25 | 26 | #### Give it a spin 27 | 28 | 1. Pull this repo 29 | 2. Update the username & password for mongo in `docker-compose.yml` 30 | 3. Update the password for redis in `docker-compose.yml` 31 | 4. Update the mongo & redis username & passwords in `flask/app/__init__.py` 32 | 5. `docker-compose up --build` 33 | 6. Go to `127.0.0.1/` in your browser 34 | 35 | Consider installing MongoDB compass and connect to the mongo container for a helpful debugging & data exploring tool 36 | 37 | Remove the following lines from `docker-compose.yml` to remove the exposed mongo ports for production 38 | 39 | ``` 40 | ports: 41 | - "27017:27017" 42 | ``` 43 | 44 | #### TODO 45 | 46 | 1. Write a solid nginx.conf 47 | 2. Automate SSL certs 48 | 3. Better handling/automation of usernames, passwords & environment variables. Look into `.env` file 49 | 4. Integrate a celery container & setup background tasks 50 | 5. Learn more about Docker 51 | 52 | #### Notes 53 | 54 | Mongo auth has been a bit buggy 55 | 56 | Feedback welcome! 57 | 58 | 59 | 60 | 61 | --------------------------------------------------------------------------------