악마

악마

Debian 9에서 실행되는 .NET Core 서비스가 있습니다. 이를 MyService라고 하겠습니다. 어느 시점 update.sh에서 서비스 Process.Start()ShellExecute=true.

이 스크립트는 기본적으로 작동합니다 apt-get update; apt-get upgrade.

패키지 업그레이드 중에 MyService 프로세스가 종료됩니다. 업데이트 스크립트도 종료되어 apt-get upgrade종료되므로 수동으로 수정해야 하는 일관성 없는 패키지가 남습니다.

내가 원하는 것은 update.shMyService가 종료될 때 종료되지 않는다는 것입니다.

나는 그것을 update.sh두 부분으로 나누려고 시도했습니다. 첫 번째 부분은 두 번째 부분을 다르게 실행하려고 시도했지만 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 Core "기능"이라고 생각합니다.

업데이트 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대부분의 신호를 무시하는 것으로 대체하기 때문입니다 (특히 not 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 프로그램이 종료될 때 종료되지 않은 프로세스가 작성한 두 줄이 포함되어 있습니다.

APT 명령의 출력을 어딘가에 저장해야 하며, 두 개(또는 그 이상!) 업데이트가 동시에 실행되지 않도록 뭔가가 필요할 수도 있습니다. 이 버전은 문제가 발생해도 멈추지 않고 모든 TERM신호를 무시합니다( INT무시해야 할 수도 있음).

#!/bin/sh
trap '' TERM
set -e
apt-get --yes update
apt-get --yes upgrade

답변3

나도 같은 일로 어려움을 겪고 있습니다.

내가 찾은 한 가지 해결책은 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();

관련 정보