2019年4月7日 星期日

2019 年第 14 周記事

拖了一季的鄉下 Pi 3 更換 SD 卡升版工程趁連假時間稍多終於在昨天下午完成了, 看來很複雜的事原來只要一步步去規劃其實很簡單, 很多事都是靠摸著石頭過河而成就的, 顧慮再三通常一事無成, 關鍵在於去除 "這很難, 等準備好再來做" 這個推託思維.

本周順利完成了樹莓派架設 Nginx + uWSGI 的測試, 加上鄉下 Pi 3 主機 SD 卡也順利完成更換, 我的私人雲端運算基礎設施終於搞定了. 想說何必費這麼大勁呢? 原本 Apache + PHP + MySQL 的架構不就可以運作了嗎? 但別忘了 PHP 函式庫支援有限, 切換到 Python 架構勢在必行, 現在忍一下建起來, 以後要做啥都方便.

二哥的申請入學作業已近完成, 去花蓮的車票訂好了, 嘉義則打算開車前往, 火車要轉車不是很方便. 五月中放榜確定後得規劃一下二哥的自學課程, 尤其是遠端教學的做法要研究一下.

昨天週六晚上大帥與小惠夫婦來鄉下投宿, 因他們一大早要去爬月靈縱走, 我原本想陪他們去, 但因林務局修路不知結束沒有, 他們打算先去探路, 若不行則往內門去踏青. 三人聊了一晚上真是盡興.

昨天早上載爸去劉眼科回診, 回程去圖書館還書時找到下面這本 :

# 活到天年養老有方 :解讀經典中的長壽祕密




此書介紹清朝養生名家曹廷棟的傳世著作 "老老恆言", 曹氏生活於康熙乾隆年間, 因幼患肺癆身體虛弱, 故而居家研究中國古醫書, 年老之際將畢生研究心得寫成書, 曹翁本人亦享高壽, 參考 :

# 名醫曹廷棟和他的善養之術

下周開始要為邏輯設計課備課了, 這要一直忙到六月初才會結束. 另外就是評鑑作業, 這五月初就會搞定了.

說得多做得少, 終將一事無成. 

市圖還書 2 本

下列書籍有人預約須還 :
  1. PhoneGap+Node.js整合實作! :用JavaScript做出跨平臺手機App和雲端運用
  2. 靈界的科學 :李嗣涔博士25年科學實證,以複數時空.量子心靈模型,帶你認識真實宇宙
Node.js 上回停在 Express 安裝, 等六月忙完授課後再回頭整理.

2019年4月6日 星期六

樹莓派 Pi 3 主機更換 SD 卡升版

今天將鄉下的 Pi 3 主機升版, 自 2017 年啟用後到現在已兩年, 原來的 8GB SD 卡在安裝測試各式套件與軟體後已不敷使用, 查過 Pi 3 最高支援 32 GB SD 卡, 所以去年底去建國路維碁買了一張 Sandisk SD 卡, 升版為 2018/11/13 版的 Raspbian Stretch 後就沒時間正式更換, 趁連假時間較多換一換, 免得要安裝測試甚麼都沒辦法做.

以下是繼續上回未完的升版作業, 參考下面幾篇 :

# 樹莓派 Raspbian 升版
# 樹莓派 Wifi 固定 IP 的新作法
# 利用樹莓派的 Python 程式定時回報外網 IP 的方法 (Heart beat)
# 樹莓派自動偵測網路斷線時重開機的方法
# 樹莓派遠端連線 VNC Connect 設定
# 在樹莓派上安裝 cURL 與設定 Crontab
# 樹莓派常用的 Linux 指令
# 樹莓派 Raspberry Pi 文章列表

另外找到一篇寫得很好的 Crontab 教學 :

# cron jobs crontab 排程教學


首先是準備中做 : 備份原系統資料, 步驟如下 :

1. 用 WinSCP 備份 /home/pi 底下的檔案到 PC

主要是 reportop2.py 與 reportip3.py 這兩個檔案.

2. 記錄目前的 crontab 內容 :

pi@raspberrypi:~ $ crontab -l
*/10 * * * * /usr/bin/python2 /home/pi/reportip2.py
0 * * * * sudo /usr/local/bin/checkwifi.sh
30 * * * * /usr/bin/python3 /home/pi/reportip3.py

3. 備份 checkwifi.sh 內容 : 

pi@raspberrypi:~ $ cat /usr/local/bin/checkwifi.sh
ping -c4 192.168.2.2 > /dev/null
if [ $? != 0 ]
then
  sudo reboot
fi

事實上 checkwifi.sh 與 reportip2.py, reportip3.py 我都先存在 /home/pi 目錄下, 權限均改為可執行, 另外 checkwifi.sh 須複製到 /usr/local/bin 底下.

接下來就可以進行 SD 卡更換作業, 先下 sudo poweroff 停機, 更換 SD 卡後開機, 然後進行下列程序以恢復自動通知外網 IP 與自動偵測網路斷線時重開機的功能 :

1. 更改固定 IP 與 DNS 伺服器設定 :

將 static routers 改為 192.168.2.2
鄉下的信舟 AP 路由器 IP 是 192.168.2.2, 而高雄的卻是 192.168.2.1.

$ sudo nano /etc/dhcpcd.conf   
$ sudo cat /etc/dhcpcd.conf 
interface wlan0
static ip_address=192.168.2.192 
static routers=192.168.2.2 
static domain_name_servers=192.168.2.2

注意, domain_name_servers 要設與 routers 相同 (即無線基地台 IP), 或者設8.8.8.8 (Google Public DNS) 亦可.

2. 將 reportip2.py, reportip3.py, checkwifi.sh 用 WinSCP 傳回 /home/pi 下.

3. 安裝 dos2unix 將 checkwifi.sh 轉成 UNIX 格式, 改為 775 權限後複製到 /usr/local/bin 底下 :

pi@raspberrypi:~ $ sudo apt-get install dos2unix 
pi@raspberrypi:~ $ dos2unix checkwifi.sh
pi@raspberrypi:~ $ sudo cp checkwifi.sh /usr/local/bin/checkwifi.sh 
pi@raspberrypi:~ $ sudo chmod 775 /usr/local/bin/checkwifi.sh
pi@raspberrypi:~ $ sudo /usr/local/bin/checkwifi.sh 

4. 更改 reportip2.py, reportip3.py 權限為可執行 : 

pi@raspberrypi:~ $ sudo chmod +x /home/pi/reportip2.py
pi@raspberrypi:~ $ sudo chmod +x /home/pi/reportip3.py   

5. 編輯 root 權限的 crontab 定時執行 reportip3.py, checkwifi.sh :

pi@raspberrypi:~ $ sudo crontab -e  (注意, 須用 sudo)
0 * * * * sudo /usr/local/bin/checkwifi.sh
30 * * * * /usr/bin/python3 /home/pi/reportip3.py

這回我捨棄 reportip2.py, 不再比較 IP 變化, 固定每小時回報 IP. 改好 crontab 後在 30 分時檢查有收到郵件通知目前 IP :





6. 登入 VNC 設定 Cloud Connection : 

參考 "樹莓派遠端連線 VNC Connect 設定", 登入 VNC Cloud, 可不需要 IP 即可透過 VNC 雲端連線 Pi 3.

以上就順利完成 SD 卡升級作業了. 用 df -h 查詢 SD 卡使用情形 :

pi@raspberrypi:~ $ df -h
檔案系統        容量  已用  可用 已用% 掛載點
/dev/root        30G  4.5G   24G   17% /
devtmpfs        460M     0  460M    0% /dev
tmpfs           464M  6.8M  457M    2% /dev/shm
tmpfs           464M   18M  446M    4% /run
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
tmpfs           464M     0  464M    0% /sys/fs/cgroup
/dev/mmcblk0p1   44M   23M   22M   51% /boot
tmpfs            93M     0   93M    0% /run/user/1000

用掉 4.5G, 還有 24G 可用, 比以前大多了.

但完成後用瀏覽器上網, 卻發現連 Google 都無法連線, 出現如下錯誤 :

ping : www.google.com.tw 解析名稱時發生暫時性的錯誤 

查詢網路找到下面這篇 :

# 解决树莓派连载路由器上后无法解析域名导致无法上网的问题

原來還要修改 /etc/resolv.conf, 將原先在高雄用的路由器 IP=192.168.2.1 改為鄉下的 192.168.2.2, 上面那篇是改為 8.8.8.8, 這是 Google Public DNS, 參考 :

# Google Public DNS上網跑更快,用戶端趕快更換IPv4 DNS設定8.8.8.8與8.8.4.4

pi@raspberrypi:~ $ ping www.google.com.tw 
ping: www.google.com.tw: 解析名稱時發生暫時性的錯誤 
pi@raspberrypi:~ $ sudo cat /etc/resolv.conf 
# Generated by resolvconf
nameserver 192.168.2.1
pi@raspberrypi:~ $ sudo nano /etc/resolv.conf 
pi@raspberrypi:~ $ sudo cat /etc/resolv.conf 
# Generated by resolvconf
nameserver 192.168.2.2

這樣再 ping 就有回應了, 瀏覽器也能上網了 :

pi@raspberrypi:~ $ ping www.google.com.tw 
PING www.google.com.tw (172.217.27.131) 56(84) bytes of data.
64 bytes from tsa03s02-in-f3.1e100.net (172.217.27.131): icmp_seq=1 ttl=55 time=45.9 ms
64 bytes from tsa03s02-in-f3.1e100.net (172.217.27.131): icmp_seq=2 ttl=55 time=48.8 ms
64 bytes from tsa03s02-in-f3.1e100.net (172.217.27.131): icmp_seq=3 ttl=55 time=48.6 ms
64 bytes from tsa03s02-in-f3.1e100.net (172.217.27.131): icmp_seq=4 ttl=55 time=49.3 ms
--- www.google.com.tw ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 45.925/48.179/49.303/1.342 ms

OK, 現在開始要用這台主機做測試運算了!

2019-04-07 補充 :

下午發現沒收到 IP 信件, 檢查發現 /etc/resolv.conf 內容又被改回 nameserver 192.168.2.1, 研判是有發生系統重啟, 檢查 /etc/dhcpcd.conf 發現昨天忘記改最後一航 static domain_name_servers 設定, 仍然是 192.168.2.1, 導致系統重啟重新設定 /etc/dhcpcd.conf 時又把  /etc/resolv.conf 內容改為 192.168.2.1, 已經修正上面設定程序 1.

2019年4月5日 星期五

jQuery UI 學習筆記索引

三月因為學習 Django 的範本時因發覺以前在專案中用了這麼多 jQuery UI, 卻因為時間關係沒寫下多少筆記 (只有 Autocomplete 與 DataTables), 因此花了三周時間把它補起來, 以下是索引, 方便以後查找 :

2013 年 :

# jQuery UI 執行環境配置
# jQuery UI widget 之訊息盒與輸入盒應用範例
# jQuery UI widget 之確認盒應用範例
# jQuery UI 的自動完成器 autocomplete 測試
# jQuery UI 的日期選擇器 datepicker 測試
# jQuery 套件 DataTables 的測試

2019 年 :

# jQuery UI 學習筆記 (一) : 主題布景 (Themes)
# jQuery UI 學習筆記 (二) : 按鈕 (Button)
# jQuery UI 學習筆記 (三) : 對話盒 (Dialog)
# jQuery UI 學習筆記 (四) : 頁籤面板 (Tabs)
# jQuery UI 學習筆記 (五) : 微調器 (Spinner)
# jQuery UI 學習筆記 (六) : 滑桿 (Slider)
# jQuery UI 學習筆記 (七) : 進度條 (ProgressBar)
# jQuery UI 學習筆記 (八) : 按鈕群組 (Buttonset)
# jQuery UI 學習筆記 (九) : 單選與複選按鈕 (CheckboxRadio)
# jQuery UI 學習筆記 (十) : 下拉式選單 (Selectmenu)
# jQuery UI 學習筆記 (十一) : 選單 (Menu)
# jQuery UI 學習筆記 (十二) : 折疊選單 (Accordion)
# jQuery UI 學習筆記 (十三) : 控制群組 (ControlGroup)
# jQuery UI 學習筆記 (十四) : 文字輸入框的主題樣式
# jQuery UI 學習筆記 (十五) : 工具提示 (Tooltips)
# jQuery UI 學習筆記 (十六) : 凸顯 (Hightlight) 與錯誤 (Error) 訊息
# jQuery UI 學習筆記 (十七) : 用下拉式選單做倒數日數計數器

參考書籍 :
  1. jQuery UI 使用者介面設計 (歐萊里, Studio Tib. 譯)
  2. jQuery UI 與 Plugin 開發實戰 (悅知文化, 吳哲穎譯)
  3. jQuery 全能權威指南 (上奇, 張亞飛)
  4. Pro jQuery 2.0 2nd ed. (Apress, Freeman Adam)
  5. jQuery UI in Action (Manning, TJ Vantoll)
  6. jQuery 應用程式設計極速上手 (上奇, 羅友志譯)
  7. 打造 jQuery 網頁風暴 (張子秋, 佳魁)
2020-09-11 完成

在樹莓派 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月3日 星期三

空軍防砲學校

今天在找東華大學地圖時, 想到以前服役時曾駐防空軍花蓮防砲學校一段時間, 似乎就在東華大學這一帶, 查詢才知原來舊防校不是在壽豐, 而是在吉安, 現在已經改為空軍後指部南埔營區了.

順藤摸瓜還找到防校畢業生架設的紀念網站 (空軍防校因為併入陸軍砲兵飛彈校而廢校了), 在裡面赫然發現以前在砲校受預官訓時的朱中隊長原來是專科班 18 期生, 而我退伍前的頂頭上司沈連長則是專科班 22 期生, 上個月退休的老同事竟然也是, 我以前沒問都不知道, 人生真的有非常多的巧遇啊! 六度分隔理論看來並非虛言.

雖然我與防校並無淵源, 但駐防在那裏的幾個月時間 (應該是民國 77~78 年r間) 卻是我近兩年預官生涯的最高峰, 因為奉令 "被" 整訓, 上級下令全營從金門移防到防校後須比照新訓中心操練, 例如三分鐘戰鬥澡等等, 當時的阮連長要求值星排長必須負起責任掌控, 所以我整天就與值星班長一起吼叫, 老兵原先是看笑話, 但兩天後就笑不出來了. 因為認真辦事讓阮連長刮目相看, 叫我代替他當營值星官, 還問我要不要簽下去, 呵呵.

我突發奇想到 facebook 去尋找阮連長, 還真的被我找到了. 雖然歲月的滄桑掩不住, 但出身空官 63 期的他還是像以前那樣高大帥氣, 那時不能飛戰機而下放到地面部隊應該很鬱卒, 但現在看來是退役後過著悠遊的生活, 也不錯. 我自金門下部隊到移防花蓮都是在他的磨練下蛻變成長, 他的威嚴讓我敬畏. 在金門時帶兵遇到過兩個超級危機, 這個菜鳥預官那時應該帶給他不少困擾. 正在考慮去花蓮時要不要去找他, 應該還記得我吧.

2019年4月2日 星期二

二哥申請入學匯款

這兩天二哥備審資料寫好了, 匯款單上周也都收到了, 東華與高大要去土銀匯款, 嘉大則是網路匯至中信銀, 各校收費標準不一 :
  1. 東華大學 : 1250 元
  2. 高雄大學 : 1200 元
  3. 嘉義大學 : 1000 元
東華面試日期 4/18, 已訂 4/17 會館一間, 往花蓮自強號班次不多 :
  1. 去程 : 4/17 自強 317 (16:24~21:51)
  2. 回程 : 4/18 自強 324 (15:24~19:51)
這兩班都是周一至周四打 97 折, 但回程 324 列車只停靠花蓮站.

# 台鐵網路訂便當

2019年4月1日 星期一

高科大還書 4 本

跟母校高科大借閱的下列 4 本書有人預約, 近期將還, 在此做個備忘錄, 以後再借, 特別是 2, 3 兩本 :
  1. 科班出身的MVC網頁開發 : 使用Python + Django
  2. Python量化投資縱橫金融 : 從程式到現金之路
  3. Python神乎其技 : 精要剖析語法精髓,大幅提升程式功力
  4. 一次搞定 : 所有Python Web框架開發百科全書
四本均調借自原第一科大, 此分校藏書頗豐. 我看一個學校是從圖書館藏書看起, 如果館藏少且舊, 那評價馬上掉一半. 大學本就是做學問的地方, 沒書怎行.

在樹莓派 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/