- Home »

Как создать первое веб-приложение с Flask и Python 3
Сегодня поговорим о том, как создать своё первое веб-приложение с Flask и Python 3. Если ты системный администратор или девопс-инженер, который привык работать с серверами, но никогда не пробовал создавать веб-приложения, то эта статья именно для тебя. Flask — это микрофреймворк для Python, который позволяет быстро создавать веб-приложения без лишних сложностей. Он отлично подходит для создания API, дашбордов для мониторинга, инструментов автоматизации и различных админских панелей.
Зачем это нужно? Представь, что тебе нужно создать простой веб-интерфейс для мониторинга серверов, или API для автоматизации задач, или даже простую админскую панель. Flask справится с этими задачами гораздо быстрее и проще, чем более тяжёлые решения типа Django. К тому же, понимание основ веб-разработки сделает тебя более универсальным специалистом.
Как это работает: архитектура Flask
Flask построен на концепции WSGI (Web Server Gateway Interface) — стандарта взаимодействия между веб-серверами и Python-приложениями. Когда пользователь отправляет HTTP-запрос, веб-сервер передаёт его Flask-приложению через WSGI, приложение обрабатывает запрос и возвращает HTTP-ответ.
Основные компоненты Flask:
- Роутинг — определяет, какая функция будет обрабатывать конкретный URL
- Шаблоны — используется движок Jinja2 для генерации HTML
- Контекст запроса — объекты request и response доступны в любой функции
- Расширения — модульная архитектура для добавления функционала
Быстрый старт: пошаговая настройка
Для начала тебе понадобится VPS-сервер с установленным Python 3. Если планируешь серьёзные нагрузки, то лучше сразу взять выделенный сервер.
Шаг 1: Установка Python и виртуального окружения
# Обновляем систему (Ubuntu/Debian)
sudo apt update && sudo apt upgrade -y
# Устанавливаем Python 3 и pip
sudo apt install python3 python3-pip python3-venv -y
# Создаём директорию для проекта
mkdir ~/flask_app && cd ~/flask_app
# Создаём виртуальное окружение
python3 -m venv venv
# Активируем виртуальное окружение
source venv/bin/activate
# Устанавливаем Flask
pip install Flask
Шаг 2: Создание первого приложения
Создай файл app.py
:
from flask import Flask, render_template, request, jsonify
import os
import subprocess
app = Flask(__name__)
@app.route('/')
def index():
return '''
<h1>Админская панель</h1>
<p>Добро пожаловать в систему мониторинга сервера!</p>
<ul>
<li><a href="/system">Информация о системе</a></li>
<li><a href="/processes">Процессы</a></li>
<li><a href="/api/status">API статус</a></li>
</ul>
'''
@app.route('/system')
def system_info():
# Получаем информацию о системе
uptime = subprocess.check_output(['uptime']).decode('utf-8')
df = subprocess.check_output(['df', '-h']).decode('utf-8')
return f'''
<h2>Информация о системе</h2>
<h3>Uptime:</h3>
<pre>{uptime}</pre>
<h3>Дисковое пространство:</h3>
<pre>{df}</pre>
<a href="/">Назад</a>
'''
@app.route('/processes')
def processes():
# Получаем список процессов
ps = subprocess.check_output(['ps', 'aux']).decode('utf-8')
return f'''
<h2>Процессы</h2>
<pre>{ps}</pre>
<a href="/">Назад</a>
'''
@app.route('/api/status')
def api_status():
# API эндпоинт для получения статуса в JSON
try:
load_avg = os.getloadavg()
status = {
'status': 'ok',
'load_average': {
'1min': load_avg[0],
'5min': load_avg[1],
'15min': load_avg[2]
},
'memory': get_memory_info(),
'disk': get_disk_info()
}
return jsonify(status)
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
def get_memory_info():
# Простая функция для получения информации о памяти
with open('/proc/meminfo', 'r') as f:
lines = f.readlines()
memory_info = {}
for line in lines[:3]: # Берём только первые 3 строки
key, value = line.split(':')
memory_info[key] = value.strip()
return memory_info
def get_disk_info():
# Информация о дисковом пространстве
statvfs = os.statvfs('/')
free_bytes = statvfs.f_frsize * statvfs.f_bavail
total_bytes = statvfs.f_frsize * statvfs.f_blocks
used_bytes = total_bytes - free_bytes
return {
'total_gb': round(total_bytes / (1024**3), 2),
'used_gb': round(used_bytes / (1024**3), 2),
'free_gb': round(free_bytes / (1024**3), 2),
'usage_percent': round((used_bytes / total_bytes) * 100, 2)
}
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
Шаг 3: Запуск приложения
# Запускаем приложение
python3 app.py
Теперь твоё приложение доступно по адресу http://your-server-ip:5000
. Но для продакшена такой способ запуска не подойдёт.
Продакшен-настройка: Gunicorn + Nginx
Установка Gunicorn
# Устанавливаем Gunicorn
pip install gunicorn
# Создаём файл конфигурации
cat > gunicorn_config.py << 'EOF'
bind = "127.0.0.1:5000"
workers = 4
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 100
timeout = 30
keepalive = 2
user = "www-data"
group = "www-data"
tmp_upload_dir = None
errorlog = "/var/log/gunicorn/error.log"
accesslog = "/var/log/gunicorn/access.log"
loglevel = "info"
EOF
# Создаём директорию для логов
sudo mkdir -p /var/log/gunicorn
sudo chown www-data:www-data /var/log/gunicorn
# Запускаем с Gunicorn
gunicorn -c gunicorn_config.py app:app
Настройка Nginx
# Устанавливаем Nginx
sudo apt install nginx -y
# Создаём конфигурацию для нашего приложения
sudo cat > /etc/nginx/sites-available/flask_app << 'EOF'
server {
listen 80;
server_name your-domain.com; # Замени на свой домен
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
location /static {
alias /home/user/flask_app/static; # Путь к статическим файлам
expires 30d;
}
# Логирование
access_log /var/log/nginx/flask_app_access.log;
error_log /var/log/nginx/flask_app_error.log;
}
EOF
# Активируем конфигурацию
sudo ln -s /etc/nginx/sites-available/flask_app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
# Проверяем конфигурацию и перезапускаем Nginx
sudo nginx -t
sudo systemctl reload nginx
Создание systemd-сервиса
# Создаём systemd-сервис
sudo cat > /etc/systemd/system/flask_app.service << 'EOF'
[Unit]
Description=Flask App with Gunicorn
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/home/user/flask_app
Environment="PATH=/home/user/flask_app/venv/bin"
ExecStart=/home/user/flask_app/venv/bin/gunicorn -c gunicorn_config.py app:app
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
# Активируем и запускаем сервис
sudo systemctl daemon-reload
sudo systemctl enable flask_app
sudo systemctl start flask_app
# Проверяем статус
sudo systemctl status flask_app
Практические примеры и кейсы
Пример 1: API для управления системными службами
@app.route('/api/service/<service_name>/<action>', methods=['POST'])
def manage_service(service_name, action):
allowed_services = ['nginx', 'apache2', 'mysql', 'postgresql']
allowed_actions = ['start', 'stop', 'restart', 'status']
if service_name not in allowed_services:
return jsonify({'error': 'Service not allowed'}), 403
if action not in allowed_actions:
return jsonify({'error': 'Action not allowed'}), 400
try:
result = subprocess.run(['systemctl', action, service_name],
capture_output=True, text=True)
if result.returncode == 0:
return jsonify({
'status': 'success',
'service': service_name,
'action': action,
'output': result.stdout
})
else:
return jsonify({
'status': 'error',
'service': service_name,
'action': action,
'error': result.stderr
}), 500
except Exception as e:
return jsonify({'error': str(e)}), 500
Пример 2: Мониторинг логов в реальном времени
from flask import Response
import time
@app.route('/api/logs/<log_file>/tail')
def tail_log(log_file):
allowed_logs = {
'nginx': '/var/log/nginx/access.log',
'auth': '/var/log/auth.log',
'syslog': '/var/log/syslog'
}
if log_file not in allowed_logs:
return jsonify({'error': 'Log file not allowed'}), 403
def generate():
try:
with open(allowed_logs[log_file], 'r') as f:
# Переходим к концу файла
f.seek(0, 2)
while True:
line = f.readline()
if line:
yield f"data: {line}\n\n"
else:
time.sleep(1)
except Exception as e:
yield f"data: ERROR: {str(e)}\n\n"
return Response(generate(), mimetype='text/plain')
Сравнение с альтернативными решениями
Характеристика | Flask | Django | FastAPI | Bottle |
---|---|---|---|---|
Сложность изучения | Низкая | Высокая | Средняя | Очень низкая |
Производительность | Хорошая | Средняя | Отличная | Хорошая |
Размер фреймворка | Микро | Полноценный | Современный | Ультра-мини |
API разработка | Требует расширений | DRF нужен | Из коробки | Ручная настройка |
Экосистема | Большая | Огромная | Растущая | Малая |
Для системных администраторов Flask — оптимальный выбор, так как он:
- Быстро изучается
- Не навязывает архитектуру
- Отлично подходит для небольших инструментов
- Имеет минимальные зависимости
- Легко интегрируется с системными командами
Расширения и интеграции
Полезные расширения для админских задач:
# Устанавливаем полезные расширения
pip install flask-sqlalchemy flask-migrate flask-login flask-wtf celery redis
# Пример интеграции с базой данных
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///monitoring.db'
db = SQLAlchemy(app)
class ServerLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
server_name = db.Column(db.String(100), nullable=False)
log_level = db.Column(db.String(20), nullable=False)
message = db.Column(db.Text, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
# Создание таблиц
with app.app_context():
db.create_all()
@app.route('/api/logs', methods=['POST'])
def add_log():
data = request.get_json()
log = ServerLog(
server_name=data['server_name'],
log_level=data['log_level'],
message=data['message']
)
db.session.add(log)
db.session.commit()
return jsonify({'status': 'success'})
Интеграция с мониторингом
# Интеграция с Prometheus
from prometheus_client import Counter, Histogram, generate_latest
REQUEST_COUNT = Counter('requests_total', 'Total requests', ['method', 'endpoint'])
REQUEST_LATENCY = Histogram('request_duration_seconds', 'Request latency')
@app.before_request
def before_request():
request.start_time = time.time()
@app.after_request
def after_request(response):
REQUEST_COUNT.labels(method=request.method, endpoint=request.endpoint).inc()
REQUEST_LATENCY.observe(time.time() - request.start_time)
return response
@app.route('/metrics')
def metrics():
return Response(generate_latest(), mimetype='text/plain')
Интересные факты и нестандартные применения
1. Flask как инструмент для CI/CD
Многие команды создают простые Flask-приложения для управления деплоями. Например, webhook-эндпоинты для автоматического деплоя при пуше в Git:
@app.route('/webhook/deploy', methods=['POST'])
def deploy_webhook():
# Проверяем подпись от GitHub/GitLab
signature = request.headers.get('X-Hub-Signature-256')
if not verify_signature(request.data, signature):
return jsonify({'error': 'Invalid signature'}), 403
# Запускаем деплой в фоне
subprocess.Popen(['./deploy.sh'])
return jsonify({'status': 'Deploy started'})
2. Микросервисная архитектура
Flask идеально подходит для создания микросервисов. Каждый сервис может быть небольшим Flask-приложением с одной ответственностью.
3. Интеграция с Docker
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "-c", "gunicorn_config.py", "app:app"]
Автоматизация и скрипты
Flask открывает множество возможностей для автоматизации:
- Веб-интерфейсы для скриптов — превращай консольные утилиты в веб-приложения
- API для автоматизации — создавай REST API для управления серверами
- Дашборды мониторинга — визуализируй метрики и логи
- Интеграция с внешними системами — создавай мосты между разными сервисами
Пример автоматизации бэкапов:
@app.route('/api/backup/create', methods=['POST'])
def create_backup():
backup_type = request.json.get('type', 'full')
if backup_type == 'database':
cmd = ['mysqldump', '-u', 'root', '-p', 'mydb']
elif backup_type == 'files':
cmd = ['tar', '-czf', f'/backups/files_{datetime.now().strftime("%Y%m%d_%H%M%S")}.tar.gz', '/var/www']
else:
return jsonify({'error': 'Invalid backup type'}), 400
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
return jsonify({'status': 'success', 'message': 'Backup created'})
else:
return jsonify({'status': 'error', 'message': result.stderr}), 500
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
Безопасность и лучшие практики
Основные принципы безопасности:
# Защита от CSRF и XSS
from flask_wtf.csrf import CSRFProtect
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
csrf = CSRFProtect(app)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
# Конфигурация для продакшена
app.config.update(
SECRET_KEY='your-secret-key', # Используй os.urandom(24)
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
PERMANENT_SESSION_LIFETIME=timedelta(minutes=30)
)
# Аутентификация
@app.before_request
def require_auth():
if request.endpoint and request.endpoint.startswith('api/'):
auth_header = request.headers.get('Authorization')
if not auth_header or not verify_token(auth_header):
return jsonify({'error': 'Unauthorized'}), 401
Мониторинг и логирование
import logging
from logging.handlers import RotatingFileHandler
# Настройка логирования
if not app.debug:
file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
# Структурированное логирование
import json
@app.after_request
def log_request_info(response):
app.logger.info(json.dumps({
'method': request.method,
'url': request.url,
'status_code': response.status_code,
'remote_addr': request.remote_addr,
'user_agent': request.headers.get('User-Agent'),
'response_time': time.time() - request.start_time
}))
return response
Производительность и оптимизация
Кэширование
from flask_caching import Cache
cache = Cache(app, config={'CACHE_TYPE': 'redis'})
@app.route('/api/stats')
@cache.cached(timeout=300) # Кэш на 5 минут
def get_stats():
# Тяжёлые вычисления
return jsonify(expensive_calculation())
# Инвалидация кэша
@app.route('/api/stats/refresh', methods=['POST'])
def refresh_stats():
cache.delete('view//api/stats')
return jsonify({'status': 'cache cleared'})
Оптимизация для высоких нагрузок:
- Используй Redis для кэширования
- Настрой connection pooling для БД
- Используй CDN для статики
- Настрой gzip-сжатие в Nginx
- Мониторь метрики с помощью Prometheus
Заключение и рекомендации
Flask — это отличный выбор для системных администраторов и девопс-инженеров, которые хотят быстро создавать веб-интерфейсы для своих инструментов. Его главные преимущества:
- Простота изучения — можно начать писать полезные приложения уже через несколько часов
- Гибкость — не навязывает архитектуру, позволяет решать задачи по-своему
- Минимализм — включает только необходимое, остальное добавляется по мере необходимости
- Отличная документация — официальная документация очень качественная
Когда стоит использовать Flask:
- Создание API для автоматизации
- Веб-интерфейсы для системных инструментов
- Дашборды мониторинга
- Прототипирование веб-приложений
- Микросервисная архитектура
Когда лучше выбрать что-то другое:
- Большие корпоративные приложения (Django)
- Высокопроизводительные API (FastAPI)
- Простейшие однофайловые решения (Bottle)
Начни с простых задач — создай веб-интерфейс для одного из своих скриптов, API для мониторинга или простую админскую панель. Flask позволит тебе сфокусироваться на логике приложения, а не на изучении сложного фреймворка.
Помни про безопасность, особенно если приложение будет доступно из интернета. Используй HTTPS, аутентификацию, валидацию входных данных и регулярно обновляй зависимости.
Полезные ссылки для дальнейшего изучения:
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.