Installazione di uWsgi PHP, Python CentOs

Introduzione

uWSGI è un potente webgateway che può lanciare applicativi scritti in diversi linguaggi (Python, Django PHP,Ruby,Java). Ho già scritto un tutorial che spiega come compilare uWSGI da zero, embeddando i plugin, oppure compilandoli come moduli esterni. Questo tutorial spiega come installare uWSGI come pacchetto di sistema su CentOs. Fornisce inoltre file di configurazione di esempio per Wordpress e Django. Anche se uWSGI è un software semplice, supporta configurazioni e infrastrutture molto complesse, quali file system condiviso, cluster, loadbalicing.

Cambiando alcuni comandi la guida può essere tranquillamente replicata su Ubuntu server, i concetti sono ovviamente gli stessi. La guida spiega come installare tutto il software partendo da una macchina pulita, nel mio caso CenotOs Minimal. Questa guida fornisce indicazioni per effettuare un'istallazione di uWSGI adatta alla produzione.  Questo articolo non cosidera, come installare un webserver condiviso tra più applicativi.

Installazione di Nginx

Installiamo il webserver Nginx, uWSGI potrebbe fare anche da webserver, tuttavia se abbiamo più VirtualHost avremo la necessità di posizionare un web server davanti.

# Centos
yum install epel-release

yum install nginx

yum remove epel-release

Nginx si occupera' di ricevere le richieste, servire i file statici e invierà le richieste a uWSGI tramite socket unix, protocollo wsgi.

Installazione di uWSGI

Installazione del webgateway uWSGI, con i plugin per php, e python

# Cent-os
yum install uwsgi uwsgi-plugin-php uwsgi-plugin-python

Configuriamo uwsgi

Concetti di uWSGI basilari di uWSGI, emperor:

Emperor: Imperatore del insieme dei processi uWSGI, controlla lo stato dei processi, Avvia, Ferma, Riavvia i Vassalli.

Vassallo: Processo nel quale gira l'applicativo. Durante la fase di inizializzazione, il vassallo può cambiare untete, e cambiare la sua CWD. In oltre ogni vassallo può avere piu thread di esecuzione.

Spiegazione dettagliata:

uWSGI avvia un processo di controllo, detto Emperor. Ogni emperor ha piu vassalli, ogni vassallo corrisponde ad un applicativo (Nota:Questa è un'imprecisone infatti alcune configurazioni di uWSGI permettono la gestione di un singolo applicativo in più processi vassalli. Nel caso generico tuttavia, ogni vassallo è un applicativo).

uWSGI monitora una cartella del fie system, nel mio caso /etc/uwsgi.d/ e quando viene salvato un nuovo file .ini avvia un nuovo vassallo.

Durante la fase di inizializzazione (prima del Fork&Die) il vassallo può autonomamente droppare i permesi, e passare su un utente meno provilegiato. Nuovi permessi, e utente sono specificati nel singolo file di configurazione del vassallo. Allo stesso modo, il socket di ricezione e/o porta di ascolto, e se è stato configurato, anche il file di log del vassallo.

Ogni vassallo può avere più worker, quindi elaborare le richieste su piu thread. uWSGI può inoltre bilanciare autonomamente il numero di thread/Worker attivi simultaneamente in base al carico.

Se come primo passo non abbiamo installato nginx, perche non serve al caso specifico, ricordo di cambiare utente da nginx a uwsgi.

EMPEROR:Configurazione di uWSGI master

# FILE:/etc/uwsgi.ini

[uwsgi]
uid = nginx
gid = nginx
pidfile = /run/uwsgi/uwsgi.pid
emperor = /etc/uwsgi.d
stats = /run/uwsgi/stats.sock
chmod-socket = 660
emperor-tyrant = true
cap = setgid,setuid

Systemd Unit

FILE:/etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor Service
After=syslog.target

[Service]
EnvironmentFile=-/etc/sysconfig/uwsgi
ExecStartPre=/bin/mkdir -p /run/uwsgi
ExecStartPre=/bin/chown -R nginx:nginx /run/uwsgi
ExecStartPre=/bin/chown -R nginx:nginx /var/log/uwsgi
ExecStartPre=/bin/chown -R nginx:nginx /var/venv

ExecStartPre=/bin/chcon -R system_u:object_r:httpd_log_t:s0 /var/log/uwsgi
ExecStartPre=/bin/chcon system_u:object_r:httpd_exec_t:s0 /usr/sbin/uwsgi

ExecStart=/usr/sbin/uwsgi --ini /etc/uwsgi.ini
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
Restart=always
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Unit di systemd per uWSGI, contiene già le configurazioni per far funzioare correttamente SELinux. Se non usi SELinux, potresti iniziare a pensare di utilizzarlo.

É il momento di far partire il servizio con Systemd.

# Creare la dir per i log di uwsgi
mkdir -p /var/log/uwsgi
mkdir -p /var/venv

systemctl daemon-reload
systemctl start uwsgi

Troubleshooting

Non funziona e non parte, cosa guardare?

  1. Il JournalCRL, Magari aprendo una seconda shell in ssh, e lasciando la prima aperta su journactl.
    journalctl -exf -u uwsgi
  2. I permessi, il mio uwsgi sta girando con i permessi di nginx. Uno dei motivi, potrebbe essere che sta partendo con i permessi sbagliati.
  3. Verificare l'esistenza di tutte le cartelle necessarie. È molto più efficiente per il debug lo status, quindi:
    systemctl status uwsgi

VirtualENV, VirtualWrapper

Virtualenv, permette di creare un ambiente python virtuale controllato. In altre parole, permette la creazione di un ambiente, con librerie, e versioni controllate; rende possibile installare la medesima libreria sulla medesima macchina in varie versioni. Per comodità tendo a creare un virtualenv per applicazione, ottenendo un ambiente virtuale per ogni sito/applicativo. Così facendo sarà possibile, usare molteplici versioni di Django, molteplici librerie senza che gli applicativi si disturbino a vicenda, o, ancora peggio, che un applicativo aggiorni le librerie di un altro applicativo rompendo lo stesso.

Installiamo Pip, virtualenv, virtualwrapper.

yum install python2-pip.noarch python-virtualenv-api.noarc

Quindi installiamo virtualwrapper.

pip install virtualenvwrapper
# questa configurazione va bene per una macchina che fa webserver, su un installazione locale mettiamola nella nostra home da qualche parte.
export WORKON_HOME=/var/venv

# La prima volta che si installa
mkdir -p $WORKON_HOME


# Andrebbe inserito nel nostro .bashrc così che viene caricato in ogni terminale
# quindi con un editor di testo: nano, vim apriamo ~/.bashrc
# e a fondo file mettiamo

#.basrc Da qui
"
# VIRTUAL WRAPPER
export WORKON_HOME=/var/venv
source /usr/local/bin/virtualenvwrapper.sh
" Fine

Configurazione per il singolo Progetto/SitoWeb Django

Creiamo un nuovo virtualenv

# Di solito utlizzo questa formula poiche molto generica, va sempre bene,
# difficilmente si fa confusione.

mkvirtualenv subdomain.domain.toplevel
# Questo file va posizionato in /etc/uwsgi.d/nomefile_comevuoi.ini

[uwsgi]
# Questa e una semplice variabile che potra essere chiamata poi con %(projectname)
projectname     = PRJ_NAME

# specifica quale plugin deve caricare uwsgi per gestire le richieste di questo socket
plugin          = python

# config
# Current Working Directory CWD
chdir           = /var/www/domain.com/blog

# path del soket
socket          = /run/uwsgi/domain.com.sock
wsgi-file       = %(projectname)/wsgi.py
env             = DJANGO_SETTINGS_MODULE=%(projectname).settings
home            = /var/venv/blog.domain.com
logto           = /var/log/uwsgi/domain.com.log
# uwsgi rimuove il soket quando viene spento
vacuum          = true

# Worker handling
# Gestione worker

# Algoritmo con il quale vengono lanciati nuovi worker
cheaper-algo=spare
# Numero minimo di worker, su piccoli siti va bene anche 1
cheaper=1
# Numero di worker lanciati alla partenza
cheaper-initial=2
# Numero massimo di worker
processes=4
# Numero di worker avviati per volta
cheaper-step=1

# Sezione statistiche
# Nel socket di statistica vengono riportati anche i dati sulla memoria occupata dal processo
memory-report = true
# path del socket statistica
stats=/run/uwsgi/stats/project.sock

Utilizzo questo template come base di paratenza per i progetti django.

Questo template esporta anche le metriche di uWSGI che possono essere monitorate da diversi sistemi di monitoring.

Installazione di MariaDB

Installazione del database MariaDB, è il database più diffuso e utilizzato, personalmente preferisco PostgreSQL. Tuttavia per Wordpress è meglio utilizzare Mysql / MariaDB in quanto Wordpress è strutturato per lavorare con questo database.

yum install mariadb-server
systemctl start mariadb
systemctl status mariadb

systemctl enable mariadb

mysql_secure_installation

Configurazione di esempio un un VirtualHost:

Django

Questo è un buon punto di partenza per creare una configurazione di NGINX, compatibile con Django

server {
    listen      80;
    server_name blog.myblog.it;
	
    charset     utf-8;

    access_log /var/log/nginx/myblog.access.log;
    error_log /var/log/nginx/myblog.error.log;

    client_max_body_size 75M;

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types    text/plain application/javascript application/x-javascript text/javascript text/xml text/css;

    location /media  {
	expires 30d;
	add_header Pragma public;
	add_header Cache-Control "public";
        alias /var/www/myblog.it/blog/MEDIA;
    }

    location /static {
	expires 30d;
	add_header Pragma public;
        add_header Cache-Control "public";
        alias /var/www/myblog.it/blog/static;
    }

    location / {
		include     /etc/nginx/uwsgi_params;
        	uwsgi_pass  unix:///run/uwsgi/blog_soket.sock;
	}
}

Wordpress - generico PHP

Questo è un file di configurazione compatibile con php.

server {
    include /etc/nginx/default_http_port;
    server_name myblog.it;
    
    access_log  /var/log/nginx/myblog.access.log;
    error_log   /var/log/nginx/myblog.error.log;

    root /var/www/myblog;
    index index.php;

    error_page 400 401 403 404 500 502 503 /_xxx.html;
    location = /custom_xxx.html {
        root /error/400.html;
        internal;
    }

    set $cache_uri $request_uri;

    location / {
       try_files $uri $uri/ /index.php?$args;
       #try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args;
       #try_files /wp-content/cache/supercache/$http_host/index.html $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        root /var/www/myblog;
        include uwsgi_params;
        uwsgi_modifier1 14;
        uwsgi_pass unix:///run/uwsgi/myblog.sock;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
    }
}

Queste configurazioni andrebbero spostate all'interno di /etc/nginx/conf.d, per un'installazione default.

Verifichiamo che tutto sia Ok e ricarichiamo le configurazioni di nginx.

nginx -t
# Dovrebbe rispondere ok
# Solo se ok, allora ricarichiamo nginx

systemctl reload nginx

Ora si dispone di tutto il software necessario. Rimane del debug, da fare sicuramente ci saranno alcune configurazioni sbagliate. Che vanno corrette caso per caso.