Na przykładzie Fedora Linux 38
Plik settings.py:
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',]
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
[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
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:
/home
static
750
nginx
Gunicorn
usermod -aG user_app nginx
Wydajemy polecenie getfacl dla katalogu użytkownika aplikacji:
getfacl
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).
drwxr-x—+
drwxr-x—.
+
1. drwxr-x—+
d
rwx
r-x
—
2. drwxr-x—.:
.
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
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.*
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.
/tmp
/run
1. /tmp
2. /run
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.
PrivateTmp=false
/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.
gunicorn.service
/etc/systemd/system/multi-user.target.wants/nginx.service
PrivateTmp=true
Możliwe, że SELinux będzie blokował dostęp NGiNX do pliku /tmp/gunicorn.sock. Na to może pomóc:
/tmp/gunicorn.sock
ausearch -c 'nginx' --raw | audit2allow -M my-nginx semodule -X 300 -i my-nginx.pp && rm -f my-nginx.*
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:
2. Wyłączanie:
Przykładowy scenariusz wyłączania:
sudo systemctl stop nginx
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:
gunicorn
After=network-online.target remote-fs.target nss-lookup.target gunicorn.service
Pobieramy najnowszą wersję nginx. Rozpakowujemy przykładowo w lokalizacji C:\nginx.
C:\nginx
W pliku C:\nginx\nginx.conf w sekcji http dodajemy opcję:
C:\nginx\nginx.conf
http
http{ ... include waitress.onf; ... }
W tym samym katalogu tworzymy plik waitress.conf z zawartością:
waitress.conf
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.
Pobieramy najnowszą wersję php. Rozpakowujemy w katalogu C:\nginx\php.
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:
DokuWiki
/scripts$fastcgi_script_name
$document_root$fastcgi_script_name
# 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; }
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.
from project.wsgi import application
Pobieramy najnowszą wersję nssm. Rozpakowujemy w katalogu C:\nginx\nssm. Jeżeli używamy wersji 64bit to wykonujemy następujące polecenia.
C:\nginx\nssm
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
C:\nginx\nssm\win64\nssm.exe install PHPService "C:\nginx\php\php-cgi.exe" -b 127.0.0.1:9000
C:\nginx\nssm\win64\nssm.exe status PHPService
C:\nginx\nssm\win64\nssm.exe start PHPService
C:\nginx\nssm\win64\nssm.exe install NGINXService "C:\nginx\nginx.exe"
C:\nginx\nssm\win64\nssm.exe status NGINXService
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
runserver.py
nginx.conf
PHPService
Możemy katalog C:\nginx\html zmienić na C:\nginx\html_old. Utworzyć nowy katalog C:\nginx\html. W nowym katalogu html pobrać i rozpakować pliki dokuwiki. Na potrzeby bezpieczeństwa aplikacji wprowadzić zmiany w pliku C:\nginx\nginx.conf i usunąć komentarze:
C:\nginx\html
C:\nginx\html_old
html
dokuwiki
# 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:
C:\nginx\php\php.ini
[PHP] post_max_size = 40M upload_max_filesize = 40M display_errors = Off
Restartujemy usługi PHP i NGINX.