Contents
see List개요
systemd는 현대 Linux 배포판의 표준 init 시스템이자 서비스 관리자입니다. 서비스 시작/중지, 의존성 관리, 로그 수집 등을 통합적으로 제공합니다. 이 글에서는 systemd 서비스 작성부터 journalctl을 활용한 로그 분석까지 실무에 필요한 핵심 내용을 다룹니다.
핵심 개념
systemd는 unit 파일로 서비스를 정의합니다. Unit 파일은 /etc/systemd/system/ 또는 /usr/lib/systemd/system/에 위치하며, .service, .timer, .socket 등 다양한 타입이 있습니다. journald는 systemd의 로그 시스템으로 모든 서비스 로그를 중앙에서 수집합니다.
1. systemd 서비스 파일 작성
간단한 Node.js 앱을 systemd 서비스로 등록하는 예제입니다.
# /etc/systemd/system/myapp.service
[Unit]
Description=My Node.js Application
Documentation=https://example.com/docs
After=network.target postgresql.service # 의존성 명시
Wants=postgresql.service # soft dependency (실패해도 시작)
[Service]
Type=simple # 프로세스가 바로 시작 (기본값)
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=/opt/myapp/.env # 환경변수 파일
# 실행 명령
ExecStart=/usr/bin/node /opt/myapp/server.js
# 재시작 정책
Restart=always
RestartSec=10s
# 리소스 제한
LimitNOFILE=65536
MemoryLimit=1G
CPUQuota=50%
# 보안 강화
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/data # 쓰기 가능 경로만 허용
# 시작 타임아웃
TimeoutStartSec=60s
TimeoutStopSec=30s
# 로그
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target # 부팅 시 자동 시작
2. 서비스 관리 명령
# 서비스 시작/중지/재시작
sudo systemctl start myapp
sudo systemctl stop myapp
sudo systemctl restart myapp
sudo systemctl reload myapp # 설정 리로드 (서비스가 지원하는 경우)
# 부팅 시 자동 시작
sudo systemctl enable myapp
sudo systemctl disable myapp
# 상태 확인
sudo systemctl status myapp
sudo systemctl is-active myapp # active/inactive
sudo systemctl is-enabled myapp # enabled/disabled
# 설정 파일 수정 후 리로드
sudo systemctl daemon-reload
# 서비스 의존성 확인
systemctl list-dependencies myapp
# 모든 서비스 목록
systemctl list-units --type=service
systemctl list-unit-files --type=service
# 실패한 서비스 확인
systemctl --failed
3. Timer (Cron 대체)
systemd timer는 cron보다 강력하고 로그 통합이 잘 됩니다.
# /etc/systemd/system/backup.service
[Unit]
Description=Backup Database
[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
# /etc/systemd/system/backup.timer
[Unit]
Description=Backup Database Timer
[Timer]
OnCalendar=daily # 매일 자정
# OnCalendar=*-*-* 02:00:00 # 매일 오전 2시
# OnCalendar=Mon,Fri 10:00 # 월/금 10시
Persistent=true # 놓친 실행을 부팅 후 수행
RandomizedDelaySec=300 # 0~5분 랜덤 지연
[Install]
WantedBy=timers.target
# Timer 활성화
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
# Timer 목록 확인
systemctl list-timers --all
# 수동 실행 (테스트)
sudo systemctl start backup.service
4. journalctl 로그 분석
journald는 바이너리 로그를 사용하므로 journalctl로 조회합니다.
# 특정 서비스 로그
sudo journalctl -u myapp
# 실시간 로그 (tail -f 같은 기능)
sudo journalctl -u myapp -f
# 최근 N줄
sudo journalctl -u myapp -n 100
# 특정 시간대 로그
sudo journalctl -u myapp --since "2025-02-11 09:00:00"
sudo journalctl -u myapp --since "1 hour ago"
sudo journalctl -u myapp --since today
sudo journalctl -u myapp --until "2025-02-11 18:00:00"
# 우선순위 필터 (0=emerg, 3=err, 6=info, 7=debug)
sudo journalctl -u myapp -p err # 에러만
sudo journalctl -p warning # 전체 시스템 경고 이상
# 부팅별 로그
sudo journalctl --list-boots # 부팅 목록
sudo journalctl -b 0 # 현재 부팅
sudo journalctl -b -1 # 이전 부팅
# 커널 로그 (dmesg 대체)
sudo journalctl -k
# 특정 PID
sudo journalctl _PID=1234
# 로그 용량 확인
sudo journalctl --disk-usage
# 오래된 로그 정리
sudo journalctl --vacuum-time=7d # 7일 이상 삭제
sudo journalctl --vacuum-size=1G # 1GB 이하로 축소
# JSON 출력 (파싱용)
sudo journalctl -u myapp -o json-pretty
# 로그 내보내기
sudo journalctl -u myapp --since today --no-pager > myapp.log
실전 예제
1. Docker Compose를 systemd로 관리
# /etc/systemd/system/docker-compose-app.service
[Unit]
Description=Docker Compose Application
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
2. 서비스 헬스 체크 자동화
#!/bin/bash
# /usr/local/bin/service-monitor.sh
SERVICES=("nginx" "postgresql" "redis" "myapp")
ALERT_EMAIL="admin@example.com"
for service in "${SERVICES[@]}"; do
if ! systemctl is-active --quiet "$service"; then
echo "Service $service is DOWN. Attempting restart..."
# 로그 수집
journalctl -u "$service" -n 50 > /tmp/${service}_failure.log
# 재시작 시도
systemctl restart "$service"
sleep 5
# 재시작 실패 시 알림
if ! systemctl is-active --quiet "$service"; then
mail -s "CRITICAL: $service failed to restart" "$ALERT_EMAIL" < /tmp/${service}_failure.log
fi
fi
done
# /etc/systemd/system/service-monitor.timer
[Unit]
Description=Service Monitor Timer
[Timer]
OnCalendar=*:0/5 # 5분마다
Persistent=true
[Install]
WantedBy=timers.target
3. 로그 분석 스크립트
#!/bin/bash
# analyze-service-logs.sh
SERVICE="myapp"
SINCE="1 hour ago"
echo "=== Service Log Analysis: $SERVICE ==="
echo ""
# 에러 건수
echo "[Error Count]"
journalctl -u "$SERVICE" --since "$SINCE" -p err --no-pager | wc -l
echo ""
# 가장 많이 발생한 에러 (Top 5)
echo "[Top 5 Errors]"
journalctl -u "$SERVICE" --since "$SINCE" -p err --no-pager | grep -oP '(?<=Error: ).*' | sort | uniq -c | sort -rn | head -5
echo ""
# 재시작 이벤트
echo "[Restart Events]"
journalctl -u "$SERVICE" --since "$SINCE" --no-pager | grep -i "started|stopped|restarting"
echo ""
# 평균 응답 시간 (로그에 "Response time: 123ms" 패턴이 있는 경우)
echo "[Average Response Time]"
journalctl -u "$SERVICE" --since "$SINCE" --no-pager | grep -oP 'Response time: Kd+' | awk '{sum+=$1; count++} END {if(count>0) print sum/count "ms"}'
echo ""
# 메모리 사용량 피크 (systemd-cgtop 사용)
echo "[Memory Usage]"
systemctl status "$SERVICE" | grep Memory
활용 팁
- Drop-In 파일: 기본 서비스 파일을 수정하지 않고 /etc/systemd/system/myapp.service.d/override.conf로 설정을 덮어쓸 수 있습니다.
- 로그 포워딩: journald 로그를 rsyslog나 Fluentd로 전송하여 중앙 로그 서버에 저장합니다.
- 통지 설정: Type=notify를 사용하면 서비스가 준비 완료 시점을 systemd에 알릴 수 있습니다 (sd_notify 사용).
- Cgroup 리소스 제한: MemoryMax, CPUQuota, TasksMax로 서비스별 리소스를 격리합니다.
- Watchdog: WatchdogSec=30s로 헬스 체크를 설정하면 응답 없는 서비스를 자동 재시작합니다.
- 로그 압축: /etc/systemd/journald.conf에서 Compress=yes로 디스크 사용량을 줄입니다.
마무리
systemd는 처음에는 복잡해 보이지만, 일단 익숙해지면 서비스 관리가 매우 편리합니다. 특히 journalctl의 강력한 필터링 기능은 로그 분석 시간을 크게 단축시킵니다. 프로덕션 환경에서는 서비스별로 리소스 제한과 재시작 정책을 반드시 설정하고, 정기적으로 journalctl --vacuum으로 로그 용량을 관리하세요. systemd timer는 cron보다 디버깅이 쉽고 로그가 통합되므로 배치 작업에 적극 활용할 것을 권장합니다.