콘텐츠로 이동
Study Note서버 관리 일반

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 nginx
sudo systemctl restart nginx # 껐다 켠다 (연결이 끊긴다)
sudo systemctl reload nginx # 설정만 다시 읽는다 (무중단 — 지원하는 서비스만)
sudo systemctl enable --now nginx # 부팅 시 자동 시작 + 지금도 켠다

reload-or-restart는 reload를 지원하면 reload를, 아니면 restart를 한다 — 스크립트에 좋다. 명령·옵션만 빨리 찾으려면 명령 기준으로 정리한 systemctl · journalctl 명령 참조를 쓴다.

● 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(자동 시작 여부)
Activeactive (running) · inactive (dead) · failed · activating
Main PID4장의 진단으로 이어가는 열쇠
CGroup자식 프로세스까지 전부 보인다 — ps로 헤맬 필요가 없다

상태만 조용히 확인하려면 systemctl is-active nginx · is-enabled nginx (종료 코드로 판정된다 — 1장).

지금다음 부팅
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-in
systemctl 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)
    • …
  1. drop-in 편집기를 연다 — 파일 경로와 디렉터리를 알아서 만들어 준다

    터미널 창
    sudo systemctl edit nginx # 일부만 덮기 (권장)
    sudo systemctl edit --full nginx # 유닛 전체를 /etc 아래로 복사해 편집
  2. 덮을 항목만 섹션째로 적는다

    [Service]
    Environment="HTTP_PROXY=http://proxy.corp.local:3128"
    Environment="NO_PROXY=localhost,127.0.0.1,.corp.local"
    Restart=always
    RestartSec=5
    MemoryMax=2G
  3. 유닛 파일을 바꿨으면 반드시 다시 읽힌다

    터미널 창
    sudo systemctl daemon-reload
    sudo systemctl restart nginx
    systemctl cat nginx # 합쳐진 결과 확인

/etc/systemd/system/myapp.service

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

[Install]의 WantedBy가 enable이 무엇을 하는지 정의한다 — 이 줄이 없으면 systemctl enable이 “unit is not designed to be enabled”로 거부한다. 로그는 따로 설정할 필요 없이 stdout/stderr가 그대로 journal에 들어간다(6장).

터미널 창
systemctl list-timers --all # 다음 실행 시각 · 마지막 실행 · 담당 유닛
systemctl status logrotate.timer
journalctl -u logrotate.service # 실행 결과가 로그로 남는다
cronsystemd 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 reboot
sudo systemctl poweroff
sudo shutdown -r +5 "패치 적용 재부팅" # 5분 뒤 (로그인 사용자에게 공지된다)
sudo shutdown -c # 예약 취소
ls /var/run/reboot-required # 있으면 재부팅이 필요한 상태 (12장)
  • start(지금) · enable(다음 부팅) · enable --now(둘 다) · mask(완전 차단)
  • systemctl status의 CGroup 트리에 자식 프로세스가 전부 보인다
  • 접속하면 systemctl --failed 한 번 — 조용히 죽은 유닛이 여기 있다
  • 설정은 systemctl edit(drop-in) 로. 원본 수정은 업데이트 때 날아간다
  • 유닛 파일을 바꾸면 daemon-reload, 결과 확인은 systemctl cat
  • 결과를 봐야 하는 주기 작업은 cron보다 타이머(로그가 journal에 남는다)
  • 부팅 지연은 blame이 아니라 critical-chain 으로 본다