====== Serwer Django ======
Na przykładzie Fedora Linux 38
===== Ustawienia Django =====
Plik ''settings.py'':
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'
Dla wersji starszej niż 3.6 powinniśmy użyć:
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
W niektórych wypadkach może zajść konieczność dodania parametru:
CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1',]
===== NGINX =====
user nginx;
http {
include /etc/nginx/conf.d/*.conf;
server {
listen 81;
listen [::]:81;
}
server {
listen 80;
listen [::]:80;
server_name localhost;
charset utf-8;
# max upload size
client_max_body_size 75M;
# Django media
location /media {
alias /path/to/your/mysite/media;
}
location /static {
alias /home/user_app/PycharmProjects/venvFolder/FolderProjektDjango/FolerApp/static;
}
location / {
proxy_pass http://localhost:8080;
}
}
systemctl restart nginx
===== Gunicorn =====
[Unit]
Description=Gunicorn instance to serve application
After=network.target
[Service]
User=user_app
Group=user_app
StandardOutput=journal
StandardError=journal
WorkingDirectory=/home/user_app/PycharmProjects/venvFolder/FolderProjektDjango
ExecStart=/home/user_app/PycharmProjects/venvFolder/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8080 ProjektDjango.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable gunicorn.service
systemctl start gunicorn.service
systemctl status gunicorn.service
journalctl -xe
===== Uprawnienia do katalogu static =====
Katalog ''/home'' powinien mieć uprawnienia 755. Katalog ''static'' i nadrzędne powinny mieć uprawnienia ''750'' a użytkownik ''nginx'', który uruchamia usługę ''nginx'' powinien być dodany do grupy użytkownika ''Gunicorn'':
usermod -aG user_app nginx
===== Problemy ACL =====
Wydajemy polecenie ''getfacl'' dla katalogu użytkownika aplikacji:
getfacl /home/user_app
getfacl: Usunięcie wiodącego '/' ze ścieżek bezwzględnych
# file: home/user_app
# owner: user_app
# group: user_app
user::rwx
user:qemu:--x
group::---
mask::r-x
other::---
Jak widać katalog nie należy do żadnej grupy. Dodajemy katalog do grupy użytkownika (taka sama nazwa jak nazwa użytkownika.
setfacl -m g:user_app:r-x /home/user_app
Możemy sprawdzić zmiany:
getfacl /home/user_app
getfacl: Usunięcie wiodącego '/' ze ścieżek bezwzględnych
# file: home/user_app
# owner: user_app
# group: user_app
user::rwx
user:qemu:--x
group::---
group:user_app:r-x
mask::r-x
other::---
Jeżeli nadal nie mamy dostępu musimy sprawdzić poszczególne katalogi:
ls -all -d /home/user_app/
drwxr-x---+ 1 user_app user_app 1380 01-03 11:01 /home/user_app/
Różnica między ''drwxr-x---+'' a ''drwxr-x---.'' polega na obecności atrybutu ''+'' w pierwszym przypadku. Ten atrybut oznacza, że istnieje dodatkowa kontrola dostępu, tak zwane Access Control List (ACL).
1. **drwxr-x---+**
- ''d'': Katalog.
- ''rwx'': Prawa dostępu właściciela (czytanie, zapisywanie, wykonywanie).
- ''r-x'': Prawa dostępu grupy (czytanie, brak zapisywania, wykonywanie).
- ''---'': Brak praw dostępu dla innych.
- ''+'': Wskaźnik na fakt, że istnieje ACL, który może zawierać dodatkowe reguły dostępu dla konkretnych użytkowników i grup.
2. **drwxr-x---.:**
- ''d'': Katalog.
- ''rwx'': Prawa dostępu właściciela (czytanie, zapisywanie, wykonywanie).
- ''r-x'': Prawa dostępu grupy (czytanie, brak zapisywania, wykonywanie).
- ''---'': Brak praw dostępu dla innych.
- ''.'': Wskazuje na to, że nie ma dodatkowych reguł ACL. To zazwyczaj oznacza, że kontrola dostępu opiera się wyłącznie na standardowych prawach dostępu (''owner'', ''group'', ''others'').
Atrybut ACL pozwala na bardziej elastyczną kontrolę dostępu niż standardowe prawa dostępu. Dzięki ACL można, na przykład, precyzyjnie kontrolować dostęp dla konkretnych użytkowników i grup, a także określać dodatkowe uprawnienia.
Na koniec przykład zmian uprawnień dla użytkownika:
setfacl -m u:nginx:r-x /home/user_app
===== Problemy SELinux =====
setsebool -P httpd_read_user_content 1
setenforce 1
getenforce
ausearch -c '(gunicorn)' --raw | audit2allow -M my-gunicorn
semodule -X 300 -i my-gunicorn.pp && rm -f my-gunicorn.*
===== NGINX i Gunicorn na jednym serwerze =====
Jeżeli Gunicon i NGINX jest na jednym serwerze komunikację między nimi najlepiej użyć za pomocą pliku socket.
Decyzja, czy plik socket Gunicorn powinien być tworzony w katalogu ''/tmp'' czy ''/run'', zazwyczaj zależy od preferencji i praktyk administratora systemu oraz zaleceń dystrybucji.
1. **/tmp**
* Często używane do przechowywania plików tymczasowych.
* Może zostać wyczyszczony podczas ponownego uruchamiania systemu.
* Odpowiednie dla procesów, które nie wymagają trwałego przechowywania plików socket.
* Może być bardziej dostępny dla różnych usług i użytkowników.
2. **/run**
* Zazwyczaj zawiera pliki związane z uruchamianiem usług.
* Często używane do plików, które muszą być dostępne między restartami systemu.
* Często dostępne tylko dla określonych usług i użytkowników.
* Często trwalsze niż ''/tmp''.
Najważniejsze jest, aby pamiętać o tym, żeby nadać odpowiednie uprawnienia do katalogu, w którym utworzysz plik socket, aby Gunicorn i Nginx mogły współpracować. Upewnij się również, że obie usługi (Gunicorn i Nginx) mają dostęp do tego samego katalogu i pliku socket. Ostateczna decyzja zależy od wymagań twojej konkretnego środowiska i preferencji.
Plik uruchamiający usługę Gunicorn:
[Unit]
Description=Gunicorn instance to serve application
After=network.target
[Service]
User=user_app
Group=user_app
StandardOutput=journal
StandardError=journal
WorkingDirectory=/home/user_app/PycharmProjects/venvFolder/FolderProjektDjango
ExecStart=/home/user_app/PycharmProjects/venvFolder/venv/bin/gunicorn --workers 3 --bind unix:/tmp/gunicorn.sock ProjektDjango.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=false
[Install]
WantedBy=multi-user.target
Należy zwrócić uwagę na opcję ''PrivateTmp=false''. Bez niej usługa może utworzyć plik w losowym podkatalogu. Przykładowo ''/tmp/systemd-private-1ab04e6700c74be2a90e7f49edc79e19-gunicorn.service-94a5le/tmp/gunicorn.sock''.
Plik konfiguracji NGiNX do Gunicorn:
server {
listen 80;
server_name localhost;
charset utf-8;
client_max_body_size 75M;
location /media {
alias /path/to/your/mysite/media;
}
location /static {
alias /home/user_app/PycharmProjects/venvFolder/FolderProjektDjango/static;
}
location / {
proxy_pass http://unix:/tmp/gunicorn.sock;
}
}
Podobnie jak z usługą ''gunicorn.service'' w pliku ''/etc/systemd/system/multi-user.target.wants/nginx.service'' musimy dokonać zmiany i opcję ''PrivateTmp=true'' zmienić na ''PrivateTmp=false''.
Możliwe, że SELinux będzie blokował dostęp NGiNX do pliku ''/tmp/gunicorn.sock''. Na to może pomóc:
ausearch -c 'nginx' --raw | audit2allow -M my-nginx
semodule -X 300 -i my-nginx.pp && rm -f my-nginx.*
===== Kolejność uruchamiania i wyłączania usług =====
Zachowanie określonej kolejności przy uruchamianiu i wyłączaniu usług Nginx i Gunicorn może być kluczowe w niektórych przypadkach. Oto kilka uwag na ten temat:
1. **Uruchamianie:**
* Gunicorn zazwyczaj musi zostać uruchomiony przed Nginx, ponieważ Nginx działa jako serwer HTTP, który przekierowuje żądania do Gunicorn, gdzie są obsługiwane przez aplikację Django lub inną aplikację WSGI.
* Gdy Gunicorn jest uruchamiany, może zaczekać na żądania od Nginx.
2. **Wyłączanie:**
* Kolejność wyłączania może być również ważna. Zazwyczaj zaleca się najpierw wyłączenie Nginx, a następnie Gunicorna.
* Wyłączenie Nginx może uniemożliwić nowym żądaniom docieranie do Gunicorn, co pozwala zakończyć obsługę istniejących żądań.
Przykładowy scenariusz wyłączania:
* Wyłącz Nginx: ''sudo systemctl stop nginx''
* Wyłącz Gunicorn: ''sudo systemctl stop gunicorn''
Pamiętaj, że konkretna kolejność może się różnić w zależności od konfiguracji systemu operacyjnego. Ważne jest, aby dostosować się do specyfiki środowiska i stosować praktyki zgodne z dokumentacją usług oraz zaleceniami dostawcy systemu operacyjnego.
Korzystnie jest zmodyfikować skrypt uruchamiania serwisu ''nginx'' i dodać opcje ustalającą uruchomienie ''nginx'' po usłudze ''gunicorn'':
After=network-online.target remote-fs.target nss-lookup.target gunicorn.service
===== Django i DokuWiki na systemie Windows =====
==== NGINX ====
Pobieramy najnowszą wersję [[https://nginx.org/en/download.html|nginx]]. Rozpakowujemy przykładowo w lokalizacji ''C:\nginx''.
W pliku ''C:\nginx\nginx.conf'' w sekcji ''http'' dodajemy opcję:
http{
...
include waitress.onf;
...
}
W tym samym katalogu tworzymy plik ''waitress.conf'' z zawartością:
server{
listen localhost:81;
location /static {
alias "C:/Python/PycharmProjects/DjangoProjects/project/static";
}
location / {
proxy_pass http://localhost:8080;
}
}
Należy zwrócić uwagę, że mimo konfiguracji nginx w systemie windows to w ścieżkach używamy ukośnika jak w systemie UNIX.
==== PHP ====
Pobieramy najnowszą wersję [[https://windows.php.net/download|php]]. Rozpakowujemy w katalogu ''C:\nginx\php''.
Oczywiście Django nie wymaga PHP ale tak przy okazji instalacji ''DokuWiki'' podaje jak to zrobić. Wracamy ponownie do pliku nginx.conf. Domyślnie w pliku powinna znajdować się sekcja jak poniżej. Należy ją odkomentować i jedynie domyślny wpis ''/scripts$fastcgi_script_name'' zmienić na ''$document_root$fastcgi_script_name''. Efekt jak poniżej:
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
==== Waitress ====
W środowisku naszej aplikacji Django instalujemy pakiet waitress:
pip install waitress
W katalogu naszego projekty Django tworzymy plik runserver.py z zawartością:
from waitress import serve
from project.wsgi import application
if __name__ == '__main__':
serve(application, host = 'localhost', port='8080')
Przy czym **project** w ''from project.wsgi import application'' to nazwa naszego projektu Django. Projektu, a nie aplikacji Django.
==== NSSM ====
Pobieramy najnowszą wersję [[https://nssm.cc/download|nssm]]. Rozpakowujemy w katalogu ''C:\nginx\nssm''. Jeżeli używamy wersji 64bit to wykonujemy następujące polecenia.
=== Tworzenie usługi waitress: ===
C:\nginx\nssm\win64\nssm.exe install WaitressService "C:\Python\PycharmProjects\DjangoProjects\.venv\Scripts\python.exe" "C:\Python\PycharmProjects\DjangoProjects\project\runserver.py"
Możemy sprawdzić status usługi:
C:\nginx\nssm\win64\nssm.exe status WaitressService
lub uruchomić:
C:\nginx\nssm\win64\nssm.exe start WaitressService
=== Tworzenie usługi PHP: ===
C:\nginx\nssm\win64\nssm.exe install PHPService "C:\nginx\php\php-cgi.exe" -b 127.0.0.1:9000
Możemy sprawdzić status usługi:
C:\nginx\nssm\win64\nssm.exe status PHPService
lub uruchomić:
C:\nginx\nssm\win64\nssm.exe start PHPService
=== Tworzenie usługi NGINX: ===
C:\nginx\nssm\win64\nssm.exe install NGINXService "C:\nginx\nginx.exe"
Możemy sprawdzić status usługi:
C:\nginx\nssm\win64\nssm.exe status NGINXService
lub uruchomić:
C:\nginx\nssm\win64\nssm.exe start NGINXService
Jeżeli zajdzie taka konieczność to usługę możemy zatrzymać:
C:\nginx\nssm\win64\nssm.exe stop NGINXService
lub usunąć:
C:\nginx\nssm\win64\nssm.exe remove NGINXService confirm
==== Jeżeli problem to sprawdź: ====
* Czy port 8080 lub inny jest taki sam w pliku ''runserver.py'' i ''waitress.conf''
* Czy port 9000 lub inny dla PHP jest taki sam w pliku ''nginx.conf'' i poleceniu startu usługi ''PHPService''
==== DokuWiki ====
Możemy katalog ''C:\nginx\html'' zmienić na ''C:\nginx\html_old''. Utworzyć nowy katalog ''C:\nginx\html''. W nowym katalogu ''html'' [[https://download.dokuwiki.org/|pobrać]] i rozpakować pliki ''dokuwiki''. Na potrzeby bezpieczeństwa aplikacji wprowadzić zmiany w pliku ''C:\nginx\nginx.conf'' i usunąć komentarze:
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
a pod nimi dodać:
location ~ /(data|conf|bin|inc|vendor)/ {
deny all;
}
Po zmianach restart usługi:
C:\nginx\nssm\win64\nssm.exe restart NGINXService
Tym sposobem zanim powstanie nasza aplikacja Django możemy na bieżąco w DokuWiki tworzyć do niej dokumentację.
Aby przesyłać pliki multimedialne przykładowo .jpg to w w pliku ''C:\nginx\nginx.conf'' dodajemy opcję:
http {
...
client_max_body_size 40M;
...
}
Następnie w pliku konfiguracyjnym ''C:\nginx\php\php.ini'':
[PHP]
post_max_size = 40M
upload_max_filesize = 40M
display_errors = Off
Restartujemy usługi PHP i NGINX.