Linux(寶塔)部署.Net Core完整記錄
阿新 • • 發佈:2020-12-07
## 前言
最近在V站上看到一個外賣推廣的小程式,意思大概是類似淘寶聯盟那種,別人走自己的連結後,自己可以抽取大概4%-6%的提成。覺得還蠻有意思的,一開始開源的是靜態頁面寫死的,所以我這邊用.Net Core寫了個簡單的後臺。
左邊是無後臺的,右邊紅色框是後臺配置的。當然功能是很簡單的,主要是記錄釋出到Ubuntu18.4的時候遇到的問題與解決辦法。
## 安裝寶塔
> 寶塔Linux面板是提升運維效率的伺服器管理軟體,支援一鍵LAMP/LNMP/叢集/監控/網站/FTP/資料庫/JAVA等100多項伺服器管理功能。
這裡節省時間直接使用寶塔面板了,這個真的是太方便了,哈哈。安裝也非常簡單。
因為我使用的是Ubuntu,安裝指令碼
`wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh`
其他版本請參考官方文件:https://www.bt.cn/download/linux.html
安裝完成後會顯示登入地址、使用者名稱、密碼資訊。登入後瀏覽器將彈出推薦安裝套件,為方便直接一鍵安裝LNMP。
## 安裝.NetCore SDK 3.1
> 微軟官方文件:https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-ubuntu
因為我使用的18.04,所以找到對應文件。
使用 APT 進行安裝可通過幾個命令來完成。 安裝 .NET 之前,請執行以下命令,將 Microsoft 包簽名金鑰新增到受信任金鑰列表,並新增包儲存庫。
開啟終端並執行以下命令:
```bash
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
```
### 安裝 SDK
.NET SDK 使你可以通過 .NET 開發應用。 如果安裝 .NET SDK,則無需安裝相應的執行時。 若要安裝 .NET SDK,請執行以下命令:
```bash
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-3.1
```
### 安裝執行時
```bash
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-3.1
```
作為 ASP.NET Core 執行時的一種替代方法,你可以安裝不包含 ASP.NET Core 支援的 .NET 執行時:將上一命令中的 `aspnetcore-runtime-5.0` 替換為 `dotnet-runtime-5.0`:
```bash
sudo apt-get install -y dotnet-runtime-5.0
```
其實上述就是照搬微軟的官方文件,官方文件還是寫的很清楚的。
## 釋出.NetCore專案
我一開始目標執行時選擇的Linux-64,但是出現了這樣的錯誤`錯誤 NU1605: 檢測到包降級: XXXXXXXXXXXXX 從 4.3.0 降級到 XXXXXXXXXXXXX。直接從專案引用包以選擇不同版本。
![image-20201207114647335](https://cdn.jsdelivr.net/gh/jellydong/img/images/image-20201207114647335.png)
通過檢視微軟官方文件:https://docs.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1605
***問題***當在 .NET Core 3.0 或更高版本的專案中同時引用時,與 .NET Core 1.0 和1.1 隨附的某些包組合不相容。 問題包通常以或開頭 `System.` `Microsoft.` ,並具有4.0.0 和4.3.1 之間的版本號。 在這種情況下,降級訊息將具有從執行時開始的包。 依賴關係鏈。
***解決方案***若要解決此問題,請新增以下 PackageReference:
```xml
```
就是新增引用,但實際上你要保證所有專案的包引用版本是一致的。
***另一種方法***
> 釋出的時候目標執行時直接選擇可移植吧~
## 寶塔面板釋出.Net Core專案,並啟動專案
在檔案 wwwroot新建專案資料夾,將本地釋出檔案打包拷貝至伺服器解壓。
在伺服器上終端命令進入部署檔案所在目錄,然後使用dotnet命令啟動服務:
``` bash
dotnet XXXXXX.Admin.dll --urls "http://localhost:5000"
```
![image-20201207115503272](https://cdn.jsdelivr.net/gh/jellydong/img/images/image-20201207115503272.png)
## Nginx設定反代訪問
現在我們還不能直接訪問到我們新部署專案,需要使用Nginx設定反向代理,將特定的埠代理到`http://localhost:5000`,這一步可以通過寶塔面板來完成,步驟如下:
> * 在寶塔面板上新建一個網站,設定為靜態網站即可,並繫結好域名。
>
> * 在剛才新建的網站中設定反向代理,目標URL填寫http://localhost:5000即可,傳送域名localhost
![image-20201207115748843](https://cdn.jsdelivr.net/gh/jellydong/img/images/image-20201207115748843.png)
![image-20201207115850382](https://cdn.jsdelivr.net/gh/jellydong/img/images/image-20201207115850382.png)
瀏覽器正式可訪問專案,此處可能需要重啟一下。
## 使用 Supervisor 守護程序
> 現在還有個問題,就是當我們關閉xShell等SSH工具的時候服務程序也會停止執行,我們可使用 Supervisor 守護程序執行。
>
> * 在寶塔面板上安裝Supervisor
> * 新增守護程序(使用者建議選擇www,不要使用root)
![image-20201207120537225](https://cdn.jsdelivr.net/gh/jellydong/img/images/image-20201207120537225.png)
## 問題
### 問題1
> `錯誤 NU1605: 檢測到包降級: XXXXXXXXXXXXX 從 4.3.0 降級到 XXXXXXXXXXXXX。直接從專案引用包以選擇不同版本`
>
> 這個問題一開始我按照官方文件修改了,實際還是不可以。所以我選擇了可移植髮布的。而我在寫這篇文章的時候又可以了。
## 問題2
> 驗證碼我使用了`System.Drawing`,不過在Linux下的話,這個是無法顯示的。
>
> ***解決辦法***
>
> `System.Drawing.Common` 元件提供對GDI+圖形功能的訪問。它是依賴於GDI+的,那麼在Linux上它如何使用GDI+,因為Linux上是沒有GDI+的。Mono 團隊使用C語言實現了GDI+介面,提供對非Windows系統的GDI+介面訪問能力(個人認為是模擬GDI+,與系統圖像介面對接),這個就是 `libgdiplus`。進而可以推測 `System.Drawing.Common` 這個元件實現時,對於非Windows系統肯定依賴了 `ligdiplus` 這個元件。如果我們當前系統不存在這個元件,那麼自然會報錯,找不到它,安裝它即可解決。
>
>***Ubuntu一鍵命令***
>
> ```bash
> sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/ubuntu.sh|sh
> ```
>
> 參考:https://www.cnblogs.com/stulzq/p/10172550.html
## 問題3
> 指定埠啟動
>
> 修改`Program.cs`
>
> 增加程式碼
>
> ```csharp
> .ConfigureAppConfiguration(builder =>
> {
> //dotnet test.dll --urls "http://*:5000;https://*:5001"
> builder.AddCommandLine(args);//設定新增命令列
> })
> ```
>
> 完整程式碼
>
> ```csharp
> public static IHostBuilder CreateHostBuilder(string[] args) =>
> Host.CreateDefaultBuilder(args)
> //將預設ServiceProviderFactory指定為AutofacServiceProviderFactory https://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting
> .UseServiceProviderFactory(new AutofacServiceProviderFactory())
> .ConfigureAppConfiguration(builder =>
> {
> //dotnet test.dll --urls "http://*:5200;https://*:5100"
> builder.AddCommandLine(args);//設定新增命令列
> })
> .ConfigureWebHostDefaults(webBuilder =>
> {
> webBuilder.UseStartup();
> });
> ```
## 問題4
>驗證碼生成程式碼
>
>驗證碼生成程式碼應該是蠻多的,我把我的分享下
>
>```csharp
>using System;
>using System.Drawing;
>using System.Drawing.Drawing2D;
>using System.Drawing.Imaging;
>using System.IO;
>
>namespace XXX.Util
>{
> public static class ValidateCodeHelper
> {
> ///
> /// 驗證碼的最大長度
> ///
> public static int MaxLength => 10;
>
> ///
> /// 驗證碼的最小長度
> ///
> public static int MinLength => 1;
>
> ///
> /// 生成驗證碼
> ///
> /// 指定驗證碼的長度
> ///
> public static string CreateValidateCode(int length)
> {
> int[] randMembers = new int[length];
> int[] validateNums = new int[length];
> string validateNumberStr = "";
> //生成起始序列值
> int seekSeek = unchecked((int)DateTime.Now.Ticks);
> Random seekRand = new Random(seekSeek);
> int beginSeek = (int)seekRand.Next(0, Int32.MaxValue - length * 10000);
> int[] seeks = new int[length];
> for (int i = 0; i < length; i++)
> {
> beginSeek += 10000;
> seeks[i] = beginSeek;
> }
> //生成隨機數字
> for (int i = 0; i < length; i++)
> {
> Random rand = new Random(seeks[i]);
> int pownum = 1 * (int)Math.Pow(10, length);
> randMembers[i] = rand.Next(pownum, Int32.MaxValue);
> }
> //抽取隨機數字
> for (int i = 0; i < length; i++)
> {
> string numStr = randMembers[i].ToString();
> int numLength = numStr.Length;
> Random rand = new Random();
> int numPosition = rand.Next(0, numLength - 1);
> validateNums[i] = Int32.Parse(numStr.Substring(numPosition, 1));
> }
> //生成驗證碼
> for (int i = 0; i < length; i++)
> {
> validateNumberStr += validateNums[i].ToString();
> }
> return validateNumberStr;
> }
> ///
> /// 得到驗證碼圖片的長度
> ///
> /// 驗證碼的長度
> ///
> public static int GetImageWidth(int validateNumLength)
> {
> return (int)(validateNumLength * 12.0);
> }
> ///
> /// 得到驗證碼的高度
> ///
> ///
> public static double GetImageHeight()
> {
> return 22.5;
> }
>
>
> //C# MVC 升級版
> ///
> /// 建立驗證碼的圖片
> ///
> /// 驗證碼
> public static byte[] CreateValidateGraphic(string validateCode)
> {
> Bitmap image = new Bitmap((int)Math.Ceiling(validateCode.Length * 12.0), 22);
> Graphics g = Graphics.FromImage(image);
> try
> {
> //生成隨機生成器
> Random random = new Random();
> //清空圖片背景色
> g.Clear(Color.White);
> //畫圖片的干擾線
> for (int i = 0; i < 25; i++)
> {
> int x1 = random.Next(image.Width);
> int x2 = random.Next(image.Width);
> int y1 = random.Next(image.Height);
> int y2 = random.Next(image.Height);
> g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
> }
> Font font = new Font("Arial", 12, (FontStyle.Bold | FontStyle.Italic));
> LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height),
> Color.Blue, Color.DarkRed, 1.2f, true);
> g.DrawString(validateCode, font, brush, 3, 2);
> //畫圖片的前景干擾點
> for (int i = 0; i < 100; i++)
> {
> int x = random.Next(image.Width);
> int y = random.Next(image.Height);
> image.SetPixel(x, y, Color.FromArgb(random.Next()));
> }
> //畫圖片的邊框線
> g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
> //儲存圖片資料
> MemoryStream stream = new MemoryStream();
> image.Save(stream, ImageFormat.Jpeg);
> //輸出圖片流
> return stream.ToArray();
> }
> finally
> {
> g.Dispose();
> image.Dispose();
> }
> }
> }
>}
>
>```
>
>
## 總結
其實就是一篇流水賬,記錄了釋出的過程和遇到的問題及解決辦法。之前伺服器一直是使用的WinServer,因為熟悉。勇於嘗試並去解決問題,慢慢進步~
大學裡也學過Linux,受不了。但是真的去使用了,去探索了,嗯