Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker new #4

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
02d6f1c
Begin docker migration
Dosenpfand Jul 14, 2023
ea5128b
Switch to vanilla python image for ARM
Dosenpfand Jul 14, 2023
00cf984
Add missing config
Dosenpfand Jul 14, 2023
9d86a18
FIx db config
Dosenpfand Jul 14, 2023
50258ae
Add prestart
Dosenpfand Jul 14, 2023
5d73897
Remove translation command
Dosenpfand Jul 14, 2023
f484d3b
Check before collate
Dosenpfand Jul 14, 2023
01c88ef
Add table check for achievements
Dosenpfand Jul 14, 2023
bacba78
FIx docker env
Dosenpfand Jul 14, 2023
c2bcb3b
Fix entrypoint
Dosenpfand Jul 14, 2023
1d471dc
FIx path
Dosenpfand Jul 14, 2023
18e5543
Add create user
Dosenpfand Jul 14, 2023
d94942e
FIx prestart path
Dosenpfand Jul 14, 2023
0fb66de
Fix admin creation
Dosenpfand Jul 14, 2023
8484b3f
Remove duplicate permission
Dosenpfand Jul 14, 2023
e2b26fe
FIx app name
Dosenpfand Jul 14, 2023
94d5657
Add missin secret
Dosenpfand Jul 14, 2023
2275500
Add dev container config
Dosenpfand Jul 15, 2023
0627f5b
Cleanup docker
Dosenpfand Jul 18, 2023
c821281
Remove meinheld and greenlet
Dosenpfand Oct 13, 2024
09145a8
Bump Python version to 3.12
Dosenpfand Oct 13, 2024
1a9d7cc
Fix deps
Dosenpfand Oct 13, 2024
e73417a
Merge branch 'main' into docker-new
Dosenpfand Oct 13, 2024
283da85
Add persistence to compose
Dosenpfand Oct 20, 2024
39787d5
Improve
Dosenpfand Oct 20, 2024
938f4fd
Fix typo
Dosenpfand Jan 12, 2025
89996af
Add auto-reloading in dev mode
Dosenpfand Jan 12, 2025
7dea02c
Merge branch 'main' into docker-new
Dosenpfand Jan 12, 2025
2394033
Merge branch 'main' into docker-new
Dosenpfand Feb 5, 2025
d310fcb
Merge branch 'main' into docker-new
Dosenpfand Feb 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
{
"name": "Dev Container",
"dockerComposeFile": [
"../docker-compose.yml",
"docker-compose.yml"
],
"service": "app",
"workspaceFolder": "/app",
"postStartCommand": "git config --global --add safe.directory /app"
}
6 changes: 6 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3'
services:
app:
volumes:
- .:/app:cached
command: flask run --debug --host=0.0.0.0 -p 80
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.12

COPY requirements.txt /tmp/requirements.txt
RUN pip install --upgrade pip
RUN pip install --upgrade wheel
RUN pip install --no-cache-dir -r /tmp/requirements.txt

COPY ./ /app
WORKDIR /app

ENV PYTHONPATH=/app
ENV MODULE_NAME=wsgi
EXPOSE 80
ENTRYPOINT ["./entrypoint.sh"]
CMD ["./start.sh"]
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ Import database backup:
sudo -u postgres psql matheueben < backup.sql
```

or in the container:
```bash
psql -U db_user -h postgres -p 5432 -d db_database < backup.sql
```

Clean up database:
```bash
flask fab security-cleanup
Expand Down
11 changes: 11 additions & 0 deletions app.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FLASK_SECRET_KEY=ONLY_FOR_DEVELOPMENT
FLASK_SQLALCHEMY_DATABASE_URI=postgresql://db_user:db_password@postgres:5432/db_database
FLASK_RECAPTCHA_PUBLIC_KEY=10000000-ffff-ffff-ffff-000000000001
FLASK_RECAPTCHA_PRIVATE_KEY=0x0000000000000000000000000000000000000000
FLASK_MAIL_SERVER=TODO
FLASK_MAIL_PORT=587
FLASK_MAIL_USE_TLS=False
FLASK_MAIL_USERNAME=TODO
FLASK_MAIL_PASSWORD=TODO
FLASK_MAIL_DEFAULT_SENDER=TODO
FLASK_ADMIN_PASSWORD=TODO
42 changes: 22 additions & 20 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from flask_debugtoolbar import DebugToolbarExtension
from flask_migrate import Migrate
from sentry_sdk.integrations.flask import FlaskIntegration

from sqlalchemy import inspect
from app.models.achievements import achievements
from app.models.general import Achievement, Question
from app.tools.mail import send_mail
Expand Down Expand Up @@ -41,29 +41,31 @@ def create_app(config="config"):
db.init_app(app)

# TODO: Only necessary until SQLAlchemy 2 is used.
result = db.session.execute(
"SELECT * FROM pg_collation WHERE collname = 'numeric';"
)
if not result.first():
if inspect(db.engine).has_table(Question.__tablename__):
result = db.session.execute(
"SELECT * FROM pg_collation WHERE collname = 'numeric';"
)
if not result.first():
db.session.execute(
"CREATE COLLATION numeric (provider = icu, locale = 'de_DE@colNumeric=yes');"
)
db.session.execute(
"CREATE COLLATION numeric (provider = icu, locale = 'de_DE@colNumeric=yes');"
f'ALTER TABLE "{Question.__tablename__}" '
f'ALTER COLUMN "{Question.external_id.name}" type VARCHAR COLLATE numeric;'
)
db.session.execute(
f'ALTER TABLE "{Question.__tablename__}" '
f'ALTER COLUMN "{Question.external_id.name}" type VARCHAR COLLATE numeric;'
)

# Init achievements
for achievement in achievements:
result = (
db.session.query(Achievement).filter_by(name=achievement.name).first()
)
if not result:
db.session.add(achievement)
else:
result.title = achievement.title
result.description = achievement.description
db.session.commit()
if inspect(db.engine).has_table(Achievement.__tablename__):
for achievement in achievements:
result = (
db.session.query(Achievement).filter_by(name=achievement.name).first()
)
if not result:
db.session.add(achievement)
else:
result.title = achievement.title
result.description = achievement.description
db.session.commit()

migrate.init_app(app, db)
appbuilder.init_app(app, db.session)
Expand Down
1 change: 0 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@
["ExtendedUserDBModelView", "can_confirm_account_delete"],
["ExtendedUserDBModelView", "can_export_data"],
["ExtendedUserDBModelView", "export_data_action"],
["QuestionRandom", "can_random_question_redirect"],
["IdToForm", "can_id_to_form"],
["Verwaltung", "menu_access"],
["classes", "menu_access"],
Expand Down
37 changes: 37 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
services:
app:
build: ./
restart: always
ports:
- 5000:80
env_file:
- app.env
environment:
- GUNICORN_CONF=/app/gunicorn_conf_dev.py
depends_on:
postgres:
condition: service_healthy
volumes:
- ./:/app
- static:/app/app/static

postgres:
image: postgres:alpine
restart: always
environment:
- POSTGRES_PASSWORD=db_password
- POSTGRES_DB=db_database
- POSTGRES_USER=db_user
volumes:
- postgres:/var/lib/postgresql/data
- ./:/app
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U db_user -d db_database" ]
start_period: 10s
interval: 5s
timeout: 5s
retries: 10

volumes:
static:
postgres:
16 changes: 16 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env sh
set -e

if [ -f /app/app/main.py ]; then
DEFAULT_MODULE_NAME=app.main
elif [ -f /app/main.py ]; then
DEFAULT_MODULE_NAME=main
fi
MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}
VARIABLE_NAME=${VARIABLE_NAME:-app}
export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"}

DEFAULT_GUNICORN_CONF=/app/gunicorn_conf.py
export GUNICORN_CONF=${GUNICORN_CONF:-$DEFAULT_GUNICORN_CONF}

exec "$@"
40 changes: 40 additions & 0 deletions gunicorn_conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import multiprocessing
import os

workers_per_core_str = os.getenv("WORKERS_PER_CORE", "2")
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
host = os.getenv("HOST", "0.0.0.0")
port = os.getenv("PORT", "80")
bind_env = os.getenv("BIND", None)
use_loglevel = os.getenv("LOG_LEVEL", "info")
if bind_env:
use_bind = bind_env
else:
use_bind = "{host}:{port}".format(host=host, port=port)

cores = multiprocessing.cpu_count()
workers_per_core = float(workers_per_core_str)
default_web_concurrency = workers_per_core * cores
if web_concurrency_str:
web_concurrency = int(web_concurrency_str)
assert web_concurrency > 0
else:
web_concurrency = int(default_web_concurrency)

# Gunicorn config variables
loglevel = use_loglevel
workers = web_concurrency
bind = use_bind
keepalive = 120
errorlog = "-"

# For debugging and testing
log_data = {
"loglevel": loglevel,
"workers": workers,
"bind": bind,
# Additional, non-gunicorn variables
"workers_per_core": workers_per_core,
"host": host,
"port": port,
}
19 changes: 19 additions & 0 deletions gunicorn_conf_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import json
from gunicorn_conf import *

reload = True

# For debugging and testing
log_data = {
"loglevel": loglevel,
"workers": workers,
"bind": bind,
# Additional, non-gunicorn variables
"workers_per_core": workers_per_core,
"host": host,
"port": port,
"reload": reload,
}


print(json.dumps(log_data))
13 changes: 0 additions & 13 deletions matheueben.service

This file was deleted.

17 changes: 17 additions & 0 deletions prestart.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/sh

if flask db current | grep "(head)"; then
echo "Running database upgrade."
flask db upgrade
else
echo "Initializing database."
flask fab create-db
flask db stamp
# TODO: Fix! user creation functionality broken!
flask fab create-admin \
--username admin \
--firstname admin \
--lastname admin \
--email admin@matheworkout.at \
--password ${FLASK_ADMIN_PASSWORD:-$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20; echo)}
fi
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
git+https://github.com/Dosenpfand/Flask-AppBuilder.git@multiple-actions-fix#egg=Flask-AppBuilder
# TODO: rebase!
# git+https://github.com/Dosenpfand/Flask-AppBuilder.git@multiple-actions-fix#egg=Flask-AppBuilder
flask-appbuilder~=4.5
flask-mail~=0.9
Pillow~=10.0 # For image processing in Flask-AppBuilder
psycopg2-binary~=2.9 # for postgresql
Flask-Migrate~=3.1
WTForms~=2.3
Flask~=2.1
alembic~=1.7
gunicorn~=22.0 # for deployment via nginx
python-dotenv~=0.21
Flask-DebugToolbar~=0.13
gunicorn~=23.0
Flask-DebugToolbar~=0.15
simpleeval~=0.9
sentry-sdk[flask,sqlalchemy]~=2.12 # for error collection
14 changes: 14 additions & 0 deletions start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#! /usr/bin/env sh
set -e

PRE_START_PATH=./prestart.sh
echo "Checking for script in $PRE_START_PATH"
if [ -f $PRE_START_PATH ] ; then
echo "Running script $PRE_START_PATH"
. "$PRE_START_PATH"
else
echo "There is no script $PRE_START_PATH"
fi

# Start Gunicorn
exec gunicorn -c "$GUNICORN_CONF" "$APP_MODULE"
6 changes: 1 addition & 5 deletions wsgi.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import logging
import sys

# activate_this = '/var/www/FlaskApp/venv/bin/activate_this.py'
# with open(activate_this) as file_:
# exec(file_.read(), dict(__file__=activate_this))

logging.basicConfig(stream=sys.stderr)
sys.path.insert(0, "/var/www/FlaskApp/")
from app import create_app # noqa

application = create_app()
app = create_app()