Docker Best practices from Nick Janetakis¶
See also
Contents
Dockerfile¶
Use Alpine as a base image unless you can’t due to technical reasons
Pin versions to at least the minor version, example: 2.5-alpine not 2-alpine
Add a maintainer LABEL to keep tabs on who initially made the image
Only include ARG and ENV instructions if you really need them
Use /app to store your app’s code and set it as the WORKDIR (if it makes sense)
When installing packages, take advantage of Docker’s layer caching techniques
If your app is a web service, EXPOSE 8000 unless you have a strong reason not to
Include a wget driven HEALTHCHECK (if it makes sense)
Stick to the [] syntax when supplying your CMD instructions
docker-compose.yml¶
List your services in the order you expect them to start
Alphabetize each service’s properties
Double quote all strings and use {} for empty hashes / dictionaries
Pin versions to at least the minor version, example: 10.4-alpine not 10-alpine
Use . instead of $PWD for when you need the current directory’s path
Prefer build: “.” unless you need to use args or some other sub-property
If your service is a web service, publish port 8000 unless it doesn’t make sense to
.dockerignore¶
Don’t forget to create this file :D
Don’t forget to add the .git folder
Don’t forget to add any sensitive files such as .env.production
Example Apps for Popular Web Frameworks¶
I’ve put together a few example applications that stick to these best practices.
You can find them all on https://github.com/nickjj/docker-web-framework-examples Fully working Docker Compose based examples that you can reference:
Flask
Node / Express
Phoenix
Rails
Webpack
If you don’t see your favorite web framework listed above, open up a PR! This repo is meant to be a community effort where we can work together to make high quality example apps that demonstrate Dockerizing popular web frameworks and libraries.
Flask example¶
Flask Dockerfile¶
FROM python:2.7-alpine
LABEL maintainer="Nick Janetakis <nick.janetakis@gmail.com>"
# If you plan to use PostgreSQL then you must add this package: postgresql-dev.
RUN apk update && apk add build-base
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
HEALTHCHECK CMD wget -q -O /dev/null http://localhost:8000/healthy || exit 1
CMD ["gunicorn", "-c", "python:config.gunicorn", "hello.app:create_app()"]
Flask docker-compose.yml¶
version: "3.6"
services:
web:
build: "."
command: >
gunicorn --reload -c "python:config.gunicorn" "hello.app:create_app()"
env_file:
- ".env"
ports:
- "8000:8000"
volumes:
- ".:/app"
hello/app.py¶
from flask import Flask
from werkzeug.debug import DebuggedApplication
def create_app(settings_override=None):
"""
Create a Flask application using the app factory pattern.
:param settings_override: Override settings
:return: Flask app
"""
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config.settings')
app.config.from_pyfile('settings.py', silent=True)
if settings_override:
app.config.update(settings_override)
if app.debug:
app.wsgi_app = DebuggedApplication(app.wsgi_app, evalex=True)
@app.route('/')
def index():
return 'Hello world with DEBUG={0}'.format(app.config['DEBUG'])
@app.route('/healthy')
def healthy():
return ''
return app
.gitignore¶
See also
# Created by https://www.gitignore.io/api/python,osx
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Flask stuff:
instance/settings.py
.webassets-cache
# Scrapy stuff:
.scrapy
# celery beat schedule file
celerybeat-schedule.*
# End of https://www.gitignore.io/api/python,osx