systemd가 리눅스 표준이 된 이유와 역사
SysV init의 한계와 Upstart의 시도를 넘어 왜 리눅스 배포판들이 4년 만에 systemd로 전환했는지 분석합니다. 서비스 의존성 관리와 프로세스 추적 문제 해결 과정 및 제기된 비판의 타당성을 상세히 설명합니다.
systemd가 승리한 이유
systemd의 역사는 SysV init이 수행할 수 없었던 두 가지 기능에서 시작합니다. 리눅스가 AT&T Unix로부터 물려받은 초기 부팅 시스템인 SysV init은 서비스 간의 의존 관계를 정의할 방법이 없었고, 서비스가 실행된 이후 어떤 프로세스가 해당 서비스에 속하는지 파악할 방법도 없었습니다. systemd는 셸 스크립트로 구현할 수 없는 커널 기능을 활용하여 이 두 가지 문제에 대응했습니다. 프로세스 추적을 위한 control groups와 서비스 실행 순서 제어를 위한 미리 열린 listening sockets가 그것입니다. 그 이후의 역사는 이 두 가지 해결책이 사용자 공간(userland) 전반으로 확산하는 과정이며, 이 지점에서 여러 반론이 제기되었습니다. 그중 일부 반론은 타당한 지적을 담고 있었습니다.
SysV init의 실제 동작 방식
SysV 시스템에서 커널이 시작하는 첫 번째 프로세스인 PID 1은 /etc/inittab을 읽고 런레벨을 선택한 뒤, 해당 런레벨에 맞는 스크립트를 실행했습니다. 이 스크립트들은 /etc/init.d/에 위치했습니다. /etc/rc3.d/에 있는 심볼릭 링크가 어떤 스크립트를 어떤 순서로 실행할지 결정했으므로, /etc/rc3.d/S20nginx은 /etc/init.d/nginx를 가리키며 start라는 인자와 함께 호출되었습니다.
#!/bin/sh
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
case "$1" in
start)
start-stop-daemon --start --quiet --pidfile /run/nginx.pid \
--exec /usr/sbin/nginx
;;
stop)
start-stop-daemon --stop --quiet --retry TERM/30/KILL/5 \
--pidfile /run/nginx.pid
;;
esacS20nginx 내의 20은 의존성이 아닌 위치를 의미합니다. 이는 해당 스크립트가 S19 이후, S21 이전에 실행됨을 나타냅니다. 그 이유를 명시하지 않기에 검증할 방법이 없으며, 사람이 안전하다고 판단하지 않는 한 서로 관련 없는 두 스크립트를 동시에 실행할 수도 없습니다.
rc 프로그램은 각 스크립트를 순차적으로 실행하고 종료될 때까지 기다렸습니다. 네트워크 주소를 기다리며 30초 동안 차단된 스크립트는 네트워크를 사용하지 않는 서비스조차 포함하여 전체 부팅 과정을 30초 동안 멈추게 했습니다.
해당 스크립트 상단의 LSB(Linux Standard Base) 헤더는 내부적으로 이 문제를 해결하려는 시도였습니다. 2011년 Debian 6.0은 insserv을 기본값으로 채택했습니다. 이는 모든 스크립트에서 Required-Start를 읽어 그래프를 구성하고 심볼릭 링크의 번호를 다시 매기는 방식이었습니다. 이를 통해 Debian은 startpar을 사용하여 독립적인 스크립트를 동시에 실행할 수 있게 되었습니다. 이는 도움이 되었으나 근본적인 문제를 해결하지는 못했습니다. 의존성은 여전히 스크립트의 종료 여부에 달려 있었습니다. S20nginx가 0을 반환한다는 것은 셸 함수가 종료되었음을 의미할 뿐, nginx가 연결을 수락하고 있다는 뜻은 아닙니다.
init 스크립트로 해결할 수 없는 5가지 문제
- 병렬 시작. 파일 이름순으로 정렬하는 방식은 시스템의 모든 서비스에 대해 순차적인 실행을 강제하므로, 부팅 속도는 각 서비스 실행 시간의 합만큼 느려집니다.
- 준비 상태 확인. 시작 스크립트는 데몬을 포크(fork)한 직후 종료되지만, 데몬이 실제로 요청을 처리할 준비가 되었는지는 알 수 없습니다. 따라서 다음 스크립트가 너무 일찍 시작되는 경우가 잦습니다.
- 감독(Supervision). 데몬이 두 번 포크하고 부모 프로세스가 종료되면, 데몬은 터미널에서 분리되어 PID 1의 자식 프로세스가 됩니다. init은 자식 프로세스의 종료만 감지할 뿐, 살아남은 프로세스와의 신뢰할 수 있는 연결 고리가 없습니다.
- 온디맨드 시작. inetd(인터넷 슈퍼 서버)는 연결이 들어올 때 데몬을 실행할 수 있었지만, 이는 별도의 설정 파일을 가진 독립적인 시스템이었으며 부팅 시 다른 서비스들의 실행 순서에는 아무런 영향을 주지 못했습니다.
- 리소스 제어. init 스크립트로는 서비스의 메모리 사용량이나 CPU 점유율을 제한할 수 없었습니다.
ulimit는 단일 프로세스에만 적용되었고nice은 스케줄러에만 관여했기 때문에, 서비스에서 파생된 폭주 프로세스는 시스템의 다른 일반 프로세스와 구분되지 않았습니다.
감독 기능의 부재는 일상 운영에서 가장 큰 문제였습니다. PID 파일이 이를 우회하는 수단이었는데, 데몬이 자신의 프로세스 ID를 /run/nginx.pid에 기록하면 종료 함수가 이 파일을 읽어 들이는 방식이었습니다. 데몬이 강제로 종료되면 파일은 그대로 남았습니다. 커널이 해당 번호를 다른 프로세스에 재할당하면, start-stop-daemon --stop --pidfile은 엉뚱한 프로세스에 시그널을 보내게 됩니다. 잘못된 PID 파일은 init 스크립트가 엉뚱한 프로세스를 종료시키는 원인이 됩니다.
launchd가 소켓 문제를 먼저 해결하다
Apple은 2005년 Mac OS X 10.4에서 Dave Zarzycki가 작성한 launchd를 출시했습니다. 하나의 프로세스가 init, rc, xinetd, crond, watchdogd를 모두 대체했습니다.
복제할 가치가 있는 아이디어는 소켓 활성화(socket activation)였습니다. launchd는 모든 리스닝 소켓을 먼저 생성한 뒤 데몬을 시작합니다. 아직 시작되지 않은 데몬에 클라이언트가 연결을 시도해도 연결 거부(connection refused)가 발생하지 않습니다. 데몬이 accept()를 호출할 때까지 커널이 해당 소켓의 백로그 큐에 연결을 보관하기 때문입니다. 두 데몬 사이의 실행 순서를 사람이 명시할 필요가 없어졌습니다. 소켓이 이를 처리하기 때문입니다.
launchd는 Apple의 XNU 커널에 속하며 Linux에는 대응하는 기능이 없는 Mach IPC(프로세스 간 통신)를 기반으로 구축되었습니다. 따라서 코드를 이식하는 것은 현실적으로 불가능했습니다. 하지만 그 아이디어는 결국 다른 곳으로 전파되었습니다.
Upstart는 이벤트를 작업 단위로 설정했습니다
Canonical의 Upstart는 Scott James Remnant가 작성했으며 2006년 10월 Ubuntu 6.10에서 처음 출시되었습니다. Fedora 9부터 Fedora 14까지, 그리고 RHEL 6와 Chrome OS에서도 이 시스템을 사용했습니다. Upstart는 런레벨을 이벤트로 대체했으며, 각 작업(job)은 어떤 이벤트가 발생할 때 시작하고 종료할지를 정의했습니다.
# /etc/init/example.conf
start on filesystem and net-device-up IFACE!=lo
stop on runlevel [!2345]
respawn
respawn limit 10 5
exec /usr/local/bin/exampled작업 수가 증가함에 따라 두 가지 문제가 나타났습니다. 첫 번째는 방향성 문제입니다. 작업은 "이런 일이 발생하면 나를 시작하라"고 명령합니다. 즉, 무엇이 무엇에 의존하는지에 대한 정보가 잘못된 파일에 위치하게 됩니다. 서비스는 자신이 무엇을 필요로 하는지는 알지만, 미래에 누가 자신을 필요로 할지는 알 수 없습니다. 서비스를 추가하려면 기존 작업을 수정하여 새로운 이벤트를 발생시키도록 해야 하는 경우가 많았습니다.
두 번째는 추적 문제입니다. Upstart는 fork() 호출을 ptrace로 계수하여 포킹 데몬을 추적했는데, 이는 expect fork 또는 expect daemon으로 설정했습니다. 포크 횟수를 잘못 추측하면 Upstart는 이미 종료된 프로세스를 감독하거나, 이미 발생한 포크를 기다리게 됩니다. 이로 인해 initctl start가 오류 없이 멈추는 현상이 발생하며, 작업 파일에서는 이를 설명할 방법이 없습니다.
또한 Upstart는 기여자들에게 Canonical의 기여자 계약에 서명할 것을 요구했습니다. 이는 공학적인 결함은 아니었으나, 누가 개발에 참여할지에 영향을 미쳤습니다.
PID 1에 대한 재고, 2010년 4월
2010년 4월 30일, Lennart Poettering은 "Rethinking PID 1"이라는 게시물을 발표했습니다. Kay Sievers가 그와 함께 이 프로젝트를 진행했습니다. 이 주장은 네 가지 부분으로 구성되었습니다.
- 더 적게 시작하십시오. 많은 서비스는 실제로 요청이 들어올 때까지 기다릴 수 있습니다.
- 소켓이 순서를 암시할 수 있는 경우 순서를 선언하지 마십시오. 모든 소켓을 한 번에 열고 모든 서비스를 동시에 시작하십시오.
- PID 파일 대신 control groups를 사용하여 프로세스를 추적하십시오.
- 선언적 파일로 서비스를 기술하여, 하나의 기술서가 모든 배포판에서 작동하도록 하십시오.
그해 첫 번째 릴리스가 뒤따랐습니다. Fedora 14는 2010년 11월에 systemd를 옵션으로 제공했으며, Fedora 15는 2011년 5월에 이를 기본값으로 채택했습니다.
cgroups가 서비스 관리를 안정적으로 만든 이유
cgroup(control group)은 프로세스를 그룹화하기 위한 커널 기능으로, 2008년 Linux 2.6.24에 병합되었습니다. systemd는 모든 서비스를 각자의 cgroup에 배치합니다. 자식 프로세스는 부모의 cgroup을 상속하며, 권한이 없는 프로세스는 스스로 cgroup을 벗어날 수 없습니다. 따라서 더블 포킹(double forking)을 사용해도 아무것도 숨길 수 없습니다. PID 1은 항상 특정 유닛에 속한 프로세스 집합을 정확하게 파악합니다. 서비스를 중지한다는 것은 해당 cgroup 내의 모든 프로세스를 종료한다는 의미이며, 이는 기본값인 KillMode=control-group가 수행하는 작업입니다.
systemctl status은 해당 그룹을 출력합니다:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-08-11 09:14:22 UTC; 3min ago
Main PID: 1042 (nginx)
Tasks: 3 (limit: 4653)
Memory: 6.1M (peak: 7.4M)
CGroup: /system.slice/nginx.service
├─1042 "nginx: master process /usr/sbin/nginx"
├─1043 "nginx: worker process"
└─1044 "nginx: worker process"이 블록이 부실해진(stale) PID 파일 문제에 대한 완전한 해결책입니다. 커널 상태가 곧 프로세스 목록이므로, 부실해질 수 있는 파일 자체가 존재하지 않습니다.
cgroups는 원래 추적 용도보다 자원 계산을 위해 만들어졌기 때문에, 동일한 트리 구조에서 제한 설정도 가능합니다. MemoryMax=, CPUQuota=, TasksMax=는 각각 한 줄로 설정할 수 있습니다. 서비스에 메모리 및 CPU 하드 제한 설정하기는 오늘날 드롭인(drop-in) 파일 하나로 가능하지만, 2009년에는 아무도 작성하지 않았던 셸 스크립트 패치 수준의 작업이었습니다.
모든 배포판이 2011년에서 2015년 사이에 전환한 이유
- Fedora 15, 2011년 5월.
- openSUSE 12.1, 2011년 11월.
- Mageia 2, 2012년 5월.
- Arch Linux, 2012년 10월부터 신규 설치 시 기본값.
- RHEL 7, 2014년 6월.
- SLES 12, 2014년 10월.
- Debian 8, 2015년 4월.
- Ubuntu 15.04, 2015년 4월.
RHEL 7의 수명은 다른 버전보다 더 길었다. CentOS 7이 이를 다시 빌드했기 때문이다. 또한 대부분의 관리자가 unit 파일을 처음 접한 버전이 CentOS 7이었다. 이는 Red Hat Linux에서 CentOS를 거쳐 Rocky와 AlmaLinux로 이어지는 더 긴 역사의 한 단계다.
전환이 신속하게 이루어진 이유는 대부분 지루할 정도로 명확했습니다.
- 하나의 unit 파일이 모든 배포판에서 작동하므로, 업스트림 프로젝트들이
.service파일을 직접 제공하기 시작했고 배포판들은 릴리스마다 패키지별로 셸 스크립트를 유지 관리할 필요가 없어졌습니다. - 2012년경 ConsoleKit의 유지 관리가 중단되면서 데스크톱 세션 추적 기능이
systemd-logind로 이전되었습니다. GNOME은 logind가 필요했기에 systemd를 사용하지 않는 배포판은 대체재를 찾아야 했습니다. 그 대체재인elogind는 systemd의 logind를 별도로 추출하여 유지 관리하는 프로젝트입니다. - 장치 관리자인 udev는 2012년 4월 systemd 소스 트리로 병합되었습니다. udev를 제공하는 배포판들은 이제 systemd 저장소를 추적하게 되었습니다. 이에 대응하여 Gentoo는
eudev을 포크했습니다. - 컨테이너 기술이 도입되면서 안정적인 프로세스 추적과 서비스별 제한 기능이 중요해졌으며, 이 두 가지 모두 cgroup의 기능입니다. 어떤 슈퍼바이저가 컨테이너 프로세스를 소유할 것인가 하는 문제는 재부팅 후 Docker Compose 스택을 다시 시작하는 방법을 다룰 때마다 여전히 중요한 쟁점이 됩니다.
Debian의 결정은 가장 큰 논란을 불러일으켰습니다. 2014년 2월 기술 위원회 투표에서 찬반 동수가 나오자 의장인 Bdale Garbee가 systemd를 선택하는 결정적인 표를 던졌습니다. 며칠 후 Ubuntu는 Upstart를 계속 사용하는 대신 Debian을 따르겠다고 발표했습니다. Debian 개발자 그룹은 2014년 11월 Devuan이라는 이름으로 배포판을 포크했고, 2017년 5월 Devuan 1.0을 출시했습니다.
반대 의견에 대한 공정한 서술
범위. 현재 하나의 프로젝트가 PID 1, 로깅 데몬, 로그인 세션 관리, 장치 관리자, 네트워크 설정 데몬, DNS(도메인 네임 시스템) 리졸버, NTP(네트워크 시간 프로토콜) 클라이언트, 컨테이너 런너, 부트로더를 모두 포함하고 있습니다. 이들은 별도의 바이너리이므로 설치하지 않아도 된다는 일반적인 방어 논리는 사실이지만, 반대 의견에 대한 답변은 되지 못합니다. 데스크톱 환경이 logind를 필요로 하게 되고 logind가 systemd 트리에서 분리되어 배포되면, 선택의 자유는 더 이상 존재하지 않습니다. 이것이 논쟁에서 말하는 결합(coupling)의 의미이며, 실제로 발생한 일입니다.
바이너리 저널. journald는 일반 텍스트 대신 인덱싱된 바이너리 형식으로 로그를 기록합니다. 이를 통해 텍스트 파일에서는 불가능했던 단위별·우선순위별 필터링, 구조화된 필드, 그리고 전송 프로그램이 위조할 수 없는 메타데이터(journald가 직접 단위와 cgroup을 기록하기 때문)를 얻을 수 있습니다. journalctl -u nginx -p err --since "-1h"는 날짜 정규 표현식을 사용하는 grep을 대체합니다. 물론 대가도 따릅니다. 부팅되지 않는 시스템에서는 복구 셸에서 less로 로그를 읽을 수 없습니다. 대신 마운트된 디스크를 journalctl로 지정해야 합니다.
sudo journalctl --directory /mnt/var/log/journal --boot -1 --priority err여기에는 사용자가 한 번쯤 겪게 되는 두 번째 함정이 있습니다. /var/log/journal이 존재하지 않으면 journald는 로그를 /run/log/journal(메모리)에만 보관합니다. 이 디렉터리가 없는 시스템에서는 재부팅 후 journalctl -b -1에 아무런 내용도 표시되지 않으며, 이는 로그가 가장 필요한 바로 그 순간입니다. 확인하고 수정하십시오.
journalctl --disk-usage
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald이제 journalctl --disk-usage는 /var/log/journal 아래에 아카이브된 저널을 보고해야 합니다. 일반 텍스트 로그도 함께 원한다면 /etc/systemd/journald.conf에서 ForwardToSyslog=yes을 설정하고 rsyslog를 계속 설치해 두십시오.
부팅 디버깅. 단위(unit)가 멈추면 콘솔에는 다음과 같은 한 줄만 표시됩니다.
[ *** ] A start job is running for Wait for Network to be Configured (1min 32s / no limit)더 깊이 조사할 도구는 존재합니다. 멈춰 있는 동안에는 systemctl list-jobs을 사용하고, 이후에는 systemd-analyze blame와 systemd-analyze critical-chain를, 커널 명령줄에서는 systemd.log_level=debug을 사용합니다. 이 불만에 대한 공정한 평가는, 과거에는 sh을 아는 사람이라면 누구나 init 스크립트를 처음부터 끝까지 읽을 수 있었지만, 이제는 단위가 멈추면 수십 개의 명령어 중 무엇을 써야 할지 알아야 한다는 점입니다. 이는 실질적인 비용입니다. 관리자마다 한 번씩 치러야 하는 비용이며, 수많은 관리자가 동시에 이 비용을 치렀습니다.
모두에게 적용되는 기본값 변경. 2016년 systemd 230 버전에서 logind의 기본값이 변경되어 로그아웃 시 남은 사용자 프로세스가 종료되도록 바뀌었습니다. 분리된 tmux 및 screen 세션은 해당 세션을 시작한 세션이 종료될 때 함께 죽었습니다. 배포판들은 /etc/systemd/logind.conf에 KillUserProcesses=no을 포함해 배포했고, 지원되는 해결책은 loginctl enable-linger <user>였습니다. 하나의 프로젝트에서 바뀐 하나의 기본값이 수백만 명이 의존하던 습관을 바꾸어 놓았으며, 이것이 실무에서 말하는 "사용자 공간의 과도한 통합"의 의미입니다.
기본 의존성은 보안 표면입니다. 2024년 3월, xz-utils의 백도어는 Debian과 Ubuntu의 sshd를 표적으로 삼았습니다. 업스트림 OpenSSH는 libsystemd와 링크되지 않습니다. 해당 배포판들이 sshd가 systemd에 준비 상태를 보고할 수 있도록 패치했고, libsystemd가 백도어가 포함된 liblzma를 불러들였습니다. 준비 상태 프로토콜 자체는 $NOTIFY_SOCKET에 명시된 소켓으로 전송되는 단일 데이터그램이므로, 이를 위해 라이브러리가 필요했던 적은 없습니다. systemd의 대응은 dlopen를 통해 압축 라이브러리를 로드하도록 변경하여, 더 이상 기본적으로 링크되지 않도록 하는 것이었습니다. 이와 유사한 유형의 버그가 같은 형태로 나타납니다. 2017년에는 숫자로 시작하는 User= 값이 잘못된 것으로 처리되어 단위가 실패하는 대신 root 권한으로 실행되는 바람에, 오타가 권한 상승으로 이어진 적이 있습니다. 이후 버전에서는 해당 단위의 시작을 거부합니다.
시스템의 systemctl 프롬프트에서 확인하는 기록
위에서 언급한 모든 문제는 이제 읽을 수 있는 파일 내의 지시어 하나로 정리됩니다.
- 직렬 부팅은
After=및Wants=이 되었으며,systemd-analyze critical-chain은 실제로 부팅을 지연시킨 원인을 보여줍니다. - 준비 상태는
Type=notify가 되었으며, 서비스가 요청을 처리할 수 있을 때READY=1을$NOTIFY_SOCKET에 기록합니다.PIDFile=을 사용하는Type=forking는 구형 데몬을 위해 여전히 존재하며, PID 파일이 생성되지 않을 때start operation timed out. Terminating.오류를 발생시키는 유형입니다. 잘못된 유형을 선택하면 데몬은 이미 종료되었는데 유닛은 활성 상태로 보고되는 문제가 발생하므로, 해당 줄을 작성하기 전에 데몬의 실제 시작 방식에 맞는 Type=을 파악하는 것이 중요합니다. - 프로세스 감시는 cgroup이 담당하므로
RestartSec=을 포함한Restart=on-failure가 래퍼 스크립트를 대체하며,StartLimitBurst=은 충돌 루프가 무한히 실행되는 것을 방지합니다. - inetd는
.service유닛 옆에 위치한.socket유닛이 되었습니다. ulimit은MemoryMax=,CPUQuota=,TasksMax=이 되었습니다.- init 스크립트의
su - appuser -c줄은User=,NoNewPrivileges=yes,ProtectSystem=strict이 되었으므로, 권한이 없는 사용자로 서비스 실행하기는 추가 작업이 아닌 유닛의 기본 형태가 되었습니다.
[Unit]
Description=Example API
Wants=network-online.target
After=network-online.target postgresql.service
Requires=postgresql.service
[Service]
Type=notify
ExecStart=/usr/local/bin/exampled
Restart=on-failure
RestartSec=2
MemoryMax=512M
CPUQuota=50%
TasksMax=128
User=exampled
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
StateDirectory=exampled
[Install]
WantedBy=multi-user.target해당 파일의 한 줄은 누구나 한 번쯤 저지르는 실수입니다. Requires=postgresql.service은 순서가 아닌 필수 요건입니다. 이는 Postgres가 실패하면 유닛도 실패한다는 의미이지, Postgres를 먼저 시작하라는 의미는 아닙니다. After=postgresql.service가 없으면 두 서비스가 동시에 시작되며, 서비스가 연결을 시도할 때 포트가 아직 열리지 않은 상태일 수 있습니다. 두 설정은 의도적으로 분리되어 있는데, 때로는 하나만 필요하고 다른 하나는 필요하지 않을 수 있기 때문입니다. ProtectSystem=strict은 해당 서비스를 위해 파일 시스템을 읽기 전용으로 마운트하며, 이것이 StateDirectory=이 존재하는 이유입니다. 이는 서비스에 /var/lib 하위의 쓰기 가능한 경로 하나를 제공합니다.
2026년 서버에서 2005년의 방식을 확인하기 가장 좋은 예는 Ubuntu 24.04의 SSH이며, 이는 2026년 8월 기준으로 systemd 255를 탑재하고 있습니다. ssh.service은 기본적으로 소켓 활성화 방식입니다. ssh.socket가 수신 소켓을 유지하며, 연결이 들어오면 sshd가 시작됩니다. 따라서 /etc/ssh/sshd_config의 Port 2222는 아무런 효과가 없습니다. sshd가 포트를 연 프로세스가 아니기 때문입니다. 변경 사항은 소켓 유닛에 적용해야 합니다.
sudo systemctl edit ssh.socket[Socket]
ListenStream=
ListenStream=2222비어 있는 ListenStream=은 패키지 유닛에서 상속된 값을 지웁니다. 이를 생략하면 systemd가 값을 대체하는 대신 목록에 추가하므로 두 포트가 모두 사용됩니다. 그런 다음 적용하고 확인하십시오. 이때 SSH 세션을 하나 더 열어두는 것이 좋습니다.
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo ss -lntp | grep 2222ss은 sshd이 아닌 systemd가 소유한 포트 2222의 소켓 하나를 나열해야 합니다. 이것이 20년이 지난 지금 여러분의 VPS에서 구현된 launchd 설계입니다. 이전 방식을 선호한다면 sudo systemctl disable --now ssh.socket을 실행한 후 sudo systemctl enable --now ssh.service를 수행하십시오. 그러면 자체 설정에서 Port을 다시 읽어오는 상시 실행 sshd를 사용할 수 있습니다.
이러한 세부 사항 중 무엇을 마주하게 될지는 실행 중인 릴리스에 따라 다르므로, 업그레이드를 계획하기 전에 LTS 릴리스와 중간 Ubuntu 릴리스의 차이점을 알아두는 것이 좋습니다. 여러 대의 기기를 관리할 때 유닛 파일이 어디서나 동일하다는 사실은 한 곳에서 여러 서버 관리하기가 이제 셸 스크립팅 문제가 아닌 설정 문제인 이유입니다. 직접 유닛을 작성할 때 서비스와 타이머 쌍은 2009년에 init 스크립트와 cron 줄로 나누어 처리하던 작업을 대신 수행합니다.
FAQ
리눅스 배포판들은 왜 SysV init을 systemd로 교체했습니까?
두 가지 공학적 이유와 한 가지 유지보수적 이유 때문입니다. SysV init은 서비스 순서를 파일 이름 순서로 결정했는데, 이는 의존성이 아닌 위치에 기반한 방식이었습니다. 또한 부모 프로세스에서 분기(fork)된 데몬을 추적하지 못해, 오래된 PID 파일이 엉뚱한 프로세스를 종료하는 문제가 발생했습니다. systemd는 소켓 활성화와 의존성 지시어를 통해 순서 문제를 해결했고, 컨트롤 그룹(cgroup)을 통해 프로세스 추적 문제를 해결했습니다. 유지보수 측면에서는 속도가 결정적이었습니다. 하나의 유닛 파일이 모든 배포판에서 작동하므로, 업스트림 프로젝트는 .service 파일을 제공하고 배포판 관리자들은 패키지마다 셸 스크립트를 작성하는 일을 멈췄습니다. Fedora 15가 2011년 5월에 전환했고, Ubuntu 15.04가 2015년 4월에 마지막으로 합류했습니다.
systemd는 하나의 거대한 바이너리입니까?
아닙니다. 소스 트리에서 여러 개의 개별 프로그램을 빌드합니다. PID 1은 /usr/lib/systemd/systemd이며, journald, logind, udevd는 각각 고유한 바이너리를 가진 별도의 프로세스입니다. 직접 확인하려면 ls /usr/lib/systemd/을 실행해 보십시오. 여전히 존재하는 비판은 바이너리 크기가 아니라 릴리스 결합에 관한 것입니다. 이 프로그램들은 함께 배포되고 내부 인터페이스를 공유하므로, 배포판들은 이를 하나의 세트로 취급하는 경향이 있으며 GNOME 같은 소프트웨어는 logind를 특정하여 요구하게 되었습니다.
systemd 없이 리눅스를 실행할 수 있습니까?
네, 가능합니다. Devuan은 sysvinit을 제공하고, Gentoo는 기본값으로 OpenRC를 사용하며, Void는 runit을, Alpine은 OpenRC와 함께 busybox init을 사용하고, Slackware는 BSD 스타일 스크립트를 유지합니다. 그 대가는 호환성 작업입니다. logind를 요구하는 데스크톱 소프트웨어는 elogind이 필요한데, 이는 독립형 패키지로 유지 관리되는 systemd의 logind입니다. 또한 점점 더 많은 서버 소프트웨어가 .service 파일만 제공하므로, 시작 스크립트를 직접 작성하고 유지 관리해야 합니다.
저널(journal)은 왜 일반 텍스트 파일이 아닌 바이너리입니까?
journald는 인덱스가 포함된 구조화된 필드를 저장하기 때문입니다. 이를 통해 유닛별 필터링, 우선순위 필터링, 그리고 전송 프로그램이 위조할 수 없는 메타데이터를 제공합니다. journald는 로그 라인을 신뢰하는 대신 유닛, cgroup, 실제 UID를 직접 기록합니다. 그 대가로 내용을 읽으려면 journalctl가 필요하며, 복구 시스템에서 읽을 때는 journalctl --directory /mnt/var/log/journal을 사용하여 마운트된 디스크를 지정해야 합니다. 텍스트 파일도 원한다면 /etc/systemd/journald.conf에서 ForwardToSyslog=yes을 설정하십시오.
/etc/init.d 스크립트를 편집하는 방식은 무엇으로 대체되었습니까?
드롭인(drop-in) 파일입니다. /usr/lib/systemd/system/에 있는 유닛 파일을 직접 편집하지 마십시오. 패키지 업그레이드 시 덮어쓰이기 때문입니다. sudo systemctl edit nginx.service를 실행하면 systemd가 /etc/systemd/system/nginx.service.d/override.conf를 생성하며, 이는 패키지된 유닛 위에 병합됩니다. systemctl cat nginx.service은 병합된 결과를 보여주며, systemd-delta은 시스템의 모든 오버라이드 목록을 보여줍니다. 수동으로 편집한 후에는 반드시 sudo systemctl daemon-reload을 실행하십시오. 그렇지 않으면 다음 명령 실행 시 Warning: The unit file, source configuration file or drop-ins of nginx.service changed on disk.라는 경고가 출력됩니다.