1. 程式人生 > 其它 >.NET Worker Service 部署到 Linux 作為 Systemd Service 執行

.NET Worker Service 部署到 Linux 作為 Systemd Service 執行

上一篇文章我們瞭解瞭如何將.NET Worker Service 作為 Windows 服務執行,今天我接著介紹一下如何將 Worker Service 部署到 Linux 上,並作為 Systemd Service 執行。

我在本文中要覆蓋的內容包含:

  • 作為 Linux 控制檯程式執行
  • 作為 Systemd Service 執行
  • 開機自動啟動、檢視日誌資訊

建立專案併發布

下載 Worker Service 原始碼

我將基於上一篇文章中的 Worker Service 原始碼來修改,如果您安裝有 git,可以用下面的命令獲取它:

git clone [email protected]:ITTranslate/WorkerServiceAsWindowsService.git

然後,使用 Visual Studio Code 開啟此專案,構建一下,以確保一切正常:

dotnet build

刪除用不到的依賴項

刪除上一篇文章中用於 Windows Services 的依賴程式包:

dotnet remove package Microsoft.Extensions.Hosting.WindowsServices

然後,刪除 Program.cs 中的 .UseWindowsService() 方法呼叫。

修改配置檔案

開啟配置檔案 appsettings.json,將日誌檔案儲存路徑中的 \ 改為 /,其他不用做任何更改。

{
  "Name": "RollingFile",
  "Args": {
    "pathFormat": "Logs/{Hour}.log",
    "outputTemplate": "{Timestamp:o} [{Level:u3}] ({MachineName}/{ProcessId}/{ProcessName}/{ThreadId}) {Message}{NewLine}{Exception}"
  }
},
{
  "Name": "SQLite",
  "Args": {
    "sqliteDbPath": "Logs/log.db",
    "tableName": "Logs",
    "maxDatabaseSize": 1,
    "rollOver": true
  }
}

這是因為 Windows 中使用反斜槓 \ 來表示目錄,而 Linux 中使用正斜槓 / 來表示目錄。

假如不修改儲存路徑,您將會看到日誌被儲存成如下的尷尬檔名:

'Logs\2021061715.log'
'Logs\log.db'

釋出程式

執行 dotnet publish 命令將應用程式及其依賴項釋出到資料夾。

dotnet publish -c Release -r linux-x64 -o c:\test\workerpub\linux

這裡我使用 -r linux-x64 引數,指定釋出獨立部署於 Linux 系統的應用程式。

命令執行完成後,您會在 C:\test\workerpub\linux

資料夾中看到適用於 Linux 系統的可執行程式及其所有依賴項。

作為 Linux 控制檯程式執行

將資料夾 C:\test\workerpub\linux 下的檔案壓縮為 linux.zip

開啟 Xshell 工具,連線到一臺 Linux 測試機(我的測試機作業系統為 CentOS 7.3),在測試機上新建 /srv/Worker 目錄:

mkdir /srv/Worker

使用 rz 命令將 linux.zip 複製到測試機,

然後在測試機上解壓 linux.zip/srv/Worker 目錄:

unzip linux.zip -d /srv/Worker

為我們的應用程式分配可執行許可權,並執行:

# 分配可執行許可權
chmod +x /srv/Worker/MyService
# 執行
/srv/Worker/MyService

按下 Ctrl+C 關閉應用,等待關閉前必須完成的任務正常結束後,應用退出。輸入 ls /srv/Worker 命令回車,您會看到在該目錄下多了一個 Logs 目錄,日誌檔案輸出正常。

作為 Systemd Service 執行

新增 Systemd Service 依賴

為了讓我們的 Worker 監聽來自 Systemd 的啟動和停止訊號,我們需要新增 Microsoft.Extensions.Hosting.Systemd NuGet 包:

dotnet add package Microsoft.Extensions.Hosting.Systemd

然後,我們需要修改 Program.cs 中的 CreateHostBuilder 方法,新增 UseSystemd 方法呼叫,將宿主(Host)生命週期設定為 Microsoft.Extensions.Hosting.Systemd.SystemdLifetime,以便應用程式可以接收啟動和停止訊號,並配置控制檯輸出記錄為 systemd 格式。

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSystemd() // Sets the host lifetime to Microsoft.Extensions.Hosting.Systemd.SystemdLifetime...
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        })
        .UseSerilog(); //將 Serilog 設定為日誌提供程式

重新執行以下命令將程式釋出到資料夾:

dotnet publish -c Release -r linux-x64 -o c:\test\workerpub\linux

然後重複前面的步驟,在 Xshell 中使用 rz 命令將應用程式複製到測試機,併為 /srv/Worker/MyService 檔案分配可執行許可權。

配置檔案

接下來我們需要建立配置檔案,將服務的有關資訊告知 systemd,以便它知道如何執行此服務。為此,我們需要建立一個 .service 檔案,我們將在註冊和執行此服務的 Linux 機器上使用該檔案。

在我們的專案中建立一個名為 MyService.service 的服務單元配置檔案,內容如下:

[Unit]
Description=Long running service/daemon created from .NET worker template

[Service]
# The systemd service file must be configured with Type=notify to enable notifications.
Type=notify
# will set the Current Working Directory (CWD). Worker service will have issues without this setting
WorkingDirectory=/srv/Worker
# systemd will run this executable to start the service
ExecStart=/srv/Worker/MyService
# to query logs using journalctl, set a logical name here  
SyslogIdentifier=MyService

# Use your username to keep things simple.
# If you pick a different user, make sure dotnet and all permissions are set correctly to run the app
# To update permissions, use 'chown yourusername -R /srv/Worker' to take ownership of the folder and files,
#       Use 'chmod +x /srv/Worker/MyService' to allow execution of the executable file
User=yourusername

# This environment variable is necessary when dotnet isn't loaded for the specified user.
# To figure out this value, run 'env | grep DOTNET_ROOT' when dotnet has been loaded into your shell.
Environment=DOTNET_ROOT=/usr/share/dotnet/dotnet

# This gives time to MyService to shutdown gracefully.
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target

使用時應將 User=yourusername 項中的 yourusername 改為具體的 linux 系統的登入名。

Systemd 期望所有的配置檔案放置在 /etc/systemd/system/ 目錄下,我們開啟此目錄,並使用 rz 命令將服務配置檔案複製到 /etc/systemd/system/MyService.service

cd /etc/systemd/system/

rz

然後執行以下命令讓 systemd 重新載入配置檔案:

systemctl daemon-reload

管理服務

之後,可以執行以下命令來檢查 systemd 是否識別了我們的服務:

systemctl status MyService

結果顯示如下:

這表明我們註冊的新服務被禁用了,可以通過執行以下命令來啟動它:

systemctl start MyService

重新執行 systemctl status MyService 命令檢視服務狀態,顯示如下:

停止服務可以執行以下命令:

systemctl stop MyService

如果您希望該服務在開機時自動啟動,那麼可以執行以下命令:

systemctl enable MyService

禁用開機自動啟動,可以執行以下命令:

systemctl disable MyService

檢視服務是否開機自動啟動,可以執行以下命令:

systemctl is-enabled MyService

Systemd 服務日誌

命令 journalctl 可以用來檢視 systemd 收集的日誌。systemd-journald 服務負責 systemd 的日誌收集,它從核心、systemd 服務和其他源檢索資訊。日誌的集中收集,有利於對其進行檢索查詢。journal 中的日誌記錄是結構化和有索引的,因此 journalctl 能夠以各種有用的格式來展現日誌資訊。

我們可以使用 journalctl 命令來驗證應用程式是否成功執行,因為該命令可以跟蹤顯示應用程式的輸出資訊:

journalctl -u MyService -f

Ctrl-C 退出命令。

當我們在程式中呼叫 UseSystemd 方法時,會將 Extensions.LogLevel 對映到 Syslog 日誌級別:

LogLevel Syslog level systemd name
Trace/Debug 7 debug
Information 6 info
Warning 4 warning
Error 3 err
Critical 2 crit

所以,我們可以使用 journalctl 命令的優先順序標記(priority-flag)-p 來根據日誌級別過濾應用程式的輸出資訊:

journalctl -p 4 -u MyService -f

總結

在本文中,我通過一個例項詳細介紹瞭如何將 .NET Worker Service 部署到 Linux 系統作為 Systemd Service 執行,並說明了如何使用 systemctl 命令來管理服務,如何使用 journalctl 命令檢視 Systemd 服務日誌。

當我們向 HostBuilder 添加了 .UseSystemd() 方法呼叫後,編譯出的程式,既可以作為 Linux 控制檯應用執行,也可以作為 Systemd Service 執行。

您可以從 GitHub 下載本文中的原始碼

參考:


相關閱讀:

作者 : 技術譯民
出品 : 技術譯站