Flask Project Deployment
This article describes how to integrate Celery (async task queue) into a Flask project and how to manage Celery workers and scheduled tasks in production using Supervisor (a process control system).
Integrating Celery into Flask
Project Structure
The original standalone Celery project (mycelery/) is merged into the Flask project:
mycelery/
├── config.py # Celery config — copy settings to Flask's Config class (uppercase)
├── main.py # Celery initialization — move to application/__init__.py
├── relation/
│ └── tasks.py # Task module → apps.relation.tasks
└── sms/
└── tasks.py # Task module → apps.home.tasksFlask Settings (Celery section)
Add these settings to your Flask Config class:
# Celery configuration
BROKER_URL = 'redis://127.0.0.1:6379/15' # task queue address
CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/14" # result store address
CELERY_ACCEPT_CONTENT = ['json'] # accepted content types
CELERYD_CONCURRENCY = 20 # number of concurrent workers
CELERYD_MAX_TASKS_PER_CHILD = 500 # max tasks per worker before respawn (prevents memory leaks)
CELERYD_TASK_TIME_LIMIT = 10 * 60 # single task timeout in seconds
CELERY_DISABLE_RATE_LIMITS = True # prevent task deadlocks
CELERYD_FORCE_EXECV = True # use fork+exec to prevent deadlocks
# Celery Beat (periodic tasks)
CELERYBEAT_SCHEDULE = {
"check_order_outtime": {
"task": "check_mongo_status",
"schedule": crontab(), # crontab schedule expression
}
}Application Factory (application/__init__.py)
import os
import sys
import eventlet # required for Celery with gevent/eventlet concurrency
eventlet.monkey_patch() # patch standard library for async compatibility
from celery import Celery
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from flask_cors import CORS
from flask_socketio import SocketIO
from flask_pymongo import PyMongo
from application.utils import init_blueprint
from application.utils.config import load_config
manager = Manager()
db = SQLAlchemy()
redis = FlaskRedis()
session_store = Session()
migrate = Migrate()
cors = CORS()
socketio = SocketIO()
mongo = PyMongo() # used by the Celery task below (application.mongo)
celery = Celery()
def init_app(config_path):
app = Flask(import_name=__name__)
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Config = load_config(config_path)
app.config.from_object(Config)
# Connect Celery to Flask config
celery.main = app.name
celery.app = app
celery.conf.update(app.config)
celery.autodiscover_tasks(app.config.get("INSTALLED_APPS"))
# Initialize extensions
db.init_app(app)
redis.init_app(app)
mongo.init_app(app)
session_store.init_app(app)
migrate.init_app(app, db)
manager.add_command('db', MigrateCommand)
# Register blueprints
init_blueprint(app)
manager.app = app
cors.init_app(app, resources={r"/api/*": {"origins": "*"}})
socketio.init_app(app, cors_allowed_origins=app.config["CORS_ALLOWED_ORIGINS"],
async_mode=app.config["ASYNC_MODE"])
return managerCelery Task Definition
# apps/home/tasks.py
import datetime
from application import mongo, celery
@celery.task(name="check_mongo_status", bind=True)
def check_mongo_status(*args, **kwargs):
res = mongo.db.user_heartbeat.find()
current_time = datetime.datetime.now()
expire_time = 60
ttye = current_time - datetime.timedelta(seconds=expire_time)
for i in res:
if i.get("datetime") < ttye.strftime('%Y-%m-%d %H:%M:%S'):
mongo.db.user_heartbeat.update_one(
{"_id": i.get("_id")},
{"$set": {"status": 0}}
)Entry Point (main.py)
from application import init_app, celery
manager = init_app("application.settings.dev")
app = manager.app
@app.route('/')
def index():
return "ok"
if __name__ == '__main__':
manager.run()Managing Processes with Supervisor
Supervisor is a Python-based process control system that converts command-line processes into background daemons, monitors their status, and auto-restarts them on failure.
pip install supervisorInitialize Supervisor Configuration
# Create a scripts/ directory in the project root
mkdir -p scripts && cd scripts
# Generate the default supervisord.conf
echo_supervisord_conf > supervisord.confEdit supervisord.conf — key changes:
- Uncomment
[unix_http_server]and[inet_http_server]sections - Set
[include] files = *.iniat the bottom so Supervisor auto-loads all.inifiles in the same directory
Celery Worker Configuration (sd_wan_celery_worker.ini)
[program:sd_wan_celery_worker]
command=/root/.virtualenvs/sd-wan/bin/celery -A main.celery worker -P eventlet -l info -n worker1
directory=/root/projects/sd_wan_demo
enviroment=PATH="/root/.virtualenvs/sd-wan/bin/"
stdout_logfile=/root/projects/sd_wan_demo/logs/celery_worker_info.log
stderr_logfile=/root/projects/sd_wan_demo/logs/celery_worker_error.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=60Celery Beat Scheduler (sd_wan_celery_beat.ini)
[program:sd_wan_celery_beat]
command=/root/.virtualenvs/sd-wan/bin/celery -A main.celery beat -l info
directory=/root/projects/sd_wan_demo
enviroment=PATH="/root/.virtualenvs/sd-wan/bin/"
stdout_logfile=/root/projects/sd_wan_demo/logs/celery_beat_info.log
stderr_logfile=/root/projects/sd_wan_demo/logs/celery_beat_error.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=60Celery Flower Monitor (sd_wan_celery_flower.ini)
pip install flower # install the monitoring dashboard first[program:sd_wan_celery_flower]
command=/root/.virtualenvs/sd-wan/bin/celery --broker=redis://127.0.0.1:6379/13 flower --address=0.0.0.0 --port=5555
directory=/root/projects/sd_wan_demo
enviroment=PATH="/root/.virtualenvs/sd-wan/bin/"
stdout_logfile=/root/projects/sd_wan_demo/logs/celery_flower_info.log
stderr_logfile=/root/projects/sd_wan_demo/logs/celery_flower_error.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=60
priority=990Starting Supervisor
# Start supervisord from the project root
cd /root/projects/sd_wan_demo
supervisord -c scripts/supervisord.confCommon Supervisor Commands
supervisorctl reload # reload config and restart all processes
supervisorctl stop <program> # stop a specific process
supervisorctl start <program> # start a specific process
supervisorctl restart <program> # restart a specific process
supervisorctl stop all # stop all processes
supervisorctl update # apply config changes to changed/new programs only
# Check if supervisord is running
ps aux | grep supervisordRegister Supervisor as a Systemd Service (Boot Autostart)
Create scripts/supervisor.service:
[Unit]
Description=supervisor
After=network.target
[Service]
Type=forking
ExecStart=/root/.virtualenvs/sd-wan/bin/supervisord -c /root/projects/sd_wan_demo/scripts/supervisord.conf
ExecStop=/root/.virtualenvs/sd-wan/bin/supervisorctl $OPTIONS shutdown
ExecReload=/root/.virtualenvs/sd-wan/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.targetRegister and enable:
chmod 766 supervisor.service
# Ubuntu
sudo cp supervisor.service /lib/systemd/system/
# CentOS
cp supervisor.service /usr/lib/systemd/system/
systemctl enable supervisor.service # enable autostart
systemctl is-enabled supervisor.service # verify
systemctl status supervisor.service # check statusLast updated on