운영체제에서 프로세스를 단순히 시작하는 대신 지속적으로 실행 상태로 유지하는 올바른 방법과 각 시스템별 감독 도구를 설명합니다.
2013년 12월 10일 - 2026년 1월 7일
7월 8월 9월 30 2018 2019 2020
성공
실패
수집 주체
기관: Internet Archive
Internet Archive는 다양한 웹 크롤링을 통해 웹 페이지를 발견하고 수집합니다. 어느 시점이든 여러 개의 서로 다른 크롤이 실행 중이며, 몇 달 동안 지속되는 것도 있고, 매일 또는 그보다 더 자주 실행되는 것도 있습니다. Wayback Machine을 통해 웹 아카이브를 볼 수 있습니다.
Wayback Machine의 Live Proxy를 통해, 주로 web.archive.org의 Save Page Now 기능으로 수집된 콘텐츠입니다.
Liveweb 프록시는 Internet Archive의 wayback machine 프로젝트 구성 요소입니다. liveweb 프록시는 웹 페이지의 콘텐츠를 실시간으로 수집해 ARC 또는 WARC 파일로 보관하고, 처리할 수 있도록 ARC/WARC 레코드를 wayback machine으로 다시 돌려보냅니다. 기록된 ARC/WARC 파일은 시간이 지나면 wayback machine의 일부가 됩니다.
타임스탬프

The Wayback Machine - https://web.archive.org/web/20190830050609/http://dustin.sallings.org/2010/02/28/running-processes.html

나는 내가 사용하는 운영체제들이 제공하는 괜찮은 프로세스 감독 메커니즘의 부재로 계속 고통받고 있다.
대부분의 시스템과 소프트웨어는 “시작 스크립트”가 올바른 접근법이라고 생각하는 듯하다.
이 생각이 얼마나 끔찍하게 잘못되었는지는, 그 위에 덧씌워 동작하도록 존재하는 수많은 수많은 수많은 도구들과, 사람들이 대충 짜 맞춘 정말 형편없는 “cron으로 ps 돌리기”류의 것들, 혹은 nagios로 프로세스 개수를 세는 식의 것들만 봐도 알 수 있다.
이 글은 내 고통을 덜어주는 데 도움이 된 대안들을 설명하지만, 결과적으로 엄청나게 길어졌다. 그래서 특정 섹션으로 바로 갈 수 있는 빠른 링크를 제공하겠다.
내가 개인적으로 본 것 중 최악은 tibco를 이용해 시스템을 짜깁기한 한 계약업체 팀이었다. 한 컴퓨터에는 감시가 필요한 프로세스가 있었고, 다른 컴퓨터에는 모니터 역할을 하는 무언가가 돌아가며 tibco를 통해 메시지를 보냈다. 그러면 첫 번째 머신의 에이전트가 그 메시지를 처리해서 매우 긴 셸 스크립트를 실행했고, 그 스크립트는 ps, grep, awk 같은 것들과 기타 잡다한 것들을 이용해 프로세스를 찾은 뒤, 필요하면 재시작하고 tibco 버스를 통해 다시 응답했다.
이 작업을 대략 1분에 한 번씩 했고, 그 머신이 여전히 실행 중인지 확인하는 데 CPU를 약 60%나 사용했다. 그들은 이것을 내게 시연하면서, 내가 그들의 Rube Goldberg식 시스템 관리자 기술에 얼마나 감탄하는지 말해주길 기대하고 있었다. 하지만 내 반응은 그들이 원한 기분 좋은 것이 아니었다.
“와. init와 cron을 재발명했는데, 둘 다 더 신뢰성이 떨어지고 내가 상상할 수 있었던 것보다 더 많은 CPU를 먹게 만들었군요.”
## monit/god/nagios/etc…의 문제는 무엇인가?
가끔은 이런 도구들이 좋은 용도로 쓰일 수 있지만, 흔히 위에서 말한 것과 같은 주제의 변형일 뿐이다. 프로세스 목록이나 pid 파일 같은 것을 폴링해서 다른 프로세스가 실행 중인지 확인하려 하고, 실패하면 그것을 재시작한다.
많은 사람들은 프로세스가 실행 중이 아님을 알아차린 어느 시점 이후에 시작 스크립트를 다시 실행하려는 목표로 이런 도구들을 집어 든다. 내가 바로잡고 싶은 사고방식이 이것이다.
그리고 여기서 내 핵심으로 이어진다…
프로그램을 _시작_하지 말고, 프로그램을 _실행 상태로 유지_하라.
init는 이미 이것을 한다. 그것도 매우 효율적으로, 일반적인 경우에는 CPU 오버헤드 없이, 예외적인 경우에는 지연 없이, 따로 작성할 사용자 정의 스크립트 없이, 그리고 절대적인 신뢰성을 가지고 한다. 즉, 네 명령을 실행하는 것을 잊지 않고, 운영체제 전체를 함께 끌고 가지 않고서는 스스로 죽지도 않는다.
안타깝게도 역사적으로 init는 사용자의 자체 프로세스에 활용하기가 그다지 쉽지 않았다. 네가 네 것들을 계속 실행 상태로 유지할 수 있도록 여기서 조금 풀어서 설명해 보겠다.
참고: 모든 경우에서, 여기서는 스스로 daemonize하지 않는 프로그램을 실행하려 한다고 가정한다. 스스로 데몬화하는 프로그램은 너를 지옥으로 가는 길에 올려놓는다. 그러면 제정신인 keepalive 기법을 전혀 사용할 수 없어서 프로세스 목록을 폴링하거나 pid를 확인하는 식으로 후퇴할 수밖에 없다. 여기에 안전을 위해 자기 uid를 바꾸는 것들까지 결합되면 그 pidfile 관리조차 어려워진다. 물론 무엇이든 root로 실행해서는 안 되기 때문이다.
Mac OS X에는 launchd가 있는데, 이것은 init, cron, inetd, 그리고 몇 가지 다른 것들을 하나로 합쳐 놓은 것이다.
Launchd는 프로세스 제어에 대해 엄청난 수준의 세분성을 제공하며, 그 기능을 사용자 자신에게까지 확장해서 이벤트에 따라 애플리케이션을 일관되게 실행할 수 있게 해 준다.
다음 예시는 로그인해 있는 동안 꾸준히 실행되는 프로그램을 만드는 방법을 보여준다. 이것을 ~/Library/LaunchAgents/com.example.someprogram.plist에 두면, launchd가 마법을 부려 계속 실행되도록 유지해 줄 것이다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC
"-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.someprogram</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/path/to/someprogram</string>
</array>
</dict>
</plist>
로그인하지 않은 상태에서도 사용하려면 비슷한 plist를 만들어 /Library/LaunchDaemons/에 두고, UserName 속성을 추가하면 시스템이 다른 사용자를 대신해 프로그램을 유지하도록 아주 쉽게 만들 수 있다.
Launchd로는 훨씬 더 많은 일을 할 수 있다. 예를 들어 파일시스템 및 네트워크 이벤트에 따라 프로그램을 실행할 수 있다. 오픈 소스이며, 어디에나 존재하지 않는다는 점이 정말 안타까울 정도로 엄청난 이점을 제공한다.
Solaris에는 smf가 있는데, launchd와 비슷하지만 수명주기, 권한, 의존성, 장애 관리 및 많은 다른 것들에 대해 훨씬 더 많은 제어를 제공한다.
Solaris를 사용 중이라면 아마 이미 이것을 이해하고 있을 것이고, 아니라면 시스템 관리에 그것을 어떻게 사용하는지 이해하기 위해 smf 문서를 읽어보길 권한다.
최근 버전의 ubuntu는 upstart를 함께 제공하는데, 현대적인 시스템 기능들 중에서는 가장 덜 현대적이지만, 적어도 애플리케이션을 단지 _시작_하는 대신 _실행 상태로 유지_하고 싶다고 지정할 수는 있다.
다음은 ubuntu 박스에서 계속 실행되도록 유지해야 했던 twisted 앱을 위해 내가 작성한 upstart 스크립트 예시다.
description "useful description"
author "Dustin Sallings <dustin@spy.net>"
start on runlevel 2
start on runlevel 3
stop on runlevel 0
stop on runlevel 1
stop on runlevel 4
stop on runlevel 5
stop on runlevel 6
chdir /path/to/project/directory
exec /usr/bin/twistd --uid=daemonuser --syslog -ny project.tac
respawn
여기서는 twistd가 uid를 바꾸는 데 의존한다는 점에 주목하라. 시작 스크립트를 호출하기 전에 사용자 ID를 바꾸기 위해 su나 sudo를 사용하는 것보다 그것이 더 단순하기 때문이다.
FreeBSD와, 그 문제로 말하면 대부분의 BSD는 /etc/ttys에 정의된 프로세스를 감독하는 init를 가지고 있다. 이것은 거의 가장 원시적인 수준이지만, 잘 동작한다.
예를 들어 FreeBSD 박스에서 sshd를 실행하고 그것이 절대 죽지 않게 하고 싶다면, /etc/ttys에 다음을 추가할 수 있다.
sshd "/usr/local/etc/sshd_tty" unknown on
스크립트 /usr/local/etc/sshd_tty는 주로 init가 실행하는 프로그램에 암묵적으로 넘기는 인자를 먹어치우기 위해 존재한다. 이를 위해 나는 다음 스크립트를 사용했다.
#!/bin/sh
exec /usr/sbin/sshd -D
/etc/ttys는 기본적으로 getty 유형의 서비스를 위해 존재하지만, 감독이 필요한 다른 어떤 프로세스에도 적합하다.
/etc/ttys를 수정한 뒤에는 변경 사항이 적용되도록 반드시 init q를 실행해야 한다.
어떤 순정 Linux 시스템에서든, 즉 현대적인 init를 사용하지 않는 시스템과 유사한 init 메커니즘을 가진 다른 시스템에서는 위와 비슷한 것을 /etc/inittab로 할 수 있다. 다만 이것은 암묵적인 인자가 없으므로 sshd를 직접 호출할 수 있다.
예를 들어, 내 RedHat 5.4 박스에서는 다음이 동작한다.
sshd:2345:respawn:/usr/sbin/sshd -D
이 항목을 추가한 뒤에는, 물론 이미 sshd 서버가 실행 중이 아닌지 확인하고, /sbin/telinit q를 실행해서 다시 읽어들이게 할 수 있다.
launchd와 smf보다 덜 멋진 init를 보완하기 위해 서드파티 프로그램을 들여오는 것도 가능하다. 나는 개인적으로 이것을 해 본 경험이 많지는 않지만, 그중 특히 하나는 사용해 보았고 꽤 좋은 일을 한다는 것을 알게 되었다.
daemontools는 djb 특유의 괴상한 물건이지만, 훌륭한 UNIX 프로세스 감독 프레임워크 역할을 한다.
다만 기본 상태에서는 조금 이상하다. 이것은 어떤 UNIX 시스템에서도 자연스럽지 않은 파일 구조가 존재하길 정말로 바라지만, 몇몇 포트들은 그 점을 조금 완화해 준다.
하지만 매우 조합 가능하고, 각각 한 가지 일을 정말 잘하는 작은 도구들을 많이 가지고 있다.
내게는 OpenBSD 머신이 하나 있는데, 내가 /etc/rc를 처음부터 새로 쓸 정도로 많이 커스터마이즈되어 있다. 이 머신은 내 DNS와 DHCP 서비스뿐 아니라 집안의 몇 가지 다른 것들도 돌린다.
예전에는 정전 같은 일이 발생하면 프로세스 중 하나가 깔끔하게 올라오지 않는 문제가 있었다. 아주 자주 DHCP가 그랬고, 그 때문에 꽤 곤란했다. 그래서 나는 이것이 daemontools를 처음 시도해 보기 좋은 곳이라고 생각했다.
지금까지는 이것 덕분에 상황을 훨씬 다루기 쉬워졌다.
예를 들어 내가 실행 중인 서비스 하나는 내 ibutton 모음의 sample_devices다. 기본적으로 온도계를 멀티캐스트로 보내는 것이다. 이것은 /dev/tty01에 접근할 수 있는 사용자로 실행되어야 하고, 약간의 init 작업도 필요하다. 나는 절대적으로 필요하지 않은 한 root로 무언가를 실행하지 않는다.
이것이 동작하게 하려면 /services/sample_devices 안에 run 파일 하나만 만들면 되는데, 내용은 다음과 같다.
#!/bin/sh
mkdir /tmp/sample
chown uucp /tmp/sample
exec setuidgid uucp /usr/local/sbin/sample_devices \
-b /dev/tty01 -c /tmp/sample \
-m 225.0.0.37 -p 6789 -t 64 -s 2121
exec가 중요하다는 점에 주목하라. daemontools에는 많은 제어 유틸리티가 있는데, 그것들이 실제로 실행 중인 프로세스의 pid를 알아야 하기 때문이다. 그것을 시작한 셸의 pid가 아니라 말이다. 어쨌든 좋은 습관이기도 하다.
daemontools에 대해 내가 마음에 들어 하지 않는 한 가지는 서비스 디렉터리에 시작 스크립트, 제어 소켓, 기타 잠금 상태 등이 모두 함께 들어 있다는 점이다. 내게는 서비스 정의가 /etc/ 아래 어딘가에 있고, 런타임 제어는 /var/run 아래 어딘가에 있는 편이 더 좋겠지만, 결과에는 꽤 만족하고 있다.
Dustin Sallings