====== Dockeritzant una aplicació Django ====== En aquest article dockeritzarem una aplicació Django. Cal haver seguit el [[Django|tutorial inicial Django]] o, com a mínim disposar d'un projecte Django amb el complement django-environ per a les variables d'entorn. {{ django-docker.png }} {{tag> #Ciber #CiberMp03 #Ceti #CetiMp03 docker django python }} Referències: * [[Django]] : primer de tot cal seguir aquest article per disposar d'una app Django apta per a ser dockeritzada, en particular amb els ajustos per utilitzar les variables d'entorn (django-environ). * [[Docker]] * [[https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/|Un bon article de com dockeritzar Django amb Nginx i Gunicorn]]. * [[https://programacionymas.com/blog/docker-diferencia-entrypoint-cmd|Utilitzant ENTRYPOINT i CMD en Dockerfile]] \\ ===== Dockeritzant amb el servidor de desenvolupament ===== 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 [[https://pypi.org/project/mysqlclient/|llibreria mysqlclient]]. He aconseguit que funcionés canviant ''requirements.txt'' a una versió anterior (Juny 2023): mysqlclient==2.1.1 \\ ===== Exercicis ===== Connecta't al contenidor de l'app Django i: - Fes un //migrate// per tal de crear les taules de la BD. - Crea un superusuari i loga't al panell d'administració ''/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: * A l'afegir la MySQL la ''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 * Afegiu un arxiu ''.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/ \\ ===== Utilitzant uWSGI + Ngnix ===== 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ó. {{:django:diagrama_django_produccio.png?direct}} 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. [[https://python.plainenglish.io/dockerizing-django-rest-apis-with-uwsgi-and-nginx-cabffc153c78|Aquest tutorial segueix la filosofia "un servei per contenidor"]]. En canvi, [[https://gabimelo.medium.com/developing-a-flask-api-in-a-docker-container-with-uwsgi-and-nginx-e089e43ed90e|aquest altre tutorial per a Flask integra els 2 processos (uWSGI + Nginx) en un sol contenidor]]. Per tal de tenir diversos serveis utilitza [[http://supervisord.org/|Supervisord]], una utilíssima eina que a mode de //watchdog// vigila que no s'aturi cap dels dos processos. ==== Dockeritzant uWSGI + Nginx en un sol contenidor ==== 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.