Flask ist ein leichtgewichtiges Python-Webframework. Es eignet sich ideal für APIs, kleine Webapps und Microservices. Für den Produktionsbetrieb wird Gunicorn als WSGI-Server empfohlen.

Flask vs Django

| Aspekt | Flask | Django | |--------|-------|--------| | Philosophie | Minimal, flexibel | Batteries included | | Lernkurve | Flach | Steiler | | ORM | Optional (SQLAlchemy) | Integriert | | Admin | Nicht integriert | Integriert | | Anwendung | APIs, Microservices | Vollständige Webapps |

Projekt-Setup

Verzeichnis erstellen

mkdir -p /var/www/flaskapp
cd /var/www/flaskapp

Virtual Environment

python3 -m venv venv
source venv/bin/activate

Dependencies installieren

pip install flask gunicorn python-dotenv

Einfache Flask-App

# /var/www/flaskapp/app.py

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def index():
    return jsonify({'message': 'Hello from Flask!'})

@app.route('/health')
def health():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run()

Application Factory

# /var/www/flaskapp/app/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)

    # Konfiguration
    app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev')
    app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
        'DATABASE_URL', 'sqlite:///app.db'
    )
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    # Extensions initialisieren
    db.init_app(app)

    # Blueprints registrieren
    from app.routes import main
    app.register_blueprint(main)

    return app
# /var/www/flaskapp/app/routes.py

from flask import Blueprint, jsonify

main = Blueprint('main', __name__)

@main.route('/')
def index():
    return jsonify({'message': 'Hello from Flask!'})

@main.route('/api/users')
def get_users():
    return jsonify({'users': []})
# /var/www/flaskapp/wsgi.py

from app import create_app

application = create_app()

if __name__ == '__main__':
    application.run()

requirements.txt

flask==3.0.0
gunicorn==21.2.0
python-dotenv==1.0.0
flask-sqlalchemy==3.1.1
psycopg2-binary==2.9.9

Gunicorn-Konfiguration

gunicorn.conf.py

# /var/www/flaskapp/gunicorn.conf.py

import multiprocessing

# Socket
bind = "unix:/run/gunicorn/flaskapp.sock"

# Worker
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
threads = 2
worker_connections = 1000

# Timeout
timeout = 30
keepalive = 2

# User
user = "www-data"
group = "www-data"

# Logging
errorlog = "/var/log/gunicorn/flaskapp-error.log"
accesslog = "/var/log/gunicorn/flaskapp-access.log"
loglevel = "info"

# Process
proc_name = "flaskapp"
daemon = False

Systemd-Service

# /etc/systemd/system/flaskapp.service

[Unit]
Description=Gunicorn daemon for Flask app
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/flaskapp
RuntimeDirectory=gunicorn

EnvironmentFile=/var/www/flaskapp/.env

ExecStart=/var/www/flaskapp/venv/bin/gunicorn \
    --config /var/www/flaskapp/gunicorn.conf.py \
    wsgi:application

ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Service starten

mkdir -p /var/log/gunicorn
chown www-data:www-data /var/log/gunicorn

systemctl daemon-reload
systemctl enable flaskapp
systemctl start flaskapp

Nginx-Konfiguration

# /etc/nginx/sites-available/flaskapp

upstream flaskapp {
    server unix:/run/gunicorn/flaskapp.sock fail_timeout=0;
}

server {
    listen 80;
    server_name api.example.de;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.de;

    ssl_certificate /etc/letsencrypt/live/api.example.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.de/privkey.pem;

    client_max_body_size 5M;

    access_log /var/log/nginx/flaskapp-access.log;
    error_log /var/log/nginx/flaskapp-error.log;

    location /static/ {
        alias /var/www/flaskapp/static/;
        expires 30d;
    }

    location / {
        proxy_pass http://flaskapp;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}
ln -s /etc/nginx/sites-available/flaskapp /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Umgebungsvariablen

.env-Datei

# /var/www/flaskapp/.env

FLASK_APP=wsgi.py
FLASK_ENV=production
SECRET_KEY=dein_sehr_langes_secret_key
DATABASE_URL=postgresql://user:pass@localhost/flaskapp

Laden in Flask

# /var/www/flaskapp/wsgi.py

from dotenv import load_dotenv
load_dotenv()

from app import create_app
application = create_app()

Datenbank

PostgreSQL einrichten

sudo -u postgres psql

CREATE DATABASE flaskapp;
CREATE USER flaskapp WITH PASSWORD 'sicheres_passwort';
GRANT ALL PRIVILEGES ON DATABASE flaskapp TO flaskapp;
\q

Flask-Migrate

pip install flask-migrate
# app/__init__.py

from flask_migrate import Migrate

migrate = Migrate()

def create_app():
    app = Flask(__name__)
    # ...
    db.init_app(app)
    migrate.init_app(app, db)
    # ...
# Migrationen
flask db init
flask db migrate -m "Initial migration"
flask db upgrade

REST-API mit Flask-RESTful

pip install flask-restful
# app/api.py

from flask import Blueprint
from flask_restful import Api, Resource, reqparse

api_bp = Blueprint('api', __name__, url_prefix='/api')
api = Api(api_bp)

class UserResource(Resource):
    def get(self, user_id):
        user = User.query.get_or_404(user_id)
        return {'id': user.id, 'name': user.name}

    def put(self, user_id):
        parser = reqparse.RequestParser()
        parser.add_argument('name', required=True)
        args = parser.parse_args()

        user = User.query.get_or_404(user_id)
        user.name = args['name']
        db.session.commit()
        return {'id': user.id, 'name': user.name}

    def delete(self, user_id):
        user = User.query.get_or_404(user_id)
        db.session.delete(user)
        db.session.commit()
        return '', 204

class UserListResource(Resource):
    def get(self):
        users = User.query.all()
        return [{'id': u.id, 'name': u.name} for u in users]

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('name', required=True)
        args = parser.parse_args()

        user = User(name=args['name'])
        db.session.add(user)
        db.session.commit()
        return {'id': user.id, 'name': user.name}, 201

api.add_resource(UserListResource, '/users')
api.add_resource(UserResource, '/users/<int:user_id>')

Async mit Gevent

pip install gevent
# gunicorn.conf.py

worker_class = "gevent"
worker_connections = 1000

Background Tasks mit Celery

pip install celery redis
# app/celery.py

from celery import Celery

def make_celery(app):
    celery = Celery(
        app.import_name,
        backend=app.config['CELERY_RESULT_BACKEND'],
        broker=app.config['CELERY_BROKER_URL']
    )
    celery.conf.update(app.config)

    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)

    celery.Task = ContextTask
    return celery
# app/__init__.py

from app.celery import make_celery

celery = None

def create_app():
    global celery
    app = Flask(__name__)
    app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
    app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
    # ...
    celery = make_celery(app)
    return app
# app/tasks.py

from app import celery

@celery.task
def send_email(to, subject, body):
    # Email senden
    pass

Logging

# app/__init__.py

import logging
from logging.handlers import RotatingFileHandler

def create_app():
    app = Flask(__name__)
    # ...

    if not app.debug:
        file_handler = RotatingFileHandler(
            '/var/log/flask/flaskapp.log',
            maxBytes=10240000,
            backupCount=10
        )
        file_handler.setFormatter(logging.Formatter(
            '%(asctime)s %(levelname)s: %(message)s'
        ))
        file_handler.setLevel(logging.INFO)
        app.logger.addHandler(file_handler)
        app.logger.setLevel(logging.INFO)
        app.logger.info('Flask app startup')

    return app

Deployment-Skript

#!/bin/bash
# /var/www/flaskapp/deploy.sh

set -e

cd /var/www/flaskapp

git pull origin main

source venv/bin/activate
pip install -r requirements.txt

flask db upgrade

sudo systemctl restart flaskapp

echo "Deployment erfolgreich"

Zusammenfassung

| Komponente | Funktion | |------------|----------| | Flask | Webframework | | Gunicorn | WSGI-Server | | Nginx | Reverse Proxy | | SQLAlchemy | ORM | | Celery | Background Tasks |

| Datei | Funktion | |-------|----------| | wsgi.py | WSGI-Einstiegspunkt | | gunicorn.conf.py | Gunicorn-Config | | .env | Umgebungsvariablen |

| Befehl | Funktion | |--------|----------| | flask db migrate | Migration erstellen | | flask db upgrade | Migration ausführen | | flask run | Development Server |

Fazit

Flask ist ideal für APIs und kleinere Webanwendungen. Die minimale Grundstruktur ermöglicht maximale Flexibilität. Mit Gunicorn und Nginx ist Flask produktionsbereit. SQLAlchemy bietet ein mächtiges ORM. Die Application Factory Pattern ermöglicht saubere Projektstruktur. Für komplexe Anwendungen mit Admin-Interface ist Django oft besser geeignet.