서비스 관리 (systemd)
Linux 시스템의 서비스, 데몬, 부팅 과정을 관리하는 systemd 완벽 가이드
Linux 시스템의 서비스, 데몬, 부팅 과정을 관리하는 systemd 완벽 가이드
목표: 서비스를 직접 작성하고, 부팅 과정을 제어할 수 있는 수준
🏗️ systemd 개요
systemd란?
Linux 시스템 및 서비스 관리자 (System and Service Manager)
- 전통 init 시스템 대체: SysV init, Upstart를 대체
- 병렬 부팅: 의존성 기반 병렬 시작으로 빠른 부팅
- 온디맨드 데몬: 필요할 때만 서비스 시작 (소켓 활성화)
- 컨트롤 그룹: 프로세스 추적 및 리소스 제한
주요 구성 요소
| 구성 요소 | 설명 |
|---|---|
| systemd | PID 1, 시스템 및 서비스 관리자 |
| systemctl | 서비스 제어 명령어 |
| journald | 시스템 로그 관리 |
| logind | 사용자 로그인 관리 |
| networkd | 네트워크 설정 관리 |
| timedated | 시스템 시간/날짜 관리 |
| resolved | DNS 확인 |
| udevd | 장치 관리 |
📋 서비스 제어
기본 명령어
# 서비스 시작
systemctl start nginx
# 서비스 중지
systemctl stop nginx
# 서비스 재시작
systemctl restart nginx
# 설정 재로드 (재시작 없이)
systemctl reload nginx
# 재시작 또는 시작 (상태에 따라)
systemctl try-restart nginx
# 재로드 또는 시작
systemctl try-reload-or-restart nginx
# 현재 상태 확인
systemctl status nginx
# 활성화 (부팅 시 자동 시작)
systemctl enable nginx
# 비활성화 (부팅 시 자동 시작 해제)
systemctl disable nginx
# 마스크 (완전히 비활성화, 수동 시작도 불가)
systemctl mask nginx
# 마스크 해제
systemctl unmask nginx
상태 확인
# 서비스 상태
systemctl is-active nginx # 활성 상태 (active/inactive)
systemctl is-enabled nginx # 활성화 상태 (enabled/disabled/masked)
systemctl is-failed nginx # 실패 상태
# 모든 서비스 목록
systemctl list-units --type=service
# 활성 서비스만
systemctl list-units --type=service --state=active
# 실패한 서비스
systemctl list-units --type=service --state=failed
# 활성화된 서비스 (부팅 시 시작)
systemctl list-unit-files --type=service --state=enabled
# 전체 서비스 목록 (활성/비활성 모두)
systemctl list-unit-files --type=service
📝 유닛 파일 (Unit Files)
유닛 파일 위치
| 우선순위 | 경로 | 설명 |
|---|---|---|
| 1 | /etc/systemd/system/ | 관리자 설정 (최우선) |
| 2 | /run/systemd/system/ | 런타임 설정 |
| 3 | /usr/lib/systemd/system/ | 패키지 기본 설정 (Debian/Ubuntu) |
| 4 | /lib/systemd/system/ | 패키지 기본 설정 (RHEL/CentOS) |
서비스 유닛 파일 구조
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
Documentation=https://example.com/docs
After=network.target postgresql.service
Wants=network.target
Requires=postgresql.service
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment="NODE_ENV=production"
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/bin/node /opt/myapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=5
KillMode=process
[Install]
WantedBy=multi-user.target
[Unit] 섹션
[Unit]
Description=서비스 설명
Documentation=man:nginx(8) https://nginx.org/docs/
# 부팅 순서 (After: 나중에, Before: 먼저)
After=network.target remote-fs.target nss-lookup.target
Before=multi-user.target
# 의존성 (Wants: 약한 의존, Requires: 강한 의존)
Wants=network.target
Requires=postgresql.service
# 조걶
ConditionPathExists=/etc/myapp/config.yml
ConditionDirectoryNotEmpty=/var/lib/myapp
# 충돌
Conflicts=apache2.service
[Service] 섹션
서비스 유형 (Type)
| 타입 | 설명 | 사용처 |
|---|---|---|
| simple | ExecStart가 메인 프로세스 | 대부분의 서비스 |
| forking | 부모가 fork 후 종료 | 전통적인 데몬 (Apache) |
| oneshot | 한 번 실행 후 종료 | 초기화 스크립트 |
| dbus | D-Bus 이름 획득 후 완료 | D-Bus 서비스 |
| notify | sd-notify로 준비 완료 알림 | 복잡한 서비스 |
| idle | 다른 작업 완료 후 시작 | 데스크톱 서비스 |
실행 설정
[Service]
# 실행 사용자/그룹
User=myapp
Group=myapp
# 작업 디렉토리
WorkingDirectory=/opt/myapp
# 환경변수
Environment="NODE_ENV=production"
Environment="PORT=8080"
EnvironmentFile=-/etc/myapp/env # - 접두사: 파일 없어도 OK
# 실행 명령
ExecStart=/usr/bin/node server.js
ExecStartPre=/opt/myapp/bin/check-config
ExecStartPost=/opt/myapp/bin/notify-start
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
ExecStopPost=/opt/myapp/bin/cleanup
# 재시작 정책
Restart=on-failure
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3
# 타임아웃
TimeoutStartSec=30
TimeoutStopSec=30
TimeoutSec=30
# PID 파일 (forking 타입)
PIDFile=/run/myapp.pid
# 표준 출력/에러
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
재시작 정책 (Restart)
| 값 | 설명 |
|---|---|
| no | 재시작 안함 (기본) |
| on-success | 정상 종료 시 (exit code 0) |
| on-failure | 실패 시 (exit code ≠ 0) |
| on-abnormal | 시그널/타임아웃/감시 실패 |
| on-abort | 정상 종료를 제외한 모든 경우 |
| always | 항상 |
킬 모드 (KillMode)
| 값 | 설명 |
|---|---|
| control-group | 전체 컨트롤 그룹 종료 (기본) |
| process | 메인 프로세스만 |
| mixed | 메인 프로세스 SIGTERM, 나머지 SIGKILL |
| none | 종료하지 않음 |
[Install] 섹션
[Install]
# 활성화 시 타겟
WantedBy=multi-user.target
# 별칭
Alias=myappd.service
주요 타겟:
| 타겟 | 설명 |
|---|---|
| graphical.target | GUI 환경 |
| multi-user.target | 다중 사용자 CLI (runlevel 3) |
| rescue.target | 복구 모드 (runlevel 1) |
| emergency.target | 긴급 모드 |
🔄 유닛 파일 관리
새 서비스 등록
# 1. 유닛 파일 생성
sudo nano /etc/systemd/system/myapp.service
# 2. systemd 데몬 재로드
sudo systemctl daemon-reload
# 3. 서비스 시작
sudo systemctl start myapp
# 4. 부팅 시 자동 시작
sudo systemctl enable myapp
# 5. 상태 확인
sudo systemctl status myapp
유닛 파일 수정
# 기본 설정 오버라이드
sudo systemctl edit nginx
# → /etc/systemd/system/nginx.service.d/override.conf 생성
# 전체 유닛 파일 교체
sudo systemctl edit --full nginx
# 수정 후 반드시 데몬 재로드
sudo systemctl daemon-reload
유닛 파일 검증
# 문법 검사
systemd-analyze verify /etc/systemd/system/myapp.service
# 전체 설정 검사
systemd-analyze verify
📊 로그 관리 (journald)
journalctl 사용법
# 모든 로그 (역순)
journalctl
# 최신 로그 실시간 (-f = follow)
journalctl -f
# 특정 서비스 로그
journalctl -u nginx
journalctl -u nginx -f
# 특정 시간 이후
journalctl --since "2026-06-01 10:00"
journalctl --since "1 hour ago"
journalctl --since yesterday
# 시간 범위
journalctl --since "2026-06-01 00:00" --until "2026-06-01 12:00"
# 부팅 이후 로그
journalctl -b
journalctl -b -1 # 이전 부팅
# 특정 PID
journalctl _PID=1234
# 특정 실행 파일
journalctl /usr/bin/nginx
# 로그 레벨 필터
journalctl -p err
# emerg, alert, crit, err, warning, notice, info, debug
# 역순 출력 (최신 먼저)
journalctl -r
# 출력 라인 수 제한
journalctl -n 100
# JSON 형식 출력
journalctl -o json
journalctl -o json-pretty
# 로그 크기 확인
journalctl --disk-usage
# 로그 정리
sudo journalctl --vacuum-time=30d # 30일 이전 삭제
sudo journalctl --vacuum-size=500M # 500MB로 제한
영구 로그 저장
# 기본적으로 /run/log에 휘발성 저장
# 영구 저장 활성화
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
🎯 실전 예시
웹 애플리케이션 서비스
# /etc/systemd/system/webapp.service
[Unit]
Description=Web Application
After=network.target postgresql.service
[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
Environment="NODE_ENV=production"
Environment="PORT=3000"
ExecStart=/usr/bin/node /opt/webapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=webapp
# 리소스 제한
LimitNOFILE=65536
LimitNPROC=4096
# 보안
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/webapp/data
[Install]
WantedBy=multi-user.target
Python 가상환경 서비스
# /etc/systemd/system/python-app.service
[Unit]
Description=Python Application
After=network.target
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/python-app
Environment="PATH=/opt/python-app/venv/bin:/usr/local/bin:/usr/bin"
Environment="PYTHONPATH=/opt/python-app"
ExecStart=/opt/python-app/venv/bin/python /opt/python-app/main.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Docker Compose 서비스
# /etc/systemd/system/docker-compose-app.service
[Unit]
Description=Docker Compose Application
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/docker-app
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down
ExecReload=/usr/local/bin/docker-compose restart
[Install]
WantedBy=multi-user.target
백그라운드 작업 (타이머)
# /etc/systemd/system/backup.service
[Unit]
Description=Database Backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
Group=backup
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily
[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
# 타이머 활성화
systemctl enable backup.timer
systemctl start backup.timer
systemctl list-timers
🔧 고급 기능
소켓 활성화 (Socket Activation)
# /etc/systemd/system/myapp.socket
[Unit]
Description=My Application Socket
[Socket]
ListenStream=8080
Accept=no
[Install]
WantedBy=sockets.target
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
Requires=myapp.socket
[Service]
ExecStart=/usr/bin/myapp
StandardInput=socket
장점:
- 서비스가 필요할 때만 시작 (메모리 절약)
- 소켓 유실 없이 서비스 재시작
타겟 (Target) 관리
# 현재 타겟 확인
systemctl get-default
# 기본 타겟 변경
systemctl set-default multi-user.target
systemctl set-default graphical.target
# 특정 타겟으로 부팅
systemctl isolate rescue.target
# 타겟 의존성 확인
systemctl list-dependencies multi-user.target
컨트롤 그룹 (cgroups) 통합
[Service]
# CPU 제한
CPUQuota=50%
CPUShares=512
# 메모리 제한
MemoryMax=512M
MemorySwapMax=0
# I/O 제한
IOReadBandwidthMax=/dev/sda 10M
IOWriteBandwidthMax=/dev/sda 10M
# 태스크 수 제한
TasksMax=100
🚨 트러블슈팅
서비스 시작 실패
# 상세 오류 확인
systemctl status nginx -l
journalctl -u nginx --no-pager
# 유닛 파일 검증
systemd-analyze verify /etc/systemd/system/nginx.service
# 디버그 모드로 실행
systemd-analyze set-log-level debug
# 서비스 수동 실행 (환경 동일하게)
systemctl show-environment
systemd-run --unit=test --service-type=simple /path/to/command
의존성 문제
# 의존성 트리 확인
systemctl list-dependencies nginx
# 역방향 의존성
systemctl list-dependencies --reverse nginx
# 타겟 상태
systemctl list-units --type=target
무한 재시작 루프
# 재시작 횟수 확인
systemctl show nginx -p NRestarts
# StartLimit 설정 확인
systemctl cat nginx | grep -i startlimit
# 일시적으로 재시작 중지
systemctl stop nginx
systemctl reset-failed nginx
📋 체크리스트
- 유닛 파일 작성 (Unit, Service, Install)
- systemd 데몬 재로드
- 서비스 시작/중지/재시작
- 부팅 시 자동 시작 설정
- 로그 확인 (journalctl)
- 의존성 설정 (After, Requires, Wants)
- 재시작 정책 설정
- 리소스 제한 (cgroups)
- 보안 설정 (NoNewPrivileges, ProtectSystem)
🔗 관련 노트
- 01-04-프로세스-관리 — 프로세스 제어
- 02-03-부팅-과정-GRUB — 부팅 과정
- 03-02-성능-모니터링 — 리소스 모니터링
- 03-03-보안-하드닝 — 서비스 보안
- 03-00-Linux-내부동작-자가학습 — 커널 낮은 수준
마지막 업데이트: 2026-06-01