##1. 安裝 .NET Core SDK 或 .NET Core Runtime
先將 Microsoft 的套件簽署金鑰新增至受信賴金鑰清單及加入套件存放庫,依序執行以下命令
```bash
$ wget https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https
$ sudo apt-get update
```
接著在看是選擇要安裝 .NET Core SDK 還是 .NET Core Runtime 都可以,自行選擇一種方式(SDK 提供的功能較多,主要是用來開發除錯用,但體積較大,一般來說裝 Runtime 即可)
```sql
/* 1. 安裝 .NET Core SDK */
$ sudo apt-get install -y dotnet-sdk-3.1
/* 2. 安裝 .NET Core Runtime */
$ sudo apt-get install -y aspnetcore-runtime-3.1
/* 補充: 安裝不包含 ASP.NET Core 支援的 .NET Core Runtime */
$ sudo apt-get install -y dotnet-runtime-3.1
```
到這邊其實就已經完成 .NET Core 執行環境的安裝了,但如果執行了上面的命令遇到了類似 ***Unable to locate package {netcore-package}*** 這樣的訊息時(我就真的遇到了),那麼請參考 [疑難排解\#1](#spanstylecolorfa2a3f1span)
補充: 若之後要更新 SDK 或 Runtime,執行以下命令即可
```bash
# Step 1
$ sudo apt-get update
# Step 2
$ sudo apt-get upgrade
```
##2. 安裝 Nginx 使用編輯器開啟`/etc/apt/sources.list`並加入下面兩行 Nginx 套件來源節點(stanza) ``` bash deb https://nginx.org/packages/ubuntu/ xenial nginx # Ubuntu 版本為 18.04 的話 xenial -> bionic deb-src https://nginx.org/packages/ubuntu/ xenial nginx # Ubuntu 版本為 18.04 的話 xenial -> bionic ``` 開始安裝 Nginx ``` bash $ sudo apt-get update $ sudo apt-get install nginx ``` 到這邊就已完成 Nginx 的安裝,但若出現 ***GPG error: https://nginx.org/packages/ubuntu xenial Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY $key*** 的錯誤訊息,請參考 [疑難排解\#2](#spanstylecolorfa2a3f2span)
##3. 設定 Nginx 先試著啟動 Nginx ``` bash $ sudo service nginx start ``` 然後在瀏覽器上輸入 Server IP 應該就能看到預設畫面
![Image](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC6bo9fQixtwA9CdbNUl8s-3TLQNpvK1K2kVbXqjmFhnxbLnrqsAwKWSOI8bLhSKIDaahlDkPoAU5KD_Qu9FSbBfObouQzDEXp_hHQHcnCcOTiMx1542saNM_fVsE7qEMtR9Zx5edBAtUP/s0/01.jpg) 根據[微軟官方文件](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx)上的描述,安裝完成後在 /etc/nginx/sites-available 的路徑下應該會有一個名為`default.conf`的設定檔,但我的`default.conf`是在 conf.d 資料夾內,然後也沒有 sites-available 資料夾,猜想 Nginx 官方可能有做了些修改,因此我是在 /etc/nginx 資料夾下先自行建立 sites-available 資料夾然後在裡面新增一個`your_app_name.conf`檔(名稱可修改成你想取的)內容如下 ```bash server { listen 80; listen [::]:80; # SSL 憑證設定方式(有需要的話把以下四行的註解拿掉並指向自己放憑證的目錄) # listen 443 ssl http2; # listen [::]:443 ssl http2; # ssl_certificate /etc/nginx/ssl/your_domain_com/ssl-bundle.crt; # ssl_certificate_key /etc/nginx/ssl/your_domain_com/private.key; server_name your_domain.com *.your_domain.com; # 你的網域名稱(多個以空白分隔) location / { proxy_pass http://localhost:5000; # 根據你使用的 Port 調整 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; } # 如果有 SignalR 連線需求的話可參考以下設定(非必要) location /hub { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # $connection_upgrade 該變數來自 nginx.conf proxy_cache off; proxy_buffering off; proxy_read_timeout 100s; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` 補充: location 的匹配規則可以[參考這](https://linmasaki09.blogspot.com/2021/03/nginx-location.html) 然後參考官方文件修改`conf.d/default.conf`的設定檔,改成 ```bash server { listen 80 default_server; # 加上 default_server listen [::]:80 default_server deferred; # IPv6 用,加上 deferred 為客戶端建立完 TCP 連接後,內核會等到有請求數據時才喚醒 worker 以減輕其負擔;適用於高併發場景 server_name _; # localhost 改成_ return 444; # 加上這行可讓外部直連 IP 的方式失效 #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } } ``` 最後再參考以下範例編輯`/etc/nginx/nginx.conf`檔並在最下面加入`include /etc/nginx/sites-available/*.conf;`這行設定,如下 ```bash user nginx; worker_processes auto; # 請設定與 CPU 核心數相等的數字,進程切換代價最小,auto 為系統自動偵測 CPU 核心數,預設為 1 error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; multi_accept on; # 儘可能接受最多的連接數 use epoll; } http { server_tokens off; # 隱藏 Nginx 版本資訊 include /etc/nginx/mime.types; default_type application/octet-stream; client_max_body_size 10M; # 上傳檔案大小限制(10MB) ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 30s; client_header_timeout 30s; client_body_timeout 30s; reset_timedout_connection on; send_timeout 30s; # Gzip 設置 gzip on; gzip_proxied any; gzip_buffers 16 8k; gzip_disable "MSIE [1-6]\.(?!.*SV1)"; gzip_http_version 1.1; gzip_vary on; # 增加回應標頭 "Vary: Accept-Encoding" gzip_comp_level 6; # 壓縮比1~9, 數字越高壓縮程度越大但越耗 CPU 效能 gzip_min_length 1024; # 超過 1024k 才壓縮, 預設是 0 全壓縮 gzip_types text/plain text/css application/javascript application/x-javascript text/javascript application/json application/xml application/xml+rss text/xml; # 根據 $http_connection 來決定變數 $connection_upgrade 的值(SignalR 會用到) map $http_connection $connection_upgrade { "~*Upgrade" $http_connection; default keep-alive; } proxy_buffering off; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; proxy_http_version 1.1; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-available/*.conf; # 加入這行 } ``` 以上設定都完成後,執行以下命令 ```bash # 驗證設定檔 $ sudo nginx -t # 讓 Nginx 重新讀取新的設定檔 $ sudo nginx -s reload ```
##4. 發佈及佈署 ASP.NET Core 應用程式 在反向代理(Reverse Proxy)模式下,使用 HttpContext.Connection.RemoteIpAddress 會抓到不正確的客戶端 IP,因此要在網站專案下的`Startup.cs`檔裡加入 app.UseForwardedHeaders(),其置放的位置根據[微軟官方文件](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1#fhmo)的建議,要在診斷和處理錯誤的中介服務(Middleware)後,其它服務前,如下 ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); } else { app.UseExceptionHandler("/Home/Error"); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); /*** 省略其它代碼 ***/ } ``` 由於我們已經要使用 Nginx 了,SSL 相關的憑證到時都會直接綁在 Nginx 上,所以要在我們的 ASP.NET Core 應用程式上移掉有關 https 的設定,可參考此篇文章[設定 ASP.NET Core 3.x 的起始連結(URL)位址](https://linmasaki09.blogspot.com/2020/04/aspnet-core-3x-url.html)的說明,修改 Properties 目錄下`launchSettings.json` 檔案裡的 applicationUrl 屬性值,將 https://localhost:xxxx 移除(如果有),或是直接在相對應的`appsettings.json`裡只設定 Http 區段,如下 ``` Json { "Logging": { "LogLevel": { "Default": "Warning", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "Kestrel": { "EndPoints": { "Http": { "Url": "http://localhost:5000" /* 只保留 HTTP 這行 */ } } }, "ConnectionStrings": { "MyDbContext": "" }, /*** 省略其它代碼 ***/ } ``` 然後使用以下的指令發佈應用程式(二擇一) ```bash # 1. 不指定特地平台 $ dotnet publish -c Release -o bin\Release\netcoreapp3.1\WebPublisher # 2. 有指定特地平台 $ dotnet publish -c Release -r linux-x64 --self-contained false -o bin\Release\netcoreapp3.1\WebPublisher ``` 將產生的檔案(WebPublisher 資料夾下的所有檔案)複製到伺服器上的`/var/www/your_app_name`下,並執行 ```bash $ dotnet Your_App_Assembly_Name.dll ``` 補充: 若有指定環境變數的需求,可先輸入以下指令來改變目前應用程式的執行環境變數 ```bash $ export ASPNETCORE_ENVIRONMENT=YourEnvironment ``` 或是直接在執行時指定環境變數 ```bash $ dotnet Your_App_Assembly_Name.dll --environment=YourEnvironment # e.g. --environment=Uat ```
##5. 將 ASP.NET Core 應用程式註冊為系統服務 為了讓 ASP.NET Core 應用程式在系統重開機後都能自動啟動,還需要再進行一些設定,首先檢查系統是否已有 www-data 群組 ```bash # 1. 確認 www-data 群組是否存在 $ grep 'www-data' /etc/group # 2. 若不存在才執行此行命令 $ sudo groupadd www-data # 3. 將網站資料夾的群組擁有者設給 www-data $ sudo chgrp -R www-data /var/www/your_app_name # 4. 讓群組有讀寫權限 $ sudo chmod 775 -R /var/www/your_app_name ``` 再來切換到 /etc/systemd/system 目錄下,建立一個系統服務定義檔 ```bash $ sudo nano /etc/systemd/system/your_app_name.service ``` 編輯該檔,內容如下 ```bash [Unit] Description=Your App Name # 你的應用程式名稱 [Service] WorkingDirectory=/var/www/your_app_name # 你的應用程式路徑 ExecStart=/usr/bin/dotnet /var/www/your_app_name/Your_App_Assembly_Name.dll # 改成你的 dll 檔 Restart=always RestartSec=10 # 假如應用程式遇到錯誤而停止,10 秒後將會自動重啟 KillSignal=SIGINT SyslogIdentifier=your_app_name # your_app_name 改成你的應用程式名稱(Log用) User=www-data Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install] WantedBy=multi-user.target ``` 最後依序執行下列步驟 1 ~ 3 的指令 ```bash # 1. 開啟系統開機自動啟動服務 $ sudo systemctl enable your_app_name.service # 2. 啟動服務 $ sudo systemctl start your_app_name.service # 3. 查看狀態 $ sudo systemctl status your_app_name.service # 補充 $ sudo systemctl disable your_app_name.service # 關閉系統開機自動啟動服務 $ sudo systemctl stop your_app_name.service # 停止服務 $ sudo systemctl restart your_app_name.service # 重啟服務 ``` 看到出現類似下面畫面即大功告成
![Image](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7u9E0Zgy1HBCTUh4VqwvahHOUDmZBb2UaroynZClYLPKQDXMnziuk85PZ_a0xwzbNu4iQH1M3FmWbtnEShjH1Eux8yOLHp4D4P-jIzSwObrCMCoym0I6mHQNSZcuat7BlURioO8r05EBp/s0/02.jpg) 補充: 使用以下指令可查看系統服務的日誌 ```bash $ sudo journalctl -fu your_app_name.service $ sudo journalctl -fu your_app_name.service --since "2020-08-30" --until "2020-08-31 18:00" # 指定區間 ```
##疑難排解\#1 當遇到了`Unable to locate package {netcore-package}`的訊息時,請試著依序執行以下命令 ```bash $ sudo dpkg --purge packages-microsoft-prod && sudo dpkg -i packages-microsoft-prod.deb $ sudo apt-get update $ sudo apt-get install {dotnet-package} # {dotnet-package} = dotnet-sdk-3.1 或 aspnetcore-runtime-3.1 或 dotnet-runtime-3.1 ``` 若以上的方式還是沒有辦法解決問題,就只能再執行以下命令進行手動安裝了 ```bash $ sudo apt-get install -y gpg $ wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o microsoft.asc.gpg $ sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/ $ wget https://packages.microsoft.com/config/ubuntu/{os-version}/prod.list # {os-version} = 16.04 或 其它你使用的版號 $ sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list $ sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg $ sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list $ sudo apt-get update $ sudo apt-get install -y apt-transport-https $ sudo apt-get update $ sudo apt-get install -y {dotnet-package} # {dotnet-package} = dotnet-sdk-3.1 或 aspnetcore-runtime-3.1 或 dotnet-runtime-3.1 ``` ##疑難排解\#2 當安裝 Nginx 出現類似`GPG error: https://nginx.org/packages/ubuntu xenial Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY $key`的錯誤訊息時,請試著依序執行以下命令 ```bash $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key # $key 的值在 GPG 錯誤訊息裡 $ sudo apt-get update $ sudo apt-get install nginx ```
##參考資料 [\[Microsoft\] Install .NET Core SDK or .NET Core Runtime on Ubuntu](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#1604-) [\[Microsoft\] Host ASP.NET Core on Linux with Nginx](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1) [\[NGINX\] Install NGINX](https://www.nginx.com/resources/wiki/start/topics/tutorials/install/#official-debian-ubuntu-packages)
##2. 安裝 Nginx 使用編輯器開啟`/etc/apt/sources.list`並加入下面兩行 Nginx 套件來源節點(stanza) ``` bash deb https://nginx.org/packages/ubuntu/ xenial nginx # Ubuntu 版本為 18.04 的話 xenial -> bionic deb-src https://nginx.org/packages/ubuntu/ xenial nginx # Ubuntu 版本為 18.04 的話 xenial -> bionic ``` 開始安裝 Nginx ``` bash $ sudo apt-get update $ sudo apt-get install nginx ``` 到這邊就已完成 Nginx 的安裝,但若出現 ***GPG error: https://nginx.org/packages/ubuntu xenial Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY $key*** 的錯誤訊息,請參考 [疑難排解\#2](#spanstylecolorfa2a3f2span)
##3. 設定 Nginx 先試著啟動 Nginx ``` bash $ sudo service nginx start ``` 然後在瀏覽器上輸入 Server IP 應該就能看到預設畫面
![Image](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC6bo9fQixtwA9CdbNUl8s-3TLQNpvK1K2kVbXqjmFhnxbLnrqsAwKWSOI8bLhSKIDaahlDkPoAU5KD_Qu9FSbBfObouQzDEXp_hHQHcnCcOTiMx1542saNM_fVsE7qEMtR9Zx5edBAtUP/s0/01.jpg) 根據[微軟官方文件](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx)上的描述,安裝完成後在 /etc/nginx/sites-available 的路徑下應該會有一個名為`default.conf`的設定檔,但我的`default.conf`是在 conf.d 資料夾內,然後也沒有 sites-available 資料夾,猜想 Nginx 官方可能有做了些修改,因此我是在 /etc/nginx 資料夾下先自行建立 sites-available 資料夾然後在裡面新增一個`your_app_name.conf`檔(名稱可修改成你想取的)內容如下 ```bash server { listen 80; listen [::]:80; # SSL 憑證設定方式(有需要的話把以下四行的註解拿掉並指向自己放憑證的目錄) # listen 443 ssl http2; # listen [::]:443 ssl http2; # ssl_certificate /etc/nginx/ssl/your_domain_com/ssl-bundle.crt; # ssl_certificate_key /etc/nginx/ssl/your_domain_com/private.key; server_name your_domain.com *.your_domain.com; # 你的網域名稱(多個以空白分隔) location / { proxy_pass http://localhost:5000; # 根據你使用的 Port 調整 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; } # 如果有 SignalR 連線需求的話可參考以下設定(非必要) location /hub { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # $connection_upgrade 該變數來自 nginx.conf proxy_cache off; proxy_buffering off; proxy_read_timeout 100s; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` 補充: location 的匹配規則可以[參考這](https://linmasaki09.blogspot.com/2021/03/nginx-location.html) 然後參考官方文件修改`conf.d/default.conf`的設定檔,改成 ```bash server { listen 80 default_server; # 加上 default_server listen [::]:80 default_server deferred; # IPv6 用,加上 deferred 為客戶端建立完 TCP 連接後,內核會等到有請求數據時才喚醒 worker 以減輕其負擔;適用於高併發場景 server_name _; # localhost 改成_ return 444; # 加上這行可讓外部直連 IP 的方式失效 #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } } ``` 最後再參考以下範例編輯`/etc/nginx/nginx.conf`檔並在最下面加入`include /etc/nginx/sites-available/*.conf;`這行設定,如下 ```bash user nginx; worker_processes auto; # 請設定與 CPU 核心數相等的數字,進程切換代價最小,auto 為系統自動偵測 CPU 核心數,預設為 1 error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; multi_accept on; # 儘可能接受最多的連接數 use epoll; } http { server_tokens off; # 隱藏 Nginx 版本資訊 include /etc/nginx/mime.types; default_type application/octet-stream; client_max_body_size 10M; # 上傳檔案大小限制(10MB) ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 30s; client_header_timeout 30s; client_body_timeout 30s; reset_timedout_connection on; send_timeout 30s; # Gzip 設置 gzip on; gzip_proxied any; gzip_buffers 16 8k; gzip_disable "MSIE [1-6]\.(?!.*SV1)"; gzip_http_version 1.1; gzip_vary on; # 增加回應標頭 "Vary: Accept-Encoding" gzip_comp_level 6; # 壓縮比1~9, 數字越高壓縮程度越大但越耗 CPU 效能 gzip_min_length 1024; # 超過 1024k 才壓縮, 預設是 0 全壓縮 gzip_types text/plain text/css application/javascript application/x-javascript text/javascript application/json application/xml application/xml+rss text/xml; # 根據 $http_connection 來決定變數 $connection_upgrade 的值(SignalR 會用到) map $http_connection $connection_upgrade { "~*Upgrade" $http_connection; default keep-alive; } proxy_buffering off; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; proxy_http_version 1.1; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-available/*.conf; # 加入這行 } ``` 以上設定都完成後,執行以下命令 ```bash # 驗證設定檔 $ sudo nginx -t # 讓 Nginx 重新讀取新的設定檔 $ sudo nginx -s reload ```
##4. 發佈及佈署 ASP.NET Core 應用程式 在反向代理(Reverse Proxy)模式下,使用 HttpContext.Connection.RemoteIpAddress 會抓到不正確的客戶端 IP,因此要在網站專案下的`Startup.cs`檔裡加入 app.UseForwardedHeaders(),其置放的位置根據[微軟官方文件](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1#fhmo)的建議,要在診斷和處理錯誤的中介服務(Middleware)後,其它服務前,如下 ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); } else { app.UseExceptionHandler("/Home/Error"); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); /*** 省略其它代碼 ***/ } ``` 由於我們已經要使用 Nginx 了,SSL 相關的憑證到時都會直接綁在 Nginx 上,所以要在我們的 ASP.NET Core 應用程式上移掉有關 https 的設定,可參考此篇文章[設定 ASP.NET Core 3.x 的起始連結(URL)位址](https://linmasaki09.blogspot.com/2020/04/aspnet-core-3x-url.html)的說明,修改 Properties 目錄下`launchSettings.json` 檔案裡的 applicationUrl 屬性值,將 https://localhost:xxxx 移除(如果有),或是直接在相對應的`appsettings.json`裡只設定 Http 區段,如下 ``` Json { "Logging": { "LogLevel": { "Default": "Warning", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "Kestrel": { "EndPoints": { "Http": { "Url": "http://localhost:5000" /* 只保留 HTTP 這行 */ } } }, "ConnectionStrings": { "MyDbContext": "" }, /*** 省略其它代碼 ***/ } ``` 然後使用以下的指令發佈應用程式(二擇一) ```bash # 1. 不指定特地平台 $ dotnet publish -c Release -o bin\Release\netcoreapp3.1\WebPublisher # 2. 有指定特地平台 $ dotnet publish -c Release -r linux-x64 --self-contained false -o bin\Release\netcoreapp3.1\WebPublisher ``` 將產生的檔案(WebPublisher 資料夾下的所有檔案)複製到伺服器上的`/var/www/your_app_name`下,並執行 ```bash $ dotnet Your_App_Assembly_Name.dll ``` 補充: 若有指定環境變數的需求,可先輸入以下指令來改變目前應用程式的執行環境變數 ```bash $ export ASPNETCORE_ENVIRONMENT=YourEnvironment ``` 或是直接在執行時指定環境變數 ```bash $ dotnet Your_App_Assembly_Name.dll --environment=YourEnvironment # e.g. --environment=Uat ```
##5. 將 ASP.NET Core 應用程式註冊為系統服務 為了讓 ASP.NET Core 應用程式在系統重開機後都能自動啟動,還需要再進行一些設定,首先檢查系統是否已有 www-data 群組 ```bash # 1. 確認 www-data 群組是否存在 $ grep 'www-data' /etc/group # 2. 若不存在才執行此行命令 $ sudo groupadd www-data # 3. 將網站資料夾的群組擁有者設給 www-data $ sudo chgrp -R www-data /var/www/your_app_name # 4. 讓群組有讀寫權限 $ sudo chmod 775 -R /var/www/your_app_name ``` 再來切換到 /etc/systemd/system 目錄下,建立一個系統服務定義檔 ```bash $ sudo nano /etc/systemd/system/your_app_name.service ``` 編輯該檔,內容如下 ```bash [Unit] Description=Your App Name # 你的應用程式名稱 [Service] WorkingDirectory=/var/www/your_app_name # 你的應用程式路徑 ExecStart=/usr/bin/dotnet /var/www/your_app_name/Your_App_Assembly_Name.dll # 改成你的 dll 檔 Restart=always RestartSec=10 # 假如應用程式遇到錯誤而停止,10 秒後將會自動重啟 KillSignal=SIGINT SyslogIdentifier=your_app_name # your_app_name 改成你的應用程式名稱(Log用) User=www-data Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install] WantedBy=multi-user.target ``` 最後依序執行下列步驟 1 ~ 3 的指令 ```bash # 1. 開啟系統開機自動啟動服務 $ sudo systemctl enable your_app_name.service # 2. 啟動服務 $ sudo systemctl start your_app_name.service # 3. 查看狀態 $ sudo systemctl status your_app_name.service # 補充 $ sudo systemctl disable your_app_name.service # 關閉系統開機自動啟動服務 $ sudo systemctl stop your_app_name.service # 停止服務 $ sudo systemctl restart your_app_name.service # 重啟服務 ``` 看到出現類似下面畫面即大功告成
![Image](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7u9E0Zgy1HBCTUh4VqwvahHOUDmZBb2UaroynZClYLPKQDXMnziuk85PZ_a0xwzbNu4iQH1M3FmWbtnEShjH1Eux8yOLHp4D4P-jIzSwObrCMCoym0I6mHQNSZcuat7BlURioO8r05EBp/s0/02.jpg) 補充: 使用以下指令可查看系統服務的日誌 ```bash $ sudo journalctl -fu your_app_name.service $ sudo journalctl -fu your_app_name.service --since "2020-08-30" --until "2020-08-31 18:00" # 指定區間 ```
##疑難排解\#1 當遇到了`Unable to locate package {netcore-package}`的訊息時,請試著依序執行以下命令 ```bash $ sudo dpkg --purge packages-microsoft-prod && sudo dpkg -i packages-microsoft-prod.deb $ sudo apt-get update $ sudo apt-get install {dotnet-package} # {dotnet-package} = dotnet-sdk-3.1 或 aspnetcore-runtime-3.1 或 dotnet-runtime-3.1 ``` 若以上的方式還是沒有辦法解決問題,就只能再執行以下命令進行手動安裝了 ```bash $ sudo apt-get install -y gpg $ wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o microsoft.asc.gpg $ sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/ $ wget https://packages.microsoft.com/config/ubuntu/{os-version}/prod.list # {os-version} = 16.04 或 其它你使用的版號 $ sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list $ sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg $ sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list $ sudo apt-get update $ sudo apt-get install -y apt-transport-https $ sudo apt-get update $ sudo apt-get install -y {dotnet-package} # {dotnet-package} = dotnet-sdk-3.1 或 aspnetcore-runtime-3.1 或 dotnet-runtime-3.1 ``` ##疑難排解\#2 當安裝 Nginx 出現類似`GPG error: https://nginx.org/packages/ubuntu xenial Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY $key`的錯誤訊息時,請試著依序執行以下命令 ```bash $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key # $key 的值在 GPG 錯誤訊息裡 $ sudo apt-get update $ sudo apt-get install nginx ```
##參考資料 [\[Microsoft\] Install .NET Core SDK or .NET Core Runtime on Ubuntu](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#1604-) [\[Microsoft\] Host ASP.NET Core on Linux with Nginx](https://docs.microsoft.com/en-gb/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1) [\[NGINX\] Install NGINX](https://www.nginx.com/resources/wiki/start/topics/tutorials/install/#official-debian-ubuntu-packages)