ASP.NET Core でLinuxでホストする場合、以前の公式ドキュメントではsupervisorを使う方法が紹介されていました。ただ、CentOSやRHELだとEPELリポジトリにあるしなあと思っていたら、いつの間にか公式ドキュメントがsystemdを利用したサンプルに変わっていました。せっかくなので手順を一通りなぞってみます。
Publish to a Linux Production Environment | Microsoft Docs
手抜きポイント
- リバースプロキシは設定しない (適当なポートでローカルからアクセスして動作確認)
- firewalldも設定しない (これは独立して設定できる)
- SELinuxまわりは追加の設定しない (詳細は最後に)
SELinuxは追加の設定をしないというだけで、enforcingの状態です*1。
# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28
実行バイナリとユーザーの用意
systemd のscriptを使うだけならdotnet publish
せずにdotnet run
でプロジェクトを指定して実行することもできます*2が、せっかくなのでpublishして実行バイナリ一式を適当な場所に配置して、専用のユーザーで実行してみることにします。まずはpublishします。
$ dotnet publish -c Release
bin/Release/netcoreapp1.1/publish/
以下に必要なバイナリ一式が生成されるので適当な場所に配置します。/var/www-aspnet
を作って配置してみました。
$ sudo mkdir -p /var/www-aspnet/ $ sudo cp -R bin/Release/netcoreapp1.1/publish/* /var/www-aspnet
dotnetプロセスの実行用に専用のユーザーdotnetuser
をログインシェルなしで作ってみます。
$ sudo useradd -s /sbin/nologin dotnetuser $ sudo chown -R dotnetuser /var/www-aspnet
systemd scriptの作成と実行
それではsystemdのscriptを記述します。/etc/systemd/system
にaspnet.service
という名前で以下のように記述します。実行するコマンドのパスはフルパスで、以下の例はRed Hat Software Collections でインストールした時のパスです。
[Unit] Description = hello aspnet core Documentation = Wants=network.target After=network.target [Service] ExecStart = /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet AspNetLabo.dll WorkingDirectory = /var/www-aspnet/AspNetLabo/publish/ Restart = always RestartSec = 10 User = dotnetuser Group=dotnetuser Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy = multi-user.target
記述したらsystemctl
で起動します。起動したかどうかの確認もできます。scriptファイルを修正した時などはdaemon-reload
しておきます。
$ sudo systemctl daemon-reload $ sudo systemctl start aspnet.service $ systemctl status aspnet.service ● aspnet.service - hello aspnet core Loaded: loaded (/etc/systemd/system/aspnet.service; disabled; vendor preset: disabled) Active: active (running) since Tue 2017-01-10 19:40:46 JST; 7s ago Main PID: 6815 (dotnet) CGroup: /system.slice/aspnet.service └─6815 /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet AspNetLabo.dll Jan 10 19:40:46 localhost.localdomain systemd[1]: Started hello aspnet core. Jan 10 19:40:46 localhost.localdomain systemd[1]: Starting hello aspnet core... Jan 10 19:40:46 localhost.localdomain dotnet[6815]: info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0] Jan 10 19:40:46 localhost.localdomain dotnet[6815]: User profile is available. Using '/home/dotnetuser/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Hosting environment: Production Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Content root path: /var/www-aspnet/ Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Now listening on: http://localhost:5000 Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Application started. Press Ctrl+C to shut down.
起動に失敗したときなどにログを見たい場合はjournalctl
コマンドを使います。-u
オプションでユニットを指定、-f
でtailできます。デフォルトのテンプレートで作ったままなので比較的ログが多くでています。
$ $ journalctl -u aspnet.service -f -- Logs begin at Tue 2017-01-10 18:29:33 JST. -- Jan 10 18:58:19 localhost.localdomain systemd[1]: Stopping hello aspnet core... Jan 10 18:58:19 localhost.localdomain systemd[1]: Stopped hello aspnet core. Jan 10 19:40:46 localhost.localdomain systemd[1]: Started hello aspnet core. Jan 10 19:40:46 localhost.localdomain systemd[1]: Starting hello aspnet core... Jan 10 19:40:46 localhost.localdomain dotnet[6815]: info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0] Jan 10 19:40:46 localhost.localdomain dotnet[6815]: User profile is available. Using '/home/dotnetuser/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Hosting environment: Production Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Content root path: /var/www-aspnet/ Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Now listening on: http://localhost:5000 Jan 10 19:40:47 localhost.localdomain dotnet[6815]: Application started. Press Ctrl+C to shut down.
これで無事systemdで.NET Coreプロセスを起動できました。常時起動のプロセスである必要もないので、バッチとして.NET Coreプロセスを起動することもできるはずです。
systemd scriptの書き方については下記のブログが参考になると思います。
SELinuxとの関係
最初にSELinuxはenforcingだが追加の設定はしないといいました。dotnetプロセスのSELinuxでの状態を見てみましょう。
$ ps -eZ | grep dotnet system_u:system_r:unconfined_service_t:s0 6815 ? 00:00:00 dotnet
unconfined_service_t
というタイプでdotnetプロセスが実行されています。これは制限のないプロセスと呼ばれ、SELinuxが有効な状態でも制約なくファイルにアクセスできます*3。
ですので、SELinuxを使ってdotnetプロセスに制限をかけようとする場合は、dotnetプロセスが特定のSELinuxコンテキストで実行されるようにしないといけません。そもそも、dotnetプロセスがデフォルトのターゲットでは制限のないプロセスとなっているので、手作業で設定する必要が現時点ではあります。単純にsytemdサービスの起動時に特定のコンテキストにするのであれば下記のブログが参考になりそうです。
とはいえやはり手間ですね...ということで、現時点での解としてはdockerコンテナを使って運用することにして、dockerコンテナごと管理する方法が取れると思われます。例えば、dotnet含めてOpenShiftであればdockerコンテナ内で起動するプロセス、実行するdockerコンテナ内のファイル、Volumeとして接続したファイル、がSELinux管理下におけます。こちらも検討するのがよさそうです。