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) щоб підтянулись зміни

Інші дописи

Django, Nginx, uWSGI

Деплой сайту на Django за допомогою Nginx, uWSGI, Supervisor

Python tests, parametrize

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