BACK
Featured image of post 在 CentOS 上安裝 ASP.NET Core + Nginx 的筆記

在 CentOS 上安裝 ASP.NET Core + Nginx 的筆記

ASP.NET Core 內建的 Kestrel 伺服器輕巧但功能陽春,實務上需搭配 Reverse Proxy 對外提供服務,Linux 有兩大 Reverse Proxy 選擇:Apache 及 Nginx,這邊筆記一下使用這幾年如日中天的 Nginx。

參考網站

ASP.NET Core 內建的 Kestrel 伺服器輕巧但功能陽春,實務上需搭配 Reverse Proxy 對外提供服務,Linux 有兩大 Reverse Proxy 選擇:Apache 及 Nginx,這邊筆記一下使用這幾年如日中天的 Nginx。

相較於 Apache、lighttpd,Nginx 標榜單一執行緒、記憶耗用少、穩定性高,強調效能取向,在熱門網站間獨霸一方(參考:維基百科),與強調效能的 ASP.NET Core 搭配,相得益彰。

以下是我的 CentOS Nginx 安裝設定筆記:

  1. 安裝 Nginx。參考:How To Install Nginx on CentOS 7
1
2
sudo yum install epel-release
sudo yum install nginx
  1. 發現 CentOS 預設沒裝 telnet 客戶端,檢測查修不便,安裝一下 telnet:
1
sudo yum install telnet
  1. 啟動 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 歡迎網頁即代表大功告成。

  1. 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 重新載入。

  1. 試連時我遇到 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
  1. 接著要設定 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;
  }
}

參考:

  1. NLog.config 路徑配合作業系統要改 "/var/log/Darkblog/$/$.log"

  2. 在 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
    });
  }
}
  1. 線上主機當然不好每次靠手動輸入 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
  1. 設好 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
  1. 有一則小訣竅,以服務方式執行 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 計劃繼續挺進。


comments powered by Disqus