Coding

Configurare le Bitbucket pipelines per eseguire i test di Django

Le Bitbucket pipelines sono un potente strumento per eseguire i deploy (che vedremo in seguito) in sicurezza e in un ambiente controllato. Sopratutto senza grosse perdite di tempo.
Configurare le Bitbucket pipeline non è difficile. Vediamo come.
Ad oggi, le Bitbicket Pipelines offrono “gratuitamente” 50 minuti di tempo macchina. Per progetti piccoli può andare, ma per progetti medio grandi sono estremamente pochi.

Prezzo bitbucket pipelines
Prezzo delle bitbucket pipeline a giugno 2019

Inoltre è possibile acquistare minuti di compilazione per specifici account

Bitckuet prezzo minuti per account
Prezzo per l’acquisto di minuti per account

Per abilitare le pipeline basta andare su “Settings” e dal sotto menu “Pipeline” andare ancora su “settings” e poi spuntare “Enable Pipelines”

La prima cosa che ti chiederà Bitbucket per configurare le pipelines sarà quella di creare e riempire il file “bitbucket-pipelines.yml”
Un esempio di configurazione minima del file è la seguente:

image: python:3.5.1

pipelines:
  branches:
    master:
      - step:
          caches:
            - pip
          script:
            - pip install -r requirements.txt
            - python manage.py test --settings=bay.settings_test
          services:
            - postgres

definitions:
  services:
    postgres:
      image: postgres:10
      variables:
        POSTGRES_DB: 'pipelines'
        POSTGRES_USER: 'test_user'
        POSTGRES_PASSWORD: 'test_user_password'

Definizione delle pipelines

Visto che è quasi estate, il progetto di Django di prova abbiamo deciso di chiamarlo “bay”.
Tornando al problema principale di come configurare le Bitbucket Pipelines, vediamo che cosa ho scritto nel file di configurazione.

Image: python:3.5.1 specifica l’immagine di Docker da usare per eseguire i test. Solitamente noi per django usiamo le immagini ufficiali di Python o quelle di ubuntu. Le prime sono più leggere ( niente a che vedere con le alpine però), le seconde più pesanti ma più semplici da configurare sopratutto se ci sono tante dipendenze.
La sezione pipelines specifica i comandi che verranno eseguiti sugli specifici branch. Nel nostro caso abbiamo tutti i trigger attivati solamente su master.
Il primo step riguarda la gestione delle dipendenze di python con pip.
Il comando di caches ordina a Bitbucket di salvarsi i pacchetti di pip in locale per poi riutilizzarli per le successive build. Questo comando velocizza non di poco la creazione delle build successive.
Praticamente Bitbucket andrà a cercare i pacchetti prima in locale e successivamente sui server di pip.
I comandi sotto la sezione script sono quelli necessari per installare le dipendenze ed eseguire i test. “pip insyall -r requirements.txt” scarica le dipendenze e le installa, mentre “python manage.py test –settings=bay.settings_test” esegue i test di django, però con un settings file diverso da quello standard.
Perchè?
Una delle difficoltà che ho avuto con Bitbucket è stata quella relativa alla configurazione delle variabili di ambiente. Allora la soluzione che abbiamo trovato è stata quella di creare un secondo file di settings, che importa tutte le definizioni di quello standard, ma che va a sovrascrivere solo alcuni settaggi. In parole povere il file settings_test.py è queste 13 righe:

from .settings import *


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'pipelines',
        'USER': 'test_user',
        'PASSWORD': 'test_user_password',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

La sezione services indica a quale altri servizi si dovrà attaccare il nostro applicativo. Per adesso soltanto a postgres.

Definizioni dei servizi

Sotto la sezione definitions vengono definiti tutti i servizi necessari affinché gli script della pipelnes possano essere eseguiti. L’unico servizio che al momento ci serve è postgres ed è configurato nella seguente maniera:

  services:
    postgres:
      image: postgres:10
      variables:
        POSTGRES_DB: 'pipelines'
        POSTGRES_USER: 'test_user'
        POSTGRES_PASSWORD: 'test_user_password'

Le variabile POSTGRES_DB, POSTGRES_USER e POSTGRES_PASSWORD sono le medesime che abbiamo “hardcodato” nel settings_test.py

Per testare la pipeline basterà eseguire push su master ed aspettare che la compilazione (che coincide con l’instalalzione ed esecuzione dei test) finisca.
Nel caso in cui un test fallisca, anche la pipeline fallisce.
Prima di concludere vi avviso che i 50 minuti di compilazione “offerti” da Bitbucket sono veramente pochi. Anche perché sono suddivisi fra tutti i repo dell’utente.

Coding

Deploy di Django con Apache/httpd e mod_wsgi

Premetto che ho intrapreso una personale crociata nei confronti dei deploy di Django con Apache. Spesso e volentieri mi trovo a dover utilizzare ancora questa modalità di deploy per motivi di forza maggiore.

Partiamo da dei concetti semplicissimi che devono essere dei capisaldi per ogni deploy, i file statici devono essere gestiti da servizi esterni ( ad esempio amazon S3), l’applicazione python deve essere dentro un virtualenv e devono poter convivere più applicazioni web assieme.
Sulle distribuzioni debian based, ubuntu compresa, è possibile installare il mod-wsgi lanciando il comando:

sudo apt-get install libapache2-mod-wsgi-py3

Mentre su Centos e derivate è possibile installare il modulo con:

sudo yum install python36u-mod_wsgi

Una precisazione, nei repository quando non viene specificata la versione di Python, viene assunto che il modulo è per python 2.7.x.
Successivamente, ricordatevi di riavviare Apache/httpd affinchè le modifiche abbiano effetto.

Permessi sui files

Passiamo adesso alla parte più noiosa, quella delle path e dei permessi.
Assumiamo che il vostro progetto di django si chiami test_app ( visto che fantasia!?) ed è installato nella path /home/foo/test_app/ mentre il virtualenv è installato nella path /home/foo/test_app_env/
Per quanto riguarda i permessi, di solito eseguo le seguenti operazioni:

sudo chmod +x /home/foo/test_app_env/

Il chmod + x sulle cartelle permette di entrare ed accedere ai file e cartelle. Successimavamente lancio:

find /home/foo/test_app_env -type d -exec chmod 755 {} \;
find /home/foo/test_app_env -type f -exec chmod 644 {} \;

Con il primo comando setto i permessi sulle cartelle e con il secondo sui file. In teoria adesso dovremmo essere in regola con i permessi.
E adesso il file di configurazione di VirtualHost che è il cuore del deploy di Django con Apache.

Configurazione di Apache per il deploy di Django


Nelle centos solitamente il file è posizionato in /etc/httpd/conf.d/ mentre sulle debian/ubuntu in /etc/apache2/conf.d/sites-available/ e bisogna usare gli script a2ensite e a2dissite

<VirtualHost *:80>  
ServerName foo.apptecsrl.com  

WSGIScriptAlias / /home/foo/test_app/test_app/wsgi.py 
WSGIDaemonProcess test_app processes=5 python-path=/home/foo/test_app:/home/foo/test_app_env/lib/python3.6/site-packages:/home/foo/test_app_env/lib/python3.6 threads=1 
WSGIProcessGroup test_app 
Alias /static/ /home/foo/test_app/test_app/public/static/ 
Alias /media/ /home/foo/test_app/test_app/public/media/ 
 
<Directory /home/foo/test_app/>  
  AllowOverride all  
  Require all granted  
  Options Indexes FollowSymlinks  
</Directory>  
 
</VirtualHost> 

Nota di merito di questa configurazione base, va alle direttive: WSGIDaemonProcess e ServerName.


ServerName specifica a quale nome risponde la configurazione. Quindi digitando foo.apptecsrl.com vi risponderà la configurazione appena creata.
Configurazioni diverse, con ServerName diversi corrisponsono a siti diversi. (Potete testare la configurazione editanto il file hosts)


WSGIDaemonProcess è la direttiva che specifica quanti demoni distinti devono essere creati. Ad ogni demone viene delegata l’esecuzione dell’applicazione wsgi.
test_app è il display-name dei singoli demoni, process=5 specifica il numero dei demoni che saranno avviati, nel nostro caso saranno 5.
python-path è la lista delle path necessarie per avviare la nostra applicazione. Nel nostro caso abbiamo specificato la path dell’applicazione, la site-packages del virtualenv e il binario dell’interprete.
threads=1 specifica il numero di thread che saranno creati da ogni demone per gestire la richiesta.


Ci tengo a precisare che questa configurazione l’ho usata per anni su un e-commerce con un traffico medio/basso, non ho mai avuto problemi di prestazioni, ma mi rendo anche conto che è una configurazione per niente ottimizzata.