﻿# ASP.NET Core 如何部署到 CentOS 7 作業系統

最近有個專案要上線，客戶提供一台 CentOS 7 的主機給我們部署，但是官網的文件主要都是以 Ubuntu 作業系統為主，並沒有完整的文件說明如何在 CentOS 安裝，我在架設 Labs 環境安裝的時候，也發現部署過程真的有點地雷，所以我打算用這篇文章說明一下實際的安裝步驟。

### 準備部署檔案

假設我們要從 Windows 先發行檔案到 `g:\deploy` 資料夾：

1.  發行 ASP․NET Core 應用程式
    
    ```powershell
    dotnet publish -c Release -o g:\deploy\web
    ```
    
2.  準備 MySQL 資料庫的 DDL / DML 腳本
    
    本案使用 Code First 開發，因此資料庫的結構描述(Schema)可以透過 `dotnet ef dbcontext script` 產生指令碼：
    
    ```powershell
    dotnet ef dbcontext script -p ..\OOO.Data\ -o g:\deploy\db\InitializeDB.sql
    ```
    

### 準備部署環境

1.  安裝一台 CentOS 7 作業系統 (Minimal Install)
    
    [展示 CentOS Linux release 7.9.2009 的 Minimal Install 完整安裝過程 - YouTube](https://youtu.be/nvq1Q-1jv2s)
    
2.  安裝 .NET Runtime on CentOS
    
    匯入 Microsoft package signing key 套件
    
    ```bash
    sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
    ```
    
    本案採用 ASP.NET Core 3.1 版本，所以只要安裝 ASP.NET Core 3.1 Runtime 即可：
    
    ```bash
    sudo yum install -y aspnetcore-runtime-3.1
    ```
    
    > [Install the .NET SDK or the .NET Runtime on CentOS](https://learn.microsoft.com/en-us/dotnet/core/install/linux-centos?WT.mc_id=DT-MVP-4015686)
    
3.  安裝 MariaDB 資料庫伺服器
    
    本案採用 MySQL/MariaDB 資料庫，在 CentOS 安裝 MariaDB 非常方便。
    
    ```bash
    sudo yum install mariadb-server -y
    
    sudo systemctl enable mariadb
    
    sudo systemctl start mariadb
    
    sudo systemctl status mariadb
    ```
    
    > 預設安裝的 `root` 並沒有設定密碼，部署完成後記得用 `sudo mysql_secure_installation` 加強 MySQL/MariaDB 資料庫安全性。
    
4.  安裝 Nginx 網站伺服器軟體 (反向代理伺服器)
    
    ```bash
    sudo yum install nginx -y
    ```
    

### 部署應用程式與資料庫

1.  新增 MariaDB 資料庫
    
    如果要讓 MariaDB 資料庫與 ASP.NET Core 可以無縫接軌，避免有中文亂碼的情況，你一定要特別注意建立資料庫的時候要選擇正確的 **字集**(CHARACTER SET) 與 **定序**(COLLATE)！
    
    ```sql
    CREATE DATABASE mydb
      CHARACTER SET = 'utf8mb4'
      COLLATE = 'utf8mb4_general_ci';
    ```
    
    > 其中 `mydb` 為資料庫名稱。
    
2.  新增 MariaDB 使用者
    
    任何網站都不應該使用 `root` 登入帳號連接資料庫，因此建議要為該網站的資料庫設定一組專用的登入帳號密碼，並授予特定資料庫權限。
    
    ```sql
    CREATE USER 'mydbuser'@'localhost' identified by '2UfpbP3FSSZy';
    GRANT ALL ON mydb.* TO 'mydbuser'@'localhost';
    ```
    
    > 其中 `mydbuser` 為資料庫登入使用者名稱，而 `2UfpbP3FSSZy` 則為資料庫登入密碼。
    
3.  部署 MariaDB 資料庫結構描述
    
    將 `InitializeDB.sql` 複製到 CentOS 中，並透過以下命令匯入資料庫：
    
    ```bash
    mysql -u root mydb < InitializeDB.sql
    ```
    
4.  部署 ASP․NET Core 應用程式
    
    將 `g:\deploy\web` 資料夾完整複製到 CentOS 中，並複製到 `/var/www/myweb` 目錄錄下。
    
    ```bash
    sudo mkdir -p /var/www/myweb
    sudo cp -r ~/web/* /var/www/myweb/
    ```
    
    > 其中 `myweb` 為你的站台名稱。
    
5.  建立一個 `www-data` 使用者
    
    ```bash
    sudo adduser -M www-data
    ```
    
6.  調整**檔案上傳**功能需要寫入資料夾的擁有者
    
    ```bash
     chown www-data /var/www/securityscan/wwwroot/UploadFiles
     chown www-data /var/www/securityscan/wwwroot/UploadFiles/*
    ```
    
7.  建立 [systemd](https://wiki.archlinux.org/title/Systemd_\(%E6%AD%A3%E9%AB%94%E4%B8%AD%E6%96%87\)) 服務定義檔
    
    ```bash
    sudo vi /etc/systemd/system/kestrel-myweb.service
    ```
    
    這裡最重要的設定則是 `Environment=` 環境變數的指派：
    
    ```
    [Unit]
    Description=myweb App running on CentOS
    
    [Service]
    WorkingDirectory=/var/www/myweb
    ExecStart=/usr/local/bin/dotnet /var/www/myweb/myweb.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=kestrel-myweb
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Production
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    Environment=ConnectionStrings__ServerConnection=server=127.0.0.1;port=3306;database=mydb;Uid=mydbuser;Pwd=2UfpbP3FSSZy;charset=utf8;
    
    [Install]
    WantedBy=multi-user.target
    ```
    
8.  註冊並啟動 `kestrel-myweb` 服務
    
    ```bash
    sudo systemctl enable kestrel-myweb.service
    
    sudo systemctl daemon-reload
    sudo systemctl restart kestrel-myweb.service
    
    sudo systemctl status kestrel-myweb.service
    ```
    
    查詢 `kestrel-myweb` 的執行記錄 (Logs)
    
    ```bash
    sudo journalctl -fu kestrel-myweb.service
    ```
    
9.  設定 Nginx 網站伺服器軟體 (反向代理伺服器)
    
    編輯 `/etc/nginx/nginx.conf` 檔案：
    
    ```bash
    sudo vi /etc/nginx/nginx.conf
    ```
    
    找到 `server_name _;` 片段，在這行的下一行加入以下內容：
    
    ```
    location / {
        proxy_pass         http://127.0.0.1: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;
    
        # 設定檔案上傳的 HTTP 封包最大值為 100MB
        client_max_body_size 100M;
    
        proxy_busy_buffers_size   512k;
        proxy_buffers           4 512k;
        proxy_buffer_size         256k;
    
    }
    ```
    
    > 注意: ASP․NET Core 的 Kestrel 預設可接受用戶端 HTTP 要求封包大小上限為 30MB，請參考 [How to increase file upload size in ASP.NET Core](https://www.talkingdotnet.com/how-to-increase-file-upload-size-asp-net-core/) 進行設定。
    
    設定讓 Nginx 可以 Reverse Proxy 轉發 HTTP 要求到 ASP.NET Core 網站：
    
    ```bash
    sudo setsebool -P httpd_can_network_connect 1
    ```
    
    > 上面這一個步驟是 Ubuntu 不太需要的，CentOS 沒做這段設定的話 Nginx 完全連不上 ASP.NET Core 網站！(🔥)
    
    重新啟動 Nginx 服務
    
    ```bash
    sudo systemctl restart nginx.service
    
    sudo systemctl status nginx.service
    ```
    
10.  開啟防火牆設定
     
     ```bash
     sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
     sudo firewall-cmd --reload
     ```
     
11.  測試網站連接
     
     IP: `http://ip.add.ress/`
     
12.  加強 MySQL/MariaDB 資料庫安全性
     
     ```bash
     sudo mysql_secure_installation
     ```
     

## 相關連結

-   [Host ASP.NET Core on Linux with Nginx](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-6.0&WT.mc_id=DT-MVP-4015686#publish-and-copy-over-the-app)
    -   [Module ngx\_http\_core\_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
    -   [Module ngx\_http\_proxy\_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size)
-   upstream sent too big header while reading response header from upstream
    -   [(13: Permission denied) while connecting to upstream:\[nginx\] - Stack Overflow](https://stackoverflow.com/a/24830777/910074)
    -   [How to increase file upload size in ASP.NET Core](https://www.talkingdotnet.com/how-to-increase-file-upload-size-asp-net-core/)
    -   [Nginx: 413 - Request Entity Too Large Error and Solution - nixCraft](https://www.cyberciti.biz/faq/linux-unix-bsd-nginx-413-request-entity-too-large/)
    -   [\[Nginx\] Upstream too big header 問題](https://www.azureunali.com/nginx-upstream-too-big-header-%E5%95%8F%E9%A1%8C/)
    -   [Nginx upstream sent too big header while reading response header from upstream](https://www.cyberciti.biz/faq/nginx-upstream-sent-too-big-header-while-reading-response-header-from-upstream/)
    -   [修復 Nginx 502 錯誤：upstream sent too big header while reading response header from upstream](https://web.archive.org/web/20201205223137/https://www.itread01.com/content/1550343811.html)
-   Firewall on CentOS 7
    -   [Linux / CentOS 7 : 防火牆設定 @ 彥霖 實驗筆記 :: 痞客邦 ::](https://lolikitty.pixnet.net/blog/post/201679759)
    -   [CentOS Linux 7 安裝 MySQL/MariaDB 資料庫教學](https://blog.gtwang.org/linux/centos-7-install-mariadb-mysql-server-tutorial/)
-   MariaDB / MySQL
    -   [MySQL Connector/Net出現亂碼的解決之道 | Kyle Chang's Dot Blog - 點部落](https://dotblogs.com.tw/kylessukaichang/2015/05/16/151313)
