Configurer le serveur applicatif (Gunicorn) — đŸ”” Confirmé¶

Gunicorn est le serveur qui exĂ©cute l’application Django en production. Il reçoit les requĂȘtes HTTP (directement, ou via Nginx ou la plateforme d’hĂ©bergement), les confie Ă  Django et renvoie les rĂ©ponses.

Il est indispensable en production, oĂč le serveur de dĂ©veloppement de Django (runserver) ne doit pas ĂȘtre utilisĂ©. Gunicorn permet notamment :

  • de servir plusieurs visiteurs en mĂȘme temps, en rĂ©partissant les requĂȘtes entre plusieurs processus ;

  • de garder le site disponible, en relançant automatiquement un processus qui plante ou ne rĂ©pond plus.

Il ne rend pas le chargement d’une page plus rapide en soi, mais ses rĂ©glages dĂ©terminent combien de requĂȘtes le site peut traiter Ă  la fois : ils jouent donc sur la rĂ©activitĂ© du site lorsqu’il reçoit beaucoup de visites, et sur la mĂ©moire utilisĂ©e par le serveur.

Sites Conformes fournit une configuration prĂȘte Ă  l’emploi : la plupart des instances n’ont rien Ă  rĂ©gler. Cette page explique son fonctionnement et comment l’adapter, notamment lorsque la mĂ©moire du serveur est limitĂ©e.

OĂč se trouve la configuration¶

La configuration se trouve dans le fichier gunicorn.conf.py Ă  la racine du dĂ©pĂŽt. Gunicorn le charge automatiquement lorsqu’il est lancĂ© depuis ce dossier, quel que soit le mode de dĂ©ploiement : Procfile sur Scalingo, entrypoint.sh avec Docker, service systemd ou just run_gunicorn.

Les valeurs de ce fichier peuvent ĂȘtre modifiĂ©es par des variables d’environnement GUNICORN_* (voir RĂ©glages), sans toucher au code.

Note

Les options passĂ©es en ligne de commande Ă  Gunicorn sont prioritaires sur le fichier gunicorn.conf.py. Par exemple, l’exemple de service systemd de la page Sur un serveur Linux (VPS, dĂ©diĂ© ou interne) — đŸ”” ConfirmĂ© passe --workers 3 : cette valeur remplace alors GUNICORN_WORKERS. Ordre de prioritĂ©, du plus faible au plus fort :

  1. gunicorn.conf.py (et donc les variables GUNICORN_* qu’il lit) ;

  2. la variable GUNICORN_CMD_ARGS, si elle est définie ;

  3. les options de la ligne de commande.

Fonctionnement¶

Gunicorn s’organise en deux niveaux :

                 Processus maĂźtre
       (surveille, ne traite aucune requĂȘte)
             │                       │
         Worker 1                Worker 2         ← processus indĂ©pendants,
    (copie de l’application)  (copie de l’application)   chacun sa mĂ©moire
       │       │                 │       │
    thread  thread            thread  thread      ← requĂȘtes traitĂ©es
                                                    en mĂȘme temps
  • Le processus maĂźtre dĂ©marre les workers, les surveille et en relance un s’il plante ou ne rĂ©pond plus.

  • Les workers sont des processus Python complets, qui contiennent chacun une copie de Django, de Wagtail et du code du site. Ce sont eux qui traitent les requĂȘtes.

  • Les threads permettent Ă  un worker de traiter plusieurs requĂȘtes en mĂȘme temps.

Le nombre de requĂȘtes traitĂ©es simultanĂ©ment est donc : nombre de workers × nombre de threads.

Astuce

En Python, un seul thread d’un processus exĂ©cute du code Ă  un instant donnĂ©. Les threads n’accĂ©lĂšrent donc pas le calcul d’une page : ils sont utiles pendant les attentes (requĂȘtes Ă  la base de donnĂ©es, appels Ă  un service externe), pendant lesquelles un autre thread peut avancer. Ils ne servent que si le site reçoit souvent plusieurs requĂȘtes en mĂȘme temps.

Chargement anticipĂ© de l’application¶

La configuration active preload_app : le processus maĂźtre charge l’application une seule fois, puis crĂ©e les workers en se dupliquant. Les workers dĂ©marrent ainsi plus vite, ce qui accĂ©lĂšre les dĂ©ploiements et les redĂ©marrages.

Au dĂ©part, les workers partagent la mĂ©moire du processus maĂźtre. En Python, ce partage diminue au fil du temps, car le simple fait d’utiliser un objet le modifie en mĂ©moire. Il faut donc compter qu’à terme, le processus maĂźtre et chaque worker occupent chacun une copie de l’application.

Réglages¶

Variable

RĂŽle

Défaut

Effet sur la mémoire

GUNICORN_WORKERS

Nombre de workers. À dĂ©faut, WEB_CONCURRENCY est utilisĂ© (dĂ©finie automatiquement par Scalingo).

2

Fort : chaque worker ajoute une copie complùte de l’application.

GUNICORN_THREADS

Nombre de threads par worker, c’est-Ă -dire de requĂȘtes qu’un worker traite en mĂȘme temps.

1

Fort sous charge : chaque requĂȘte en cours occupe de la mĂ©moire pendant la gĂ©nĂ©ration de la page.

GUNICORN_TIMEOUT

Délai (en secondes) au-delà duquel un worker qui ne répond plus est redémarré.

120

Aucun.

GUNICORN_MAX_REQUESTS

Nombre de requĂȘtes traitĂ©es par un worker avant qu’il soit redĂ©marrĂ©.

10000

Bénéfique : remet réguliÚrement la mémoire du worker à zéro.

GUNICORN_MAX_REQUESTS_JITTER

AlĂ©a ajoutĂ© Ă  GUNICORN_MAX_REQUESTS, pour que les workers ne redĂ©marrent pas tous en mĂȘme temps.

2500

Aucun.

Deux rĂ©glages sont fixĂ©s dans le code et ne dĂ©pendent d’aucune variable :

  • worker_class = "gthread" : workers capables d’utiliser des threads ;

  • preload_app = True : chargement anticipĂ© de l’application (voir ci-dessus).

Mémoire et taille du serveur¶

La mĂ©moire utilisĂ©e par Gunicorn dĂ©pend surtout du nombre de workers et du nombre de requĂȘtes traitĂ©es en mĂȘme temps :

mĂ©moire ≈ processus maĂźtre
        + workers × (application + requĂȘtes simultanĂ©es par worker × mĂ©moire par requĂȘte)

La mĂ©moire nĂ©cessaire varie selon la taille du site et le contenu des pages. À titre d’exemple, sur une instance de production hĂ©bergĂ©e sur un conteneur Scalingo de 1 Go, avec 2 workers :

Threads par worker

RequĂȘtes simultanĂ©es

Mémoire constatée

4

8

environ 800 Mo, plus du swap : la limite était atteinte

2

4

environ 300 Ă  520 Mo selon le trafic, avec par moments un peu de swap

1

2

environ 450 Mo, sans swap

Avec 2 threads, la mĂ©moire dĂ©passe dĂ©jĂ  par moments 512 Mo : ce rĂ©glage convient Ă  un conteneur de 1 Go, mais pas Ă  un conteneur de 512 Mo. C’est pourquoi la valeur par dĂ©faut de GUNICORN_THREADS est 1 : elle convient aussi aux petits conteneurs (512 Mo et moins).

Recommandations¶

  • Gardez les valeurs par dĂ©faut tant que le site fonctionne correctement.

  • Si la mĂ©moire approche de la limite ou si le serveur utilise du swap, rĂ©duisez d’abord le nombre de workers, par exemple GUNICORN_WORKERS=1 sur un petit conteneur.

  • Si le site est lent sous forte affluence et que la mĂ©moire le permet, augmentez progressivement GUNICORN_THREADS ou GUNICORN_WORKERS, puis surveillez la mĂ©moire pendant quelques heures de trafic normal.

  • AprĂšs chaque changement, comparez la mĂ©moire juste aprĂšs le redĂ©marrage et aprĂšs une Ă  deux heures de trafic : les threads n’occupent de mĂ©moire que lorsque plusieurs requĂȘtes arrivent en mĂȘme temps.

Modifier un réglage¶

Sur Scalingo¶

DĂ©finissez la variable, puis redĂ©marrez l’application : la modification d’une variable n’est prise en compte qu’au dĂ©marrage suivant.

Depuis le tableau de bord Scalingo, ajoutez ou modifiez la variable dans l’onglet « Environnement » de votre application (voir Sur Scalingo (ou PaaS) — 🟱 DĂ©butant), puis redĂ©marrez l’application depuis le tableau de bord.

En ligne de commande, l’équivalent est :

scalingo --app mon-site env-set GUNICORN_THREADS=1
scalingo --app mon-site restart

Pour revenir à la valeur par défaut :

scalingo --app mon-site env-unset GUNICORN_THREADS
scalingo --app mon-site restart

La mĂ©moire de l’application se suit dans le tableau de bord, onglet « Metrics », graphique « Memory – web » (mĂ©moire, swap et limite du conteneur).

Sur un serveur Linux ou avec Docker¶

Ajoutez la variable au fichier .env de l’instance :

GUNICORN_THREADS=1

Puis redĂ©marrez l’application, par exemple sudo systemctl restart sites-conformes avec le service dĂ©crit dans Sur un serveur Linux (VPS, dĂ©diĂ© ou interne) — đŸ”” ConfirmĂ©.

VĂ©rifiez aussi que la commande qui lance Gunicorn ne passe pas la mĂȘme option en ligne de commande, qui serait prioritaire (voir OĂč se trouve la configuration).

Voir aussi¶