de Ali Alavi

Django în sălbăticie: sfaturi pentru supraviețuirea desfășurării

Django in salbaticie sfaturi pentru supravietuirea desfasurarii
Fotografie de Thanh Tran pe Unsplash

Înainte de a implementa aplicația dvs. web Django în lumea reală, trebuie să vă asigurați că proiectul dvs. este pregătit pentru producție. Mai bine să începeți să le implementați mai devreme. Vă economisește pe dvs. și echipa dvs. mult timp și dureri de cap. Iată câteva puncte pe care le-am învățat pe parcurs:

  1. Utilizați pipenv (sau requirements.txt + venv). Confirmați Pipefile și Pipefile.lock (sau requirements.txt). Numiți-vă venv.
  2. Aveți un script de pornire rapidă.
  3. Scrieți teste. Folosește Cadrul de testare Django și Ipoteză.
  4. Utilizare mediu și direnv pentru a vă gestiona setările și a încărca automat variabilele de mediu.
  5. Asigurați-vă că toți dezvoltatorii își comit migrațiile. Migrații de squash din când în când. Resetați-le dacă este necesar. Arhitectați-vă proiectul pentru migrații mai ușoare. Citiți despre migrații.
  6. Folosiți integrarea continuă. Protejați-vă ramura principală.
  7. Treci prin Lista oficială de verificare a implementării Django.
  8. Nu gestionați propriul server, dar dacă este necesar, utilizați o structură de directoare adecvată și utilizați Supervisord, Gunicorn și NGINX.

Această listă a crescut organic pe măsură ce lansam prima noastră aplicație Django și nu este exhaustivă. Dar cred că acestea sunt câteva dintre cele mai importante puncte de care trebuie să fii conștient.

Citiți împreună pentru o discuție despre fiecare dintre puncte.

Gestionați-vă dependențele și mediile virtuale în mod corect

Tu și echipa dvs. ar trebui să conveniți asupra unui mod de a vă gestiona dependențele și mediile virtuale. Recomand fie utilizarea pipenv, care este noul mod de a vă gestiona atât mediile virtuale, cât și dependențele, sau de a folosi vechea abordare bună de a crea un venv și de a vă urmări dependențele cu un requirements.txt fişier.

ad-banner

Folosind requirements.txt abordarea este predispusă la erori umane, deoarece dezvoltatorii tind să uite de actualizarea listei de pachete. Nu este o problemă cu pipenv, deoarece se actualizează automat Pipefile. Dezavantajul pipenv este că nu a existat suficient de mult timp. Chiar dacă este recomandat oficial de către Python Software Foundation, s-ar putea să aveți probleme cu executarea acestuia pe unele mașini. Personal, folosesc în continuare vechea abordare, dar o voi folosi pipenv pentru următorul meu proiect.

Folosind venv și requirements.txt

Dacă utilizați Python ≥ 3.6 (ar trebui să fiți), puteți crea unul cu python -m venv ENV . Asigurați-vă că vă denumiți mediul virtual (în loc să utilizați . ). Uneori trebuie să ștergeți mediul virtual și să îl creați din nou. Îl face mai ușor. De asemenea, ar trebui să adăugați ENV directorul dumneavoastră.gitignore fișier (prefer fișierul ENV nume în loc de venv, .env, … deoarece se remarcă când eu eu sunt folderul proiectului).

Pentru a gestiona dependențele, fiecare dezvoltator rulează pip freeze > requirements.txt ori de câte ori instalează un pachet nou și îl adaugă și îl angajează la repo. Vor folosi pip install -r requirements.txt ori de câte ori se retrag din depozitul la distanță.

Folosind pipenv

Dacă utilizați pipenv, trebuie doar să adăugați Pipfile și Pipfile.lock la repo.

Aveți un script de pornire rapidă

Acest lucru vă ajută să vă asigurați că dezvoltatorii dvs. petrec cât mai puțin timp lucrând la lucruri care nu au legătură directă cu munca lor.

Acest lucru nu numai că economisește timp și bani, dar se asigură și că toate lucrează pe medii similare (aceleași versiuni de Python și pip, de exemplu).

Deci, încercați să automatizați cât mai multe sarcini de configurare posibil.

Mai mult, a avea un singur script de construire a pasului este foarte mult ceea ce Al doilea pas al lui Joel Test este despre.

Iată un mic script pe care îl folosesc, care salvează dezvoltatorii mei câteva accidente cheie:

#!/bin/bash
python3.6 -m venv ENV
source ENV/bin/activate
pip install --upgrade pip
pip install -r requirements.txt 
source .envrc
python ./manage.py migrate
python ./manage.py loaddata example-django/fixtures/quickstart.json
python ./manage.py runserver

Scrieți teste

Toată lumea știe că testele de scriere sunt o practică bună. Dar mulți trec cu vederea, de dragul dezvoltării mai rapide, cred ei. NU. Testele sunt o necesitate absolută atunci când vine vorba de scrierea software-ului utilizat în producție, mai ales atunci când lucrați în echipă. Singurul mod în care poți ști că ultima actualizare nu a spart ceva este să ai teste bine scrise. De asemenea, aveți absolut nevoie de teste pentru integrarea și livrarea continuă a produsului dvs.

Django are un decent cadru de testare. De asemenea, puteți utiliza cadre de testare bazate pe proprietăți, cum ar fi Ipoteză, care vă ajută să scrieți teste mai scurte și riguroase din punct de vedere matematic pentru codul dvs. Pentru multe cazuri, scrierea testelor bazate pe proprietăți este mai rapidă. În practică, s-ar putea să ajungeți la utilizarea ambelor cadre pentru a scrie teste cuprinzătoare, ușor de citit și de scris.

Utilizați variabile de mediu pentru setări

Ta settings.py fișier este locul în care stocați toate setările importante ale proiectului: adresa URL a bazei de date, căile către folderele media și statice și așa mai departe. Acestea vor avea valori diferite pe mașina de dezvoltare și pe serverul de producție. Cel mai bun mod de a aborda acest lucru este de a utiliza variabile de mediu. Primul pas este să vă actualizați fișierul settings.py pentru a citi din variabilele de mediu, folosind mediu:

import environ
import os
root = environ.Path(__file__) - 2 # two folders back (/a/b/ - 2 = /)
env = environ.Env(DEBUG=(bool, False),) # set default values and casting
GOOGLE_ANALYTICS_ID=env('GOOGLE_ANALYTICS_ID')

SITE_DOMAIN = env('SITE_DOMAIN') 
SITE_ROOT = root()

DEBUG = env('DEBUG') # False if not in os.environ

DATABASES = {
    'default': env.db(), # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
}

public_root = root.path('./public/')

MEDIA_ROOT = public_root('media')
MEDIA_URL = '/media/'

STATIC_ROOT = public_root('static')
STATIC_URL = '/static/'

AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY')

..

Pentru a evita încărcarea manuală a envvars-urilor, configurați direnv pe mașinile de dezvoltare și stocați invvars într-un .envrc în directorul proiectului. Acum, ori de câte ori cd în folderul dvs. de proiecte, variabilele de mediu sunt încărcate automat. Adăuga .envrc în depozitul dvs. (dacă toți dezvoltatorii folosesc aceleași setări) și asigurați-vă că rulați direnv allow ori de câte ori există o schimbare în .envrc fişier.

Nu folosiți direnv pe serverul dvs. de producție. În schimb, creați un fișier numit .server.envrc, adăugați-l la .gitignore, și pune setările de producție acolo. Acum, creați un script,runinenv.sh, pentru a sursa automat variabilele de mediu din .server.envrc, activați mediul virtual și rulați comanda furnizată. Veți vedea cum este utilizat în secțiunea următoare. Iată cum runinenv.sh ar trebui sa arate (link către GitHub).

#!/bin/bash
WORKING_DIR=/home/myuser/example.com/example-django
cd ${WORKING_DIR}
source .server.envrc
source ENV/bin/activate
exec $@

Gestionează-ți corect migrările

Migrațiile Django sunt grozave, dar lucrul cu ele, mai ales într-o echipă, este departe de a fi fără probleme.

În primul rând, trebuie să vă asigurați că toți dezvoltatorii comit fișierele de migrare. Da, s-ar putea să (veți) ajunge să aveți conflicte, mai ales dacă lucrați cu o echipă mare, dar este mai bine decât să aveți o schemă inconsecventă.

În general, gestionarea migrațiilor nu este atât de ușoară. Trebuie să știți ce faceți și trebuie să urmați câteva dintre cele mai bune practici pentru a asigura un flux de lucru lin.

Un lucru pe care l-am crezut este că, de obicei, vă ajută dacă spălați migrațiile din când în când (de exemplu, săptămânal). Acest lucru ajută la reducerea numărului de fișiere și a dimensiunii graficului de dependență, ceea ce, la rândul său, duce la un timp de construire mai rapid și, de obicei, la mai puține (sau mai ușor de gestionat) conflicte.

Uneori este mai ușor să vă resetați migrațiile și să le faceți din nou, iar uneori trebuie să remediați manual migrările conflictuale sau să le îmbinați. În general, tratarea migrațiilor este un subiect care merită propria sa postare și există câteva citiri bune despre acest subiect:

Mai mult, modul în care ajung migrațiile proiectului dvs. depinde de arhitectura proiectului, de modelele dvs. și așa mai departe. Acest lucru este important mai ales atunci când baza de cod crește, când aveți mai multe aplicații sau când aveți relații complicate între modele.

Vă recomand cu drag să citiți această postare minunată despre scalarea aplicațiilor Django, care acoperă tema destul de bine. De asemenea, are un script de migrare mic și util.

Utilizați integrarea continuă

Ideea din spatele CI este simplă: executați teste de îndată ce noul cod este împins.

Folosiți o soluție care se integrează bine cu platforma dvs. de control al versiunilor. Am ajuns să folosesc Cerc CI. CI este util în special atunci când lucrați cu mai mulți dezvoltatori, deoarece puteți fi siguri că codul lor trece toate testele înainte de a trimite o cerere de extragere. Din nou, după cum puteți vedea, este foarte important să aveți teste bine acoperite. Mai mult, asigurați-vă că filiala dvs. principală este protejată.

Lista de verificare a implementării

Site-ul oficial Django oferă un listă de verificare a implementării la îndemână. Vă ajută să vă asigurați securitatea și performanța proiectului. Asigurați-vă că respectați aceste linii directoare.

Dacă trebuie să vă gestionați propriul server …

Există multe motive întemeiate pentru a nu vă gestiona propriul server. Docker vă oferă mai multă portabilitate și securitate, iar arhitecturile fără server, cum ar fi AWS Lambda, vă pot oferi și mai multe avantaje pentru mai puțini bani.

Dar există cazuri în care aveți nevoie de mai mult control asupra serverului dvs. (dacă aveți nevoie de mai multă flexibilitate, dacă aveți servicii nu pot funcționa cu aplicații containerizate – cum ar fi agenții de monitorizare a securității și așa mai departe).

Utilizați o structură de director adecvată

Primul lucru de făcut este să utilizați o structură de folder adecvată. Dacă tot ce vrei să servești pe serverul tău este aplicația Django, poți clona repozitoriul tău și îl poți folosi ca director principal. Dar acest lucru este rareori cazul: de obicei, trebuie să aveți și câteva pagini statice (pagina de pornire, contacte, …). Acestea ar trebui să fie separate de baza codului dvs. Django.

O modalitate bună de a face acest lucru este crearea unui depozit părinte, care are diferite părți ale proiectului dvs. ca submodule. Dezvoltatorii dvs. Django lucrează la un depozit django, designerii dvs. lucrează la depozitul de pagini de pornire, … și îi integrați pe toți într-un depozit:

example.com/
   example-django/
   homepage/

Utilizați Supervisord, NGINX și Gunicorn

Sigur, manage runserver funcționează, dar numai pentru un test rapid. Pentru orice lucru grav, trebuie să utilizați un server de aplicații adecvat. Gunicorn este calea de urmat.

Rețineți că orice server de aplicații este un proces de lungă durată. Și trebuie să vă asigurați că funcționează în continuare, este repornit automat după o eroare a serverului, înregistrează corect erorile și așa mai departe. În acest scop, folosim supervisord.

Supervisord are nevoie de un fișier de configurare, în care să spunem cum vrem să ruleze procesele noastre. Și nu se limitează la serverul nostru de aplicații. Dacă avem alte procese de lungă durată (de exemplu, țelina), ar trebui să le definim în /etc/supervisor/supervisord.conf. Iată un exemplu (de asemenea, pe GitHub):

[supervisord]
nodaemon=true
logfile=supervisord.log

[supervisorctl]

[inet_http_server]
port = 127.0.0.1:9001

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[program:web-1]
command=/home/myuser/example.com/example-django/runinenv.sh gunicorn example.wsgi --workers 3 --reload --log-level debug --log-file gunicorn.log --bind=0.0.0.0:8000
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/web-1.log
stderr_logfile=/var/log/example-django/web-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django

[program:celery-1]
command=/home/myuser/example.com/example-django/runinenv.sh celery worker --app=example --loglevel=info
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/celery-1.log
stderr_logfile=/var/log/example-django/celery-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django

[program:beat-1]
command=/home/myuser/example.com/example-django/runinenv.sh celery beat --app=example --loglevel=info
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/beat-1.log
stderr_logfile=/var/log/example-django/beat-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django

[group:example-django]
programs=web-1,celery-1,beat-1

Rețineți cum folosim runinenv.sh aici (liniile 14, 24 și 34). De asemenea, acordați atenție liniei 14, unde vă spunem gunicorn să trimită 3 muncitori. Acest număr depinde de numărul de nuclee pe care le are serverul dvs. Numărul recomandat de lucrători este: 2 * number_of_cores + 1.

De asemenea, aveți nevoie de un proxy invers pentru a vă conecta serverul de aplicații la lumea exterioară. Doar folosiți NGINX, deoarece are o bază largă de utilizatori și este foarte ușor de configurat (puteți găsi acest cod și pe GitHub):

server {
    server_name www.example.com;
    access_log  /var/log/nginx/example.com.log;
    error_log    /var/log/nginx/example.com.error.log debug;
    root  /home/myuser/example.com/homepage;
    sendfile on;
    
# if the uri is not found, look for index.html, else pass everthing to gunicorn
    location / {
 index index.html;
 try_files $uri $uri/
     @gunicorn;
    }
    
# Django media
    location /media  {
        alias /home/myuser/example.com/example-django/public/media;      # your Django project's media files
    }
    
# Django static files
    location /static {
        alias /home/myuser/example.com/example-django/public/static;   # your Django project's static files
    }
    
location @gunicorn {

proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
 #proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_redirect off;
 
proxy_pass http://0.0.0.0:8000;
    }
    
client_max_body_size 100M;

listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
}

server {
    server_name example.com;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
    return 301 https://www.example.com$request_uri;
    
}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    
if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    
listen 80 default_server;
    listen [::]:80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Stocați fișierul de configurare în /etc/nginx/sites-availableși creați o legătură simbolică către acesta în /etc/nginx/sites-enabled.

Sper că ați găsit utilă această postare. Vă rog să-mi spuneți ce părere aveți despre asta și arătați-i câteva ❤ dacă vreți.