From 24a92139eb8f2a93d15800200ef6d9fb4eb162d8 Mon Sep 17 00:00:00 2001 From: Daniel afx Date: Fri, 4 Feb 2022 00:27:25 +0200 Subject: [PATCH] provide example flask project --- docker-compose.prod.yml | 14 +++---- docker-compose.yml | 6 +-- flask/Dockerfile | 23 ++++++++++++ flask/Dockerfile.prod | 69 +++++++++++++++++++++++++++++++++++ flask/entrypoint.prod.sh | 14 +++++++ flask/entrypoint.sh | 16 ++++++++ flask/forest/__init__.py | 57 +++++++++++++++++++++++++++++ flask/forest/config.py | 11 ++++++ flask/forest/media/.gitkeep | 0 flask/forest/static/hello.txt | 1 + flask/manage.py | 23 ++++++++++++ flask/requirements.txt | 4 ++ nginx/Dockerfile | 4 ++ nginx/nginx.conf | 24 ++++++++++++ 14 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 flask/Dockerfile create mode 100644 flask/Dockerfile.prod create mode 100755 flask/entrypoint.prod.sh create mode 100755 flask/entrypoint.sh create mode 100644 flask/forest/__init__.py create mode 100644 flask/forest/config.py create mode 100644 flask/forest/media/.gitkeep create mode 100644 flask/forest/static/hello.txt create mode 100644 flask/manage.py create mode 100644 flask/requirements.txt create mode 100644 nginx/Dockerfile create mode 100644 nginx/nginx.conf diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 2385e28..6238f0a 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,14 +1,14 @@ version: '3.8' services: - web: + flask: build: - context: ./services/web + context: ./flask dockerfile: Dockerfile.prod command: gunicorn --bind 0.0.0.0:5000 manage:app volumes: - - static_volume:/home/app/web/project/static - - media_volume:/home/app/web/project/media + - static_volume:/home/app/web/forest/static + - media_volume:/home/app/web/forest/media expose: - 5000 env_file: @@ -22,10 +22,10 @@ services: env_file: - ./.env.prod nginx: - build: ./services/nginx + build: ./nginx volumes: - - static_volume:/home/app/web/project/static - - media_volume:/home/app/web/project/media + - static_volume:/home/app/web/forest/static + - media_volume:/home/app/web/forest/media ports: - 1337:80 depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index 15b0027..d292577 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,11 @@ version: '3.8' services: - web: - build: ./services/web + flask: + build: ./flask command: python manage.py run -h 0.0.0.0 volumes: - - ./services/web/:/usr/src/app/ + - ./flask/:/usr/src/app/ ports: - 5000:5000 env_file: diff --git a/flask/Dockerfile b/flask/Dockerfile new file mode 100644 index 0000000..080ab9b --- /dev/null +++ b/flask/Dockerfile @@ -0,0 +1,23 @@ +# pull official base image +FROM python:3.9.5-slim-buster + +# set work directory +WORKDIR /usr/src/app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install system dependencies +RUN apt-get update && apt-get install -y netcat + +# install dependencies +RUN pip install --upgrade pip +COPY ./requirements.txt /usr/src/app/requirements.txt +RUN pip install -r requirements.txt + +# copy project +COPY . /usr/src/app/ + +# run entrypoint.sh +ENTRYPOINT ["/usr/src/app/entrypoint.sh"] diff --git a/flask/Dockerfile.prod b/flask/Dockerfile.prod new file mode 100644 index 0000000..d3b450a --- /dev/null +++ b/flask/Dockerfile.prod @@ -0,0 +1,69 @@ +########### +# BUILDER # +########### + +# pull official base image +FROM python:3.9.5-slim-buster as builder + +# set work directory +WORKDIR /usr/src/app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install system dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends gcc + +# lint +RUN pip install --upgrade pip +RUN pip install flake8==3.9.1 +COPY . /usr/src/app/ +RUN flake8 --ignore=E501,F401 . + +# install python dependencies +COPY ./requirements.txt . +RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt + + +######### +# FINAL # +######### + +# pull official base image +FROM python:3.9.5-slim-buster + +# create directory for the app user +RUN mkdir -p /home/app + +# create the app user +RUN addgroup --system app && adduser --system --group app + +# create the appropriate directories +ENV HOME=/home/app +ENV APP_HOME=/home/app/web +RUN mkdir $APP_HOME +WORKDIR $APP_HOME + +# install dependencies +RUN apt-get update && apt-get install -y --no-install-recommends netcat +COPY --from=builder /usr/src/app/wheels /wheels +COPY --from=builder /usr/src/app/requirements.txt . +RUN pip install --upgrade pip +RUN pip install --no-cache /wheels/* + +# copy entrypoint-prod.sh +COPY ./entrypoint.prod.sh $APP_HOME + +# copy project +COPY . $APP_HOME + +# chown all the files to the app user +RUN chown -R app:app $APP_HOME + +# change to the app user +USER app + +# run entrypoint.prod.sh +ENTRYPOINT ["/home/app/web/entrypoint.prod.sh"] diff --git a/flask/entrypoint.prod.sh b/flask/entrypoint.prod.sh new file mode 100755 index 0000000..37fa201 --- /dev/null +++ b/flask/entrypoint.prod.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if [ "$DATABASE" = "postgres" ] +then + echo "Waiting for postgres..." + + while ! nc -z $SQL_HOST $SQL_PORT; do + sleep 0.1 + done + + echo "PostgreSQL started" +fi + +exec "$@" diff --git a/flask/entrypoint.sh b/flask/entrypoint.sh new file mode 100755 index 0000000..3069d16 --- /dev/null +++ b/flask/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ "$DATABASE" = "postgres" ] +then + echo "Waiting for postgres..." + + while ! nc -z $SQL_HOST $SQL_PORT; do + sleep 0.1 + done + + echo "PostgreSQL started" +fi + +python manage.py create_db + +exec "$@" diff --git a/flask/forest/__init__.py b/flask/forest/__init__.py new file mode 100644 index 0000000..e3f93bf --- /dev/null +++ b/flask/forest/__init__.py @@ -0,0 +1,57 @@ +import os + +from werkzeug.utils import secure_filename +from flask import ( + Flask, + jsonify, + send_from_directory, + request, + redirect, + url_for +) +from flask_sqlalchemy import SQLAlchemy + +app = Flask(__name__) +app.config.from_object("project.config.Config") +db = SQLAlchemy(app) + + +class User(db.Model): + __tablename__ = "users" + + id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(128), unique=True, nullable=False) + active = db.Column(db.Boolean(), default=True, nullable=False) + + def __init__(self, email): + self.email = email + + +@app.route("/") +def hello_world(): + return jsonify(hello="world") + + +@app.route("/static/") +def staticfiles(filename): + return send_from_directory(app.config["STATIC_FOLDER"], filename) + + +@app.route("/media/") +def mediafiles(filename): + return send_from_directory(app.config["MEDIA_FOLDER"], filename) + + +@app.route("/upload", methods=["GET", "POST"]) +def upload_file(): + if request.method == "POST": + file = request.files["file"] + filename = secure_filename(file.filename) + file.save(os.path.join(app.config["MEDIA_FOLDER"], filename)) + return """ + + upload new File +
+

+

+ """ diff --git a/flask/forest/config.py b/flask/forest/config.py new file mode 100644 index 0000000..56ba102 --- /dev/null +++ b/flask/forest/config.py @@ -0,0 +1,11 @@ +import os + + +basedir = os.path.abspath(os.path.dirname(__file__)) + + +class Config(object): + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite://") + SQLALCHEMY_TRACK_MODIFICATIONS = False + STATIC_FOLDER = f"{os.getenv('APP_FOLDER')}/forest/static" + MEDIA_FOLDER = f"{os.getenv('APP_FOLDER')}/forest/media" diff --git a/flask/forest/media/.gitkeep b/flask/forest/media/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/flask/forest/static/hello.txt b/flask/forest/static/hello.txt new file mode 100644 index 0000000..32aad8c --- /dev/null +++ b/flask/forest/static/hello.txt @@ -0,0 +1 @@ +hi! diff --git a/flask/manage.py b/flask/manage.py new file mode 100644 index 0000000..8ceb95f --- /dev/null +++ b/flask/manage.py @@ -0,0 +1,23 @@ +from flask.cli import FlaskGroup + +from project import app, db, User + + +cli = FlaskGroup(app) + + +@cli.command("create_db") +def create_db(): + db.drop_all() + db.create_all() + db.session.commit() + + +@cli.command("seed_db") +def seed_db(): + db.session.add(User(email="daniel@deflax.net")) + db.session.commit() + + +if __name__ == "__main__": + cli() diff --git a/flask/requirements.txt b/flask/requirements.txt new file mode 100644 index 0000000..de90336 --- /dev/null +++ b/flask/requirements.txt @@ -0,0 +1,4 @@ +Flask==1.1.2 +Flask-SQLAlchemy==2.5.1 +gunicorn==20.1.0 +psycopg2-binary==2.8.6 diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..5f9269f --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:1.19-alpine + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/conf.d diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..c82e537 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,24 @@ +upstream forest { + server flask:5000; +} + +server { + + listen 80; + + location / { + proxy_pass http://forest; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_redirect off; + } + + location /static/ { + alias /home/app/web/project/static/; + } + + location /media/ { + alias /home/app/web/project/media/; + } + +}