전통적 부팅에서는 PID 1번 init이 /etc/inittab을 읽어 시스템을 어느 런레벨(runlevel)로 띄울지 결정했다.
런레벨 = "시스템이 제공하는 서비스의 묶음 상태"를 숫자 0~6으로 나눈 것.
런레벨
의미
설명
0
Halt(종료)
시스템 전원 끔
1 (S, single)
단일 사용자 모드
복구·점검용, root만, 네트워크 없음
2
다중 사용자(NFS 없음)
네트워크 일부
3
다중 사용자 + 네트워크
텍스트(CLI) 환경 — 서버 표준
4
미사용(사용자 정의)
예약됨
5
다중 사용자 + 네트워크 + GUI
그래픽(X11) 환경
6
Reboot(재부팅)
시스템 재시작
runlevel # 현재 런레벨 확인 (예: N 3 → 이전 N, 현재 3)who -r # 런레벨과 전환 시각 확인init 3 # 런레벨 3(텍스트)으로 전환init 0 # 시스템 종료 (init 6 = 재부팅)telinit 5 # init과 동일하게 런레벨 전환
⚠️ 함정: 런레벨 0과 6을 부팅 기본값으로 지정하면 절대 안 된다. 0(종료)이나 6(재부팅)을 기본 런레벨로 두면 시스템이 켜지자마자 꺼지거나 무한 재부팅에 빠진다. 기본값은 보통 텍스트 서버는 3, GUI 데스크톱은 5다. 그리고 1번 = 단일 사용자(복구) 모드라는 점도 알아 둔다.
런레벨 3과 5의 차이는 딱 하나, GUI(X 윈도)의 유무다. 3은 텍스트 콘솔, 5는 그래픽 로그인 화면까지 띄운다. 서버는 자원을 아끼려 3을, 데스크톱은 5를 기본으로 쓴다. 런레벨별 스크립트는 /etc/rc.d/rc{0~6}.d/에 S(시작)·K(종료) 접두사로 들어 있었다.
systemd는 init의 한계(순차적 느린 부팅, 의존성 관리 부족)를 극복하려 등장했다.
특징: 서비스를 병렬로 띄우고, 의존성을 명시적으로 관리하며, 모든 것을 unit이라는 단위로 통일했다.
채택 배포판: RHEL 7+, CentOS 7+, Ubuntu 16.04+, Fedora 등 현대 배포판 전부.
systemd는 런레벨 대신 target을 쓴다. 옛 런레벨과의 대응을 외워야 한다.
런레벨
systemd target
의미
0
poweroff.target
종료
1
rescue.target
단일 사용자(복구)
2
multi-user.target
다중 사용자(텍스트)
3
multi-user.target
다중 사용자 + 네트워크(텍스트)
4
multi-user.target
(사용자 정의)
5
graphical.target
그래픽(GUI)
6
reboot.target
재부팅
systemctl get-default # 현재 기본 target 확인systemctl set-default multi-user.target # 기본을 텍스트 모드로systemctl set-default graphical.target # 기본을 GUI 모드로systemctl isolate multi-user.target # 즉시 텍스트 target으로 전환(init 3 격)systemctl rescue # 단일 사용자(복구) 모드로systemctl reboot # 재부팅systemctl poweroff # 종료
런레벨 2·3·4가 모두 multi-user.target 하나로 합쳐진 점이 핵심이다(텍스트 다중 사용자). GUI는 graphical.target(옛 5). 따라서 "GUI에서 텍스트로 바꿔라"는 systemctl set-default multi-user.target이고, "즉시 전환"은 systemctl isolate(옛 init N 역할)다. set-default는 다음 부팅부터, isolate는 지금 당장이다.
systemd는 2010년 레드햇의 Lennart Poettering이 발표했다. init의 셸 스크립트 기반 순차 부팅은 서비스가 늘수록 느려졌는데, systemd는 소켓·D-Bus를 미리 만들어 두고 서비스를 동시에 띄워 부팅 시간을 크게 줄였다. 도입 당시 "유닉스 철학을 깬다"는 논쟁이 컸지만, 현재는 사실상 표준이 되었다.
systemd 환경에서 서비스(데몬)를 다루는 단일 명령이 systemctl이다. 옛 service·chkconfig를 모두 대체한다.
명령
동작
옛 SysV 대응
systemctl start 서비스
지금 즉시 시작
service X start
systemctl stop 서비스
지금 즉시 중지
service X stop
systemctl restart 서비스
재시작
service X restart
systemctl reload 서비스
설정만 다시 읽기(무중단)
service X reload
systemctl status 서비스
상태·최근 로그 확인
service X status
systemctl enable 서비스
부팅 시 자동 시작 등록
chkconfig X on
systemctl disable 서비스
부팅 시 자동 시작 해제
chkconfig X off
systemctl is-enabled 서비스
자동 시작 여부 확인
—
systemctl list-units
활성 unit 목록
—
systemctl start sshd # SSH 데몬 즉시 시작systemctl enable sshd # 부팅 때마다 자동 시작 등록systemctl enable --now sshd # 등록 + 즉시 시작 한 번에systemctl status sshd # 상태(active/inactive)와 로그 일부systemctl is-active sshd # 동작 중이면 active 출력
⚠️ 함정: start와 enable은 다르다.start는 "지금 한 번 켠다"(재부팅하면 다시 꺼질 수 있음), enable은 "부팅할 때마다 자동으로 켠다"(지금 당장 켜지는 건 아님)이다. 둘 다 하려면 systemctl enable --now를 쓴다. 시험에서 이 둘을 바꿔 묻는 함정이 있다.
직접 쳐보기: systemctl status sshd(또는 crond)를 실행해 Active: 줄과 Loaded: 줄을 본다. Active: active (running)이면 실행 중, 괄호 안 enabled/disabled가 부팅 자동 시작 여부다. 실행 여부(active)와 자동 시작 여부(enabled)가 별개 정보임을 한 화면에서 확인할 수 있다.
패키지가 제공하는 원본 → /usr/lib/systemd/system/(또는 /lib/systemd/system/)
관리자가 수정·추가한 것 → /etc/systemd/system/
/etc의 것이 /usr/lib의 것보다 우선한다.
서비스 unit 파일의 기본 구조는 세 섹션이다.
[Unit]Description=My Web ServiceAfter=network.target # network.target 이후에 시작[Service]ExecStart=/usr/bin/myapp # 시작 명령Restart=on-failure # 실패 시 자동 재시작User=myuser[Install]WantedBy=multi-user.target # enable 시 어느 target에 연결할지
unit 파일을 새로 만들거나 고친 뒤에는 systemd가 변경을 인식하도록 다시 읽혀야 한다.
systemctl daemon-reload # unit 파일 변경 후 반드시 실행systemctl start myapp # 새 서비스 시작systemctl enable myapp # WantedBy의 target에 자동 시작 등록
[Install]의 WantedBy=multi-user.target이 enable 시 동작의 핵심이다. enable하면 이 target의 .wants/ 디렉터리에 심볼릭 링크가 생겨, 부팅 때 해당 target에 도달하면 자동 시작된다. [Unit]의 After=/Before=는 시작 순서를, Requires=/Wants=는 의존성을 정의한다.
unit 파일을 수정한 뒤 systemctl daemon-reload를 빼먹으면 변경이 반영되지 않는다. systemd는 unit 파일을 메모리에 캐시하기 때문에, 디스크의 파일을 고쳐도 daemon-reload 전까지는 옛 내용으로 동작한다. "unit 수정 → daemon-reload → restart" 순서를 한 세트로 외운다.
systemd는 모든 서비스의 로그를 journal이라는 바이너리 형식으로 모아 관리한다.
이것을 조회하는 도구가 journalctl이며, 옛날의 /var/log/messages 텍스트 로그를 보완(또는 대체)한다.
journalctl # 전체 로그(오래된 것부터)journalctl -u sshd # sshd 서비스의 로그만journalctl -f # 실시간 로그 추적(tail -f 같음)journalctl -b # 이번 부팅 이후 로그만journalctl --since "1 hour ago" # 최근 1시간 로그journalctl -p err # 에러(우선순위 err 이상)만journalctl -k # 커널 메시지(dmesg 격)
옵션
의미
-u 서비스
특정 unit의 로그
-f
실시간 추적(follow)
-b
현재 부팅 세션 로그
-p
우선순위 필터(emerg~debug)
-r
최신순 역정렬
--since/--until
시간 범위 지정
journalctl -u 서비스 -f는 특정 서비스의 로그를 실시간으로 보는 가장 유용한 조합이다. journal은 기본적으로 메모리/임시 저장이라 재부팅하면 사라질 수 있는데, /etc/systemd/journald.conf에서 Storage=persistent로 바꾸면 디스크(/var/log/journal/)에 영구 보존된다.
직접 쳐보기: journalctl -b -p err로 이번 부팅에서 발생한 에러만 추려 본다. 시스템에 문제가 있었다면 빨갛게 표시된다. 이어 journalctl -u sshd --since "today"로 오늘 SSH 관련 로그만 좁혀 보면, 필터 조합의 위력을 체감할 수 있다.