ASP.NET Core + Nginx on CentOS 安裝筆記
9 | 19,064 |
先前文章已初步驗證 ASP.NET Core 程式可以不經修改直接搬到 Linux 執行,接下來得真的把它配置好才能上戰場。
ASP.NET Core 內建的 Kestrel 伺服器輕巧但功能陽春,實務上需搭配 Reverse Proxy 對外提供服務,在 ASP.NET Core 值得學嗎? 提過 Linux 有兩大 Reverse Proxy 選擇:Apache 及 Nginx,評估後決定使用這幾年如日中天的 Nginx。
相較於 Apache、lighttpd,Nginx 標榜單一執行緒、記憶耗用少、穩定性高,強調效能取向,在熱門網站間獨霸一方(參考:維基百科),與強調效能的 ASP.NET Core 搭配,相得益彰。
以下是我的 CentOS Nginx 安裝設定筆記:
-
安裝 Nginx。參考:How To Install Nginx on CentOS 7
sudo yum install epel-release sudo yum install nginx
-
發現 CentOS 預設沒裝 telnet 客戶端,檢測查修不便,跑一下
sudo yum install telnet
。 -
啟動 Nginx
sudo systemctl start nginx
啟動後 telnet localhost 80 如有連上就是成功了。如從外部連不上多是防火牆緣故,需額外設定:
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.confserver { 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 錯誤:
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+ 新加的安全鎖,需下指令解除封印:
sudo setsebool -P httpd_can_network_connect on
-
接著要設定 SSL。
有個很威的工具叫 Certbot,可以自動申請、驗證、下載、安裝並定期更新 Let's Enrypt 憑證。但這部分要將網站正式掛上 Internet 才好測試,真實憑證留待未來再玩,我先做一張自發憑證驗證 SSL 功能。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
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 決定要不要啟用,一勞永逸。 :
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,內容如下:
[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 後註冊並啟動服務
sudo systemctl enable kestrel-darkblog.service sudo systemctl start kestrel-darkblog.service sudo systemctl status kestrel-darkblog.service
馬上遇到錯誤:
(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 群組可以寫入: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 看不到主控台顯示的錯誤訊息,可改用
sudo journalctl -fu kestrel-darkblog.service
查看。
就醬,ASP.NET Core + Nginx on CentOS 執行成功,雖然從瀏覽器的角度完全看不出跟在 IIS 跑有什麼不同 XD
附上 CPU / Memory 使用狀況,這是在開瀏覽器狂按 F5 下的數字,總記憶體 1GB,dotnet CPU 在 5% 以下,RAM 耗用不到 10%,有個 kworker CPU 偏高,爬文與硬碟有關,推測與 Win10 Hyper-V VM 不怎麼的虛擬磁碟效能有點關係。但整體數字讓我很滿意,遷都 CentOS 計劃繼續挺進。
Tips of converting ASP.NET Core web as service and configurating Nginx as reverse proxy on a CentOS box
Comments
# by Calos
關於第四步,conf.d 應該是放 Nginx Server global settings 較為合適,而針對不同站點的個別設定則放在 site-available,並且使用 ln -s 建立一個 symbol link 到 site-enabled。 https://wiki.debian.org/Nginx/DirectoryStructure https://serverfault.com/a/527641/406347
# by Jeffrey
to Calos, 謝謝補充,稍晚來研究。
# by 唯
SELinux 問題真的多到我快受不了… 搞到最後關掉他才是真的
# by Steven
sudo yum intsall telnet install 才對XD
# by Jeffrey
to Steven, 感謝,已修正。
# by ryan
第六步有個nginx 設定的截圖 server_name linux.darkblog.net; 這樣就可以綁定網域嗎? 剛好正在查設定網域的教學 網域已購買好
# by Jeffrey
to ryan, 在 DNS 可以多個網域名稱指向同一個 IP,例如:web1.darkblog.net、web2.darkblog.net 都指向 192.168.1.1,因此我輸入 http: //web1.darkblog.net 或 http: //web1.darkblog.net 都是連向 192.168.1.1 這台主機的 80 Port,Nginx 指定 server_name web1.darkblog.net; 及 server_name web2.darkblog.net; 的話,一台主機同一個 IP 可以跑多個網站,依輸入 URL 不同導向不同網站。但前題是你要在 DNS 伺服器設定將網域名稱指向 IP,這部分可以用網域註冊廠商的介面設定。
# by ryan
to Jeffrey 已去網域註冊商設定 nginx server_name 也加了 輸入網址出現 Access Denied (policy_denied) Your system policy has denied access to the requested URL. 剩下應該是我主機設定問題了 再查查 謝謝回覆
# by ryan
to Jeffrey 成功了 剛發現是因為用公司網路連 被公司擋住了 其實是成功的 感謝