5. systemd — 서비스와 부팅
start는 지금 켜는 것이고enable은 다음 부팅에 켜는 것이다 — 이 둘을 헷갈리면 “재부팅했더니 서비스가 안 떠 있어요”가 된다
이 장에서 처음 나오는 말5개
유닛unit- systemd가 관리하는 대상 하나. 확장자로 종류가 갈린다 —
.service(데몬) ·.timer(예약 실행) ·.socket(소켓) ·.mount(마운트) ·.target(묶음). 타깃target- 유닛을 묶은 그룹이자 부팅 단계. 옛 런레벨의 후계다 —
multi-user.target이 일반적인 서버 부팅 완료 상태,graphical.target이 데스크톱이다. drop-in- 원본 유닛 파일을 건드리지 않고 일부 설정만 덮어쓰는 조각 파일.
/etc/systemd/system/<유닛>.d/override.conf에 놓는다 — 패키지 업데이트에도 살아남는 유일한 방법이다. mask- 유닛을
/dev/null로 연결해 어떤 방법으로도 못 뜨게 막는 것.disable보다 강하다 — 다른 서비스가 의존성으로 끌어올리는 것까지 막는다. cgroupcontrol group- 프로세스 묶음에 CPU·메모리 한도를 거는 커널 기능. systemd는 서비스마다 cgroup을 하나씩 만든다 —
systemctl status에 자식 프로세스가 다 보이는 이유다.
매일 쓰는 여섯 개
섹션 제목: “매일 쓰는 여섯 개”systemctl status nginx # 상태 + 최근 로그 10줄 ← 가장 많이 친다sudo systemctl start nginx # 지금 켠다sudo systemctl stop nginxsudo systemctl restart nginx # 껐다 켠다 (연결이 끊긴다)sudo systemctl reload nginx # 설정만 다시 읽는다 (무중단 — 지원하는 서비스만)sudo systemctl enable --now nginx # 부팅 시 자동 시작 + 지금도 켠다reload-or-restart는 reload를 지원하면 reload를, 아니면 restart를 한다 — 스크립트에 좋다.
명령·옵션만 빨리 찾으려면 명령 기준으로 정리한
systemctl · journalctl 명령 참조를 쓴다.
status 읽는 법
섹션 제목: “status 읽는 법”● nginx.service - A high performance web server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled) Active: active (running) since Mon 2026-08-10 09:12:31 KST; 2h 51min ago Main PID: 1123 (nginx) Tasks: 5 (limit: 18901) Memory: 12.4M CPU: 3.201s CGroup: /system.slice/nginx.service ├─1123 "nginx: master process" └─1124 "nginx: worker process"| 줄 | 봐야 할 것 |
|---|---|
Loaded | 유닛 파일 경로 + enabled/disabled(자동 시작 여부) |
Active | active (running) · inactive (dead) · failed · activating |
Main PID | 4장의 진단으로 이어가는 열쇠 |
CGroup | 자식 프로세스까지 전부 보인다 — ps로 헤맬 필요가 없다 |
상태만 조용히 확인하려면 systemctl is-active nginx · is-enabled nginx
(종료 코드로 판정된다 — 1장).
start · enable · mask
섹션 제목: “start · enable · mask”| 지금 | 다음 부팅 | |
|---|---|---|
start | 켠다 | 영향 없음 |
enable | 영향 없음 | 켜진다 |
enable --now | 켠다 | 켜진다 |
disable --now | 끈다 | 안 켜진다 |
mask | — | 아예 못 뜬다 (의존성으로도) |
문제 있는 유닛 찾기
섹션 제목: “문제 있는 유닛 찾기”systemctl --failed # 실패한 유닛만 ← 서버 접속 직후 습관systemctl list-units --type=service # 지금 로드된 서비스systemctl list-unit-files --state=enabled # 부팅 시 켜지도록 돼 있는 것 전부systemctl list-dependencies nginx # 무엇에 의존하나systemctl cat nginx # 실제 적용 중인 유닛 파일 전문 + drop-insystemctl show nginx -p Restart -p ExecStart # 최종 계산된 설정값systemctl cat은 원본 + 모든 drop-in을 합쳐서 보여준다 —
“설정을 고쳤는데 왜 안 먹지”의 답이 여기서 나온다.
유닛 파일 — 어디에 있고 어떻게 덮나
섹션 제목: “유닛 파일 — 어디에 있고 어떻게 덮나”디렉터리/usr/lib/systemd/system/ 패키지가 설치한 원본. 여기를 직접 고치면 업데이트 때 날아간다
- …
디렉터리/etc/systemd/system/ 관리자용. 여기 것이 우선한다
디렉터리nginx.service.d/
- override.conf drop-in — 권장 방식
디렉터리/run/systemd/system/ 런타임 생성물 (재부팅하면 사라진다)
- …
디렉터리~/.config/systemd/user/ 사용자 단위 유닛 (
systemctl --user)- …
설정을 안전하게 덮는 법
섹션 제목: “설정을 안전하게 덮는 법”-
drop-in 편집기를 연다 — 파일 경로와 디렉터리를 알아서 만들어 준다
터미널 창 sudo systemctl edit nginx # 일부만 덮기 (권장)sudo systemctl edit --full nginx # 유닛 전체를 /etc 아래로 복사해 편집 -
덮을 항목만 섹션째로 적는다
[Service]Environment="HTTP_PROXY=http://proxy.corp.local:3128"Environment="NO_PROXY=localhost,127.0.0.1,.corp.local"Restart=alwaysRestartSec=5MemoryMax=2G -
유닛 파일을 바꿨으면 반드시 다시 읽힌다
터미널 창 sudo systemctl daemon-reloadsudo systemctl restart nginxsystemctl cat nginx # 합쳐진 결과 확인
최소한의 서비스 유닛
섹션 제목: “최소한의 서비스 유닛”/etc/systemd/system/myapp.service
[Unit]Description=My applicationAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=myappWorkingDirectory=/opt/myappExecStart=/opt/myapp/bin/serverRestart=on-failureRestartSec=5
[Install]WantedBy=multi-user.target[Install]의 WantedBy가 enable이 무엇을 하는지 정의한다 —
이 줄이 없으면 systemctl enable이 “unit is not designed to be enabled”로 거부한다.
로그는 따로 설정할 필요 없이 stdout/stderr가 그대로 journal에 들어간다(6장).
cron 대신 타이머 권장
섹션 제목: “cron 대신 타이머 ”systemctl list-timers --all # 다음 실행 시각 · 마지막 실행 · 담당 유닛systemctl status logrotate.timerjournalctl -u logrotate.service # 실행 결과가 로그로 남는다| cron | systemd timer | |
|---|---|---|
| 로그 | 기본적으로 메일//var/log/syslog로 흩어진다 | journalctl -u로 한곳에 |
| 꺼져 있던 동안의 실행 | 그냥 건너뛴다 | Persistent=true면 부팅 후 따라잡는다 |
| 자원 제한·의존성 | 없다 | 유닛의 모든 기능을 쓴다 |
| 간편함 | crontab -e 한 줄 | 파일 두 개(.service + .timer) |
간단한 주기 작업은 cron으로 충분하다 — 다만 cron 작업이 조용히 실패하는 사고가
잦아서, 결과를 봐야 하는 작업은 타이머 쪽이 낫다.
기존 cron은 crontab -l(내 것) · sudo crontab -l -u root · /etc/cron.d/ · /etc/cron.daily/에 흩어져 있다.
부팅이 왜 느린가
섹션 제목: “부팅이 왜 느린가”systemd-analyze # 전체 부팅 시간 (펌웨어/커널/유저스페이스)systemd-analyze blame | head -15 # 오래 걸린 유닛 순위systemd-analyze critical-chain # 실제로 부팅을 지연시킨 사슬 ← blame보다 정확하다journalctl -b -p err # 이번 부팅의 오류만blame은 “오래 걸린 것”을 보여줄 뿐 병렬로 돌아 부팅을 안 늦춘 것도 포함한다.
“무엇이 부팅을 붙잡았나”는 critical-chain이 답한다.
재부팅과 전원
섹션 제목: “재부팅과 전원”sudo systemctl rebootsudo systemctl poweroffsudo shutdown -r +5 "패치 적용 재부팅" # 5분 뒤 (로그인 사용자에게 공지된다)sudo shutdown -c # 예약 취소ls /var/run/reboot-required # 있으면 재부팅이 필요한 상태 (12장)5장 요약
섹션 제목: “5장 요약”start(지금) ·enable(다음 부팅) ·enable --now(둘 다) ·mask(완전 차단)systemctl status의 CGroup 트리에 자식 프로세스가 전부 보인다- 접속하면
systemctl --failed한 번 — 조용히 죽은 유닛이 여기 있다 - 설정은
systemctl edit(drop-in) 로. 원본 수정은 업데이트 때 날아간다 - 유닛 파일을 바꾸면
daemon-reload, 결과 확인은systemctl cat - 결과를 봐야 하는 주기 작업은 cron보다 타이머(로그가 journal에 남는다)
- 부팅 지연은
blame이 아니라critical-chain으로 본다