Try .NET Core

.NET Coreを動かした、試した記録を書き残します。

Linux(CentOS7)で、ASP.NET Coreアプリをサービス化する

ASP.NET Coreプロジェクトで、dotnet publishコマンドの動作が確認できました。
今のところdotnet runもしくはdotnet [dllファイル名]で動作確認しています。

しかし実際にサービス運用するのに、このままシェルを占有させる訳にもいきません。
PHPと違って、apache がプログラムをキックしてくれるわけでもなく。
何かの不具合で異常終了しまうと、こわい。

というわけで、OSにプログラムの面倒を見てもらえるように、サービス化します。

systemd用のサービス定義を書く

公式ドキュメントをあたると、どうやらsystemdを使うようです。

sudo nano /etc/systemd/system/kestrel-hellomvc.service の、nanoってナニソレ?と思ったら、テキストエディタなんですね。
ふむふむ、じゃviでいいや。
サービス定義ファイルを作ります。

[root@centos7 ~]# vi /etc/systemd/system/dotnet_sample.service

中身はこんな感じに。

[Unit]
    Description=Service Sample on CentOS7

    [Service]
    ExecStart=/usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll
    Restart=always
    RestartSec=10
    SyslogIdentifier=dotnet_sample
    User=root
    Environment=ASPNETCORE_ENVIRONMENT=Production

    [Install]
    WantedBy=multi-user.target

公式サンプルをコピペして、実行ファイルのパス、実行ユーザーなどを書き換えました。

さて、どうなるでしょう?

[root@centos7 system]# systemctl daemon-reload
[root@centos7 system]# systemctl start dotnet_sample
[root@centos7 system]# systemctl status dotnet_sample
● dotnet_sample.service - Service Sample on CentOS7
   Loaded: loaded (/etc/systemd/system/dotnet_sample.service; enabled; vendor preset: disabled)
   Active: active (running) since 土 2016-12-03 17:56:09 JST; 6s ago
 Main PID: 3618 (dotnet)
   CGroup: /system.slice/dotnet_sample.service
           mq3618 /usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll

12月 03 17:56:09 centos7.local systemd[1]: Started Service Sample on CentOS7.
12月 03 17:56:09 centos7.local systemd[1]: Starting Service Sample on CentOS7...
12月 03 17:56:10 centos7.local dotnet_sample[3618]: Hosting environment: Production
12月 03 17:56:10 centos7.local dotnet_sample[3618]: Content root path: /
12月 03 17:56:10 centos7.local dotnet_sample[3618]: Now listening on: http://localhost:5000
12月 03 17:56:10 centos7.local dotnet_sample[3618]: Application started. Press Ctrl+C to shut down.

あー。
一見、動いてるように見えますが、Content root path: /と出てます。
publish先のパスでなく、ルートディレクトリになっちゃってます。
前回末尾に書いた、Internal Server Errorになっちゃうヤツですね。
f:id:try_dot_net_core:20161203180017j:plain
はい、動いていません。
実行時のカレントパスを指定しないといけませんね。

参考:
Publish to a Linux Production Environment

カレントパスは、どうやって書く?

かなり頑張ってぐぐりました。
おかげさまで、図らずもsystemdのことがざっくり飲み込めました。

どうやら、*.service ファイルでWorkingDirectoryという項目を指定できるようです。

追記してみます。

[Unit]
    Description=Service Sample on CentOS7

    [Service]
    ExecStart=/usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll
    WorkingDirectory=/home/webroot/dotnet_sample/bin/   # <-この行を追記
    Restart=always
    RestartSec=10
    SyslogIdentifier=dotnet_sample
    User=root
    Environment=ASPNETCORE_ENVIRONMENT=Production

    [Install]
    WantedBy=multi-user.target

どうなるでしょう?

[root@centos7 system]# systemctl stop dotnet_sample
Error: No space left on device
Warning: dotnet_sample.service changed on disk. Run 'systemctl daemon-reload' to reload units.
[root@centos7 system]# systemctl daemon-reload
[root@centos7 system]# systemctl start dotnet_sample
[root@centos7 system]# systemctl status dotnet_sample
● dotnet_sample.service - Service Sample on CentOS7
   Loaded: loaded (/etc/systemd/system/dotnet_sample.service; enabled; vendor preset: disabled)
   Active: active (running) since 土 2016-12-03 18:09:45 JST; 7s ago
 Main PID: 3698 (dotnet)
   CGroup: /system.slice/dotnet_sample.service
           mq3698 /usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll

12月 03 18:09:45 centos7.local systemd[1]: Started Service Sample on CentOS7.
12月 03 18:09:45 centos7.local systemd[1]: Starting Service Sample on CentOS7...
12月 03 18:09:45 centos7.local dotnet_sample[3698]: Hosting environment: Production
12月 03 18:09:45 centos7.local dotnet_sample[3698]: Content root path: /home/webroot/dotnet_sample/bin
12月 03 18:09:45 centos7.local dotnet_sample[3698]: Now listening on: http://localhost:5000
12月 03 18:09:45 centos7.local dotnet_sample[3698]: Application started. Press Ctrl+C to shut down.

おっ。パスがセットできてますね!
動作はいかが?
f:id:try_dot_net_core:20161203175303j:plain 動きましたね~~!

参考:
Executing chdir before starting systemd service

実行権限をapacheに変える

いつまでも、rootで動かしてちゃダメですよね。一応公開を前提にするわけで。
というわけで、apacheユーザーで動作するように、設定を見直します。

dotnet_sample.serviceファイルより、実行ユーザーを変更します。

[Unit]
    Description=Service Sample on CentOS7

    [Service]
    ExecStart=/usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll
    WorkingDirectory=/home/webroot/dotnet_sample/bin/
    Restart=always
    RestartSec=10
    SyslogIdentifier=dotnet_sample
#    User=root
    User=apache
    Environment=ASPNETCORE_ENVIRONMENT=Production

    [Install]
    WantedBy=multi-user.target

これでいいはず...?

[root@centos7 system]# systemctl stop dotnet_sample
Warning: dotnet_sample.service changed on disk. Run 'systemctl daemon-reload' to reload units.
[root@centos7 system]# systemctl daemon-reload
[root@centos7 system]# systemctl start dotnet_sample
[root@centos7 system]# systemctl status dotnet_sample
● dotnet_sample.service - Service Sample on CentOS7
   Loaded: loaded (/etc/systemd/system/dotnet_sample.service; enabled; vendor preset: disabled)
   Active: activating (auto-restart) (Result: exit-code) since 土 2016-12-03 18:19:50 JST; 3s ago
  Process: 3748 ExecStart=/usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll (code=exited, status=203/EXEC)
 Main PID: 3748 (code=exited, status=203/EXEC)

12月 03 18:19:50 centos7.local systemd[1]: Unit dotnet_sample.service entered failed state.
12月 03 18:19:50 centos7.local systemd[1]: dotnet_sample.service failed.

あらー。起動出来なかった...。

原因なんだっけなーと、諸々探してみますと。

[root@centos7 dotnet]# pwd
/opt/dotnet
[root@centos7 dotnet]# ls -alF
合計 148
drwxr-xr-x. 5 root root     99 12月  2 16:35 ./
drwxr-xr-x. 3 root root     19 12月  2 16:31 ../
-rwxr--r--. 1 root root   9410 11月  3 08:49 LICENSE.txt*
-rwxr--r--. 1 root root   8103 11月  3 08:49 ThirdPartyNotices.txt*
-rwxr--r--. 1 root root 128693 11月  9 06:52 dotnet*
drwxr-xr-x. 3 root root     16 12月  2 16:35 host/
drwxr-xr-x. 4 root root     64 12月  2 16:52 sdk/
drwxr-xr-x. 3 root root     34 12月  2 16:35 shared/

あらっ。
dotnetコマンド実体ファイルの権限が、root以外で実行出来なくなってますやん。
こらあかん。
全員が実行出来るよう、サブフォルダを含めて一括修正します。

[root@centos7 dotnet]# cd ../
[root@centos7 opt]# pwd
/opt
[root@centos7 opt]# chmod -Rf 755 ./dotnet

どやっ?

[root@centos7 system]# systemctl stop dotnet_service
Failed to stop dotnet_service.service: Unit dotnet_service.service not loaded.
[root@centos7 system]# systemctl start dotnet_sample
[root@centos7 system]# systemctl status dotnet_sample
● dotnet_sample.service - Service Sample on CentOS7
   Loaded: loaded (/etc/systemd/system/dotnet_sample.service; enabled; vendor preset: disabled)
   Active: active (running) since 土 2016-12-03 18:30:04 JST; 1min 1s ago
 Main PID: 3875 (dotnet)
   CGroup: /system.slice/dotnet_sample.service
           mq3875 /usr/bin/dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll

12月 03 18:30:04 centos7.local systemd[1]: Started Service Sample on CentOS7.
12月 03 18:30:04 centos7.local systemd[1]: Starting Service Sample on CentOS7...
12月 03 18:30:04 centos7.local dotnet_sample[3875]: Hosting environment: Production
12月 03 18:30:04 centos7.local dotnet_sample[3875]: Content root path: /home/webroot/dotnet_sample/bin
12月 03 18:30:04 centos7.local dotnet_sample[3875]: Now listening on: http://localhost:5000
12月 03 18:30:04 centos7.local dotnet_sample[3875]: Application started. Press Ctrl+C to shut down.
12月 03 18:31:02 centos7.local systemd[1]: Started Service Sample on CentOS7.

おっしゃ!
イケましたな。

dotnetプロセスを殺してみる

これで、dotnet_sampleサービスが死んでもsystemdが再起動してくれる、はずです。
試してみます...。

[root@centos7 ~]# ps -A | grep dotnet
  3875 ?        00:00:00 dotnet
[root@centos7 ~]# kill 3875
-->(dotnet_sample.serviceファイルの RestartSec設定どおり、10秒待ってから)-->
[root@centos7 ~]# ps -A | grep dotnet
  3914 ?        00:00:00 dotnet

大丈夫!理不尽に殺害されても、生き返ります!

ちなみに、supervisorだと?

systemdが標準化する以前は、プロセス監視にsupervisorを使ってた、ようです。
や、すいません。
LinuxだとPHPオンリーで、監視が必要なサービスは書いたこと無かったんです...。

CentOS7では、yum install supervisorでインストールできました。
設定ファイルは/etc/supervisord.d/[各種サービス].iniに書きます。

こちらを参考に、こんな感じで動きました。

[program:dotnet_sample]
directory=/home/webroot/dotnet_sample/bin/
command=dotnet /home/webroot/dotnet_sample/bin/WebApplication1.dll
user=apache  ; 起動ユーザ
autostart=true
autorestart=true  ; プロセスダウン時に自動再起動
stdout_logfile=/var/log/supervisor/dotnet_sample.out.log ; 標準出力ログ
stderr_logfile=/var/log/supervisor/dotnet_sample.err.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
redirect_stderr=true  ; エラー出力を標準出力にリダイレクト
environment=Hosting__Environment=Production,HOME=/home/webroot/dotnet_sample/bin/
stopsignal=INT

参考:
ASP.NET Core アプリを Ubuntu サーバーで公開



今回分のソースコードはこちら。
まだ、Visual Studioが生成するテンプレートのままです。
github.com