顯示具有 Nginx 標籤的文章。 顯示所有文章
顯示具有 Nginx 標籤的文章。 顯示所有文章

2024年2月21日 星期三

在樹莓派 Nginx 伺服器上佈署 Django 專案

今天在整理筆記時找到這篇 2019 年底寫的文章, 看起來有發布過, 不知為何又被改回草稿, 看完後覺得具有參考價值, 所以重新發布 : 

[2019-09-11] 

這幾天重回睽違一季的 Django 學習軌道, 雖然感覺有點陌生, 不過複習之前的三篇測試紀錄之後很快就恢復戰力了 :
  1. 在樹莓派 Raspbian Stretch 上安裝 Nginx 伺服器
  2. 在樹莓派 Nginx 伺服器上執行 Python 網頁應用程式
  3. Python 學習筆記 : Django 2 測試 (一) : 請求與回應處理
第三篇的 Django 2 測試 (一) 是在 Win 10 上進行的, 以下實驗則是在樹莓派 Pi 3 上進行.


1. 安裝或更新 Django :

因為之前安裝的 Django 版本可能有點舊了, 所以在重新測試之前先用 pip3 install django 指令升版, 在後面加上 -U 參數可更新到最新版 :

pi@raspberrypi:~ $ pip3 install django -U 
Collecting Django
  Using cached https://files.pythonhosted.org/packages/39/b0/2138c31bf13e17afc32277239da53e9dfcce27bac8cb68cf1c0123f1fdf5/Django-2.2.3-py3-none-any.whl
Collecting sqlparse (from Django)
  Using cached https://files.pythonhosted.org/packages/ef/53/900f7d2a54557c6a37886585a91336520e5539e3ae2423ff1102daf4f3a7/sqlparse-0.3.0-py2.py3-none-any.whl
Collecting pytz (from Django)
  Using cached https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl
Installing collected packages: sqlparse, pytz, Django
Successfully installed Django-2.2.3 pytz-2019.1 sqlparse-0.3.0
pi@raspberrypi:~ $ python3 -m django --version 
2.2.3 

可見 Django 目前最新版為 v2.2.3, 有兩個相依套件 sqlparse  與 pytz.


2. 在 Nginx 虛擬目錄下建立 Djando 網站 :

我的目標是在樹莓派上以 Nginx 作為 HTTP 伺服器佈署 Django 專案, Nginx 伺服器的操作指令整理如下表 :

 Nginx 伺服器操作指令 說明
 nginx -s start 啟動伺服器
 nginx -s stop 停止伺服器
 nginx -t 檢視伺服器狀態
 nginx -s reload 重啟伺服器

Nginx 伺服器的所有站台設定檔放在 /etc/nginx/sites-enabled/ 目錄下, 預設站台設定檔為 default, 其預設根目錄絕對路徑為 /var/www/html, 預設監聽埠號為 80 埠. 安裝好 Nginx 後在瀏覽器中開啟 localhost 或 127.0.0.1 或我為樹莓派指定之固定 IP 192.168.2.192 都會看到一個 Nginx 的歡迎網頁.

我們可以直接在 /var/www/html 下佈署 Web 專案, 但為了保留 Nginx 預設站台之歡迎網頁 (作為測試 Nginx 有否開啟用), 我在前次的 "在樹莓派 Raspbian Stretch 上安裝 Nginx 伺服器" 這篇裡面已於 /var/www/ 下建立一個子目錄 test, 除了將 /var/www/test 設定為虛擬目錄外, 還進一步將此虛擬目錄設定成一個虛擬站台 test.com, 亦即若在 /var/www/test 下佈署 Django 專案的話, 可以用 localhost/test/mysite 或 test.com/mysite 去瀏覽網頁.

OK, 這樣就可以在 /var/www/test 底下建立一個 Django 網站了, 但是一出手就不太順, 直接以 Windows 下所用的 django-admin startproject mysite 指令建立網頁專案時就出現 "Command not found" 錯誤 :

pi@raspberrypi:~ $ cd /var/www/test 
pi@raspberrypi:/var/www/test $ django-admin startproject  mysite 
bash: django-admin:命令找不到  

經查詢才知道原來在 Linux 底下還要幫所安裝的 Django 套件在 /usr/local/bin 底下建立連結, 才能在任何目錄下執行其管理指令, Windows 因為在安裝 Python 時已把 Scripts 放在環境變數裡, 因此可以馬上執行所安裝的套件, 參考 :

# Command not found: django-admin.py
# -bash: django-admin: command not found

用 pip3 show 檢視 Django 安裝位置 :

pi@raspberrypi:~ $ pip3 show django 
Name: Django
Version: 2.2.3 
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD
Location: /home/pi/.local/lib/python3.5/site-packages
Requires: sqlparse, pytz

可見 Django 被安裝在 /home/pi/.local/lib/python3.5/site-packages 底下, 用 ls 指令檢視 djando 子目錄 :

pi@raspberrypi:/var/www/test $ ls -ls /home/pi/.local/lib/python3.5/site-packages/django
總計 84
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 apps
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 bin 
4 drwxr-xr-x  7 pi pi 4096  4月  9 00:37 conf
4 drwxr-xr-x 18 pi pi 4096  4月  9 00:37 contrib
4 drwxr-xr-x 11 pi pi 4096  4月  9 00:37 core
4 drwxr-xr-x  6 pi pi 4096  4月  9 00:37 db
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 dispatch
4 drwxr-xr-x  5 pi pi 4096  4月  9 00:37 forms
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 http
4 -rw-r--r--  1 pi pi  799  7月 31 08:30 __init__.py
4 -rw-r--r--  1 pi pi  211  7月 31 08:30 __main__.py
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 middleware
4 drwxr-xr-x  2 pi pi 4096  4月  9 00:37 __pycache__
8 -rw-r--r--  1 pi pi 5588  7月 31 08:30 shortcuts.py
4 drwxr-xr-x  5 pi pi 4096  4月  9 00:37 template
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 templatetags
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 test
4 drwxr-xr-x  3 pi pi 4096  4月  9 00:37 urls
4 drwxr-xr-x  4 pi pi 4096  4月  9 00:37 utils
4 drwxr-xr-x  6 pi pi 4096  4月  9 00:37 views

Django 的工具程式 django-admin 則是放在 bin 子目錄下 :

pi@raspberrypi:~ $ ls -ls /home/pi/.local/lib/python3.5/site-packages/django/bin  
總計 8
4 -rwxr-xr-x 1 pi pi  128  7月 30 16:45 django-admin.py    
4 drwxr-xr-x 2 pi pi 4096  4月  9 00:37 __pycache__

所以必須為此 django-admin.py 在 /usr/local/bin 底下建立連結才能在任何目錄下執行此程式 :

sudo ln -s /home/pi/.local/lib/python3.5/site-packages/django/bin/django-admin.py /usr/local/bin

在建立連結之前先檢查 /usr/local/bin :

pi@raspberrypi:/var/www/test $ ls -ls /usr/local/bin 
總計 976
  4 -rwxrwxr-x 1 root staff     70  4月  6 15:49 checkwifi.sh
972 -rwxr-xr-x 1 root staff 992484  4月  5 10:58 uwsgi

目前只有之前回報 IP 檢查 WiFi 連線用的 shell 程式 checkwifi.sh 與所安裝的 uwsgi 而已. 執行建立 django-admin.py 連結之指令 :

pi@raspberrypi:/var/www/test $ sudo ln -s /home/pi/.local/lib/python3.5/site-packages/django/bin/django-admin.py /usr/local/bin 
pi@raspberrypi:/var/www/test $ ls -ls /usr/local/bin 
總計 980
  4 -rwxrwxr-x 1 root staff     70  4月  6 15:49 checkwifi.sh   
  4 lrwxrwxrwx 1 root staff     70  7月 31 11:45 django-admin.py -> /home/pi/.local/lib/python3.5/site-packages/django/bin/django-admin.py 
972 -rwxr-xr-x 1 root staff 992484  4月  5 10:58 uwsgi

可見連結已建立, 但在 /var/www/test 底下用 django-admin.py 指令建立一個網站 mysite 時卻還是失敗, 但這回出現的是 "No module named django.core" 的錯誤訊息 :

pi@raspberrypi:/var/www/test $ django-admin.py startproject mysite 
Traceback (most recent call last):
  File "/usr/local/bin/django-admin.py", line 2, <module>
    from django.core import management
ImportError: No module named django.core 

即使在 /home/pi 底下也是一樣 : 

pi@raspberrypi:~ $ django-admin.py startproject mysite    
Traceback (most recent call last):
  File "/usr/local/bin/django-admin.py", line 2, in <module>
    from django.core import management
ImportError: No module named django.core   

經爬文研究在下面這篇文章發現可能是 Python 版本的關係, 參考 : 


因為 Rasbian 預設 python 指令是 v2.7 版, 而我的 Django 等套件都是安裝在 Python 3 底, 所以我之前在安裝完 Rasbian 後有用 alias 指令將 Python 3.5 版指令取 python3 的別名, 但執行 django-admin.py 時卻會預設執行 Python 2.7, 導致找不到 django.core 模組. 

依照其建議應移除 /usr/bin/python 與 Python 2.7 的連結, 再建立 Python 3 與 /usr/bin/python 的連結 :

pi@raspberrypi:~ $ sudo rm -f /usr/bin/python   
pi@raspberrypi:~ $ sudo ln -s /usr/bin/python3 /usr/bin/python       
pi@raspberrypi:~ $ python -V    
Python 3.5.3  
pi@raspberrypi:~ $ which python    
/usr/bin/python   

可見這樣執行 .py 預設就會使用 Python 3 了, 再次於 /home/pi 底下執行 django-admin.py 就成功了, 順利建立了 mysite 網站架構 :  

pi@raspberrypi:~ $ django-admin.py startproject mysite      
pi@raspberrypi:~ $ tree mysite    
mysite
├── manage.py
└── mysite
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

1 directory, 5 files

這時切換到 mysite 底下執行 manage.py 並開啟 8000 運行 Django 本身的開發伺服器 :

pi@raspberrypi:~/mysite $ python manage.py runserver 127.0.0.1:8000 
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

July 31, 2019 - 11:07:19
Django version 2.2.3, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

然後開啟樹莓派的 Chromium 瀏覽器連線 localhost:8000 即顯示 Django 歡迎網頁 :




[31/Jul/2019 11:08:08] "GET / HTTP/1.1" 200 16348
[31/Jul/2019 11:08:08] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
[31/Jul/2019 11:08:09] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 85876
[31/Jul/2019 11:08:09] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 86184
[31/Jul/2019 11:08:09] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 85692
Not Found: /favicon.ico
[31/Jul/2019 11:08:09] "GET /favicon.ico HTTP/1.1" 404 1972


但是如果在 Nginx 的預設站台 /var/www/html 或上一篇文章中自建的虛擬目錄 /var/www/test 執行 django-admin.py 卻出現  "No module named 'django'" 的錯誤訊息, why? 

pi@raspberrypi:/var/www/test $ sudo django-admin.py startproject mysite   
Traceback (most recent call last):
  File "/usr/local/bin/django-admin.py", line 2, in <module>
    from django.core import management
ImportError: No module named 'django'    

因為我對 Linux 的設定還不熟, 所以這個問題以後再研究.

總結以上測試, 結論是 :
  1. 要把 Django 安裝目錄下的 django-admin.py 與 /user/local/bin 建立連結 :
    sudo ln -s /home/pi/.local/lib/python3.5/site-packages/django/bin/django-admin.py /usr/local/bin
  2. 要把預設的 python 指令改連結到 Python 3 程式 :
    sudo rm -f /usr/bin/python
    sudo ln -s /usr/bin/python3 /usr/bin/python
只要先完成這兩個動作, 就可以在 /home/pi 下建立 Django 站台 :

django-admin.py startproject mysite


參考 :

# 使用uWSGI和nginx来设置Django和你的web服务器
# 树莓派部署django项目
# 在樹莓派上搭建 Nginx + uWSGI + Django 架構伺服器的過程中所學到的
# Django Uwsgi Nginx 實現生產環境部署
# Python/WSGI应用快速入门
# 树莓派:django,uwsgi,nginx安装与设置
# 三步在樹莓派上部署nginx+uWSGI+flask
# Setting up an NGINX web server on a Raspberry Pi
# 树莓派部署django项目 (一鍵重啟)
# 树莓派:django,uwsgi,nginx安装与设置
# linux複製指定目錄下的全部檔案到另一個目錄中,linux cp 資料夾
# 在Raspbian上安裝nginx、PHP、Django與uWSGI
# No Module named django.core


2024年2月20日 星期二

Mapleboard MP510-50 測試 (十八) : 註冊 Let's Encrypt 的 SSL/TLS 憑證

申請好網域名稱後, 網站其實只有 HTTP 協定功能, 接下來必須安裝 SSL/TLS 憑證, 這樣網站才會有 HTTPS 加密傳輸功能, 否則瀏覽器連線網站時都會先顯示這是不安全的網站, 雖然按繼續瀏覽還是可以進入網站首頁, 但這樣的存取並不安全. 

以前憑證須要花錢買 (一年大約要 2~3 千元), 且年年要續約, 現在有免費的 Let's Encrypt 可用, 但先決條件是你的網站必須要有一個域名 (domain name), 我過年前便已向 Namecheap 購買了 tony1966.cc 的域名 (10 年 59.8 美元, 每年約 191 元台幣, 如其名真的很便宜), 參考 :


我原先是在下面這本書的 16.3 節看到 Let's Encrypt 的介紹 :

# 活用 django4 建構動態網站的 16 堂課 (博碩, 2023, 何敏煌, 林亮昀) 

不過此書是以 Apache 伺服器為例說明設定方式, 但我用的是 Nginx 伺服器, 所以主要是參考下面兩篇教學文章進行設定 : 



一. 關於 SSL 與 HTTPS 加密傳輸 : 

先摘要整理 SSL 加密憑證的相關知識如下 : 
  1. 傳統的 HTTP 協定是以明碼方式傳遞, 並未將內容加密, 只要用封包探測軟體就能輕易擷取用 HTTP 傳送的機敏內容, 例如帳號密碼, API 的存取密鑰 (access key) 或權杖 (csrf_token) 等, 駭客就能利用這些資訊送出假造的請求來進行網路攻擊或滲透. 
  2. SSL (Secure Soket Layer) 使用非對稱金鑰在後端網站與前端瀏覽器之間建立可信任的 HTTPS 加密傳輸, 當以 HTTPS 連線網站伺服器時, 伺服器主機會提供一把公鑰 (public key) 給瀏覽器, 讓它用來將欲傳送的內容加密, 當伺服器收到這加密過的資料, 就會用與那把公鑰對應的私鑰 (private key) 進行解密來取得原始的資料. 不過, SSL 只是安全性憑證的通稱, 事實上現在使用的是比 SSL 更安全的進階版 : TLS (Tranport Layer Security) .
  3. 但是當瀏覽器連線 HTTPS 網站時收到對方發送的公鑰時, 要如何辨別這把公鑰是真是假? 這時候就需要一個第三方的公正單位來提供信任查詢服務, 這個公正單位稱為憑證中心. 網站管理者如果要提供 HTTPS 安全連線功能, 必須提供網域名稱等資料向憑證中心提出申請, 當驗證通過後會得到一對金鑰並安裝在伺服器主機中並做好設定, 當客戶端提出 HTTPS 請求時伺服器會向其傳送公鑰, 瀏覽器收到後自動向憑證中心查詢此公鑰持有者是否可信任, 若驗證通過才會進行 SSL 加密傳輸. 
  4. 大部分的商用憑證必須付費購買且定期重新驗證, Let's Encrypt 是由 Google, IBM, Cisco 等大企業捐資成立的免費憑證頒發機構 ( CA, Certificate Authority, 憑證中心), 任何擁有網域名稱的主機管理人均可從 Let's Encrypt 獲得可信任的憑證, 替網站啟用 HTTPS (SSL/TLS) 功能. 其頒發的憑證效期為三個月, 亦即每三個月須重新驗證 (可透過程式自動更新憑證). Let's Encrypt 的驗證方式較初階, 憑證申請者 (也就是網站管理者) 必須在伺服器的特定資料夾下放置憑證中心指定之檔案, 以證明申請者擁有網站的管理權限. 參考 :

    # https://letsencrypt.org/zh-tw/

二. 使用 Certbot 套件註冊 Let's Encrypt 憑證 : 

Certbot 套件是一個軟體工具, 用來向 Let's Encrypt 註冊憑證並於到期前自動更新, 此軟體由位於美國舊金山的非營利機構電子前沿基金會 (Electronic Frontier Foundation) 設計與維護, 參考


Certbot 同時支援 Apache 與 Nginx 伺服器. 以下是安裝程序 :


1. 更新本機套件索引 : 

用下列指令更新套件索引 :

sudo apt update   

tony1966@LX2438:~$ sudo apt update     
[sudo] tony1966 的密碼: 
下載:1 http://packages.microsoft.com/repos/code stable InRelease [3,589 B]
已有:2 http://deb.mapleboard.org/mp510 jammy InRelease                 
下載:3 http://packages.microsoft.com/repos/code stable/main amd64 Packages [16.2 kB]
下載:4 https://linux.teamviewer.com/deb stable InRelease [11.9 kB]             
已有:5 http://ports.ubuntu.com jammy InRelease                                 
下載:6 http://packages.microsoft.com/repos/code stable/main arm64 Packages [16.4 kB]
下載:7 http://packages.microsoft.com/repos/code stable/main armhf Packages [16.3 kB]
下載:8 http://ports.ubuntu.com jammy-security InRelease [110 kB]
下載:9 http://ports.ubuntu.com jammy-updates InRelease [119 kB]
已有:10 http://ports.ubuntu.com jammy-backports InRelease
下載:11 http://ports.ubuntu.com jammy-updates/main arm64 Packages [1,193 kB]
下載:12 http://ports.ubuntu.com jammy-updates/main armhf Packages [768 kB]
取得 2,254 kB 用了 4s (504 kB/s)                           
正在讀取套件清單... 完成
正在重建相依關係... 完成
正在讀取狀態資料... 完成  
可升級 37 個套件。執行 apt list --upgradable 檢視


2. 安裝 Certbot 套件 : 

指令如下 :

sudo apt install certbot python3-certbot-nginx -y   

tony1966@LX2438:~$ sudo apt install certbot python3-certbot-nginx -y    
正在讀取套件清單... 完成
正在重建相依關係... 完成  
正在讀取狀態資料... 完成  
下列的額外套件將被安裝:
  python3-acme python3-certbot python3-certifi python3-configargparse python3-configobj python3-icu python3-idna
  python3-josepy python3-openssl python3-parsedatetime python3-requests python3-requests-toolbelt python3-rfc3339
  python3-tz python3-zope.component python3-zope.event python3-zope.hookable python3-zope.interface
建議套件:
  python-certbot-doc python3-certbot-apache python-acme-doc python-certbot-nginx-doc python-configobj-doc
  python-openssl-doc python3-openssl-dbg python3-socks python-requests-doc
下列【新】套件將會被安裝:
  certbot python3-acme python3-certbot python3-certbot-nginx python3-certifi python3-configargparse python3-configobj
  python3-icu python3-idna python3-josepy python3-openssl python3-parsedatetime python3-requests
  python3-requests-toolbelt python3-rfc3339 python3-tz python3-zope.component python3-zope.event
  python3-zope.hookable python3-zope.interface
升級 0 個,新安裝 20 個,移除 0 個,有 37 個未被升級。
需要下載 1,440 kB 的套件檔。
此操作完成之後,會多佔用 7,316 kB 的磁碟空間。
下載:1 http://ports.ubuntu.com jammy/main arm64 python3-openssl all 21.0.0-1 [45.2 kB]
下載:2 http://ports.ubuntu.com jammy/universe arm64 python3-josepy all 1.10.0-1 [22.0 kB]
下載:3 http://ports.ubuntu.com jammy/main arm64 python3-certifi all 2020.6.20-1 [150 kB]
下載:4 http://ports.ubuntu.com jammy/main arm64 python3-idna all 3.3-1 [49.3 kB]
下載:5 http://ports.ubuntu.com jammy-security/main arm64 python3-requests all 2.25.1+dfsg-2ubuntu0.1 [48.8 kB]
下載:6 http://ports.ubuntu.com jammy/main arm64 python3-requests-toolbelt all 0.9.1-1 [38.0 kB]
下載:7 http://ports.ubuntu.com jammy-updates/main arm64 python3-tz all 2022.1-1ubuntu0.22.04.1 [30.7 kB]
下載:8 http://ports.ubuntu.com jammy/main arm64 python3-rfc3339 all 1.1-3 [7,110 B]
下載:9 http://ports.ubuntu.com jammy-updates/universe arm64 python3-acme all 1.21.0-1ubuntu0.1 [36.4 kB]
下載:10 http://ports.ubuntu.com jammy/universe arm64 python3-configargparse all 1.5.3-1 [26.9 kB]
下載:11 http://ports.ubuntu.com jammy/main arm64 python3-configobj all 5.0.6-5 [34.8 kB]
下載:12 http://ports.ubuntu.com jammy/universe arm64 python3-parsedatetime all 2.6-2 [32.9 kB]
下載:13 http://ports.ubuntu.com jammy/universe arm64 python3-zope.hookable arm64 5.1.0-1build1 [11.4 kB]
下載:14 http://ports.ubuntu.com jammy/main arm64 python3-zope.interface arm64 5.4.0-1build1 [142 kB]
下載:15 http://ports.ubuntu.com jammy/universe arm64 python3-zope.event all 4.4-3 [8,180 B]
下載:16 http://ports.ubuntu.com jammy/universe arm64 python3-zope.component all 4.3.0-3 [38.3 kB]
下載:17 http://ports.ubuntu.com jammy/universe arm64 python3-certbot all 1.21.0-1build1 [175 kB]
下載:18 http://ports.ubuntu.com jammy/universe arm64 certbot all 1.21.0-1build1 [21.3 kB]
下載:19 http://ports.ubuntu.com jammy/universe arm64 python3-certbot-nginx all 1.21.0-1 [35.4 kB]
下載:20 http://ports.ubuntu.com jammy/main arm64 python3-icu arm64 2.8.1-0ubuntu2 [486 kB]
取得 1,440 kB 用了 5s (300 kB/s)      
正在預先設定套件 ...
選取了原先未選的套件 python3-openssl。
(讀取資料庫 ... 目前共安裝了 289364 個檔案和目錄。)
正在準備解包 .../00-python3-openssl_21.0.0-1_all.deb……
解開 python3-openssl (21.0.0-1) 中...
選取了原先未選的套件 python3-josepy。
正在準備解包 .../01-python3-josepy_1.10.0-1_all.deb……
解開 python3-josepy (1.10.0-1) 中...
選取了原先未選的套件 python3-certifi。
正在準備解包 .../02-python3-certifi_2020.6.20-1_all.deb……
解開 python3-certifi (2020.6.20-1) 中...
選取了原先未選的套件 python3-idna。
正在準備解包 .../03-python3-idna_3.3-1_all.deb……
解開 python3-idna (3.3-1) 中...
選取了原先未選的套件 python3-requests。
正在準備解包 .../04-python3-requests_2.25.1+dfsg-2ubuntu0.1_all.deb……
解開 python3-requests (2.25.1+dfsg-2ubuntu0.1) 中...
選取了原先未選的套件 python3-requests-toolbelt。
正在準備解包 .../05-python3-requests-toolbelt_0.9.1-1_all.deb……
解開 python3-requests-toolbelt (0.9.1-1) 中...
選取了原先未選的套件 python3-tz。
正在準備解包 .../06-python3-tz_2022.1-1ubuntu0.22.04.1_all.deb……
解開 python3-tz (2022.1-1ubuntu0.22.04.1) 中...
選取了原先未選的套件 python3-rfc3339。
正在準備解包 .../07-python3-rfc3339_1.1-3_all.deb……
解開 python3-rfc3339 (1.1-3) 中...
選取了原先未選的套件 python3-acme。
正在準備解包 .../08-python3-acme_1.21.0-1ubuntu0.1_all.deb……
解開 python3-acme (1.21.0-1ubuntu0.1) 中...
選取了原先未選的套件 python3-configargparse。
正在準備解包 .../09-python3-configargparse_1.5.3-1_all.deb……
解開 python3-configargparse (1.5.3-1) 中...
選取了原先未選的套件 python3-configobj。
正在準備解包 .../10-python3-configobj_5.0.6-5_all.deb……
解開 python3-configobj (5.0.6-5) 中...
選取了原先未選的套件 python3-parsedatetime。
正在準備解包 .../11-python3-parsedatetime_2.6-2_all.deb……
解開 python3-parsedatetime (2.6-2) 中...
選取了原先未選的套件 python3-zope.hookable。
正在準備解包 .../12-python3-zope.hookable_5.1.0-1build1_arm64.deb……
解開 python3-zope.hookable (5.1.0-1build1) 中...
選取了原先未選的套件 python3-zope.interface。
正在準備解包 .../13-python3-zope.interface_5.4.0-1build1_arm64.deb……
解開 python3-zope.interface (5.4.0-1build1) 中...
選取了原先未選的套件 python3-zope.event。
正在準備解包 .../14-python3-zope.event_4.4-3_all.deb……
解開 python3-zope.event (4.4-3) 中...
選取了原先未選的套件 python3-zope.component。
正在準備解包 .../15-python3-zope.component_4.3.0-3_all.deb……
解開 python3-zope.component (4.3.0-3) 中...
選取了原先未選的套件 python3-certbot。
正在準備解包 .../16-python3-certbot_1.21.0-1build1_all.deb……
解開 python3-certbot (1.21.0-1build1) 中...
選取了原先未選的套件 certbot。
正在準備解包 .../17-certbot_1.21.0-1build1_all.deb……
解開 certbot (1.21.0-1build1) 中...
選取了原先未選的套件 python3-certbot-nginx。
正在準備解包 .../18-python3-certbot-nginx_1.21.0-1_all.deb……
解開 python3-certbot-nginx (1.21.0-1) 中...
選取了原先未選的套件 python3-icu。
正在準備解包 .../19-python3-icu_2.8.1-0ubuntu2_arm64.deb……
解開 python3-icu (2.8.1-0ubuntu2) 中...
設定 python3-configargparse (1.5.3-1) ...
設定 python3-parsedatetime (2.6-2) ...
設定 python3-icu (2.8.1-0ubuntu2) ...
設定 python3-zope.event (4.4-3) ...
設定 python3-zope.interface (5.4.0-1build1) ...
設定 python3-openssl (21.0.0-1) ...
設定 python3-tz (2022.1-1ubuntu0.22.04.1) ...
設定 python3-zope.hookable (5.1.0-1build1) ...
設定 python3-configobj (5.0.6-5) ...
設定 python3-certifi (2020.6.20-1) ...
設定 python3-idna (3.3-1) ...
設定 python3-josepy (1.10.0-1) ...
設定 python3-rfc3339 (1.1-3) ...
設定 python3-zope.component (4.3.0-3) ...
設定 python3-requests (2.25.1+dfsg-2ubuntu0.1) ...
設定 python3-requests-toolbelt (0.9.1-1) ...
設定 python3-acme (1.21.0-1ubuntu0.1) ...
設定 python3-certbot (1.21.0-1build1) ...
設定 certbot (1.21.0-1build1) ...
Created symlink /etc/systemd/system/timers.target.wants/certbot.timer → /lib/systemd/system/certbot.timer.
設定 python3-certbot-nginx (1.21.0-1) ...
執行 man-db (2.10.2-1) 的觸發程式……
tony1966@LX2438:~$ 


3. 向 Let's Encrypt 註冊憑證 : 

指令如下 :

sudo certbot --nginx --redirect -d tony1966.cc     

此處 -d tony1966.cc 表示為我的網站域名 http://tony1966.cc 註冊憑證, 如有多個網址就在後面加 -d domain_name 即可. --redirect 表示會自動將對 http://tony1966.cc 的請求轉成 https://tony1966.cc :

tony1966@LX2438:~$ sudo certbot --nginx --redirect -d tony1966.cc    
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): 此處輸入我的 Hinet 信箱  

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y     <== 必須輸入 Y 才能繼續

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y     <== 建議輸入 Y (可收到關於 Certbot 與 Let's Encrypt 消息)
Account registered.
Requesting a certificate for tony1966.cc

Successfully received certificate.     <== 收到憑證了
Certificate is saved at: /etc/letsencrypt/live/tony1966.cc/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/tony1966.cc/privkey.pem     <== 金鑰儲存位置
This certificate expires on 2024-05-20.    <== 憑證到期日 (有效期限 3 個月) 
These files will be updated when the certificate renews.     <== 到期前Certbot 會自動更新
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for tony1966.cc to /etc/nginx/sites-enabled/default
Congratulations! You have successfully enabled HTTPS on https://tony1966.cc

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tony1966@LX2438:~$ 

這樣便註冊與安裝好憑證了, 有效期限為 3 個月, 但 Certbot 會自動檢查是否需要更新憑證 (使用 Crontab 計時器, 每天檢查 2 次), 所以不用擔心 HTTPS 功能會因為憑證到期未更新而停擺的問題. 可以用下列指令檢查 Certbot 自動檢查機制目前的狀態 :

sudo systemctl status certbot.timer   

tony1966@LX2438:~$ sudo systemctl status certbot.timer    
● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2024-02-20 21:12:14 CST; 25min ago
    Trigger: Wed 2024-02-21 08:06:44 CST; 10h left
   Triggers: ● certbot.service

看到 active 表示自動檢查憑證是否到期功能目前正啟動中. 

如果想要手動更新憑證, 指令如下 :

sudo certbot renew    

這樣便完成 HTTPS 憑證的註冊安裝了, 亦即我的網站已有 HTTPS 加密傳輸功能了, 馬上開啟瀏覽器測試, 在網址列輸入 http://tony1966.cc :




網址果然如預期被自動轉成 https://tony1966.cc 且正常顯示網頁, 收工啦. 


2024-02-21 補充 :

Nginx 預設的網站設定檔位於 /etc/nginx/sites-enabled 下的 default, 用 cat 瀏覽此檔, 在最底下會看到 Certbot 已經幫我們自動修改內容 : 

tony1966@LX2438:~$ cat /etc/nginx/sites-enabled/default  

... (略) ...

server {
listen 80 default_server;          <== 監聽 80 埠 (HTTP)
listen [::]:80 default_server;

... (略) ...

root /var/www/html;               <== 網站根目錄位置

# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
    server_name tony1966.cc; # managed by Certbot    

...(略)...

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot        <== 監聽 443 埠 (HTTPS)
    ssl_certificate /etc/letsencrypt/live/tony1966.cc/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/tony1966.cc/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = tony1966.cc) {
        return 301 https://$host$request_uri;     <== 將 http 自動轉成 https
    } # managed by Certbot


listen 80 ;
listen [::]:80 ;
    server_name tony1966.cc;
    return 404; # managed by Certbot


}

2023年1月24日 星期二

好站 : 安裝 LNAMP Server + phpMyAdmin 在 Linux 系統上輕鬆架設網站

過年前將 Mapleboard 帶回鄉下家, 打算在上面架設網頁伺服器, 但居然忘記帶最重要的電源線! 今日 (年初三) 菁菁有客人約做睫毛要載她回高雄, 順便拿電源線 + HDMI 線回來測試. 早上只好先進行紙上作業, 爬文做一下功課, 再決定要架 Apache 還是 Nginx. 早上找到下面這篇文章, 發現居然有可同時架 NGINX 與 Apache 的套件可用 : 


原來 LNAMP=LAMP + NGINX, 這組合的優點是可用 NGINX 來處理處理靜態檔案, 反向代理, 資源分配; 用 Apache 來處理動態網頁程式, 這樣的綜效比單獨使用 Apache 要好得多. 不過, 作者認為, 如果沒有要用到 Apache 的 .hraccess 功能, 其實不建議用 Apache 來跑 PHP, 而是應該使用 NGINX. 關於 .htaccess 參考 : 


總之, 若沒用到 .htaccess 的話, 用 NGINX 就好, 參考作者另一篇文章 : 


我是想同時可執行 Python + Django + Flask 與 PHP (雖然比較少用了), 所以 port 80 應該會給 Python Web, 8080 給 PHP.

參考 :


作者還有更多很棒的作品分享 (寫了很多 Rust 解題文章), 參考 :


2019年4月5日 星期五

在樹莓派 Nginx 伺服器上執行 Python 網頁應用程式

完成樹莓派 Nginx 伺服器安裝測試後, 我迫不及待想要測試如何在 Nginx 上執行 Python 網頁程式, 但我發現這牽涉到 Python 網頁程式開發背後的技術, 即 WSGI 介面規範, 所以得先搞清楚整個運作機制才行.

以下測試主要是參考下列兩本書 :

# 一次搞定所有 Python Web 框架開發百科全書 (佳魁, 劉長龍) 第 5.3 節
# 科班出身的MVC網頁開發 : 使用Python + Django (佳魁, 王友釗)

以及下面這幾篇文章 :

# 化整為零的次世代網頁開發標準: WSGI
# python中WSGI是什麼,Python應用WSGI詳解
# Python Web开发最难懂的WSGI协议,到底包含哪些内容?
# 做python Web开发你要理解:WSGI & uwsgi
# 理解Python WSGI

要在 Web 伺服器上跑 Python 應用程式必須先了解 WSGI 介面, 這是專為 Python 而定義的後端通訊協定, 相關知識摘要整理如下 :


一. WSGI 規範 (協定) :

WSGI (Web Server Gateway Interface) 是源自 CGI 的 Python 網頁介面標準, 問世於 2003 年, 它定義了 Python 網頁應用程式 (web application) 與 Web 伺服器 (web server) 的溝通方式, 讓 Python 網頁應用程式可以跟 Web 伺服器溝通. 對於 Client-Server 運作模式而言, 所謂的溝通其實就是伺服器如何傳遞請求與應用程式如何回應罷了.

WSGI 是在 CGI 的基礎上專門為 Python 語言制定的後端通訊標準, 功能上可說是 CGI 的延伸, 但 WSGI 為 Python 做了更多的定義, 其規格定義詳見 Python 文件 PEP333 :

# https://www.python.org/dev/peps/pep-0333/

WSGI 協定包括了 Server 與 Application 兩部分 :
  1. WSGI Server :
    接收客戶端請求打包後轉發給 Application; 然後接收 Application 的回應, 將其回傳給客戶端.
  2. WSGI Application :
    接收 server 轉發的客戶端請求, 處理後將結果傳回 Server. 這部分可以是單獨的 Application, 也可以是包覆著 Application 的多個 middleware 堆疊. 
架構如下圖所示 :




注意, WSGI 只是一個分別定義了 Server 與 Application 介面的規範, 本身不是一個實作軟體. 關於 middleware 的妙用, 可參考下面這篇 :

# 化整為零的次世代網頁開發標準: WSGI

WSGI 的兩個部分可以分別實作, 實作 Application 部分的就是各式各樣的 Python 網頁框架, 例如 Django, Flask, Tornado 等都是. 而實作 Server 部分的稱為 WSGI Server, 例如 Python 3 內建的 wsgiref, 介接 Apache 的 mod_wsgi, 或移植自 Unix 的 Gunicorn 等等都是 WSGI server 的實作. 更多 WSGI Server 參考 :

# Servers which support WSGI


二. Python 3 內建的 WSGI 伺服器 wsgiref :

由上面的概念圖可知 WSGI Server 的介面有兩個 :
  1. 與 Web 伺服器的介面
  2. 與 Python 應用程式的介面
其中與 Web 伺服器的介面由 WSGI Server 負責, Python 網頁應用程式開發者不需要處理. 此介面用來介接 Web 伺服器, 因為一般網頁伺服器例如 Apache 或 Nginx 必須支援各種程式語言, 因此它們不會直接與網頁應用程式溝通, 而是透過 WSGI 伺服器代勞, 例如 wsgiref, uWSGI, 或 mod_wsgi 等等. 除了介接外, WSGI 伺服器本身也會內建基本的 Web 伺服器功能, 但那只是做為開發測試用, 其功能太少且效能無法應付上線的流量.

網頁程式開發者需要處理的是 WSGI 伺服器的第二個介面, 即面向網頁應用程式的介面. WSGI 標準定義了一個簡單且形式固定的函數做為 Web 伺服器與 Python 網頁應用程式的溝通介面, 開發者只要按此介面撰寫網頁應用程式即可 :

def app_name(environ, start_response): 

函數名稱 app_name 是自訂的, 兩個傳入參數 environ 與 start_response 即應用程式與 Web 伺服器的單向雙工溝通管道, environ 表示來訊; 而 start_response() 表示回訊 :
  1. environ : (來訊用)
    字典型態的環境變數, 儲存 Web 伺服器傳來的請求相關資訊 
  2. start_response(status, response_headers) : (回訊用)
    回呼函數, 可讓應用程式起始一個回應, 其第一參數為回應狀態 (字串), 第二參數是回應標頭 (串列), 回應的本體 (body) 則用 return 傳回 HTML 代碼.
所謂環境變數是指包含客戶端請求訊息與 Web 伺服器相關資訊的字典變數. WSGI 與 CGI 一樣, 都是透過環境變數從伺服器取得外部資訊, 例如請求資訊 REQUEST_METHOD, HTTP_XXX, 與 PATH_INFO 等屬性, 伺服器資訊如 SERVER_NAME, 以及 WSGI 資訊如版本等. 當伺服器接到用戶端請求時, 會依據 HTTP 協定從 socket 串流剖析客戶端的請求資訊, 加上伺服器資訊打包成環境變數傳給應用程式處理.

例如 :

#myapp.py
def application(environ, start_response):
    status='200 OK'
    response_headers=[('Content-type','text/plain')]
    start_response(status, response_headers)
    return [b'Hello World!n']

將此函數存成 myapp.py 即成為一個會回應 "Hello World!" 的應用程式了.

另外是 WSGI 伺服器部分, Python 3 的內建模組 wsgiref 的 simple_server 類別可用來實作一個簡易的 WSGI+HTTP 伺服器, 在測試開發階段可以用它做為 Web 伺服器的代用品. 只要呼叫 simple_server 的 make_server() 方法並傳入 host, port, 以及處理函數即可建立一個 Web 伺服器 :

wsgiref.simple_server.make_server(host, port, app) 

詳細文件參考 :

# https://docs.python.org/2/library/wsgiref.html#module-wsgiref.simple_server

例如下面程式就實作了一個 Web 伺服器 :

#wsgi_server.py    
from wsgiref.simple_server import make_server   
from myapp import application    

http_server=make_server('', 8000, application)
http_server.serve_forever()  

將此檔案存成 wsgi_server.py, 與上面的應用程式 myapp.py 放在同一目錄下, 開啟命令提示字元視窗, 執行 WSGI 伺服器程式 wsgi_server.py, 然後開啟瀏覽器視窗, 連線 localhost:8000 即可看到網頁輸出 "Hello World!" :




注意, 在 Python 3, 傳回的回應字串必須加 b 以轉成 byte string, 否則會出現 "AttributeError: 'NoneType' object has no attribute 'split'" 錯誤.

也可以將上面的 Web 伺服器程式 wsgi_server.py 與應用程式 myapp.py 合併寫在單一檔案裡面, 例如 :

#helloworld.py
from wsgiref.simple_server import make_server

def application(environ, start_response):
    status='200 OK'
    response_headers=[('Content-type','text/plain')]
    start_response(status, response_headers)
    return [b'Hello World!']

http_server=make_server('localhost', 8000, application)
http_server.serve_forever()

執行結果是一樣的 :

D:\Python\test>python helloworld.py
127.0.0.1 - - [04/Apr/2019 20:09:20] "GET / HTTP/1.1" 200 12
127.0.0.1 - - [04/Apr/2019 20:09:20] "GET /favicon.ico HTTP/1.1" 200 12


三. uWSGI 伺服器 :

上面提到有很多實作 WSGI Server 介面的 WSGI 伺服器, 在上線運營的 Python 網站中常見的配置是 :
  1. Nginx + uWSGI 
  2. Nginx + Gunicorn
  3. Apache + mod_wsgi
Nginx 的高效能是有目共睹的, 它的最佳搭檔 uWSGI 也是常見的 WSGI 伺服器, 它不僅實作了獨家的 uwsgi 協定, 也實作了 WSGI 與 HTTP 協定. 注意, 小寫的 uwsgi 指的是協定, 而開頭小寫的 uWSGI 則是其實作. 關於 uwsgi 參考:

# https://uwsgi-docs.readthedocs.io/en/latest/Protocol.html

我在 Win10 上用 pip3 安裝結果失敗 :

D:\Python\test>pip3 install uwsgi 
Collecting uwsgi
  Downloading https://files.pythonhosted.org/packages/e7/1e/3dcca007f974fe4eb369bf1b8629d5e342bb3055e2001b2e5340aaefae7a/uwsgi-2.0.18.tar.gz (801kB)
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\User\AppData\Local\Temp\pip-install-0i8q8nuo\uwsgi\setup.py", line 3, in <module>
        import uwsgiconfig as uc
      File "C:\Users\User\AppData\Local\Temp\pip-install-0i8q8nuo\uwsgi\uwsgiconfig.py", line 8, in <module>
        uwsgi_os = os.uname()[0]
    AttributeError: module 'os' has no attribute 'uname'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\Users\User\AppData\Local\Temp\pip-install-0i8q8nuo\uwsgi\

查詢網路, 似乎 uWSGI 不支援 Windows :

# https://github.com/unbit/uwsgi/issues/1930
# Windows 10安装uWSGI:不可行、失败了

在樹莓派上就能安裝了 :

pi@raspberrypi:~ $ pip3 install uwsgi 
Collecting uwsgi
  Using cached https://files.pythonhosted.org/packages/e7/1e/3dcca007f974fe4eb369bf1b8629d5e342bb3055e2001b2e5340aaefae7a/uwsgi-2.0.18.tar.gz
Building wheels for collected packages: uwsgi
  Running setup.py bdist_wheel for uwsgi ... done
  Stored in directory: /home/pi/.cache/pip/wheels/2d/0c/b0/f3ba1bbce35c3766c9dac8c3d15d5431cac57e7a8c4111c268
Successfully built uwsgi
Installing collected packages: uwsgi
Successfully installed uwsgi-2.0.18

安裝完畢用 pip3 show 顯示版本訊息為 2.0.18 版 :

pi@raspberrypi:~ $ pip3 show uwsgi 
Name: uWSGI
Version: 2.0.18
Summary: The uWSGI server
Home-page: https://uwsgi-docs.readthedocs.io/en/latest/
Author: Unbit
Author-email: info@unbit.it
License: GPLv2+
Location: /home/pi/.local/lib/python3.5/site-packages
Requires: 

安裝好後, 我在 /home/pi 底下編輯了一個網頁檔 myapp.py :

pi@raspberrypi:~ $ nano myapp.py 
pi@raspberrypi:~ $ cat myapp.py 
def application(environ, start_response):
    status='200 OK'
    response_headers=[('Content-type','text/plain')]
    start_response(status, response_headers)
    return [b'Hello World!']

然後在命令列開啟 uWSGI 伺服器讀取網頁 : 

pi@raspberrypi:~ $ uwsgi --http :8080 --wsgi-file myapp.py

卻出現 "bash : uwsgi command not found" 錯誤訊息, why?  

後來在葉難的 "在Raspbian上安裝nginx、PHP、Django與uWSGI" 看到它安裝的是 uWSGI, 難道就是上面說的 uwsgi 是協定, 而 uWSGI 是伺服器的差別嗎? 所以就安裝 uWSGI :

pi@raspberrypi:~ $ sudo pip3 install uWSGI
Collecting uWSGI
  Using cached https://files.pythonhosted.org/packages/e7/1e/3dcca007f974fe4eb369bf1b8629d5e342bb3055e2001b2e5340aaefae7a/uwsgi-2.0.18.tar.gz
Building wheels for collected packages: uWSGI
  Running setup.py bdist_wheel for uWSGI ... done
  Stored in directory: /root/.cache/pip/wheels/2d/0c/b0/f3ba1bbce35c3766c9dac8c3d15d5431cac57e7a8c4111c268
Successfully built uWSGI
Installing collected packages: uWSGI
Successfully installed uWSGI-2.0.18

用 pip3 show 顯示套件資訊 :

pi@raspberrypi:~ pip3 show uWSGI  
Name: uWSGI
Version: 2.0.18
Summary: The uWSGI server
Home-page: https://uwsgi-docs.readthedocs.io/en/latest/
Author: Unbit
Author-email: info@unbit.it
License: GPLv2+
Location: /home/pi/.local/lib/python3.5/site-packages
Requires: 
pi@raspberryp

看起來跟 uwsgi 的幾乎一樣, 就大小寫不同而已. 再試試看用 uwsgi 指令啟動伺服器, 結果沒再出現 "bash : uwsgi command not found" 錯誤訊息, 而是出現 "uwsgi: unrecognized option" 錯誤訊息 :

pi@raspberrypi:~ $ uwsgi --http:8000 --wsgi-file myapp.py 
uwsgi: unrecognized option '--http:8000'
getopt_long() error

查詢官網文件, 應該加上 "--http-websockets", 參考 :


果然加上 proxy 後就可以順利啟動伺服器了 : 

pi@raspberrypi:~ $ uwsgi --http :8080 --http-websockets --wsgi-file myapp.py 
*** Starting uWSGI 2.0.18 (32bit) on [Fri Apr  5 11:30:48 2019] ***
compiled with version: 6.3.0 20170516 on 05 April 2019 02:47:26
os: Linux-4.14.79+ #1159 Sun Nov 4 17:28:08 GMT 2018
nodename: raspberrypi
machine: armv6l
clock source: unix
detected number of CPU cores: 1
current working directory: /home/pi
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
*** WARNING: you are running uWSGI without its master process manager ***
your processes number limit is 3400
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on :8080 fd 4
spawned uWSGI http 1 (pid: 21648)
uwsgi socket 0 bound to TCP address 127.0.0.1:37339 (port auto-assigned) fd 3
Python version: 3.5.3 (default, Sep 27 2018, 17:25:39)  [GCC 6.3.0 20170516]
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0x1766130
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 64392 bytes (62 KB) for 1 cores
*** Operational MODE: single process ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x1766130 pid: 21647 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 21647, cores: 1)
[pid: 21647|app: 0|req: 1/1] 127.0.0.1 () {36 vars in 665 bytes} [Fri Apr  5 11:31:18 2019] GET / => generated 12 bytes in 1 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
[pid: 21647|app: 0|req: 2/2] 127.0.0.1 () {36 vars in 644 bytes} [Fri Apr  5 11:31:21 2019] GET /favicon.ico => generated 12 bytes in 1 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)

瀏覽 localhost:8080 成功顯示網頁 :





sudo -u www-data uwsgi uwsgi_config.ini

參考 :

# 葉難: 在Raspbian上安裝nginx、PHP、Django與uWSGI
# Setting up Nginx and uWSGI for CGI scripting
# NGINX and uWSGI work but don't begin at startup

2019年4月1日 星期一

在樹莓派 Raspbian Stretch 上安裝 Nginx 伺服器

前天周六在鄉下的 Pi 3 上安裝 Nginx 伺服器沒有成功, 參考 :

# 樹莓派安裝 Nginx 伺服器失敗

我懷疑是 Raspbian Jessie 太舊了 (那是 2017 年的版本), 因此週六晚上回高雄後, 在另一台 Pi 3 就順利安裝成功了, 這是 2018-11 的 Stretch 版本.

以下測試參考了下面幾篇文章 :

# Raspberry Pi 的實作 - 用 Nginx 架設網頁伺服器
# 瞧你賊西西的-nginx 基礎設定教學
# 啟用 Nginx Status 的設定
# 使用uWSGI和nginx来设置Django和你的web服务器
# 树莓派组建web服务器django,uwsgi,nginx,php,python,sqlite
# Setting up an NGINX web server on a Raspberry Pi


1. 安裝 Nginx : 

pi@raspberrypi:~ $ sudo apt-get install nginx
正在讀取套件清單... 完成
正在重建相依關係       
正在讀取狀態資料... 完成
The following packages were automatically installed and are no longer required:
  libqt5qml5 libqt5quick5 libqt5webkit5 ncurses-term openssh-sftp-server
  qml-module-qtgraphicaleffects qml-module-qtquick-controls
  qml-module-qtquick-dialogs qml-module-qtquick-layouts
  qml-module-qtquick-privatewidgets qml-module-qtquick-window2
  qml-module-qtquick2
Use 'sudo apt autoremove' to remove them.
下列的額外套件將被安裝:
  libnginx-mod-http-auth-pam libnginx-mod-http-dav-ext libnginx-mod-http-echo
  libnginx-mod-http-geoip libnginx-mod-http-image-filter
  libnginx-mod-http-subs-filter libnginx-mod-http-upstream-fair
  libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream
  nginx-common nginx-full
建議套件:
  fcgiwrap nginx-doc ssl-cert
下列【新】套件將會被安裝:
  libnginx-mod-http-auth-pam libnginx-mod-http-dav-ext libnginx-mod-http-echo
  libnginx-mod-http-geoip libnginx-mod-http-image-filter
  libnginx-mod-http-subs-filter libnginx-mod-http-upstream-fair
  libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream nginx
  nginx-common nginx-full
升級 0 個,新安裝 13 個,移除 0 個,有 0 個未被升級。
需要下載 1,505 kB 的套件檔。
此操作完成之後,會多佔用 2,563 kB 的磁碟空間。
是否繼續進行 [Y/n]? [Y/n] Y
下載:1 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf nginx-common all 1.10.3-1+deb9u2 [105 kB]
下載:2 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-auth-pam armhf 1.10.3-1+deb9u2 [85.6 kB]
下載:3 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-dav-ext armhf 1.10.3-1+deb9u2 [87.3 kB]
下載:4 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-echo armhf 1.10.3-1+deb9u2 [95.3 kB]
下載:5 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-geoip armhf 1.10.3-1+deb9u2 [86.8 kB]
下載:6 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-image-filter armhf 1.10.3-1+deb9u2 [89.5 kB]
下載:7 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-subs-filter armhf 1.10.3-1+deb9u2 [88.7 kB]
下載:8 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-upstream-fair armhf 1.10.3-1+deb9u2 [88.9 kB]
下載:9 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-http-xslt-filter armhf 1.10.3-1+deb9u2 [88.3 kB]
下載:10 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-mail armhf 1.10.3-1+deb9u2 [113 kB]
下載:11 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf libnginx-mod-stream armhf 1.10.3-1+deb9u2 [106 kB]
下載:12 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf nginx-full armhf 1.10.3-1+deb9u2 [389 kB]
下載:13 http://mirror.ossplanet.net/raspbian/raspbian stretch/main armhf nginx all 1.10.3-1+deb9u2 [81.8 kB]
取得 1,505 kB 用了 1min 9s (21.7 kB/s)                                       
正在預先設定套件 ...
選取了原先未選的套件 nginx-common。
(讀取資料庫 ... 目前共安裝了 133472 個檔案和目錄。)
Preparing to unpack .../00-nginx-common_1.10.3-1+deb9u2_all.deb ...
Unpacking nginx-common (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-auth-pam。
Preparing to unpack .../01-libnginx-mod-http-auth-pam_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-auth-pam (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-dav-ext。
Preparing to unpack .../02-libnginx-mod-http-dav-ext_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-dav-ext (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-echo。
Preparing to unpack .../03-libnginx-mod-http-echo_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-echo (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-geoip。
Preparing to unpack .../04-libnginx-mod-http-geoip_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-geoip (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-image-filter。
Preparing to unpack .../05-libnginx-mod-http-image-filter_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-image-filter (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-subs-filter。
Preparing to unpack .../06-libnginx-mod-http-subs-filter_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-subs-filter (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-upstream-fair。
Preparing to unpack .../07-libnginx-mod-http-upstream-fair_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-upstream-fair (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-http-xslt-filter。
Preparing to unpack .../08-libnginx-mod-http-xslt-filter_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-http-xslt-filter (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-mail。
Preparing to unpack .../09-libnginx-mod-mail_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-mail (1.10.3-1+deb9u2) ...
選取了原先未選的套件 libnginx-mod-stream。
Preparing to unpack .../10-libnginx-mod-stream_1.10.3-1+deb9u2_armhf.deb ...
Unpacking libnginx-mod-stream (1.10.3-1+deb9u2) ...
選取了原先未選的套件 nginx-full。
Preparing to unpack .../11-nginx-full_1.10.3-1+deb9u2_armhf.deb ...
Unpacking nginx-full (1.10.3-1+deb9u2) ...
選取了原先未選的套件 nginx。
Preparing to unpack .../12-nginx_1.10.3-1+deb9u2_all.deb ...
Unpacking nginx (1.10.3-1+deb9u2) ...
設定 nginx-common (1.10.3-1+deb9u2) ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.
設定 libnginx-mod-http-image-filter (1.10.3-1+deb9u2) ...
設定 libnginx-mod-http-subs-filter (1.10.3-1+deb9u2) ...
Processing triggers for systemd (232-25+deb9u6) ...
設定 libnginx-mod-http-auth-pam (1.10.3-1+deb9u2) ...
設定 libnginx-mod-http-dav-ext (1.10.3-1+deb9u2) ...
設定 libnginx-mod-mail (1.10.3-1+deb9u2) ...
Processing triggers for man-db (2.7.6.1-2) ...
設定 libnginx-mod-http-xslt-filter (1.10.3-1+deb9u2) ...
設定 libnginx-mod-http-upstream-fair (1.10.3-1+deb9u2) ...
設定 libnginx-mod-http-geoip (1.10.3-1+deb9u2) ...
設定 libnginx-mod-stream (1.10.3-1+deb9u2) ...
設定 libnginx-mod-http-echo (1.10.3-1+deb9u2) ...
設定 nginx-full (1.10.3-1+deb9u2) ...
設定 nginx (1.10.3-1+deb9u2) ...

查詢 Dabian 版本果確實是 9 版 :

pi@raspberrypi:~ $ cat /etc/debian_version   
9.4
Jessie 的 8 版可能真的太舊了, 與目前的 Nginx 不速配.

Nginx 安裝完成後會自動加入 /etc/ini.d/ 目錄下, 亦即重開機後會自動啟動 :




i@raspberrypi:~ $ ls /etc/init.d/ 
alsa-utils        dhcpcd             lightdm     plymouth-log  ssh
avahi-daemon      dphys-swapfile     networking  procps        sudo
bluetooth         fake-hwclock       nfs-common  raspi-config  triggerhappy
console-setup.sh  hwclock.sh         nginx       rpcbind       udev
cron              keyboard-setup.sh  paxctld     rsync         x11-common
dbus              kmod               plymouth    rsyslog



2. 啟動伺服器連線預設網頁 :

Nginx 安裝完成後正常來說會自動啟動伺服器, 若沒有可用下列指令啟動 :

pi@raspberrypi:~ $ sudo service nginx restart

這時開啟樹莓派瀏覽器連線 localhost 應該要能顯示預設網頁 :




此網頁放在 /var/www/html 下, 安裝伺服器時, Nginx 會在此目錄下建立一個 index.nginx-debian.html 檔案 :

pi@raspberrypi:~ $ ls /var/www/html
index.nginx-debian.html   
pi@raspberrypi:~ $ cat /var/www/html/index.nginx-debian.html   
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Nginx 伺服器的操作指令如下表 : 

 Nginx 伺服器操作指令 說明
 sudo service nginx start 啟動伺服器
 sudo service nginx stop 停止伺服器
 sudo service nginx status 檢視伺服器狀態
 sudo service nginx restart 重啟伺服器

或者 :

 Nginx 伺服器操作指令 說明
 nginx -s start 啟動伺服器
 nginx -s stop 停止伺服器
 nginx -t 檢視伺服器狀態
 nginx -s reload 重啟伺服器

參考 :

# https://gist.github.com/jackyu/6379418

pi@raspberrypi:~ $ sudo service nginx start   
pi@raspberrypi:~ $ sudo service nginx stop
pi@raspberrypi:~ $ sudo service nginx restart
pi@raspberrypi:~ $ sudo service nginx status 

● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2019-03-31 18:21:58 CST; 12s ago
     Docs: man:nginx(8)
  Process: 5222 ExecStop=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/ngin
  Process: 5242 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/
  Process: 5239 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, 
 Main PID: 5243 (nginx)
   CGroup: /system.slice/nginx.service
           ├─5243 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           ├─5244 nginx: worker process
           ├─5245 nginx: worker process
           ├─5246 nginx: worker process
           └─5247 nginx: worker process

 3月 31 18:21:58 raspberrypi systemd[1]: Starting A high performance web server and a reverse proxy
 3月 31 18:21:58 raspberrypi systemd[1]: Started A high performance web server and a reverse proxy 
lines 1-17/17 (END)


3. Nginx 設定檔 :

Nginx 伺服器的設定檔放在 /etc/nginx 下 :

pi@raspberrypi:~ $ ls /etc/nginx/ 
conf.d          koi-win            nginx.conf       sites-enabled
fastcgi.conf    mime.types         proxy_params     snippets
fastcgi_params  modules-available  scgi_params      uwsgi_params
koi-utf         modules-enabled    sites-available  win-utf

其中 nginx.conf  是主設定檔, sites-enabled 資料夾用來存放已啟用站台之設定檔, 而 sites-available 資料夾則是存放尚未啟用之站台設定檔.

Nginx 的主設定檔 /etc/nginx/nginx.conf 是全域的, 亦即會套用到 Nginx 底下架設的全部網站, 其預設內容如下 :

pi@raspberrypi:~ $ cat /etc/nginx/nginx.conf
user www-data;
worker_processes auto;     # Nginx 處理器數目
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 768;     # 每個程序之最大連接數
# 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_disable "msie6";

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# 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 text/javascript;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;       # 載入系統設定檔
include /etc/nginx/sites-enabled/*;    # 載入已啟用網站設定檔
}


#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen     localhost:110;
# protocol   pop3;
# proxy      on;
# }
#
# server {
# listen     localhost:143;
# protocol   imap;
# proxy      on;
# }
#}

基本上都不需要更改, 我會用到的大概是 keepalive_timeout, 主要是應付網路爬蟲程式需等待主機撈資料的等待時間, 以前在虛擬主機我都設 120 秒. 下面這篇文章對如何設定以達到 Nginx 最大效能有詳細說明 :

# How to properly host Flask application with Nginx and Guincorn

每次啟動伺服器時, 都會從 /etc/nginx/conf.d/ 載入所有 .conf 設定檔, 並從 /etc/nginx/sites-enabled/ 下載入所有站台設定檔. 

Nginx 伺服器的所有站台設定檔放在 /etc/nginx/sites-enabled/ 目錄下, 預設只有一個檔案 default :

pi@raspberrypi:~ $ ls /etc/nginx/sites-enabled
default 
pi@raspberrypi:~ $ cat /etc/nginx/sites-enabled/default  
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
listen 80 default_server;           # 監聽 80 埠
listen [::]:80 default_server;

# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;

root /var/www/html;      # 預設站台之根目錄絕對路徑

# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;   #預設首頁檔名

server_name _;      # 網站域名, 例如 tw.yahoo.com (_ 表示 localhost)

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}

# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}

Nginx 底下可建立多個站台, 每一個站台都用一個 server {} 來設定, 上面就是預設網站之設定, 使用如下設定指令 (directive) :

 設定指令 說明
 listen 設定監聽埠, 預設為 80 埠 (www)
 root 設定預設站台之根目錄絕對路徑, 預設為 /var/www/html/
 index 列舉首頁檔名
 server_name 設定伺服器名稱, 預設為 _
 location 設定請求處理方式

注意, 首頁是按照 index 中列舉之順序尋找的, 因此若網站習慣用 index.htm 當首頁, 最好將 index.htm 排在 index.html 前面, 可添加 index.php 或 index.py 等. 請求處理方式 location 首先將 $url 視為檔案, 其次將其視為目錄, 如果都不存在則回應 404.

另外, 不論是系統設定檔或站台設定檔, 只要有更改內容都要重新啟動伺服器才會生效.


4. 自訂預設網站首頁 : 

我們可直接在預設根目錄下添加自己的 index.htm 來取代預設的 index.nginx-debian.html. 例如可在根目錄 /var/www/html 下用 nano 編輯一個 index.htm 在瀏覽器上顯示首頁內容為 "Hello World!" :

pi@raspberrypi:~ $ sudo nano /var/www/html/index.htm
pi@raspberrypi:~ $ cat /var/www/html/index.htm 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>MyWeb</title>
  </head>
  <body>
    <b>Hello World!</b>
  </body>
</html>
pi@raspberrypi:~ $ ls /var/www/html 
index.htm  index.nginx-debian.html     

這時再開啟瀏覽器連線 127.0.0.1 或樹莓派本身 IP (我都設定為 STATIC 192.168.2.192) 就會顯示 "Hello World" 網頁而非 Nginx 預設網頁了 :




5. 虛擬目錄 :

除了直接在預設網站根目錄 /var/www/html 下儲存網頁外, 也可以使用虛擬目錄, 將網頁儲存在其他目錄例如 /var/www/test 下, 仍然使用預設網站的 DNS 加上虛擬目錄存取, 例如 127.0.0.1/test.

首先在 /var/www 下建立一個資料夾 test, 並用 Nano 在虛擬目錄 /var/www/test/ 下編輯首頁檔 index.htm :

pi@raspberrypi:~ $ sudo mkdir /var/www/test
pi@raspberrypi:~ $ sudo nano /var/www/test/index.htm 
pi@raspberrypi:~ $ cat /var/www/test/index.htm 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>MyWeb</title>
  </head>
  <body>
    <b>Virtual directory : /var/www/test</b>
  </body>
</html>

然後用 Nano 編輯預設站台的設定檔 /etc/nginx/sites-enabled/default :

pi@raspberrypi:~ $ sudo nano /etc/nginx/sites-enabled/default 

在 location ~ /\.ht 底下加入虛擬目錄的 location {} 段落, 將 /test 路徑對應到目錄 /var/www/test :

  location ~ /\.ht {
    deny all;
  }

  location /test {      # 虛擬目錄添加的部分 
    index index.html index.htm;   
    alias /var/www/test; 
  }

按 Ctrl+O 存檔後按 Ctrl+X 跳出 Nano, 因有修改設定檔, 故需重啟伺服器 :

pi@raspberrypi:~ $ sudo service nginx restart   

開啟瀏覽器連線 127.0.0.1/test 或 192.168.2.192/test 應該顯示如下網頁 :




最後我在虛擬目錄下編輯了一個 jQuery UI 測試網頁 jqtest.htm 如下, 這是一個 Slider 滑桿製作的調色盤, 且可以用下拉式選單選擇主題布景 :

pi@raspberrypi:~ $ sudo nano /var/www/test/jqtest.htm
pi@raspberrypi:~ $ cat /var/www/test/jqtest.htm 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>jQuery UI Example Page</title>
    <link id="theme" href="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
    <script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.4.min.js"></script>
    <script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/jquery-ui.min.js"></script>
    <style>
      body {
        font-family: Arial, Helvetica, sans-serif;
        font-size:10px;
        }
    </style>
  </head>
  <body>
    <select id="themes">
      <option value="base">base</option>
      <option value="black-tie">black-tie</option>
      <option value="blitzer">blitzer</option>
      <option value="cupertino">cupertino</option>
      <option value="dark-hive">dark-hive</option>
      <option value="dot-luv">dot-luv</option>
      <option value="eggplant">eggplant</option>
      <option value="excite-bike">excite-bike</option>
      <option value="flick">flick</option>
      <option value="hot-sneaks">hot-sneaks</option>
      <option value="humanity">humanity</option>
      <option value="le-frog">le-frog</option>
      <option value="mint-choc">mint-choc</option>
      <option value="overcast">overcast</option>
      <option value="pepper-grinder">pepper-grinder</option>
      <option value="redmond">redmond</option>
      <option value="smoothness">smoothness</option>
      <option value="south-street">south-street</option>
      <option value="start">start</option>
      <option value="sunny">sunny</option>
      <option value="swanky-purse">swanky-purse</option>
      <option value="trontastic">trontastic</option>
      <option value="ui-darkness">ui-darkness</option>
      <option value="ui-lightness">ui-lightness</option>
      <option value="vader">vader</option>
    </select><br>
    R <div id="red" style="width:200px;"></div><br>
    G <div id="green" style="width:200px;"></div><br>
    B <div id="blue" style="width:200px;"></div><br>
    <div id="canvas" style="width:200px;height:100px;border-style:solid;">
    </div>
  </body>
  <script>
    $(document).ready(function(){
      $("#themes").selectmenu();
      $("#themes").val("hot-sneaks");
      $("#themes").selectmenu("refresh");
      $('#themes').on('selectmenuchange', function() {
        var theme=$(this).val();
        var href="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/themes/" +
                 theme + "/jquery-ui.css";
        $("#theme").attr("href", href);
        });
      var create=function(e, ui) {
        var style={"width":"25px","text-align":"center"};
        $(this).find(".ui-slider-handle").css(style);
        };
      var slide=function(e, ui) {
        $(this).find(".ui-state-focus").html(ui.value);
        var r=$("#red").slider("value");
        var g=$("#green").slider("value");
        var b=$("#blue").slider("value");
        var bgcolor="rgb(" + r + "," + g + "," + b + ")";
        $("#canvas").css("background-color", bgcolor);
        };
      var config={min: 0, max: 255, create: create, slide: slide};
      $("#red").slider(config);
      $("#green").slider(config);
      $("#blue").slider(config);
      });
  </script>
</html>
pi@raspberrypi:~ $

以瀏覽器連線 127.0.0.1/test/jqtest.htm 顯示如下網頁 : 




可見 jQuery UI 在樹莓派上一樣可正常運作.

另外 Nginx 也可以設定虛擬站台, 但需要設定 DNS 伺服器, 這部分其實我還不懂怎麼做, 而且目前也還用不到, 等有空再來研究.


2019-04-01 補充 :

我在 "瞧你賊西西的-nginx 基礎設定教學" 這篇找到如何修改 DNS 對應的方法了, 原來是要修改 /etc/hosts 這個系統檔, 目前的設定是 :

pi@raspberrypi:~ $ cat /etc/hosts   
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.0.1.1 raspberrypi

原來在網址列打 localhost 就能連線到 127.0.0.1 的設定是在這裡.


6. 虛擬站台 : 

在上面的虛擬目錄測試中已經建立了一個目錄 /var/www/test, 並在底下編輯了兩個網頁檔 index.htm 與 jqtest.htm. 建立虛擬站台的第一步算是已完成了.

第二步是到 /etc/nginx/sites-enabled/ 底下編輯一個網站設定檔, 設定一個虛擬站台 test.com 對應到資料夾 /var/www/test 如下 :

pi@raspberrypi:~ $ sudo nano /etc/nginx/sites-enabled/test.conf 
pi@raspberrypi:~ $ cat /etc/nginx/sites-enabled/test.conf
server {
listen 80;
listen [::]:80;

server_name test.com; 
root /var/www/test;

index index.html;
location / {
try_files $uri $uri/ =404;
}
}

第三步是編輯 /etc/hosts 系統檔, 增加一筆 test.com 對應到 127.0.0.1 這個與 localhost 相同的 IP :

pi@raspberrypi:~ $ sudo nano /etc/hosts 
pi@raspberrypi:~ $ cat /etc/hosts 
127.0.0.1 localhost
127.0.0.1       test.com 
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.0.1.1 raspberrypi

完成虛擬站台設定後必須重啟伺服器 :

pi@raspberrypi:~ $ sudo service nginx restart

然後在瀏覽器連線 test.com/jqtest.htm 就會回應網頁了 :




OK, 大功告成 !

參考 :

# https://www.cyberciti.biz/faq/nginx-restart-ubuntu-linux-command/