Linux systemd 서비스 관리 심화

systemd란?

systemd는 현대 Linux의 init 시스템이자 서비스 매니저다. Ubuntu, Debian, RHEL, Fedora 등 대부분의 배포판에서 PID 1로 동작하며 서비스 시작·중지·재시작, 의존성 관리, 로그 수집, 리소스 제어를 담당한다. 인프라 엔지니어라면 systemd unit 파일을 직접 작성하고 운영할 줄 알아야 한다.

Unit 파일 구조

systemd의 모든 관리 대상은 Unit이다. 서비스(.service), 타이머(.timer), 마운트(.mount), 소켓(.socket) 등이 있으며, /etc/systemd/system/에 커스텀 Unit 파일을 배치한다.

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Server
Documentation=https://docs.myapp.com
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service

[Service]
Type=notify                          # 서비스가 준비 완료를 알림
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
EnvironmentFile=/opt/myapp/.env      # 환경 변수 파일
ExecStartPre=/opt/myapp/pre-start.sh # 시작 전 스크립트
ExecStart=/usr/bin/node /opt/myapp/dist/main.js
ExecReload=/bin/kill -HUP $MAINPID   # reload 시 HUP 시그널
ExecStopPost=/opt/myapp/cleanup.sh   # 종료 후 정리

Restart=on-failure
RestartSec=5s
StartLimitIntervalSec=300
StartLimitBurst=5                    # 300초 내 5번 재시작 초과 시 중단

StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

Service Type 이해

Type 동작 적합 서비스
simple (기본) ExecStart 프로세스 = 메인 프로세스 포그라운드 실행 앱
exec exec() 호출 후 활성화 simple과 유사, 더 정확한 시점
forking 데몬화(fork) 후 부모 종료 전통적 데몬 (nginx, apache)
notify sd_notify()로 준비 완료 알림 초기화 시간이 긴 앱
oneshot 실행 완료 후 종료 마이그레이션, 배치 작업

재시작 정책 심화

[Service]
# 재시작 조건
Restart=on-failure      # 비정상 종료 시만 재시작
# Restart=always        # 항상 재시작 (수동 stop 제외)
# Restart=on-abnormal   # 시그널/타임아웃/watchdog 실패 시

RestartSec=5s           # 재시작 전 대기 시간

# 재시작 제한: 무한 루프 방지
StartLimitIntervalSec=300   # 이 시간 내에
StartLimitBurst=5            # 이 횟수 초과 시 재시작 중단

# 종료 정상 코드 커스텀
SuccessExitStatus=143       # SIGTERM(143)도 정상 종료로 처리
RestartPreventExitStatus=0  # 종료 코드 0이면 재시작 안 함
Restart 옵션 정상 종료 비정상 종료 시그널 종료
no
on-failure
always

리소스 제어: cgroup 통합

systemd는 Linux cgroup v2를 통해 서비스의 CPU, 메모리, I/O를 제한한다. 컨테이너 없이도 프로세스 수준 리소스 격리가 가능하다.

[Service]
# 메모리 제한
MemoryMax=2G              # 하드 리밋 (초과 시 OOM kill)
MemoryHigh=1.5G           # 소프트 리밋 (초과 시 메모리 회수 압력)

# CPU 제한
CPUQuota=200%             # 2 코어까지 사용 가능
CPUWeight=100             # 다른 서비스 대비 상대 가중치 (기본 100)

# I/O 제한
IOWeight=50               # I/O 스케줄링 가중치
IOReadBandwidthMax=/dev/sda 100M   # 읽기 100MB/s 제한

# 프로세스 수 제한
TasksMax=512              # 최대 스레드/프로세스 수

# 확인
# systemctl show myapp.service -p MemoryMax,CPUQuota,TasksMax
# systemd-cgtop (실시간 cgroup 모니터링)

보안 하드닝

systemd는 서비스의 시스템 접근을 세밀하게 제한하는 샌드박싱 옵션을 제공한다.

[Service]
# 파일시스템 보호
ProtectSystem=strict          # / 읽기 전용 마운트
ProtectHome=true              # /home, /root, /run/user 접근 차단
ReadWritePaths=/opt/myapp/data  # 쓰기 허용 디렉토리
TemporaryFileSystem=/tmp:size=100M  # 격리된 /tmp

# 네트워크 제한
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX  # 허용 소켓 타입
IPAddressDeny=any             # 기본 차단
IPAddressAllow=10.0.0.0/8    # 내부 네트워크만 허용

# 커널 기능 제한
NoNewPrivileges=true          # 권한 상승 차단
CapabilityBoundingSet=CAP_NET_BIND_SERVICE  # 필요 capability만
PrivateTmp=true               # 격리된 /tmp
PrivateDevices=true           # 장치 접근 차단
ProtectKernelTunables=true    # /proc/sys 등 변경 차단
ProtectKernelModules=true     # 커널 모듈 로드 차단

# 보안 점수 확인
# systemd-analyze security myapp.service
# 보안 점수 확인 명령
$ systemd-analyze security myapp.service
  NAME                          DESCRIPTION                    EXPOSURE
✓ PrivateDevices=               Service has no access to...    0.2
✓ ProtectSystem=strict          Service runs with strict...    0.0
✗ AmbientCapabilities=          Service process can gain...    0.1
→ Overall exposure level for myapp.service: 2.4 MEDIUM

systemd Timer: cron 대체

systemd timer는 cron보다 로깅, 의존성, 리소스 제어가 우수하다.

# /etc/systemd/system/backup.service
[Unit]
Description=Database Backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 3 AM

[Timer]
OnCalendar=*-*-* 03:00:00     # 매일 03:00
RandomizedDelaySec=600          # 0~10분 랜덤 지연 (여러 서버 분산)
Persistent=true                 # 놓친 실행 보상 (서버 다운 후 복구 시)
AccuracySec=1min                # 실행 정확도

[Install]
WantedBy=timers.target

# 활성화
# systemctl enable --now backup.timer
# systemctl list-timers --all

journalctl: 로그 관리 심화

# 특정 서비스 로그
journalctl -u myapp.service --since "1 hour ago" --no-pager

# 실시간 로그 (tail -f 대체)
journalctl -u myapp.service -f

# 에러 로그만
journalctl -u myapp.service -p err

# JSON 출력 (파이프라인용)
journalctl -u myapp.service -o json --since today | jq '.MESSAGE'

# 부팅 이후 로그
journalctl -u myapp.service -b

# 로그 디스크 사용량 확인
journalctl --disk-usage

# 로그 보관 설정 (/etc/systemd/journald.conf)
# SystemMaxUse=2G        # 최대 2GB
# MaxRetentionSec=30day  # 30일 보관
# MaxFileSec=1day        # 파일당 1일

실전 패턴: 무중단 배포

#!/bin/bash
# deploy.sh — Graceful restart 패턴

# 1. 새 바이너리 배포
rsync -az ./dist/ /opt/myapp/dist/

# 2. Graceful reload (SIGHUP → 새 워커 생성, 기존 커넥션 마무리)
systemctl reload myapp.service

# 또는 restart (Graceful shutdown → 재시작)
# ExecStop에 타임아웃 설정 필수
# TimeoutStopSec=30  → 30초 내 종료 안 되면 SIGKILL
[Service]
# Graceful shutdown 설정
TimeoutStopSec=30            # 30초 대기 후 SIGKILL
KillMode=mixed               # 메인 프로세스에 SIGTERM, 나머지에 SIGKILL
KillSignal=SIGTERM           # 종료 시그널
FinalKillSignal=SIGKILL      # 타임아웃 후 강제 종료 시그널

# Watchdog: 서비스 응답 감시
WatchdogSec=30               # 30초마다 sd_notify("WATCHDOG=1") 필요
# 응답 없으면 자동 재시작

의존성 관리: After vs Requires vs Wants

디렉티브 역할 실패 시 영향
After 순서만 결정 (A After B → B 먼저 시작) 영향 없음
Wants 약한 의존 (함께 시작하되 실패해도 계속) 영향 없음
Requires 강한 의존 (의존 대상 실패 시 자신도 중지) 함께 중지됨
BindsTo 가장 강한 의존 (대상 비활성화 시 즉시 중지) 즉시 중지
# 의존성 트리 확인
systemctl list-dependencies myapp.service
systemctl list-dependencies myapp.service --reverse  # 역방향

트러블슈팅 명령어

# 서비스 상태 상세 확인
systemctl status myapp.service

# 시작 실패 원인 분석
systemctl --failed
journalctl -u myapp.service -b --no-pager | tail -50

# 부팅 시간 분석 (어떤 서비스가 느린지)
systemd-analyze blame
systemd-analyze critical-chain myapp.service

# Unit 파일 문법 검증
systemd-analyze verify /etc/systemd/system/myapp.service

# 변경 후 리로드
systemctl daemon-reload
systemctl restart myapp.service

Linux eBPF 관측성과 결합하면 systemd 서비스의 시스템콜, 네트워크, I/O를 커널 수준에서 추적할 수 있다. nftables 방화벽과 systemd의 IPAddressAllow/Deny를 함께 사용하면 다층 네트워크 보안이 가능하다.

정리

systemd는 단순한 서비스 매니저가 아니라 리소스 제어, 보안 하드닝, 로그 관리, 타이머를 통합한 Linux 운영의 핵심이다. Unit 파일의 Restart 정책으로 자동 복구를 보장하고, cgroup 통합으로 리소스를 격리하고, 보안 디렉티브로 서비스를 샌드박싱한다. systemd-analyze security로 보안 점수를 확인하고, journalctl로 구조화된 로그를 관리하면 컨테이너 없는 환경에서도 프로덕션급 서비스 운영이 가능하다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux