Django i naturen: tip til implementeringsoverlevelse

Før du implementerer din Django webapp i den virkelige verden, skal du sikre dig, at dit projekt er produktionsklar. Bedre at begynde at implementere dem tidligere. Det sparer dig og dit team meget tid og hovedpine. Her er nogle punkter, jeg lærte undervejs:

  1. Brug pipenv (eller krav.txt + venv). Foretag Pipefile og Pipefile.lock (eller krav.txt). Navngiv din venv.
  2. Har et quickstart-script.
  3. Skriv test. Brug Django-testrammenog hypotese.
  4. Brug miljøog direnv til at administrere dine indstillinger og automatisk indlæse dine miljøvariabler.
  5. Sørg for, at alle udviklere begår deres migreringer. Squash-migrationer fra tid til anden. Nulstil dem om nødvendigt. Arkitekt dit projekt for jævnere migrationer. Læs om migrationer.
  6. Brug kontinuerlig integration. Beskyt din mestergren.
  7. Gå gennem Djangos officielle tjekliste for implementering.
  8. Administrer ikke din egen server, men hvis du skal, skal du bruge en korrekt bibliotekstruktur og bruge Supervisord, Gunicorn og NGINX.

Denne liste voksede organisk, da jeg gennemgik vores første Django-app og er ikke udtømmende. Men jeg synes, det er nogle af de vigtigste punkter, du skal være opmærksom på.

Læs med til en diskussion om hvert af punkterne.

Administrer dine afhængigheder og virtuelle miljøer på den rigtige måde

Du og dit team skal være enige om en måde at styre dine afhængigheder og virtuelle miljøer på. Jeg anbefaler enten at bruge pipenv, som er den nye måde at administrere både dine virtuelle miljøer og afhængigheder på, eller ved at bruge den gode gamle tilgang til at oprette en venv og spore dine afhængigheder med en requirements.txtfil.

Brug af requirements.txtfremgangsmåden er tilbøjelig til menneskelige fejl, da udviklere har en tendens til at glemme at opdatere pakkelisten. Dette er ikke et problem med pipenv, da det automatisk opdaterer Pipefile. Ulempen ved pipenv er, at det ikke har eksisteret længe nok. Selvom det officielt anbefales af Python Software Foundation, har du muligvis problemer med at få det til at køre på nogle maskiner. Personligt bruger jeg stadig den gamle tilgang, men jeg vil bruge pipenv til mit næste projekt.

Brug af venv og krav.txt

Hvis du bruger Python ≥ 3,6 (det burde du være), kan du blot oprette en med python -m venv ENV. Sørg for at navngive dit virtuelle miljø (i stedet for at bruge .). Nogle gange skal du slette dit virtuelle miljø og oprette det igen. Det gør det lettere. Du skal også tilføje ENVbibliotek til din. gitignorefil (Jeg foretrækker den ENV navn i stedet for venv , .env , ... da det skiller sig ud, når jeg ls mappen projektet).

For at styre afhængighederne kører hver udvikler, pip freeze > requirements.txt når de installerer en ny pakke, og tilføjer og forpligter den til repoen. De vil bruge, pip install -r requirements.txtnår de trækker fra det eksterne lager.

Brug af pipenv

Hvis du bruger pipenv , skal du bare tilføje Pipfile og Pipfile.lock til din repo.

Har et quickstart-script

Dette hjælper med at sikre, at dine udviklere bruger så lidt tid som muligt på at arbejde på ting, der ikke er direkte relateret til deres job.

Dette sparer ikke kun tid og penge, men sørger også for, at de alle arbejder i lignende miljøer (for eksempel de samme versioner af Python og pip).

Så prøv at automatisere så mange installationsopgaver som muligt.

Desuden er det meget meget at have et enkelt trin build script, hvad Joel Test's 2. trin handler om.

Her er et lille script, jeg bruger, hvilket sparer mine udviklere for et par tastetryk:

#!/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

Skriv test

Alle ved, at det er en god praksis at skrive prøver. Men mange overser det af hensyn til, mener de, hurtigere udvikling. IKKE. Test er en absolut nødvendighed, når det kommer til at skrive software, der bruges i produktionen, især når du arbejder i et team. Den eneste måde, du kan vide, at den seneste opdatering ikke ødelagde noget, er at have velskrevne tests. Du har også absolut brug for tests til kontinuerlig integration og levering af dit produkt.

Django har en anstændig testramme. Du kan også bruge ejendomsbaserede testrammer såsom hypotese, som hjælper dig med at skrive kortere, matematisk strenge tests til din kode. I mange tilfælde er det hurtigere at skrive ejendomsbaserede tests. I praksis ender du muligvis med at bruge begge disse rammer til at skrive omfattende test, der er let at læse og skrive.

Brug miljøvariabler til indstillinger

Din settings.py- fil er, hvor du gemmer alle vigtige projektindstillinger: din database-URL, stier til medier og statiske mapper osv. Disse vil have forskellige værdier på din udviklingsmaskine og din produktionsserver. Den bedste måde at tackle dette på er at bruge miljøvariabler. Det første trin er at opdatere dine settings.py for at læse fra miljøvariablerne ved hjælp af miljø :

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') ..

For at undgå at indlæse dine envvars manuelt, skal du oprette direnv på dine udviklingsmaskiner og gemme envvars i en .envrcfil i dit projektmappe. Nu, når du går cdind i din projektmappe, indlæses miljøvariablerne automatisk. Føj .envrctil dit lager (hvis alle udviklere bruger de samme indstillinger), og sørg for at køre, direnv allownår der er en ændring i .envrcfilen.

Brug ikke direnvpå din produktionsserver. Opret i stedet en fil, der hedder .server.envrc , tilføj den til .gitignoreog læg produktionsindstillingerne der. Opret nu et script, runinenv.shfor automatisk at hente miljøvariablerne fra .server.envrc, aktivere det virtuelle miljø og køre den medfølgende kommando. Du vil se, hvordan det bruges i det næste afsnit. Sådan runinenv.shskal du se ud (link til GitHub).

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

Håndter dine migrationer ordentligt

Django-migrationer er store, men at arbejde med dem, især i et team, er langt fra problemfri.

Først skal du sørge for, at alle udviklere forpligter migreringsfilerne. Ja, du vil muligvis (have) konflikter, især hvis du arbejder med et stort team, men det er bedre end at have et inkonsekvent skema.

Generelt er det ikke så let at håndtere migrationer. Du skal vide, hvad du laver, og du skal følge nogle bedste fremgangsmåder for at sikre en jævn arbejdsgang.

En ting, som jeg regnede med, er, at det normalt hjælper, hvis du squasher migreringerne fra tid til anden (f.eks. Ugentligt). Dette hjælper med at reducere antallet af filer og størrelsen på afhængighedsgrafen, hvilket igen fører til hurtigere byggetid og normalt færre (eller lettere at håndtere) konflikter.

Nogle gange er det lettere at nulstille dine migrationer og gøre dem på ny, og nogle gange er du nødt til manuelt at rette de modstridende migrationer eller flette dem sammen. Generelt er håndtering af migrationer et emne, der fortjener sit eget indlæg, og der er nogle gode læsninger om dette emne:

  • Django-migrationer og hvordan man håndterer konflikter
  • Sådan nulstilles migrationer

Moreover, how your project’s migrations end up depends on your project architecture, your models, and so on. This is especially important when your code-base grows, when you have multiple apps, or when you have complicated relations between models.

I highly recommend you to read this great post about scaling Django apps, which covers the topic pretty well. It also has a tiny, useful migration script.

Use Continuous Integration

The idea behind CI is simple: run tests as soon as new code is pushed.

Use a solution which integrates well with your version controlling platform. I ended up using CircleCI. CI is especially helpful when you work with multiple developers, since you can be sure their code passes all the tests before they send a pull request. Again, as you can see, it’s very important to have well covered tests in place. Moreover, make sure your master branch is protected.

Deployment checklist

Django’s official website provides a handy deployment checklist. It helps you ensure your project’s security and performance. Make sure you follow through those guidelines.

If you must manage your own server…

There are many good reasons to not manage your own server. Docker gives you more portability and security, and serverless architectures, such as AWS Lambda, can provide you with even more benefits for less money.

But there are cases where you need more control over your server (if you need more flexibility, if you have services cannot work with containerized apps — such as security monitoring agents, and so on).

Use a proper directory structure

The first thing to do is to use a proper folder structure. If all you want to serve on your server is the Django app, you can simple clone your repository and use that as your main directory. But that’s rarely the case: usually you also need to have some static pages (home page, contacts, …). They should be separate from your Django code base.

A good way to go about it is to create a parent repository, which has different parts of your project as submodules. Your Django developers work on a django repository, your designers work on the homepage repository, … and you integrate all of them in a repository:

example.com/ example-django/ homepage/

Use Supervisord, NGINX, and Gunicorn

Sure, manage runserver works, but only for a quick test. For anything serious, you need to use a proper application server. Gunicorn is the way to go.

Keep in mind that any application server is a long-running process. And you need to make sure that it keeps running, is automatically restarted after a server failure, logs errors properly, and so on. For this purpose, we use supervisord.

Supervisord needs a configuration file, in which we tell how we want our processes to run. And it is not limited to our application server. If we have other long-running processes (e.g. celery), we should defined them in /etc/supervisor/supervisord.conf. Here is an example (also on 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

Bemærk hvordan vi bruger runinenv.shher (linje 14, 24 og 34). Vær også opmærksom på linje 14, hvor vi beder gunicorn om at sende 3 arbejdere. Dette nummer afhænger af antallet af kerner, din server har. Det anbefalede antal arbejdstagere er: 2 * antal_af_cores + 1.

Du har også brug for en omvendt proxy for at forbinde din applikationsserver til omverdenen. Brug bare NGINX, da det har en bred brugerbase, og det er meget let at konfigurere (du kan også finde denne kode på 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 //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 //www.example.com$request_uri; } server { if ($host = www.example.com) { return 301 //$host$request_uri; } # managed by Certbot if ($host = example.com) { return 301 //$host$request_uri; } # managed by Certbot listen 80 default_server; listen [::]:80 default_server; server_name example.com www.example.com; return 301 //$server_name$request_uri; }

Gem konfigurationsfilen i /etc/nginx/sites-available, og opret et symbolsk link til den i /etc/nginx/sites-enabled.

Jeg håber, du har fundet dette indlæg nyttigt. Lad mig vide, hvad du synes om det, og vis det noget, hvis du vil.