Debian9で実行されている.NETCoreサービスがあります。これをMyServiceと呼びましょう。ある時点で、このサービスはProcess.Start()
と_update.sh
_を使用してbashスクリプト_ShellExecute=true
_を実行しています。
このスクリプトは基本的に_apt-get update; apt-get upgrade
_を実行します。
パッケージのアップグレード中に、MyServiceプロセスが終了します。更新スクリプトも終了し、_apt-get upgrade
_も強制終了されるため、一貫性のないパッケージが残り、手動で修正する必要があります。
私が欲しいのは、MyServiceが終了しても_update.sh
_は終了しないということです。
_update.sh
_を2つの部分に分割してみました。最初の部分は、2番目の部分をさまざまな方法で実行しました。 _update2.sh
_をsetsid
とNohup
で開始しようとしましたが、常に同じ結果が得られます。新しいbashシェルで_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");
}
_
update.sh:
_Nohup /opt/myservice/update2.sh > /opt/myservice/update.log &
systemctl stop MyService
_
update2.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
_
[〜#〜]更新[〜#〜]
私は次のアプローチを試しました、提案に感謝します:
すべてのアプローチで同じ結果が得られ、生成されたすべてのプロセスが終了します。これは.NETCoreの「機能」だと思います。
UPDATE 2
_systemctl stop MyService
_は、デフォルトで、サービスによって生成されたすべてのプロセスを明示的に強制終了することを発見しました。
https://stackoverflow.com/questions/40898077/systemd-systemctl-stop-aggressively-kills-subprocesses
サービス記述子に_KillMode=process
_を追加すると、サービスが終了しても更新スクリプトが終了しません。
systemctl
で開始されたサービスのPIDスペースから脱出する方法はNO WAYです。受け入れられた回答に含まれるものを含め、使用されるすべての手法は、個別のプロセスを生成しません。生成されたすべてのプロセスは、_systemctl stop MyService
_が指定されていない限り、常に_KillMode=process
_によって強制終了されます。
結局、別のサービスMyServiceUpdater
を作成しました。このサービスは、フォークなしでプレーンアップデータスクリプトを実行します。 PIDスペースが異なるため、すべてが期待どおりに機能します。それは長い道のりでした。
Centos7テストシステムで
_$ 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
_ a 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
は、ほとんどの信号を無視するものに置き換えられるためです(特に、_USR2
_ではないか、常に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
_
Crontab(またはat)(mono/.netではない)を使用してタスクをスケジュールします。
通常のオプション。
まったく同じことを自分で戦ってきました。
私が見つけた解決策の1つは、systemd-run
を使用することです。これは、基本的に、任意のコマンドを実行するための一時的な1回限りのサービスを作成します。ただし、これを機能させるには、-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();