悪魔

悪魔

Debian 9 で実行される .NET Core サービスがあります。これをMyServiceといいます。ある時点update.shで、サービスProcess.Start()ShellExecute=true

このスクリプトはデフォルトで機能しますapt-get update; apt-get upgrade

パッケージのアップグレード中に MyService プロセスが終了します。更新スクリプトも終了してapt-get upgrade終了するため、手動で変更する必要がある一貫性のないパッケージが残ります。

私が望むのは、update.shMyServiceが終了したときに終了しないことです。

私はそれをupdate.sh2つの部分に分けようとしました。最初の部分は2番目の部分を別々に実行しようとしましたが、update2.sh常に同じ結果を得ましたsetsid。同じ結果で、新しいbashシェルでスクリプトを実行してnohupみました。update2.sh/bin/bash /c "update2.sh"

スクリプトの実行中にバイナリを終了できるように、バイナリで起動されたスクリプトをバイナリプロセスから完全に分離して実行するにはどうすればよいですか。

これが私の環境です。 MyServiceはサービスとして実行されるバイナリです。update.shMyServiceによって開始されました。

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.shMyServiceが終了すると終了するため、実行されません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();

関連情報