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
두 부분으로 나누려고 시도했습니다. 첫 번째 부분은 두 번째 부분을 다르게 실행하려고 시도했지만 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 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();