En aquest article dockeritzarem una aplicació Django. Cal haver seguit el tutorial inicial Django o, com a mínim disposar d'un projecte Django amb el complement django-environ per a les variables d'entorn.
Referències:
El Dockerfile permet crear la imatge del contenidor.
# We Use an official Python runtime as a parent image FROM python:3.11.7-bullseye # install db libs RUN apt-get update RUN apt-get install -y default-mysql-client libmariadb-dev RUN apt-get install -y libmariadb-dev-compat gcc gdal-bin libjpeg-dev # install app libs COPY requirements.txt requirements.txt RUN pip3 install --no-cache-dir -r requirements.txt # Mounts the application code to the image COPY . code # establish workdir WORKDIR /code EXPOSE 8000 # runs the development server ENTRYPOINT ["python3","manage.py"] CMD ["runserver","0.0.0.0:8000"]
Fixeu-vos en què la darrera instrucció equival a python3 manage.py runserver 0.0.0.0:8000
, és a dir, que estem fent servir el servidor de desenvolupament, i no un servidor d'aplicacions específic per producció com uWSGI o Gunicorn.
El docker-compose ens facilita la posada en producció juntament amb la base de dades:
version: '3.1' services: django_app: build: . restart: always ports: - 8000:8000 environment: - DATABASE_URL=sqlite:///db.sqlite3 - DEBUG=False - SECRET_KEY=asecretkeyblabla volumes: - ./db.sqlite3:/code/db.sqlite3
Per posar-ho en marxa primer cal crear un arxiu on persistir la BD local:
$ touch db.sqlite3 $ docker-compose up -d --build
Troubleshooting
He tingut alguns problemes amb la llibreria mysqlclient. He aconseguit que funcionés canviant requirements.txt
a una versió anterior (Juny 2023):
mysqlclient==2.1.1
Connecta't al contenidor de l'app Django i:
/admin
.
Modifica el docker-compose.yml
per tal que s'utilitzi una MySQL enlloc de la SQLite de test.
Assegura't que la BD MySQL sigui persistent.
Fes el migrate i crea el superusuari tal i com hem fet abans.
Alguns tips:
django_app
depèn de la BD per poder funcionar. Per assegurar que primer arrencarà la DB i després la app, cal posar:depends_on: - db
.dockerignore
on li direm quins arxius no cal posar dins el Docker. Si no ho fem, la BD es copiarà dins del Docker de la django_app la 2a vegada que l'executem:db/
El «invento» que hem fet fins ara funciona, però no és una bona pràctica utilitzar el servidor de desenvolupament de Django (el ./manage.py runserver
). Ara configurarem el Dockerfile
per aconseguir que s'utilitzi uWSGI + Nginx , tal i com cal per a un servidor de producció.
Recordem que un dels principis bàsics de Docker és el de «un servei per contenidor». Com que necessitem 2 serveis, seguint aquesta filosofia necessitarem un docker-compose.yml
amb 2 serveis + la BD. Aquest tutorial segueix la filosofia "un servei per contenidor".
En canvi, aquest altre tutorial per a Flask integra els 2 processos (uWSGI + Nginx) en un sol contenidor. Per tal de tenir diversos serveis utilitza Supervisord, una utilíssima eina que a mode de watchdog vigila que no s'aturi cap dels dos processos.
Farem una mescla dels dos tutorials per aconseguir el nostre Django en un sol contenidor.
[supervisord] # per a dockeritzar necessitem supervisord en foreground nodaemon=true [program:uwsgi] command=/usr/local/bin/uwsgi --ini /etc/uwsgi/uwsgi.ini --die-on-term [program:nginx] command=/usr/sbin/nginx -g 'daemon off;'
[uwsgi] # TODO: ull, revisa si el teu projecte es diu 'mysite' o una altra cosa module = mysite.wsgi callable = application uid = nginx gid = nginx socket = /tmp/uwsgi.sock chown-socket = www-data:www-data chmod-socket = 664 cheaper = 1 processes = %(%k + 1) # clear environment on exit vacuum=true
uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;
server { location / { try_files $uri @yourapplication; } location @yourapplication { include uwsgi_params; uwsgi_pass unix:///tmp/uwsgi.sock; } # TODO: aquí cal afegir alguna cosa perquè funcionin el arxius estàtics }
# We Use an official Python runtime as a parent image FROM python:3.11.7-bullseye # install db libs RUN apt-get update RUN apt-get install -y default-mysql-client libmariadb-dev RUN apt-get install -y libmariadb-dev-compat gcc gdal-bin libjpeg-dev # install nginx and supervisor RUN apt-get install -y --no-install-recommends \ nginx supervisor RUN useradd --no-create-home nginx # install uwsgi RUN pip3 install uwsgi # adjustments RUN rm /etc/nginx/sites-enabled/default RUN rm -r /root/.cache # production conf files COPY deploy/django-site.conf /etc/nginx/conf.d/ COPY deploy/uwsgi.ini /etc/uwsgi/ COPY deploy/supervisord.conf /etc/ # install app libs COPY requirements.txt requirements.txt RUN pip3 install --no-cache-dir -r requirements.txt # Mounts the application code to the image COPY . code # establish workdir WORKDIR /code EXPOSE 80 # runs the development server CMD ["/usr/bin/supervisord"]
Perquè funcioni bé i veiem la web al port 8000 cal que modifiqueu el docker-compose.yml
perquè connecti el port 8000 de la màquina host amb el port 80 del contenidor del Nginx.
Canvia la configuració per a utilitzar el servidor d'aplicacions uWSGI + Nginx enlloc del servidor de desenvolupament.
Ull que alguns arxius van a una nova carpeta /deploy
.
Comprova que tornem a tenir el problema dels arxius estàtics accedint al /admin i refrescant fins que vegis que no es veuen els CSS.
Com creus que seria la millor manera de solventar el problema dels arxius estàtics? Implementa-la.