Debian 9 で実行される .NET Core サービスがあります。これをMyServiceといいます。ある時点update.sh
で、サービスProcess.Start()
はShellExecute=true
。
このスクリプトはデフォルトで機能しますapt-get update; apt-get upgrade
。
パッケージのアップグレード中に MyService プロセスが終了します。更新スクリプトも終了してapt-get upgrade
終了するため、手動で変更する必要がある一貫性のないパッケージが残ります。
私が望むのは、update.sh
MyServiceが終了したときに終了しないことです。
私はそれをupdate.sh
2つの部分に分けようとしました。最初の部分は2番目の部分を別々に実行しようとしましたが、update2.sh
常に同じ結果を得ましたsetsid
。同じ結果で、新しいbashシェルでスクリプトを実行してnohup
みました。update2.sh
/bin/bash /c "update2.sh"
スクリプトの実行中にバイナリを終了できるように、バイナリで起動されたスクリプトをバイナリプロセスから完全に分離して実行するにはどうすればよいですか。
これが私の環境です。 MyServiceはサービスとして実行されるバイナリです。update.sh
MyServiceによって開始されました。
MyServiceバイナリでシェルスクリプトを実行する.NET Coreコード:
var process = new Process();
process.EnableRaisingEvents = true; // to avoid [defunct] sh processes
process.StartInfo.FileName = "/opt/myservice/update.sh";
process.StartInfo.Arguments = "";
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit(10000);
if (process.HasExited)
{
Console.WriteLine("Exit code: " + process.ExitCode);
}
else
{
Console.WriteLine("Child process still running after 10 seconds");
}
アップデート.sh:
nohup /opt/myservice/update2.sh > /opt/myservice/update.log &
systemctl stop MyService
2.shアップデート:
apt-get update >> /opt/myservice/update.log
apt-get -y install --only-upgrade myservice-1.0 >> /opt/myservice/update.log
update2.sh
MyServiceが終了すると終了するため、実行されませんupdate.sh
。
update.sh
戻りコード 143、終了したようです。
2018-08-16 14:46:14.5215|Running update script: /opt/myservice/update.sh
2018-08-16 14:46:14.5883|Update script /opt/myservice/update.sh returned: 143
修正する
ご提案ありがとうございます。
- 設定値
- 否定する
- 北面
- 画面
- マルチプレクサ
- 共有をキャンセル
各メソッドは、生成されたすべてのプロセスを終了するのと同じ結果を持ちます。私はこれが.NETコア「機能」だと思います。
アップデート2
私はsystemctl stop MyService
基本的にサービスによって生成されたすべてのプロセスが明示的に終了することを発見しました。
https://stackoverflow.com/questions/40898077/systemd-systemctl-stop-aggressively-kills-subprocesses
KillMode=process
サービス記述子に追加すると、サービスの終了時に更新スクリプトは終了しません。
持つ絶対開始されたサービスのPIDスペースをエスケープしてくださいsystemctl
。許可された回答を含む使用された各テクノロジは、個別のプロセスを作成しません。指定systemctl stop MyService
しない限り、KillMode=process
生成された各プロセスは常に終了します。
結局別のサービスを作成しましたMyServiceUpdater
。このサービスは、分岐することなく簡単な更新スクリプトを実行します。 PID空間が異なるため、すべてが期待どおりに機能します。長い旅でした。
MyServiceUpdaterの例:
[Unit]
Description=Your Service Updater
After=network.target
[Service]
ExecStart=/path/to/update/script/updatescript.sh
ExecStopPost=
TimeoutStopSec=30
StandardOutput=null
WorkingDirectory=/path/to/service/directory/
KillMode=process
[Install]
WantedBy=multi-user.target
答え1
ジョブをスケジュールするには、crontab(またはat)(mono / .netではない)を使用してください。
一般的なオプション
- いいえmy.sh&
- 画面 -dm -S 私の my.sh
- tmux new -d -s my.sh
- サービス内起動/systemctl開始
- Ctrl+Z、bg、拒否
答え2
Centos 7テストシステムに合格
$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
$ sudo yum install dotnet-sdk-2.1
その結果、dotnet-sdk-2.1-2.1.400-1.x86_64
テストコードを使用してインストールされます。
using System;
using System.Diagnostics;
using System.ComponentModel;
namespace myApp {
class Program {
static void Main(string[] args) {
var process = new Process();
process.EnableRaisingEvents = true; // to avoid [defunct] sh processes
process.StartInfo.FileName = "/var/tmp/foo";
process.StartInfo.Arguments = "";
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit(10000);
if (process.HasExited) {
Console.WriteLine("Exit code: " + process.ExitCode);
} else {
Console.WriteLine("Child process still running after 10 seconds");
}
}
}
}
そしてシェルスクリプトを/var/tmp/foo
停止し、strace
私のシステムで/var/tmp/foo
実行されていることを示しますxdg-open
。 …何かはわかりませんが、不要な複雑さのようです。
$ strace -o foo -f dotnet run
Child process still running after 10 seconds
^C
$ grep /var/tmp/foo foo
25907 execve("/usr/bin/xdg-open", ["/usr/bin/xdg-open", "/var/tmp/foo"], [/* 37 vars */] <unfinished ...>
...
より簡単な解決策は、exec
プログラムを生成することです。このプログラムは必要な操作を実行するシェルスクリプトにすることができ、.NETではシェルを使用する必要はありません。
process.StartInfo.UseShellExecute = false;
この設定を有効にすると、strace
ディスプレイは/var/tmp/foo
(より簡単な)呼び出しで実行されますexecve(2)
。
26268 stat("/var/tmp/foo", {st_mode=S_IFREG|0755, st_size=37, ...}) = 0
26268 access("/var/tmp/foo", X_OK) = 0
26275 execve("/var/tmp/foo", ["/var/tmp/foo"], [/* 37 vars */] <unfinished ...>
そして.NETは終了を拒否します。
$ strace -o foo -f dotnet run
Child process still running after 10 seconds
^C^C^C^C^C^C^C^C
foo
ほとんどのシグナルを無視して置き換えるからです(特にnotUSR2
または常にKILL
(しかし使用しないでください!))。
$ cat /var/tmp/foo
#!/bin/sh
exec /var/tmp/stayin-alive
$ cat /var/tmp/stayin-alive
#!/usr/bin/perl
use Sys::Syslog;
for my $s (qw(HUP INT QUIT PIPE ALRM TERM CHLD USR1)) {
$SIG{$s} = \&shandle;
}
openlog( 'stayin-alive', 'ndelay,pid', LOG_USER );
while (1) {
syslog LOG_NOTICE, "oh oh oh oh oh stayin alive";
sleep 7;
}
sub shandle {
syslog LOG_NOTICE, "nice try - @_";
}
悪魔
そして親プロセスから分離されたプロセスそして、いくつかのコマンドを実行するシェルスクリプト(期待したものと同じであることを願っていますapt-get update; apt-get upgrade
)
$ cat /var/tmp/a-few-things
#!/bin/sh
sleep 17 ; echo a >/var/tmp/output ; echo b >/var/tmp/output
.NETプログラムを変更して実行できます。/var/tmp/solitary /var/tmp/a-few-things
process.StartInfo.FileName = "/var/tmp/solitary";
process.StartInfo.Arguments = "/var/tmp/a-few-things";
process.StartInfo.UseShellExecute = false;
実行すると、.NETプログラムはかなり迅速に終了します。
$ dotnet run
Exit code: 0
最終的に、/var/tmp/output
ファイルには、.NETプログラムの終了時に終了しなかったプロセスによって作成された2行が含まれています。
APTコマンドの出力をどこかに保存する必要があり、2つ(またはそれ以上!)更新が同時に実行されないように何かが必要になる場合があります。このバージョンは問題が発生しても停止せず、すべてのTERM
信号を無視します(INT
無視する必要があるかもしれません)。
#!/bin/sh
trap '' TERM
set -e
apt-get --yes update
apt-get --yes upgrade
答え3
私も同じことで苦労しています。
私が見つけた1つの解決策は、ランダムなsystemd-run
コマンドを実行するための一時的なワンタイムサービスを本質的に生成することです。ただし、これを機能させるには-r
(remain-after-exit)パラメータを指定する必要があるようです。バラよりhttps://www.freedesktop.org/software/systemd/man/systemd-run.htmlより多くの文書を入手してください。
たとえば、
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "systemd-run",
Arguments = "-r sleep 90",
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = false,
CreateNoWindow = true,
},
};
process.EnableRaisingEvents = true;
process.Start();