running celery tasks and celery beat in ECS with Django - django

I'm using ECS for the first time. I have dockerized my Django 2.2 application and using ECS and uwsgi to run the Django application in production.
While in the development environment, I had to run three commands to run Django server, celery and celery beat
python manage.py runserver
celery -A qcg worker -l info
celery beat -A qcg worker -l info
Where qcg is my application.
My Dockerfile has following uwsgi configuration
EXPOSE 8000
ENV UWSGI_WSGI_FILE=qcg/wsgi.py
ENV UWSGI_HTTP=:8000 UWSGI_MASTER=1 UWSGI_HTTP_AUTO_CHUNKED=1 UWSGI_HTTP_KEEPALIVE=1 UWSGI_LAZY_APPS=1 UWSGI_WSGI_ENV_BEHAVIOR=holy
ENV UWSGI_WORKERS=2 UWSGI_THREADS=4
ENV UWSGI_STATIC_MAP="/static/=/static_cdn/static_root/" UWSGI_STATIC_EXPIRES_URI="/static/.*\.[a-f0-9]{12,}\.(css|js|png|jpg|jpeg|gif|ico|woff|ttf|otf|svg|scss|map|txt) 315360000"
USER ${APP_USER}:${APP_USER}
ENTRYPOINT ["/app/scripts/docker/entrypoint.sh"]
The entrypoint.sh file has
exec "$#"
I have created the ECS task definition and in the container's command input, I have
uwsgi --show-config
This starts the uwsgi server.
Now I'm running 1 EC2 instance in the cluster and running one service with two instances of the task definition.
I couldn't figure out how to run the celery task and celery beat in my application.
Do I need to create separate tasks for running celery and celery-beat?

Yes, you need to run separate ECS tasks (or separate ECS services) for celery beat and celery worker. Celery Beat will send the Celery tasks to the Celery worker.
I use separate Dockerfiles for Celery, Celery beat, and Django.
Worker Dockerfile something like this:
FROM python:3.8
ENV PYTHONUNBUFFERED 1
ADD requirements.txt /requirements.txt
RUN pip install -r /requirements.txt
ADD . /src
WORKDIR /src
CMD ["celery", "-A", "<appname>", "worker"]
and Beat Dockerfile:
FROM python:3.8
ENV PYTHONUNBUFFERED 1
ADD requirements.txt /requirements.txt
RUN pip install -r /requirements.txt
ADD . /src
WORKDIR /src
CMD ["celery", "-A", "<appname>", "beat"]

Related

Elastic Beanstalk silently fails for long processes

I have a Plotly Dash application in a Docker container deployed with Elastic Beanstalk. Everything looks and runs fine, except when I run a process that takes a long time to complete. The longer processes will run, but then when a graph should be populated it does not return any graph at all. I can see in the logs that the operation is running, but the graph is not populated unless the process is shorter (< 45s approx).
I am using Amazon Linux 2 Docker + classic load balancer + nginx.
Dockerfile:
FROM python:3.9
ENV DASH_DEBUG_MODE False
COPY . /app
WORKDIR /app
RUN set -ex && \
pip install -r requirements.txt
EXPOSE 8050
CMD gunicorn -w 4 --timeout 500 -b 0.0.0.0:8050 application:server
I've tried with CMD ["python", "application.py"] as well.
I've tried using .ebextensions and .platform to modify options.config and nginx.conf but neither have worked.
Elastic Beanstalk also uses gunicorn which overrides the gunicorn in the Dockerfile.
You have to add a Procfile in the root of your app directory.
web: gunicorn --bind :8000 --workers 3 --threads 2 --timeout 500 project.wsgi:application

Docker run image_celery not able to detect redis

I have a django application i want to run redis and celery using docker run command
after I build images using docker-compose file
I run two commands on different windows powershell
docker run -it -p 6379:6379 redis
docker run -it image_celery
my celery powershell is not able to detect redis
[2020-02-08 13:08:44,686: ERROR/MainProcess] consumer: Cannot connect to redis://redis:6379/1: Error -2 connecting to redis:6379. Name or service not known..
Trying again in 2.00 seconds...
version: '3'
services:
the-redis:
image: redis:3.2.7-alpine
ports:
- "6379:6379"
volumes:
- ../data/redis:/data
celery_5:
build:
context: ./mltrons_backend
dockerfile: Dockerfile_celery
volumes:
- ./mltrons_backend:/code
- /tmp:/code/static
depends_on:
- the-redis
deploy:
replicas: 4
resources:
limits:
memory: 25g
restart_policy:
condition: on-failure
volumes:
db_data:
external: true
Dockerfile_celery
FROM python:3.6
ENV PYTHONUNBUFFERED 1
# Install Java
RUN apt-get -y update && \
apt install -y openjdk-11-jdk && \
apt-get install -y ant && \
apt-get clean && \
rm -rf /var/lib/apt/lists/ && \
rm -rf /var/cache/oracle-jdk11-installer;
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64/
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code
ENV REDIS_HOST=redis://the-redis
ENV REDIS_PORT=6379
RUN pip install --upgrade 'sentry-sdk==0.7.10'
ENTRYPOINT celery -A mlbot_webservices worker -c 10 -l info
EXPOSE 8102
celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mlbot_webservices.settings')
app = Celery('mltrons_training')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
#app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
settings.py
CELERY_BROKER_URL = 'redis://the-redis:6379/'
CELERY_RESULT_BACKEND = 'redis://the-redis:6379/'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
It is expected because when you start container as you do (docker run IMAGE), the containers use the default bridge network of Docker.
You can check it by inspecting that network : docker network inspect bridge.
The default bridge doesn't accept network resolution of the containers by container name as you do (redis).
Besides the default name of a container is not the image name but a generated name by docker.
That's why you get that error at runtime :
Cannot connect to redis://redis:6379/1
Note that you can still reference containers belonging to the default bridge by their ip addresses, but that is generally undesirable because that hard code them from the client side.
That works with Docker compose because :
By default Compose sets up a single network for your app. Each
container for a service joins the default network and is both
reachable by other containers on that network, and discoverable by
them at a hostname identical to the container name.
To be able to communicate by container name with docker run, you need :
- to add these containers in the same network but not the default one provided by Docker
- to give an explicit name to the container that you want to reference (while doing it for both container is good to monitor/manager it more simply) by the other.
For example create a user-defined bridge network and add the containers to that when you start them :
docker network create -d bridge my-bridge-network
docker run -it -p 6379:6379 --network=my-bridge-network --name=redis redis
docker run -it --network=my-bridge-network --name=celery image_celery

Deploy Django on Heroku with Docker heroku.yml

I'm using Docker locally for Django development and trying to use Heroku to deploy with Docker. But I'm getting complains about "no web processes running" aka no Dynos spun up. So missing this config somehow but find no mention of it on Heroku or the few tutorials out there.
Dockerfile:
FROM python:3.7-slim
# Set environment varibles
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /code
# Install dependencies
COPY Pipfile Pipfile.lock /code/
RUN pip install pipenv && pipenv install --system
# Copy project
COPY . /code/
heroku.yml
setup:
addons:
- plan: heroku-postgresql
build:
docker:
web: Dockerfile
run:
web: python /code/manage.py runserver 0.0.0.0:$PORT
I suspect the issue is in the run section of heroku.yml but pretty stuck.
So I figured it out. Somehow when running heroku stack:set container instead the heroku-18 stack was used, which also automatically runs collectstatic. So that was the issue.

Django + docker + periodic commands

What's the best practices for running periodic/scheduled tasks ( like manage.py custom_command ) when running Django with docker (docker-compose) ?
f.e. the most common case - ./manage.py clearsessions
Django recommends to run it with cronjobs...
But Docker does not recommend adding more then one running service to single container...
I guess I can create a docker-compose service from the same image for each command that i need to run - and the command should run infinite loop with a needed sleeps, but that seems overkill doing that for every command that need to be scheduled
What's your advice ?
The way that worked for me
in my django project I have a crontab file like this:
0 0 * * * root python manage.py clearsessions > /proc/1/fd/1 2>/proc/1/fd/2
Installed/configured cron inside my Dockerfile
RUN apt-get update && apt-get -y install cron
ADD crontab /etc/cron.d/crontab
RUN chmod 0644 /etc/cron.d/crontab
and in docker-compose.yml add a new service that will build same image as django project but will run cron -f as CMD
version: '3'
services:
web:
build: ./myprojectname
ports:
- "8000:8000"
#...
cronjobs:
build: ./myprojectname
command: ["cron", "-f"]
I ended up using this project - Ofelia
https://github.com/mcuadros/ofelia
so you just add it to your docker-compose
and have config like:
[job-exec "task name"]
schedule = #daily
container = myprojectname_1
command = python ./manage.py clearsessions
Create one docker image with your Django application.
You can use it to run your Django app (the web interface), and at the same time, using cron schedule your period tasks by passing in the command to the docker executable, like this:
docker exec --rm your_container python manage.py clearsessions
The --rm will make sure that docker removes the container once it finishes; otherwise you will quickly have containers stopped that are of no use.
You can also pass in any extra arguments, for example using -e to modify the environment:
docker exec --rm -e DJANGO_DEBUG=True -e DJANGO_SETTINGS_MODULE=production \
python manage.py clearsessions

Django on Heroku - how can I get a celery worker to run correctly?

I am trying to deploy the simplest possible "hello world" celery configuration on heroku for my Django app. My Procfile is as follows:
web: gunicorn myapp.wsgi
worker: celery -A myapp worker -l info -B -b amqp://XXXXX:XXXXX#red-thistle-3.bigwig.lshift.net:PPPP/XXXXX
This is the RABBITMQ_BIGWIG_RX_URL that I'm giving to the celery worker. I have the corresponding RABBITMQ_BIGWIG_TX_URL in my settings file as the BROKER_URL.
If I use these broker URLs in my local dev environment, everything works fine and I can actually use the Heroku RabbitMQ system. However, when I deploy my app to Heroku it isn't working.
This Procfile seems to work (although Celery is giving me memory leak issues).
web: gunicorn my_app.wsgi
celery: celery worker -A my_app -l info --beat -b amqp://XXXXXXXX:XXXXXXXXXXXXXXXXXXXX#red-thistle-3.bigwig.lshift.net:PPPP/XXXXXXXXX