
서비스 · 프로세스 관리 27 / 50 · Part 3. systemd와 서비스
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
SysV init 시절에는 서버가 몇 번 runlevel로 부팅되는지가 중요했다. 3이면 텍스트 서버, 5면 그래픽 데스크톱, 1이면 단일 사용자 복구 모드. systemd에서는 이 개념이 target 으로 바뀌었다. target은 "이 상태에 도달하려면 무엇이 켜져 있어야 하는가"를 정의한 unit 묶음이다.
이번 글에서는 부팅이 어떤 target 계층을 거쳐 진행되는지, 기본 target을 어떻게 확인·변경하는지, 그리고 장애 복구에 쓰는 rescue·emergency 모드 가 무엇이 다르고 보안상 왜 중요한지 정리한다.
| 특징 | 내용 |
|---|---|
| 프로세스 없음 | target 자체는 아무것도 실행하지 않는다. 의존성만 가진다 |
| 동기화 지점 | "network-online.target 이후", "multi-user.target에 포함" 처럼 기준점 역할 |
| 활성화 방식 | 서비스의 [Install] WantedBy=multi-user.target → enable 시 target의 *.wants/에 연결 |
| 기본 target | default.target 심볼릭 링크가 가리키는 target까지 부팅 |
| target | 용도 |
|---|---|
sysinit.target | 초기 시스템 준비 (마운트, journald, udev) |
basic.target | 소켓·타이머·경로 unit 준비. 일반 서비스는 이후에 시작 |
multi-user.target | 서버의 표준 목표 — 네트워크, 로그인, 일반 서비스 |
graphical.target | multi-user + 그래픽 로그인 |
network-online.target | 네트워크가 실제로 구성됨 |
rescue.target | 단일 사용자 복구 모드 |
emergency.target | 최소 긴급 모드 |
| 명령 | 용도 |
|---|---|
systemctl get-default | 기본 target 확인 |
systemctl set-default multi-user.target | 기본 target 변경 (링크 변경) |
systemctl isolate rescue.target | 지금 해당 target으로 전환 (나머지 unit 중지) |
systemctl list-dependencies TARGET | target이 끌어오는 unit |
커널 인자 systemd.unit=rescue.target | 부팅 시 특정 target으로 진입 |

커널 → systemd (PID 1)
→ default.target 이 가리키는 target 확인 (예: multi-user.target)
→ 그 target 의 Requires/Wants 를 재귀적으로 모아 트랜잭션 구성
→ After/Before 순서에 맞춰 병렬 시작
sysinit.target → basic.target → multi-user.target (sshd, crond, ...)
→ 목표 target 도달 = 부팅 완료
isolate는 지정한 target과 그 의존성에 포함되지 않는 unit을 모두 중지 한다. 원격 SSH 세션에서 systemctl isolate rescue.target을 실행하면 sshd가 중지되어 접속이 끊기므로, 원격에서는 사용하지 않는다.
# 1) 기본 target 과 runlevel 대응
systemctl get-default
ls -l /etc/systemd/system/default.target 2>/dev/null || readlink -f /usr/lib/systemd/system/default.target
ls -l /usr/lib/systemd/system/runlevel*.target | awk '{print $9, $10, $11}'
systemctl list-units --type=target --no-pager --no-legend | head -12
# 2) multi-user.target 이 끌어오는 것 / 기본 target 변경
systemctl list-dependencies multi-user.target --no-pager | head -14
systemctl show multi-user.target -p Requires,Wants,After --no-pager
systemctl set-default graphical.target; systemctl get-default
systemctl set-default multi-user.target
# 3) (Ubuntu) rescue.target 정의와 isolate 허용 여부
systemctl get-default
systemctl cat rescue.target --no-pager | grep -E '^(Description|Requires|Wants|Conflicts|After|AllowIsolate)'
systemctl show emergency.target rescue.target multi-user.target -p Id,AllowIsolate
실습 서버에서도
isolate rescue.target은 실행하지 않았다. 실행하면 SSH 세션이 끊기고 콘솔로만 복구할 수 있다.



텍스트 원본(실제 출력):
[root@rocky9-lab ~]# systemctl get-default
graphical.target
[root@rocky9-lab ~]# ls -l /etc/systemd/system/default.target 2>/dev/null || readlink -f /usr/lib/systemd/system/default.target
/usr/lib/systemd/system/graphical.target
[root@rocky9-lab ~]# ls -l /usr/lib/systemd/system/runlevel*.target | awk '{print $9, $10, $11}'
/usr/lib/systemd/system/runlevel0.target -> poweroff.target
/usr/lib/systemd/system/runlevel1.target -> rescue.target
/usr/lib/systemd/system/runlevel2.target -> multi-user.target
/usr/lib/systemd/system/runlevel3.target -> multi-user.target
/usr/lib/systemd/system/runlevel4.target -> multi-user.target
/usr/lib/systemd/system/runlevel5.target -> graphical.target
/usr/lib/systemd/system/runlevel6.target -> reboot.target
[root@rocky9-lab ~]# systemctl list-units --type=target --no-pager --no-legend | head -12
basic.target loaded active active Basic System
getty.target loaded active active Login Prompts
graphical.target loaded active active Graphical Interface
local-fs.target loaded active active Local File Systems
multi-user.target loaded active active Multi-User System
network-online.target loaded active active Network is Online
network-pre.target loaded active active Preparation for Network
paths.target loaded active active Path Units
remote-fs.target loaded active active Remote File Systems
slices.target loaded active active Slice Units
sockets.target loaded active active Socket Units
sshd-keygen.target loaded active active sshd-keygen.target
[root@rocky9-lab ~]# systemctl list-dependencies multi-user.target --no-pager | head -14
multi-user.target
● ├─atd.service
● ├─crond.service
● ├─labapp.service
● ├─sshd.service
● ├─systemd-ask-password-wall.path
● ├─systemd-logind.service
○ ├─systemd-update-utmp-runlevel.service
● ├─systemd-user-sessions.service
● ├─basic.target
● │ ├─paths.target
● │ ├─slices.target
● │ │ ├─-.slice
● │ │ └─system.slice
[root@rocky9-lab ~]# systemctl show multi-user.target -p Requires,Wants,After --no-pager | cut -c1-110
Requires=basic.target
Wants=labapp.service systemd-update-utmp-runlevel.service systemd-logind.service atd.service crond.service rem
After=rescue.target crond.service rescue.service labapp.service systemd-logind.service sshd.service systemd-us
[root@rocky9-lab ~]# systemctl set-default graphical.target; systemctl get-default; systemctl set-default multi-user.target
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/graphical.target.
graphical.target
Removed "/etc/systemd/system/default.target".
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
root@ubuntu-lab:~# systemctl get-default
graphical.target
root@ubuntu-lab:~# systemctl cat rescue.target --no-pager | grep -E '^(Description|Requires|Wants|Conflicts|After|AllowIsolate)'
Description=Rescue Mode
Requires=sysinit.target rescue.service
After=sysinit.target rescue.service
AllowIsolate=yes
root@ubuntu-lab:~# systemctl show emergency.target rescue.target multi-user.target -p Id,AllowIsolate
Id=emergency.target
AllowIsolate=yes
Id=rescue.target
AllowIsolate=yes
Id=multi-user.target
AllowIsolate=yes
| 관찰 | 의미 |
|---|---|
Rocky get-default → graphical.target | 컨테이너 이미지의 기본값. 그래픽 패키지가 없어도 graphical.target은 multi-user를 포함하므로 부팅은 된다 |
/etc/systemd/system/default.target 없음 → /usr/lib/.../graphical.target | 관리자가 바꾸지 않으면 패키지 기본 링크를 따른다 |
| runlevel0~6 → poweroff/rescue/multi-user/graphical/reboot | 옛 runlevel 이름은 target의 별칭 으로 남아 있다. runlevel 2·3·4는 모두 multi-user다 |
| 활성 target: basic, getty, graphical, multi-user, network-online, sockets ... | 부팅이 거쳐 온 동기화 지점들 |
| multi-user.target 의존성: atd, crond, labapp, sshd ... | 25편에서 enable한 labapp이 multi-user.target에 연결되어 있다 |
Wants=labapp.service ... atd.service crond.service | enable 링크가 target의 Wants로 계산되었다 |
set-default → default.target 링크 생성/삭제 | 기본 target 변경도 결국 심볼릭 링크 조작 이다 |
Ubuntu rescue.target: Requires=sysinit.target rescue.service | rescue는 sysinit까지만 올리고 root 셸(rescue.service)을 띄운다. 네트워크와 일반 서비스는 없다 |
AllowIsolate=yes | isolate 대상이 될 수 있는 target만 이 값이 yes다 |
| 주제 | 내용 |
|---|---|
| 콘솔 root 셸 | 부팅 메뉴에서 커널 인자에 systemd.unit=emergency.target(또는 rd.break, init=/bin/bash)을 넣으면 root 셸을 얻을 수 있다. GRUB 비밀번호, 콘솔·가상화 콘솔 접근 통제, 디스크 암호화가 방어책이다 |
| rescue 인증 | Rocky/RHEL은 rescue·emergency 진입 시 root 비밀번호를 요구한다(sulogin). root 계정이 잠겨 있으면 진입 불가 → 복구 정책과 함께 설계 |
| 불필요한 target | 서버의 기본 target이 graphical이면 디스플레이 매니저 등 불필요한 서비스가 뜰 수 있다. 서버는 multi-user.target |
| 지속성 연결점 | 악성 unit은 WantedBy=multi-user.target 또는 default.target에 연결된다. multi-user.target.wants/는 점검 1순위 디렉터리다 |
[점검] systemctl get-default ← 서버 정책과 일치하는가
ls -l /etc/systemd/system/multi-user.target.wants/ ← 부팅 시 시작 목록
ls -l /etc/systemd/system/default.target.wants/ 2>/dev/null
grep -r 'systemd.unit\|init=' /proc/cmdline ← 비정상 부팅 인자
↓
[이벤트] 재부팅 기록 확인
journalctl --list-boots | tail -3
last -x reboot | head -3
↓
[판단] 예정되지 않은 재부팅 + rescue/emergency 진입 흔적 → 콘솔 접근 사고 조사
| 흔적 | 확인 위치 |
|---|---|
| 부팅 인자 | /proc/cmdline, 이전 부팅은 journalctl -b -1 -k \| grep 'Command line' |
| rescue 진입 | journalctl -b -1 -u rescue.service -u emergency.service |
| 재부팅 이력 | journalctl --list-boots, last -x |
| 실수 | 결과 | 예방 |
|---|---|---|
원격에서 isolate rescue.target | SSH 끊김, 콘솔 복구 필요 | 원격에서는 사용 금지 |
/etc/inittab으로 runlevel 변경 시도 | systemd는 무시 | systemctl set-default |
| 서비스의 WantedBy를 graphical.target으로 | 서버(multi-user)에서 자동 시작 안 됨 | 서버 서비스는 multi-user.target |
After=network.target을 "네트워크 준비 완료"로 오해 | 준비 전 시작 | network-online.target (26편) |
[ ] get-default 로 기본 target 을 확인했다
[ ] runlevel*.target 이 target 별칭인 것을 확인했다
[ ] multi-user.target 의 의존성에 enable 한 서비스가 포함된 것을 확인했다
[ ] set-default 가 default.target 링크를 바꾸는 것을 확인했다
[ ] rescue.target 과 emergency.target 의 차이를 설명할 수 있다
[ ] 부팅 인자로 root 셸을 얻는 위험과 방어책을 설명할 수 있다
multi-user.target이다.다음 글 「28. drop-in으로 서비스 설정 변경 (systemctl edit)」 에서는 패키지 원본 unit을 건드리지 않고 설정을 바꾸는 drop-in(override.conf) 을 다룬다. 환경변수·nice·자원 한도를 바꾸고, ExecStart=를 바꿀 때 반드시 먼저 비워야 하는 규칙을 실패 로그로 확인한다.