Django 是一個免費且開源的網頁應用程式框架,使用 Python 程式語言構建。Django 非常快速、安全且具有高度可擴充性。在熟練的開發人員手中,Django 可以快速建立一個強大的網站。它可以與流行的網頁伺服器(Apache, Nginx)和資料庫(MySQL, MariaDB, PostgreSQL, Oracle、以及 SQLite)等無縫整合。Django 支援了世界上一些最大的網站,例如 Instagram、Mozilla 和 NASA。本指南示範如何藉助 Django、PostgreSQL、Nginx 和 Gunicorn 在 Ubuntu 20.04 上建立網頁應用程式的基礎。
Prerequisites
本指南要求您運行已配置基本防火牆和具有 sudo 權限的非 root 使用者的 Ubuntu 20.04 伺服器。請參閱這篇關於如何設定 Ubuntu 伺服器的詳細指南。按照本教學來配置具有 sudo 權限的非 root 使用者。您也可以按照本指南的步驟配置 Iptables 防火牆.
我們將在虛擬環境中安裝 Django。擁有特定於專案的環境可以更輕鬆地在同一台伺服器上管理多個專案。一旦資料庫和應用程式設定就緒,我們將部署 Gunicorn 應用程式伺服器。Gunicorn 將作為應用程式介面,將用戶端的 HTTP 請求轉換為我們的應用程式可以使用的 Python 呼叫。然後,我們將在 Gunicorn 前端部署 Nginx,以利用其快速的連線處理效能和易於實作的安全特性。
安裝必要的套件
首先,從安裝所有必要的套件開始。幸運的是,所有這些套件都可以直接從 Ubuntu 官方套件庫中取得。打開終端機,並更新 APT 套件快取:
|
1 |
sudo apt update |
套件清單取決於網頁應用程式是要使用 Python 2 還是 Python 3。執行以下命令以使用 Python 3 安裝 Django:
|
1 |
sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl |
Django 1.11 LTS 是支援 Python 2 的最後一個 Django 版本。如果您打算將 Django 與 Python 2 搭配使用,請安裝以下套件:
|
1 |
sudo apt install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx curl |
PostgreSQL 資料庫和使用者
至於資料庫解決方案,我們將使用 PostgreSQL。它是一個強大、開源的物件關聯式資料庫系統。PostgreSQL 提供了可靠性、健全性和效能。有關設定 PostgreSQL 的詳細步驟,請參閱這篇關於在 Ubuntu 伺服器上設定 PostgreSQL的指南。在本指南中,我們將為 Django 應用程式設定一個專用的資料庫和使用者。
預設情況下,PostgreSQL 實作「同儕節點驗證」(peer authentication)作為本機連線的驗證配置。簡而言之,如果使用者的作業系統使用者名稱與有效的 PostgreSQL 使用者名稱相符,「同儕節點驗證」將會對登入進行驗證。在安裝過程中,PostgreSQL 配置了一個作業系統使用者 postgres 以對應到 postgres PostgreSQL 管理使用者。使用以下命令以 postgres 身份登入 PostgreSQL 互動式 shell 工作階段:
|
1 |
sudo -u postgres psql |
您將進入 PostgreSQL 提示字元。第一步是為專案建立一個專用資料庫。為了進行示範,資料庫將命名為 viktor_project:
|
1 |
CREATE DATABASE viktor_project; |
下一步是為專案資料庫建立一個專用使用者。該使用者應該有一個強大的使用者名稱。為了進行示範,使用者名稱將為 viktor_project_user:
|
1 |
CREATE USER viktor_project_user WITH PASSWORD 'password123'; |
現在,我們將修改一些參數:
- 某些連線參數。簡而言之,這將不需要在每次建立連線時都查詢並設定正確的值。這能大幅提升資料庫效能。
- 預設編碼為
UTF-8。這是一個通用編碼,也是 Django 所預期的。 - 預設交易隔離層級為「read committed」(讀取已提交)。它會阻止讀取未提交的交易。
- 時區為
UTC.
所有這些參數變更都是由 Django 專案本身所推薦的。要套用這些變更,請執行以下命令。別忘了將資料庫使用者名稱修改為正確的名稱:
|
1 2 3 |
ALTER ROLE viktor_project_user SET client_encoding TO 'utf8'; ALTER ROLE viktor_project_user SET default_transaction_isolation TO 'read committed'; ALTER ROLE viktor_project_user SET timezone TO 'UTC'; |
將資料庫管理員變更為專用的資料庫使用者:
|
1 |
GRANT ALL PRIVILEGES ON DATABASE viktor_project TO viktor_project_user; |
我們目前對 PostgreSQL 的操作已完成。退出 PostgreSQL 互動式 shell:
|
1 |
\q |
Python 虛擬環境
資料庫準備就緒後,我們現在可以專注於建立專案的其他需求。為了更方便管理,我們將建立一個虛擬環境並在其中安裝所有 Python 需求套件。要產生虛擬環境,我們需要 virtualenv。它可以使用 pip 輕鬆安裝。
以下命令將升級 pip 並安裝 virtualenv。對於 Python 3,請執行以下命令:
|
1 2 |
sudo -H pip3 install --upgrade pip sudo -H pip3 install virtualenv |
對於 Python 2,請改為執行以下命令:
|
1 2 |
sudo -H pip install --upgrade pip sudo -H pip install virtualenv |
一旦 virtualenv 安裝完成後,就可以建立虛擬環境了。接下來,為虛擬環境建立一個專用目錄:
|
1 |
mkdir -v ~/viktor_project |
之後,將目前的工作目錄切換到該虛擬環境的專用目錄:
|
1 |
cd ~/viktor_project |
在該目錄中,執行以下命令。 virtualenv 工具將會建立一個與專案名稱相同的虛擬環境:
|
1 |
virtualenv viktor_project |
它將會建立一個與專案名稱相同的子目錄。該子目錄將包含本機版本的 Python 和 pip。它提供了為專案安裝和設定隔離 Python 環境的彈性。
以下命令將啟用虛擬環境:
|
1 |
source viktor_project/bin/activate |
終端機提示字元將會改變,表示您正在 Python 虛擬環境中操作。既然我們已經進入虛擬環境,我們將安裝必要的 Python 需求套件。我們需要 Django、Gunicorn 和 psycopg2(PostgreSQL 轉接器)。以下命令將指示本機的 pip 安裝這些元件:
|
1 |
pip install django gunicorn psycopg2-binary |
即使您使用的是 Python 3,pip 也是正確的命令。這是因為在虛擬環境中,pip3 已被重新命名為 pip。
新的 Django 專案
Python 元件準備就緒後,我們就可以開始處理實際的 Django 專案檔案。
-
建立 Django 專案
專案目錄已經建立。我們將指示 Django 在該處安裝其檔案。此程序將產生一個包含實際程式碼的第二層目錄。該目錄還將包含一個管理指令碼。關鍵在於,我們是明確地告訴 Django 目標目錄,而不是讓它根據目前目錄來決定相對目錄:
|
1 |
django-admin.py startproject viktor_project ~/viktor_project |
Django 將相應地建立專案。以下是我們將重點關注的一些重要檔案和目錄。目錄和檔案名稱是根據示範使用的。
~/viktor_project/manage.py:Django 的專案管理腳本。~/viktor_project/viktor_project/:這是包含 Django 專案的套件。它應該包含 __init__.py、settings.py、urls.py、asgi.py 和 wsgi.py 等檔案。
-
調整專案設定
建立專案後,第一件事就是調整其設定。在文字編輯器中打開 settings.py:
|
1 |
nano ~/viktor_project/viktor_project/settings.py |
我們尋找的第一個指令是 ALLOWED_HOSTS。它定義了可以連接到 Django 實例的伺服器或網域名稱。如果任何傳入請求的 Host 標頭與 ALLOWED_HOSTS 列表不匹配,它將產生異常。Django 建議這樣做以避免某些類型的安全性漏洞:
|
1 |
ALLOWED_HOSTS = ['<server_ip_or_domain_name_1>',' server_ip_or_domain_name_2','localhost'] |
我們接下來要關注的部分是 DATABASE。它管理資料庫存取。預設情況下,它包含 SQLite 資料庫引擎的設定。然而,我們將在專案中使用 PostgreSQL 資料庫。Django 將使用 psycopg2 轉接器與 PostgreSQL 進行通訊:
|
1 2 3 4 5 6 7 8 9 10 |
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'viktor_project', 'USER': 'viktor_project_user', 'PASSWORD': 'password123', 'HOST': 'localhost', 'PORT': '', } } |
現在,移至檔案底部。新增以下幾行以指示靜態檔案的位置。這有助於 Nginx 處理對這些項目的請求:
|
1 2 |
import os STATIC_ROOT = os.path.join(BASE_DIR, 'static/') |
我們對 settings.py 的工作暫時完成了。儲存檔案並關閉編輯器。
-
完成初始專案設定
我們現在可以將初始資料庫結構遷移到專用的 PostgreSQL 資料庫。執行以下命令:
|
1 2 |
~/viktor_project/manage.py makemigrations ~/viktor_project/manage.py migrate |
接下來,我們需要為專案建立一個超級使用者。要產生超級使用者,請執行以下命令:
|
1 |
~/viktor_project/manage.py createsuperuser |
將所有靜態檔案收集到我們在 settings.py 中指定的位置。靜態檔案將收集在專案目錄下名為「static」的獨立目錄中:
|
1 |
~/viktor_project/manage.py collectstatic |
現在,我們需要調整伺服器防火牆。如果您遵循了伺服器設定指南,那麼您應該已經設定並啟用了 UFW。我們將為連接埠 8000 建立一個例外。這是 Django 使用的預設連接埠。請參閱此指南以了解更多關於 UFW 防火牆基礎知識與用法.
|
1 |
sudo ufw allow 8000 |
接下來,驗證該操作:
|
1 |
sudo ufw status |
最後,我們可以測試伺服器的實際運作。啟動 Django 開發伺服器:
|
1 |
~/viktor_project/manage.py runserver 0.0.0.0:8000 |
如果設定成功, Django 開發伺服器應該會啟動並接受傳入的請求。打開瀏覽器並前往您伺服器的 IP/網域的連接埠 8000:
|
1 |
http://<server_or_domain>:8000 |
您應該會進入 Django 預設的首頁。要存取管理面板,請在 URL 後面加上 /admin。管理面板僅能由我們事先建立的超級使用者存取:
|
1 |
http://<server_or_domain>:8000/admin |
登入後,您將進入預設的 Django 管理介面:
我們目前已完成測試。若要停止伺服器,請在終端機視窗中按「Ctrl + C」。
-
測試 Gunicorn
在離開虛擬環境之前,我們要確保 Gunicorn 可以提供應用程式服務。測試的方法是使用 Gunicorn 來載入專案的 WSGI 模組。
Gunicorn 指令位於專案目錄中:
|
1 2 |
cd ~/viktor_project gunicorn --bind 0.0.0.0:8000 viktor_project.wsgi |
這將在 Django 運行的相同介面上啟動 Gunicorn。我們可以再次從一般的網頁瀏覽器測試該應用程式。請注意,管理介面不會套用任何樣式,因為 Gunicorn 仍然不知道如何找到靜態 CSS 內容:
|
1 |
http://<server_or_domain>:8000 |
完成後,在終端機視窗中按「Ctrl + C」以停止 Gunicorn 伺服器。
-
退出虛擬環境
Django 應用程式設定已完成。執行以下指令以退出虛擬環境:
|
1 |
deactivate |
Gunicorn Socket 與服務檔案
我們已驗證 Gunicorn 可以與 Django 應用程式互動。然而,我們需要一種更穩健的方式來管理應用程式伺服器。這時 systemd 就派上用場了。Systemd 是 Linux 上最受歡迎的初始化系統(init system)之一。這裡有一份深入指南,介紹 如何管理 systemd 服務與單元.
我們可以為 Gunicorn 建立 socket 和服務檔案,讓 systemd 像管理服務一樣管理它。在開機時,系統會產生 Gunicorn socket。該 socket 將監聽傳入的連線。當連線發生時,systemd 將啟動 Gunicorn 程序來處理該連線。
-
Gunicorn socket
讓我們從建立 Gunicorn socket 開始。該檔案需要使用 sudo 權限建立:
|
1 |
sudo nano /etc/systemd/system/gunicorn.socket |
在檔案中輸入以下程式碼:
|
1 2 3 4 5 6 7 |
[Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target |
如您所見,程式碼包含三個部分。
[Unit]:此部分說明 socket。[Socket]:它定義了 socket 的位置。[Install]:此部分確保 systemd 在正確的時間建立 socket。
儲存檔案並關閉編輯器。
-
Gunicorn 服務
接下來,我們將為 Gunicorn 建立一個服務檔案。與 socket 檔案類似,它也必須使用 sudo 權限建立:
|
1 |
sudo nano /etc/systemd/system/gunicorn.service |
輸入以下程式碼:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=cloudsigma Group=www-data WorkingDirectory=/home/cloudsigma/viktor_project ExecStart=/home/cloudsigma/viktor_project/viktor_project/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ viktor_project.wsgi:application [Install] WantedBy=multi-user.target |
此程式碼包含多個部分:
[Unit]:此部分指定中介資料(metadata)與相依性。它也說明了只有在達到網路目標(network target)後才會啟動。[Service]:此部分指定了運行該程序的使用者和群組。群組的所有權設定為「www-data」,以便 Nginx 可以與 Gunicorn 進行通訊。它還規劃了工作目錄並指定了啟動命令。[Install]:此部分告訴 systemd 如果在開機時啟用此服務,應將其連結到什麼。它應該在常規多使用者系統開始運行後啟動。
接下來,儲存檔案並關閉編輯器。
-
啟用 Gunicorn socket
Gunicorn socket 已準備就緒。因此,您可以運行以下命令。它將啟動並啟用該 socket。Socket 檔案將在開機時建立於 /run/gunicorn.sock 。當連接到該 socket 時,systemd 將啟動 Gunicorn 服務來處理它:
|
1 2 |
sudo systemctl start gunicorn.socket sudo systemctl enable gunicorn.socket |
檢查 Gunicorn socket 的狀態:
|
1 |
sudo systemctl status gunicorn.socket |
現在,檢查 socket 檔案是否存在:
|
1 |
file /run/gunicorn.sock |
如果 systemctl 的狀態顯示錯誤,或者找不到 gunicorn.sock 檔案,則表示 socket 未正確建立。請查看詳細日誌以獲取線索:
|
1 |
sudo journalctl -u gunicorn.socket |
別忘了再次檢查 gunicorn.socket 檔案以尋找潛在的錯誤。
-
Socket 啟用
到目前為止,我們已經啟動了 gunicorn.socket 。然而,在沒有任何連接請求的情況下,gunicorn.service 將不會啟用。接下來,驗證 Gunicorn 的狀態:
|
1 |
sudo systemctl status gunicorn |
我們可以透過使用 curl:
|
1 |
curl --unix-socket /run/gunicorn.sock localhost |
您應該會從應用程式中獲得 HTML 輸出。這表示 Gunicorn 已成功啟動並能夠為 Django 應用程式提供服務。驗證 Gunicorn 服務的目前狀態:
|
1 |
sudo systemctl status gunicorn |
如果出現任何異常行為或輸出(表示有錯誤),請查看詳細日誌以獲取線索:
|
1 |
sudo journalctl -u gunicorn |
如果對 gunicorn.service 檔案進行了更改,則需要重新載入守護程序(daemon)以重新讀取服務定義。這還需要重新啟動 Gunicorn 服務:
|
1 2 |
sudo systemctl daemon-reload sudo systemctl restart gunicorn |
設定 Nginx
現在,我們將設定 Nginx 以將傳入的流量傳遞給該程序。首先,在 Nginx 中建立一個新的伺服器區塊(server block):
|
1 |
sudo nano /etc/nginx/sites-available/viktor_project |
然後,輸入以下程式碼:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
server { listen 80; server_name 31.171.250.71; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/cloudsigma/viktor_project; } location / { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } } |
以下是設定中的多個區塊:
service:此區塊定義了伺服器應正常監聽連接埠 80,並應回應伺服器的網域名稱或 IP 地址。location:這是第一個 location 項目。它定義了在哪裡可以找到靜態資源。location:這是第二個 location 項目。此區塊定義了標準代理參數以及如何將流量傳遞到 Gunicorn socket。
儲存檔案並關閉編輯器。將檔案連結到「sites-enabled」目錄以啟用:
|
1 |
sudo ln -s /etc/nginx/sites-available/viktor_project /etc/nginx/sites-enabled |
之後,測試 Nginx 設定中是否有任何語法錯誤:
|
1 |
sudo nginx -t |
如果您沒有發現任何錯誤,請重新啟動 Nginx 以套用變更:
|
1 |
sudo systemctl restart nginx |
我們需要再次修改 UFW 規則。我們不再需要存取開發伺服器,因此可以移除連接埠 8000 的例外規則。此外,我們希望開放連接埠 80 以供一般流量使用:
|
1 2 |
sudo ufw delete allow 8000 sudo ufw allow 'Nginx Full' |
驗證這些防火牆規則變更:
|
1 |
sudo ufw status |
現在應該可以從一般的網頁瀏覽器存取該伺服器。
疑難排解步驟
如果正確遵循了所有步驟,應該就可以透過網際網路存取 Django 應用程式。如果不行,則表示安裝未按計劃進行。我們需要進行疑難排解以找出問題的根源。
-
Nginx 顯示預設頁面
如果 Nginx 顯示的是預設頁面而不是應用程式代理,通常意味著 server_name 在伺服器區塊中設定錯誤。
在此範例中,伺服器區塊儲存在以下位置:
|
1 |
/etc/nginx/sites-available/viktor_project |
The server_name 項目決定了 Nginx 將使用哪個伺服器區塊來回應請求。如果顯示預設頁面,那麼 Nginx 可能無法將請求與明確的伺服器區塊進行比對,因此它會退回到預設區塊:
|
1 |
/etc/nginx/sites-available/default |
檢查您的 Django 專案的伺服器區塊是否具有正確的 server_name.
-
502 Bad Gateway
錯誤 502 表示 Nginx 無法成功代理請求。有許多可能的設定問題會導致錯誤 502,因此我們需要線索來進行正確的疑難排解。
線索的主要來源是 Nginx 錯誤記錄。通常,它會提示在代理過程中導致問題的狀況。使用以下命令檢查 Nginx 錯誤記錄:
|
1 |
sudo tail -F /var/log/nginx/error.log |
開啟記錄後,嘗試再次存取伺服器。它應該會在記錄中產生一條新的錯誤訊息。這有助於縮小問題範圍。以下是幾條可能的訊息:
- connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)
這表示 Nginx 在設定中定義的位置找不到 gunicorn.sock。該位置由網站區塊下的 proxy_pass 指令所描述。檢查 proxy_pass 是否指示了 gunicorn.sock(由 gunicorn.socket systemd 單元產生)的正確位置:
|
1 |
/etc/nginx/sites-available/viktor_project |
如果 gunicorn.sock 沒有在 /run 目錄下被找到,這意味著 systemd 無法產生它。您應該重新檢查 Gunicorn socket 檔案的設定步驟。
- connect() to unix:/run/gunicorn.sock failed (13: Permission denied)
這表示由於權限問題,Nginx 無法連線到 Gunicorn socket。如果該程序是以 root 使用者而不是 sudo 使用者執行的,就可能會發生這種情況。雖然 systemd 成功產生了 gunicorn.sock ,但 Nginx 無法使用它。
一個可能的元兇可能是根目錄 (/) 與 gunicorn.sock 檔案之間的權限受限。檢查 socket 檔案及其每個父目錄的權限和擁有權:
|
1 |
namei -l /run/gunicorn.sock |
第一欄描述檔案權限。第二欄描述擁有者使用者,第三欄描述擁有者群組。如果通往 gunicorn.sock 的任何目錄沒有適當的讀取和執行權限, Nginx 將無法存取該 socket。
-
Django 顯示 “could not connect to the server: Connection refused”
這表示 Django 無法連線到 PostgreSQL 伺服器。請確保 PostgreSQL 伺服器已啟動並正在執行:
|
1 |
sudo systemctl status postgresql |
如果它尚未執行,請執行以下命令來啟動並啟用它:
|
1 2 |
sudo systemctl start postgresql sudo systemctl enable postgresql |
如果您仍然遇到此錯誤,請確保資料庫憑證已正確定義於 settings.py:
|
1 |
~/viktor_project/viktor_project/settings.py |
更多疑難排解
為了進行其他疑難排解,系統中設定了各種記錄檔。這些記錄檔可以幫助縮小問題來源的範圍。
以下是可提供幫助的記錄檔清單:
- Nginx 記錄檔
|
1 |
sudo journalctl -u nginx |
- 存取記錄檔-Nginx
|
1 |
sudo less /var/log/nginx/access.log |
- 錯誤記錄檔-Nginx
|
1 |
sudo less /var/log/nginx/error.log |
- 應用程式記錄檔-Gunicorn
|
1 |
sudo journalctl -u gunicorn |
- Socket 記錄檔-Gunicorn
|
1 |
sudo journalctl -u gunicorn.socket |
|
1 |
sudo systemctl restart gunicorn |
|
1 2 |
sudo systemctl daemon-reload sudo systemctl restart gunicorn.socket gunicorn.service |
|
1 2 |
sudo nginx -t sudo systemctl restart nginx |
結語
本指南成功示範了如何奠定 Django 的基礎。Django 提供了網頁應用程式的許多常用元件,讓您可以專注於獨特的元素。Django 專案將在虛擬環境中運作。Gunicorn 負責管理用戶端請求與 Django 之間的通訊。最後,Nginx 作為反向代理來處理用戶端連線。
祝您使用愉快!










留言
目前尚無留言。成為第一個留言的人吧。