參考網站
ASP.NET Core 內建的 Kestrel 伺服器輕巧但功能陽春,實務上需搭配 Reverse Proxy 對外提供服務,Linux 有兩大 Reverse Proxy 選擇:Apache 及 Nginx,這邊筆記一下使用這幾年如日中天的 Nginx。
相較於 Apache、lighttpd,Nginx 標榜單一執行緒、記憶耗用少、穩定性高,強調效能取向,在熱門網站間獨霸一方(參考:維基百科),與強調效能的 ASP.NET Core 搭配,相得益彰。
以下是我的 CentOS Nginx 安裝設定筆記:
- 安裝 Nginx。參考:How To Install Nginx on CentOS 7
1
2
| sudo yum install epel-release
sudo yum install nginx
|
- 發現 CentOS 預設沒裝 telnet 客戶端,檢測查修不便,安裝一下 telnet:
1
| sudo yum install telnet
|
- 啟動 Nginx
1
| sudo systemctl start nginx
|
啟動後 telnet localhost 80 如有連上就是成功了。如從外部連不上多是防火牆緣故,需額外設定:
1
2
3
| sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
|
設好防火牆,從 Windows 開 Chrome 連上 CentOS 主機 80 Port,如果看到 Nginx 歡迎網頁即代表大功告成。
- ASP.NET Core 文件有詳細的Nginx 設定教學,做法是直接修改
/etc/nginx/nginx.conf
,在 http 區塊加入 server 設定。
但較模組化的做法是為每個站台寫獨立 conf 檔放在 /etc/nginx/conf.d
下。例如:/etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| server {
listen 80;
server_name linux.darkblog.net; #測試用的自訂網域名稱
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
|
設定完畢先用 sudo nginx -t
測試設定檔有沒有被改壞,若 OK 就執行 sudo nginx -s reload
重新載入。
- 試連時我遇到 502 Bad Gateway 錯誤:
1
2
| 2018/09/24 15:37:53 [crit] 60137#0: *7 connect() to 127.0.0.1:5000 failed (13: Permission denied) while connecting to upstream, client: 192.168.50.159, server: linux.darkblog.net, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:5000/", host: "linux.darkblog.net"
2018/09/24 15:37:53 [error] 60137#0: *7 no live upstreams while connecting to upstream, client: 192.168.50.159, server: linux.darkblog.net, request: "GET /favicon.ico HTTP/1.1", upstream: "http://localhost/favicon.ico", host: "linux.darkblog.net", referrer: "http://linux.darkblog.net/"
|
爬文是 Security-Enhanced Linux (SELinux) 作祟,它是 RHEL 6.6+/CentOS 6.6+ 新加的安全鎖,需下指令解除封印:
1
| sudo setsebool -P httpd_can_network_connect on
|
- 接著要設定 SSL:
有個很威的工具叫 Certbot,可以自動申請、驗證、下載、安裝並定期更新 Let’s Enrypt 憑證。但這部分要將網站正式掛上 Internet 才好測試,真實憑證留待未來再玩,我先做一張自發憑證驗證 SSL 功能。
1
2
3
4
| sudo mkdir /etc/ssl/private
sudo chmod 700 /etc/ssl/private
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
|
建立一個 /var/nginx/conf.d/ssl.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| server {
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
server_name linux.darkblog.net;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
|
參考:
NLog.config 路徑配合作業系統要改 "/var/log/Darkblog/$/$.log"
在 Reverse Proxy 模式下 HttpContext.Connection.RemoteIpAddress 會抓到 ::1,而 HttpContext.Connection.RemotePort 則是 5000,並非真實客戶端 IP 及對外 Port。在 Startup.cs 加入 app.UseForwardedHeaders() 可解決問題,但啟用 UseForwardedHeaders() 若未搭配 Reverse Proxy 會有來源 IP 偽造風險,不想冒險也不想針對 IIS / Nginx 調整設定,我想到一招讓程式自動依 OS 決定要不要啟用,一勞永逸:
1
2
3
4
5
6
7
8
9
10
11
| public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//運行於 Linux 時啟用 Reverse Proxy 模式
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
}
}
|
- 線上主機當然不好每次靠手動輸入 dotnet Blah.dll 啟動網站,ASP.NET Core 文件展示了將程式包成服務的方法。先建立 /etc/systemd/system/kestrel-darkblog.service,內容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| [Unit]
Description=Darkblog.Core Server
[Service]
WorkingDirectory=/var/www/Darkblog
ExecStart=/usr/bin/dotnet /var/www/Darkblog/Darkblog.Core.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
SyslogIdentifier=darkblog-core
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
|
- 設好 kestrel-darkblog.service 後註冊並啟動服務
1
2
3
| sudo systemctl enable kestrel-darkblog.service
sudo systemctl start kestrel-darkblog.service
sudo systemctl status kestrel-darkblog.service
|
馬上遇到錯誤:
1
| (code=exited, status=217/USER) Process: 66571 ExecStart=/usr/bin/dotnet /var/www/Darkblog/Darkblog.Core.dll (code=exited, status=1/FAILURE)
|
原因是服務模式使用 www-data 身分執行,沒有權限存取 ASP.NET Core 網站所在目錄與檔案。這部分我不是很確定做法,找到的解法是用 chown 將 /var/www/Darkblog 目錄的擁有者及群組都設成 www-data,建立 www-data 帳號及群組,並將我的管理帳號也加入 www-data 群組,如此服務可以存取該目錄,而我也有權限部署檔案。(註:) 以下指令建立 www-data 使用者及群組,將 jeffrey 加入群組,並授與 www-data 群組可以寫入:
1
2
3
4
| sudo groupadd www-data
sudo useradd -g www-data www-data
sudo usermod -a -G www-data jeffrey
sudo chmod g+w -R /var/www/Darkblog
|
- 有一則小訣竅,以服務方式執行 ASP.NET Core 看不到主控台顯示的錯誤訊息,可改用這個指令來查看:
1
| sudo journalctl -fu kestrel-darkblog.service
|
以上,ASP.NET Core + Nginx on CentOS 執行成功。
附上 CPU / Memory 使用狀況,這是在開瀏覽器狂按 F5 下的數字,總記憶體 1GB,dotnet CPU 在 5% 以下,RAM 耗用不到 10%,有個 kworker CPU 偏高,爬文與硬碟有關,推測與 Win10 Hyper-V VM 不怎麼的虛擬磁碟效能有點關係。但整體數字讓我很滿意,遷都 CentOS 計劃繼續挺進。