Django, Nginx, uWSGI

28 січня 2019 р.

Розглянемо простий варіант деплою декількох сайтів на Linux VPS (дистрибютив Ubuntu). Заходити по ssh з командної стрічки (напр. ssh root@12.34.56.78, потім пароль).
Як правило серверa "голі" без nginx, apache і т.д. Але може бути вже дещо встановлено (це як на мене гірше, бо багато чого не потрібного, включаючи у conf файлах)..

Перевіряємо чи запущений apache2 і зупиняєм по потребі, перевірка/встановлення nginx:

sudo service apache2 status
sudo service apache2 stop
 
sudo service nginx status
 
sudo apt-get update
sudo apt-get install nginx
sudo service nginx start

В дистрибютиві Ubuntu (16 і вище) використовуємо Python v3+
Встановлюємо uWSGI глобально (режим Emperor, щоб запустити на однму сервері декілька проектів)
Також встановлюємо Supervisor:

sudo apt-get install python3-pip python3-dev build-essential
sudo apt-get install python3-setuptools
sudo pip3 install virtualenv
sudo pip3 install uwsgi
sudo apt-get install supervisor

Створюєм папку для конфігураційних файлів:

sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals

Заходимо в /etc/supervisor/supervisord.conf i додаємо наступні конфігурації:

[program:uwsgi]
command = /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals
autostart = true

Далі наші проекти.
Для прикладу поміщаємо все в /home
Створюємо в /home папку virtenv для "віртуалок" та віртуальне середовище:

cd /home
mkdir virtenv
cd /virtenv
virtualenv venv3

активуємо і інсталюємо все що потрібно

Розглянемо для прикладу два проекти django: kyiv та lviv (віртуалку викоритаєм одну для двох, для демонстрації). Вони будуть також в /home
структура приблизно така:

home/kyiv/mysite
- mysite
---- init.py
---- settings.py
---- urls.py
---- wsgi.py
- myapp
---- init.py
---- admin.py
---- models.py
---- views.py
- media
- static
- templates
- manage.py
- db.sqlite3

те саме для другого, замість kyiv - lviv

Готові проекти переносимо за допомогою GIT (можна і копіпастом в nautilus, якщо туго з GITом о_О ), незабуваючи про права доступу до файлів (можуть бути змінені, наприклад, доступи до папки статики і в браузері видасть 403 forbidden; допоможе chmod -R 755 або 777 /home/kyiv/mysite/static).
Також в settings.py не забуваєм поставити ALLOWED_HOSTS = ["*"] (детальніше про це налаштування краще почитати тут django allowed-hosts)
В папці де знаходиться файл manage.py створюємо файл налаштування uwsgi, напр. kyiv_uwsgi.ini (для lviv відповідно lviv_uwsgi.ini) з такими налаштуваннями:

kyiv_uwsgi.ini

[uwsgi]
chdir = /home/kyiv/mysite
module = django.core.wsgi:get_wsgi_application()
home = /home/virtenv/venv3/
env = DJANGO_SETTINGS_MODULE=mysite.settings
master = true
processes = 10
socket = /tmp/kyiv.sock
logfile = /home/kyiv/logs/uwsgi.log
chmod-socket = 666
vacuum = true

lviv_uwsgi.ini

[uwsgi]
chdir = /home/lviv/mysite
module = django.core.wsgi:get_wsgi_application()
home = /home/virtenv/venv3/
env = DJANGO_SETTINGS_MODULE=mysite.settings
master = true
processes = 10
socket = /tmp/lviv.sock
logfile = /home/lviv/logs/uwsgi.log
chmod-socket = 666
vacuum = true

Різні варіації можна подивитись тут:
uWSGI Django and Nginx

Також треба створити папку logs для logfile.
Будем вважати, що в нас є зареестровані домени kyiv.ua та lviv.ua і в них прописані NS сервера нашого хостера. А також у хостера до ІР сервера привязані дані домени.
Налаштовуємо nginx.conf в /etc/nginx
Взагальному цей файл всюди однаковий, хоча можуть бути індивідуальні варіації. Використовуємо такий варіант налаштуваня файлу nginx.conf:

# Server globals
user                    www-data;
worker_processes        auto;
worker_rlimit_nofile    65535;
error_log               /var/log/nginx/error.log crit;
pid                     /var/run/nginx.pid;
# Worker config
events {
        worker_connections  1024;
        use                 epoll;
        multi_accept        on;
}
http {
    ##
    # Basic Settings
    ##
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;
    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    ##
    # SSL Settings
    ##
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;
    ##
    # Logging Settings
    ##
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    ##
    # Gzip Settings
    ##
    gzip on;
    gzip_min_length 1000;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss application/x-font-ttf text/javascript;
    ##
    # Virtual Host Configs
    ##
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

В папці де файл manage.py створюємо файл налаштування nginx, для кожного проекту напр. kyiv_nginx.conf (для lviv відповідно lviv_nginx.conf) з такими налаштуваннями:
kyiv_nginx.conf

upstream kyiv {
    server unix:///tmp/kyiv.sock;
}
server {
    listen       80;
    server_name  kyiv.ua;
    return       301 http://www.kyiv.ua$request_uri;
}
server {
    listen      80;
    server_name    www.kyiv.ua;
    charset     utf-8;
    access_log /home/kyiv/logs/access.log;
    error_log  /home/kyiv/logs/error.log;
    root /home/kyiv/mysite/;
    client_max_body_size 75M;
    location /media  {
        alias /home/kyiv/mysite/media;
    }
    location /static {
        alias /home/kyiv/mysite/static;
    }
    location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 365d;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
    }
    location / {
        uwsgi_pass  kyiv;
        include     /home/kyiv/mysite/uwsgi_params;
    }
}

lviv_nginx.conf

upstream lviv {
    server unix:///tmp/lviv.sock;
}
server {
    listen       80;
    server_name  lviv.ua;
    return       301 http://www.lviv.ua$request_uri;
}
server {
    listen      80;
    server_name    www.lviv.ua;
    charset     utf-8;
    access_log /home/lviv/logs/access.log;
    error_log  /home/lviv/logs/error.log;
    root /home/lviv/mysite/;
    client_max_body_size 75M;
    location /media  {
        alias /home/lviv/mysite/media;
    }
    location /static {
        alias /home/lviv/mysite/static;
    }
    location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 365d;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
    }
    location / {
        uwsgi_pass  lviv;
        include     /home/lviv/mysite/uwsgi_params;
    }
}

В нашому випадку налаштований редірект сайту з без www на www
В /etc/nginx має бути файл uwsgi_params, копіюємо його в папку де файл manage.py (щоб все було в одному місці). Якщо немає то створюємо (всюди однаковий):

uwsgi_params
uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;
uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  HTTPS              $https if_not_empty;
uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

Тепер важливий момент, створюєм посилання конфігурацій для uwsgi та nginx:

sudo ln -s /home/kyiv/mysite/kyiv_uwsgi.ini /etc/uwsgi/vassals/
sudo ln -s /home/lviv/mysite/lviv_uwsgi.ini /etc/uwsgi/vassals/
sudo ln -s /home/kyiv/mysite/kyiv_nginx.conf /etc/nginx/sites-enabled/
sudo ln -s /home/lviv/mysite/lviv_nginx.conf /etc/nginx/sites-enabled/

Можем попередньо перевірити на помилки в nginx командою nginx -t
Перезапускаєм uwsgi та nginx:

sudo supervisorctl restart uwsgi
sudo service nginx restart

Можуть бути помилки в nginx і видасть попередження, що неперезапустилось - дивимось командою sudo journalctl -xe а також в логах.
uwsgi при рестарті повідомлення в терміналі не дає, тому якщо не працює дивимось в логах
Якщо все без омилок то по kyiv.ua та lviv.ua мають працювати наші сайти. Шляхи до статики в проекті і шаблонах треба налаштовувати індивідуально, шлях до папки /static чи /media прописаний в kyiv_nginx.conf та lviv_nginx.conf
Після кожних змін в проекті в .ру файлах потрібно перезапускати uwsgi (sudo supervisorctl restart uwsgi) щоб підтянулись зміни

Інші дописи

Python tests, parametrize

Як виконати декілька тестів в одній фнкції.

Git Cherry Pick

Вишенька на торті - git команда, яка забирає тільки зміни