逐步指南:將 Django 專案部署至 Ubuntu Server 上的 Nginx

再也不用受第三方平台的任何流量及檔案限制

Posted by Young on 2022-11-16
Estimated Reading Time 12 Minutes
Words 2.9k In Total
Viewed Times

部署方式

網路上部署教學非常多,但 95%都是教怎麼透過第三方部署平台例如 Heroku,Azure,GCP…等,但我就是想架在自己架設的 Ubuntu 主機上阿!而關於各式部署方法及各自的優缺點我會再另外寫一篇文章,此篇就不多加贅述

使用的工具、系統

此篇教學主要是在 Ubuntu 實作,當然用 Windows 也可以,但建議若想更有效率的管理伺服器,Linux 是必備技能,功能跟穩定性都是直接把 Windows 壓在地板上磨擦.
本篇教學重點會著重於 Django,uWSGI,Nginx,其餘提到的技術我會在專門另寫文章給大家參考

DNS 基礎知識

當使用者輸入 Domain Name 後,Web 必須要先去一台有 Domain Name 和 IP 對應資料的主機去查詢這台電腦的 IP,而這台被查詣的主機為 Domain Name Server,簡稱 DNS.
Ex:當你輸入www.shopee.com.tw 時,Web 就會將 www.shopee.com.tw傳送最近的 DNS Server 去做辨識,若查詢有結果,則會傳回這台主機的 IP,進而跟它索取資料,若沒查到,就會發生 DNS NOT FOUND 的情形,而每個 Domain Name 對應要一組 IP,且 Domain Name 跟 IP 一樣不會重覆


一般架設網站時不管你是 Code from Scratch 還是用 Wordpress 等都需申請 DNS 指向你的 IP,畢竟大家也不可能去記 IP 位址來找網站吧,申請完後才辦法讓自己以外的人找到你自行定義的網域

購買網域

我購買網域的平台為 cloudflare,個人認為他的平台是我用過 UI 最人性化且支援暗黑模式的,未來有購買網域需求不妨去 Cloudflare 網域管理平台 看看

domain_price

開始之前

連進 Server 第一件事不外乎就是先更新所有套件庫

1
sudo apt-get update && sudo apt-get upgrade -y

建立 Virtual Enviornment(虛擬環境)

不管是開發中小、大型專案,建立一個獨立的開發環境是個好習慣,能讓自己或開發團隊都比較好 Debug 且不會有一堆不需要的套件而造成需要去追源碼的情況發生

1
2
3
sudo apt-get install python3-venv
python3 -m venv venv
source venv/bin/activate

虛擬環境

建立完後檢查是否真的已在虛擬環境中,檢查目前 Python 是跑 global 還是 venv 裡的
which_python

建立 Django 專案

現在建立一個開發環境的前置作業相較以前簡單許多,不需要先架好 Server 就能直接開始寫 Code 了

1
2
3
pip install django
django-admin.py startproject yourproject_name
cd yourproject_name

建立完後就先來python manange.py runserver看是否有問題,預設為:8000 PORT,成功的話應看到以下畫面
runserver

測試網域是否運作正常

再來嘗試用你申請好的網域名稱去網頁試跑看看是否成功,若出現 DisallowedHost 錯誤代表你需要去你建立好的 Django 專案的settings.py的ALLOWED_HOSTS['']新增你的網域名稱。

注意!!!如果你的網路環境或主機設定有防火牆或其他限制,可能會導致從遠端無法連接到你的主機,也就是說用 0.0.0.0:8000 去 runserver 反而會連不到。我的情況就是只有內部網路能存取

1
2
3
4
5
6
7
8
9
10
11
12
#settings.py
SECRET_KEY = 'djang.....'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['py.butyshop.com','192.168.1.101']

# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
....,
]

ALLOWED_HOSTS 預設為空白,意思就是除了 localhost 以外其他的外部 IP 都無法連上這個網站

測試 uWSGI 是否有作用

在還沒在 Nginx 設定 .conf 檔前,記得永遠都是去 0.0.0.0:8000 或是 (防火強有擋的話就主機內部IP)192.168.1.101 去看結果畫面,而不是申請好的 DNS 網址(Ex:py.butyshop.com),畢竟還沒去 /etc/nginx/sites-available 寫 .conf 檔, uWSGI 也不知道要導到哪裡去。

uWSGI 可想成是 Django 跟 Server 的中繼站,它是 Python 程式及如 Django,Flask 等和 Web 服務之間的一種接口,可以處理大量的並發請求,支持多種協議和輸入輸出格式,包括 HTTP、FastCGI、SCGI、WebSocket、UDP、TCP 等。

1
2
3
sudo apt-get install python3.10-dev
sudo apt-get install gcc
pip install uwsgi

最終目標就是要將 client 端的 requests 傳送給 Nginx 並傳遞給暫存 socket 後,socket 會再拿給 uWSGI 處理最後才終於傳到 Django。
uWSGI_client_to_server

編寫測試 uWSGI

建立一個 test.py 測試一下 uWSGI 的功能

1
2
3
4
# test.py
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World!"]

寫好後去終端機下 uWSGI 指令,這步是為了測試 uWSGI 是否有成功與 Django 取得聯繫

1
uwsgi --http :8000 --wsgi-file test.py

此時你去yourdomain:8000看到 “Hello World!”,代表 uWSGI 成功將請求傳遞給 Python。接下來就可以使用以下命令類似地使用 uWSGI 服務 Django 項目。此時再去yourdomain:8000應能看到 Django 預設畫面。

1
uwsgi --http :8000 --module <FolderName>.wsgi # Django專案底下的 wsgi.py

如果這一步畫面顯示了Internal Server Error,不妨看一下虛擬環境 venv 有沒有啟動。

Nginx Server 設定

最後終於來到 Server 端的部分,不多說廢話,直接開始

1
sudo apt-get install nginx

此時你在去http://yourdomain查看應能看到 Nginx 下載成功的 “Welcome to nginx!”頁面

Nginx .conf 檔案設定

此時去主機的/etc/nginx/sites-available建立 nginxConfig.confyourdomain= 申請好的 DNS 網址名稱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# the upstream component nginx needs to connect to
upstream django {
server unix:///home/httpd/microdomains/microdomains.sock;
}
# configuration of the server
server {
listen 443 ssl http2;
server_name py.butyshop.com;
charset utf-8;
# max upload size
client_max_body_size 75M;

# Django media and static files
location /media {
alias /home/httpd/microdomains/media;
}
location /static {
alias /home/httpd/microdomains/static;
}
# Send all non-media requests to the Django server.
location / {
uwsgi_pass django;
include /home/httpd/microdomains/uwsgi_params;
}

ssl_certificate /etc/letsencrypt/live/akebee.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/akebee.com/privkey.pem; # managed by Certbot
}

uwsgi_params

這個檔案是在專案目錄底下而不是 nginx 目錄底下,我的話就是在/home/httpd/django_project_name這建立 uwsgi_params。

  • 用於定義轉發到 uWSGI 服務器的 HTTP 請求參數。
  • 此文件列出了所有必要參數,以便 Nginx 可以通過正確的方式將請求傳遞給 uWSGI。
  • 例如:QUERY_STRING 定義了 HTTP 請求中的查詢字符串。uWSGI 需要這些變量來識別請求的內容,並正確地將其轉發到相應的應用程序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# /home/xxxxx/<project_name>/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 REQUEST_SCHEME $scheme;
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;

建立軟連結

告訴 Nginx 我這個網址要啟用了,一般可以把所有網站寫到include檔內就不必每次都得寫這行

1
sudo ln -s /etc/nginx/sites-available/yourdomains.conf /etc/nginx/sites-enabled/

讓 Nginx 知道 static_files 的位置

我自己會習慣將靜態檔案位置設定加在最下方

1
2
3
4
5
6
7
8
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
......................

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")

最後執行這行,這步驟是要告訴 Django 將我們的靜態檔案js,css,img…等等全部打包進我們指定的目錄底下

1
python manage.py collectstatic

重啟 Nginx Server

1
sudo -s nginx reload

若成功應該就會在http://yourdomain/media/test.png 看到你放進指定的目錄的圖片了

使 Nginx, uWSGI, Django 成功串接

讓 uWSGI 生成 socket 來達到雙向溝通的目的,chmod666是為了要讓 socket 有權限寫入檔案

1
uwsgi --socket yourdomains.sock --module yourdomains.wsgi --chmod-socket=666
  • --socket microdomains.sock: 指定 uWSGI 與應用程序之間通訊的 socket 名稱,這裡命名為 “yourdomains.sock”。
  • --module microdomains.wsgi: 指定要運行的 Python 模塊,這裡為 "microdomains.wsgi ",這個模塊中包含了應用程序的主要邏輯。
  • --chmod-socket=666: 設置 socket 文件的權限,這裡設置為 666=所有用戶都可以對其進行讀寫(rw)。

撰寫 uWSGI.ini (正式用)

在正式發佈階段時 uWSGI 夾帶的參數指令會變得較繁雜,因此直接寫一個.ini檔就可省得每次都得打一長串

1
2
3
4
5
6
7
8
9
10
11
12
13
[uwsgi]
chdir = /home/httpd/microdomains # Django專案位置
module = microdomains.wsgi # # 主要運行的 py 檔案
home = /home/httpd/venv # 虛擬環境 venv 位置

master = true # enable uwsgi master process
processes = 10 # maximum number of worker processes

socket = /home/httpd/microdomains/microdomains.sock # the socket (use the full path to be safe)
chmod-socket = 666 # socket permissions

vacuum = true # clear environment on exit
daemonize = /home/httpd/venv/vassals/uwsgi-emperor.log # uwsgi-emperor.log 存放位置

之後在執行 uwsgi 指令只需簡潔的一行即可

1
uwsgi --ini yourdomains_uwsgi.ini

成功畫面

最後在去你自己的網址 http://yourdomain.com 就能看到 Django 初始頁面了!此時把此網址貼給你的朋友,他也能看到屬於你自己的網站了。
deploy_success

Emperor mode

何謂emperor mode?一般會在大型專案開發時遇到,在這種模式下,uWSGI 啟動一個特殊的 master 進程,稱為"emperor",該進程可以監控一個或多個虛擬主機或應用程序的進程。

這些虛擬主機或應用程序可以是不同的 Python 應用程序,也可以是不同的框架或應用程序。有興趣了解他的功能可以直接去看官方 docs

1
2
3
4
5
6
7
# 去 venv 目錄下新增 vassals 資料夾
cd /home/httpd/venv
mkdir vassals
# 將 uwsgi.ini 檔 link 至 vassals 下
sudo ln -s /home/httpd/django_project_name/uwsgi.ini /home/httpd/venv/vassals/
# 用 emperor mode 執行 uWSGI
uwsgi --emperor /home/httpd/venv/vassals/ --uid www-data --gid www-data

更改 uwsgi.ini 的 uwsgi-emperor.log 位置

由於原本我的 uwsgi-emperor.log 的位置是跟其他專案一起放在/home/httpd/之下,我覺得有點亂,因此我就想將換到/home/httpd/venv/vassals/底下。

1
2
3
4
5
6
7
8
# 編輯 uwsgi.ini
vim /home/httpd/django_project_name/uwsgi.ini
# uwsgi.ini 內容
# enable uwsgi master process
.........
vacuum = true
# 將這邊路徑改成新的地方
daemonize = /home/httpd/venv/vassals/uwsgi-emperor.log

改完路徑後還沒完,此時直接用 emperor mode 執行 uwsgi 會出現permission denined的問題,代表他沒有權限可以編輯uwsgi-emperor.log檔案。

1
2
3
4
5
6
# 原本的權限狀態
-rw-r----- 1 root root 2.6K 3月 10 09:52 uwsgi-emperor.log
# 更改目錄所有權為 www-data 用戶和組
sudo chown -R www-data:www-data /home/httpd/venv/vassals/
# 再次用 ll 指令察看更改後的權限狀態
-rw-r----- 1 www-data www-data 5.2K 3月 10 10:04 uwsgi-emperor.log

這樣做的目的是為了確保 uwsgi 服務可以以 www-data 用戶和組的身份運行,這樣 uwsgi 就可以擁有對/home/httpd/venv/vassals/目錄和其子目錄的讀取、寫入和執行權限。此時再執行uwsgi --emperor /home/httpd/venv/vassals/ --uid www-data --gid www-data應就能看到預設畫面了。

開機自動執行程式碼

將執行程式寫入 rc.local 檔讓他開機自動背景執行

1
2
sudo vim /etc/rc.local
su -l young -c "cd /home/httpd/django_project_name/ uwsgi --ini uwsgi.ini"

SSL 憑證

2022 年了,網路安全性已普及,因此只要能到的網站幾乎都是https://,若網站沒有 SSL 憑證就不會有https://,SEO 會大幅降低,也會被大多數瀏覽器封鎖,在.conf裡面的那兩行 SSL_certificate 是申請 SSL 憑證的證明,關於 SSL 可以參考我的另外一篇文章 Hexo 部署至 Nginx 設置 CI/CD 流程,此篇就不再多加贅述。

用 letsencrypt 加上 SSL 憑證後,nginx 底下的 .conf 檔案就會自動將 80 PORT 轉 443。listen 443 ssl; 會自動出現

完成

感謝大家的閱讀,若有問題歡迎留言。


若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~


留言版