Linux systemd 서비스 등록: 부팅 시 자동 실행 적용하기
들어가며
문제 상황
AWS 환경에서 서비스를 운영하던 중, 비용 절감 방안으로 개발 서버의 야간 중지 정책을 시행하게 되었다.
운영 프로세스:
퇴근 전: 서버 중지 (Stop Instance)
출근 후: 서버 기동 (Start Instance)
하지만 문제가 있었다.
매번 반복되는 작업:
# 서버 기동 후 매번 수동으로 실행
$ /app/tomcat/bin/startup.sh
$ /app/nginx/sbin/nginx
$ /app/application/start.sh
...
처음에는 이런 쉘 스크립트를 만들어 사용했다:
#!/bin/bash
# start_all.sh
echo "Tomcat 시작..."
/app/tomcat/bin/startup.sh
echo "Nginx 시작..."
/app/nginx/sbin/nginx
echo "Application 시작..."
/app/application/start.sh
하지만 매번 SSH 접속해서 스크립트를 실행하는 것조차 번거로웠다.
그때 Windows의 “시작프로그램”이 떠올랐다.
Linux에도 분명 부팅 시 자동으로 실행되는 메커니즘이 있을 것이다.
바로 systemd 서비스다.
systemd란?
개요
systemd는 Linux 시스템의 초기화 및 서비스 관리를 담당하는 시스템 데몬이다.
주요 기능
1. 서비스 관리
- 서비스 시작/중지/재시작
- 서비스 상태 확인
- 부팅 시 자동 시작
2. 병렬 실행
- 여러 서비스를 동시에 시작
- 빠른 부팅 시간
3. 의존성 관리
- 서비스 간 의존 관계 정의
- 순서 보장
4. 로그 관리
- journalctl을 통한 통합 로그
- 서비스별 로그 추적
systemd vs init.d
| 구분 | init.d (구형) | systemd (현대) |
|---|---|---|
| 설정 파일 | Shell Script | Unit File (INI 형식) |
| 실행 순서 | 순차적 | 병렬 |
| 의존성 | 수동 관리 | 자동 관리 |
| 로그 | 개별 로그 파일 | 통합 journalctl |
| 속도 | 느림 | 빠름 |
systemd 서비스 등록 실전
환경 정보
- OS: CentOS 7+ / Ubuntu 16.04+
- 대상 서비스: Tomcat, Nginx, 사용자 애플리케이션
- 사용자: 일반 사용자 (sudo 권한 필요)
1단계: 서비스 파일 위치 확인
systemd 서비스 파일은 다음 디렉토리에 위치한다:
# 시스템 전역 서비스 (관리자 권한 필요)
/usr/lib/systemd/system/ # CentOS/RHEL
/lib/systemd/system/ # Ubuntu/Debian
# 사용자 정의 서비스 (권장)
/etc/systemd/system/
# 사용자별 서비스 (sudo 불필요)
~/.config/systemd/user/
권장 위치:
/etc/systemd/system/
이유: 시스템 업그레이드 시에도 유지되며, 관리가 용이함
2단계: Tomcat 서비스 파일 작성
서비스 파일 생성:
# 서비스 디렉토리로 이동
cd /etc/systemd/system
# 서비스 파일 생성
sudo vi tomcat.service
기본 설정:
[Unit]
Description=Apache Tomcat Web Application Container
After=syslog.target network.target
[Service]
Type=forking
User=svcuser
Group=svcuser
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_HOME=/home/svcuser/apps/tomcat"
Environment="CATALINA_BASE=/home/svcuser/apps/tomcat"
ExecStart=/home/svcuser/apps/tomcat/bin/startup.sh
ExecStop=/home/svcuser/apps/tomcat/bin/shutdown.sh
WorkingDirectory=/home/svcuser/apps/tomcat
[Install]
WantedBy=multi-user.target
Unit 파일 구조 상세 설명
[Unit] 섹션
서비스의 기본 정보와 의존성을 정의한다.
Description
Description=Apache Tomcat Web Application Container
서비스에 대한 간단한 설명 (systemctl status에서 표시)
After
After=syslog.target network.target
- 이 서비스가 시작되기 전에 완료되어야 할 서비스 목록
network.target: 네트워크가 준비된 후 시작syslog.target: 로깅 시스템이 준비된 후 시작
Before
Before=nginx.service
이 서비스가 완료된 후에 시작될 서비스 (선택 사항)
Requires / Wants
Requires=network.target
Wants=postgresql.service
Requires: 필수 의존성 (실패 시 이 서비스도 실패)Wants: 권장 의존성 (실패해도 이 서비스는 시작)
[Service] 섹션
서비스의 실행 방식을 정의한다.
Type
서비스의 시작 방식을 정의한다.
Type=forking
주요 Type 설명:
| Type | 설명 | 사용 사례 |
|---|---|---|
| simple | ExecStart 프로세스가 메인 프로세스 (기본값) | 단순한 포그라운드 애플리케이션 |
| forking | ExecStart가 자식 프로세스를 생성하고 종료 | Tomcat, Nginx (데몬 방식) |
| oneshot | 한 번 실행하고 종료 | 초기화 스크립트 |
| notify | 서비스가 준비 완료를 systemd에 알림 | 현대적인 애플리케이션 |
| idle | 다른 작업이 없을 때 실행 | 부하를 주지 않는 백그라운드 작업 |
Tomcat은 왜 forking?
startup.sh는 Tomcat 프로세스를 백그라운드로 실행하고 종료됨- 실제 메인 프로세스는 자식 프로세스로 남음
User / Group
User=svcuser
Group=svcuser
서비스를 실행할 사용자 및 그룹 (보안상 root 사용 지양)
Environment
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_HOME=/home/svcuser/apps/tomcat"
환경 변수 설정 (여러 개 가능)
환경 변수 파일 사용:
EnvironmentFile=/home/svcuser/apps/tomcat/conf/tomcat.env
tomcat.env 파일 예시:
JAVA_HOME=/usr/lib/jvm/java-11-openjdk
CATALINA_HOME=/home/svcuser/apps/tomcat
CATALINA_OPTS="-Xms512m -Xmx2048m"
ExecStart / ExecStop
ExecStart=/home/svcuser/apps/tomcat/bin/startup.sh
ExecStop=/home/svcuser/apps/tomcat/bin/shutdown.sh
ExecStart: 서비스 시작 명령어 (절대 경로 사용)ExecStop: 서비스 중지 명령어
ExecStartPre / ExecStartPost
ExecStartPre=/usr/bin/sleep 5
ExecStartPost=/usr/bin/curl http://localhost:8080/health
ExecStartPre: 시작 전 실행할 명령어ExecStartPost: 시작 후 실행할 명령어
WorkingDirectory
WorkingDirectory=/home/svcuser/apps/tomcat
명령어 실행 시 작업 디렉토리
Restart
Restart=on-failure
RestartSec=10
no: 재시작 안 함 (기본값)on-failure: 비정상 종료 시만 재시작on-abnormal: 비정상 종료 + 시그널 종료 시 재시작on-abort: 시그널로 종료 시 재시작always: 항상 재시작
Timeout
TimeoutStartSec=90
TimeoutStopSec=30
TimeoutStartSec: 시작 타임아웃 (기본 90초)TimeoutStopSec: 중지 타임아웃 (기본 90초)
PID 파일
PIDFile=/home/svcuser/apps/tomcat/tomcat.pid
프로세스 ID 파일 위치 (forking Type에서 권장)
[Install] 섹션
서비스 활성화 시 동작을 정의한다.
WantedBy
WantedBy=multi-user.target
주요 Target 설명:
| Target | 설명 | 런레벨 |
|---|---|---|
| multi-user.target | 다중 사용자 모드 (일반적인 서버) | 3 |
| graphical.target | 그래픽 환경 | 5 |
| network.target | 네트워크 활성화 | - |
대부분의 서버 서비스는 multi-user.target 사용
3단계: 개선된 Tomcat 서비스 파일
기본 설정에 안정성과 모니터링을 강화한 버전:
[Unit]
Description=Apache Tomcat Web Application Container
Documentation=https://tomcat.apache.org/
After=syslog.target network.target
[Service]
Type=forking
# 사용자 설정
User=svcuser
Group=svcuser
# 환경 변수
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_HOME=/home/svcuser/apps/tomcat"
Environment="CATALINA_BASE=/home/svcuser/apps/tomcat"
Environment="CATALINA_PID=/home/svcuser/apps/tomcat/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512m -Xmx2048m -XX:+UseG1GC"
# 실행 명령
ExecStartPre=/bin/sleep 3
ExecStart=/home/svcuser/apps/tomcat/bin/startup.sh
ExecStop=/home/svcuser/apps/tomcat/bin/shutdown.sh
# PID 파일
PIDFile=/home/svcuser/apps/tomcat/tomcat.pid
# 작업 디렉토리
WorkingDirectory=/home/svcuser/apps/tomcat
# 재시작 정책
Restart=on-failure
RestartSec=10
# 타임아웃
TimeoutStartSec=120
TimeoutStopSec=60
# 표준 출력/에러 로그
StandardOutput=journal
StandardError=journal
SyslogIdentifier=tomcat
[Install]
WantedBy=multi-user.target
개선 사항:
- Documentation: 문서 URL 추가
- CATALINA_PID: PID 파일 명시
- CATALINA_OPTS: JVM 옵션 설정
- ExecStartPre: 시작 전 3초 대기 (의존 서비스 안정화)
- Restart: 실패 시 자동 재시작
- RestartSec: 재시작 전 10초 대기
- Timeout: 시작/중지 타임아웃 증가
- StandardOutput/Error: journalctl 로그 연동
- SyslogIdentifier: 로그 식별자 지정
4단계: Nginx 서비스 파일 예시
sudo vi /etc/systemd/system/nginx.service
[Unit]
Description=Nginx HTTP Server
After=network.target
[Service]
Type=forking
User=svcuser
Group=svcuser
PIDFile=/home/svcuser/apps/nginx/logs/nginx.pid
ExecStartPre=/home/svcuser/apps/nginx/sbin/nginx -t
ExecStart=/home/svcuser/apps/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
특징:
- ExecStartPre: 시작 전 설정 파일 검증 (
nginx -t) - ExecReload: reload 명령어 (HUP 시그널)
- PrivateTmp: 임시 디렉토리 격리 (보안)
- $MAINPID: systemd가 자동으로 PID 주입
5단계: Spring Boot 애플리케이션 서비스
sudo vi /etc/systemd/system/myapp.service
[Unit]
Description=My Spring Boot Application
After=syslog.target network.target
[Service]
Type=simple
User=svcuser
Group=svcuser
WorkingDirectory=/home/svcuser/apps/myapp
ExecStart=/usr/bin/java -jar \
-Xms512m -Xmx1024m \
-Dspring.profiles.active=prod \
/home/svcuser/apps/myapp/application.jar
SuccessExitStatus=143
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target
특징:
- Type=simple: 포그라운드 실행
- SuccessExitStatus=143: SIGTERM(143) 정상 종료로 간주
- Restart=always: 항상 재시작
6단계: 서비스 등록 및 활성화
서비스 파일 권한 설정:
# 권한 확인 (644 권장)
sudo chmod 644 /etc/systemd/system/tomcat.service
# 소유자 확인 (root 권장)
sudo chown root:root /etc/systemd/system/tomcat.service
systemd 데몬 리로드:
# 설정 파일 변경 후 반드시 실행
sudo systemctl daemon-reload
서비스 활성화 (부팅 시 자동 시작):
# 서비스 등록
sudo systemctl enable tomcat.service
# 출력 예시:
# Created symlink from /etc/systemd/system/multi-user.target.wants/tomcat.service
# to /etc/systemd/system/tomcat.service
서비스 시작:
# 서비스 시작
sudo systemctl start tomcat.service
서비스 상태 확인:
sudo systemctl status tomcat.service
출력 예시:
● tomcat.service - Apache Tomcat Web Application Container
Loaded: loaded (/etc/systemd/system/tomcat.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2025-01-08 09:30:15 KST; 2min ago
Process: 12345 ExecStart=/home/svcuser/apps/tomcat/bin/startup.sh (code=exited, status=0/SUCCESS)
Main PID: 12350 (java)
Tasks: 45 (limit: 4915)
Memory: 512.5M
CGroup: /system.slice/tomcat.service
└─12350 /usr/bin/java -Djava.util.logging.config.file=...
1월 08 09:30:15 server systemd[1]: Starting Apache Tomcat Web Application Container...
1월 08 09:30:15 server systemd[1]: Started Apache Tomcat Web Application Container.
상태 의미:
| 상태 | 의미 |
|---|---|
| Loaded | 서비스 파일 로드 상태 |
| Active: active (running) | 정상 실행 중 |
| Active: inactive (dead) | 중지됨 |
| Active: failed | 실행 실패 |
| enabled | 부팅 시 자동 시작 활성화 |
| disabled | 부팅 시 자동 시작 비활성화 |
서비스 관리 명령어
기본 명령어
서비스 시작:
sudo systemctl start tomcat.service
서비스 중지:
sudo systemctl stop tomcat.service
서비스 재시작:
sudo systemctl restart tomcat.service
서비스 리로드 (설정 반영):
sudo systemctl reload tomcat.service
서비스 상태 확인:
sudo systemctl status tomcat.service
서비스 활성화 (부팅 시 자동 시작):
sudo systemctl enable tomcat.service
서비스 비활성화:
sudo systemctl disable tomcat.service
서비스 활성화 여부 확인:
systemctl is-enabled tomcat.service
서비스 실행 여부 확인:
systemctl is-active tomcat.service
고급 명령어
모든 서비스 목록:
systemctl list-units --type=service
활성화된 서비스만:
systemctl list-unit-files --type=service --state=enabled
실패한 서비스:
systemctl --failed
서비스 의존성 확인:
systemctl list-dependencies tomcat.service
서비스 설정 파일 위치:
systemctl show tomcat.service | grep FragmentPath
로그 확인
journalctl 사용법
서비스 로그 실시간 보기:
sudo journalctl -u tomcat.service -f
최근 로그 50줄:
sudo journalctl -u tomcat.service -n 50
특정 시간 이후 로그:
sudo journalctl -u tomcat.service --since "2025-01-08 09:00:00"
특정 시간 범위:
sudo journalctl -u tomcat.service --since "09:00" --until "10:00"
오늘 로그:
sudo journalctl -u tomcat.service --since today
어제 로그:
sudo journalctl -u tomcat.service --since yesterday --until today
우선순위별 필터:
# 에러만
sudo journalctl -u tomcat.service -p err
# 경고 이상
sudo journalctl -u tomcat.service -p warning
로그 삭제 (디스크 정리):
# 7일 이상 된 로그 삭제
sudo journalctl --vacuum-time=7d
# 1GB 이하로 유지
sudo journalctl --vacuum-size=1G
트러블슈팅
문제 1: 서비스 시작 실패
증상:
$ sudo systemctl status tomcat.service
● tomcat.service - Apache Tomcat Web Application Container
Loaded: loaded (/etc/systemd/system/tomcat.service; enabled)
Active: failed (Result: exit-code)
원인 및 해결:
1. 로그 확인:
sudo journalctl -u tomcat.service -n 100 --no-pager
2. 설정 파일 검증:
# systemd 설정 검증
sudo systemd-analyze verify /etc/systemd/system/tomcat.service
# Tomcat 설정 검증
/home/svcuser/apps/tomcat/bin/catalina.sh configtest
3. 권한 확인:
# 실행 파일 권한
ls -l /home/svcuser/apps/tomcat/bin/startup.sh
# 실행 권한 부여
chmod +x /home/svcuser/apps/tomcat/bin/startup.sh
4. 환경 변수 확인:
# JAVA_HOME 확인
echo $JAVA_HOME
ls -l $JAVA_HOME/bin/java
문제 2: PID 파일 관련 오류
증상:
Failed to start tomcat.service: PID file not readable
해결:
# PID 파일 경로 확인 및 수정
[Service]
PIDFile=/home/svcuser/apps/tomcat/tomcat.pid
# startup.sh에서 PID 파일 생성 확인
# CATALINA_PID 환경 변수 설정
Environment="CATALINA_PID=/home/svcuser/apps/tomcat/tomcat.pid"
문제 3: 서비스는 시작되지만 포트가 열리지 않음
증상:
$ sudo systemctl status tomcat.service
Active: active (running)
$ curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080
원인 및 해결:
1. 프로세스 확인:
ps aux | grep tomcat
2. 포트 확인:
sudo netstat -nltp | grep 8080
sudo ss -nltp | grep 8080
3. 로그 확인:
tail -f /home/svcuser/apps/tomcat/logs/catalina.out
4. 타임아웃 증가:
[Service]
TimeoutStartSec=300 # 5분으로 증가
문제 4: 부팅 시 자동 시작 안 됨
증상: 서버 재부팅 후 서비스가 시작되지 않음
해결:
1. 활성화 확인:
systemctl is-enabled tomcat.service
# disabled라면 활성화
sudo systemctl enable tomcat.service
2. 심볼릭 링크 확인:
ls -l /etc/systemd/system/multi-user.target.wants/tomcat.service
3. 의존성 확인:
# network.target 이후 시작되도록 설정
[Unit]
After=network.target network-online.target
Wants=network-online.target
문제 5: 서비스 중지가 느림
증상:
$ sudo systemctl stop tomcat.service
(30초 이상 대기...)
원인: Tomcat이 정상 종료되지 않아 TimeoutStopSec 후 강제 종료됨
해결:
1. 타임아웃 증가:
[Service]
TimeoutStopSec=60
2. shutdown.sh 확인:
# shutdown.sh가 정상 동작하는지 확인
/home/svcuser/apps/tomcat/bin/shutdown.sh
3. 강제 종료 추가:
[Service]
ExecStop=/home/svcuser/apps/tomcat/bin/shutdown.sh
KillMode=mixed
KillSignal=SIGTERM
실전 활용 시나리오
시나리오 1: 여러 서비스 순차 시작
Tomcat → Nginx 순서로 시작:
tomcat.service:
[Unit]
Description=Apache Tomcat
After=network.target
[Service]
Type=forking
ExecStart=/home/svcuser/apps/tomcat/bin/startup.sh
...
[Install]
WantedBy=multi-user.target
nginx.service:
[Unit]
Description=Nginx
After=network.target tomcat.service
Requires=tomcat.service
[Service]
Type=forking
ExecStart=/home/svcuser/apps/nginx/sbin/nginx
...
[Install]
WantedBy=multi-user.target
의존성:
After=tomcat.service: Tomcat 시작 후 Nginx 시작Requires=tomcat.service: Tomcat 실패 시 Nginx도 시작 안 함
시나리오 2: 헬스 체크 추가
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
ExecStart=/home/svcuser/apps/myapp/start.sh
ExecStartPost=/bin/bash -c 'for i in {1..30}; do curl -f http://localhost:8080/health && break || sleep 1; done'
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
설명:
ExecStartPost: 시작 후 30초 동안 health check- health check 성공 시 서비스 정상 완료
- 실패 시 Restart 정책에 따라 재시작
시나리오 3: 환경별 서비스 분리
# 개발 환경
/etc/systemd/system/myapp-dev.service
# 운영 환경
/etc/systemd/system/myapp-prod.service
myapp-prod.service:
[Unit]
Description=My App (Production)
[Service]
Type=simple
EnvironmentFile=/home/svcuser/apps/myapp/prod.env
ExecStart=/usr/bin/java -jar /home/svcuser/apps/myapp/app.jar
[Install]
WantedBy=multi-user.target
prod.env:
SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=8080
DATABASE_URL=jdbc:postgresql://prod-db:5432/myapp
자동화 스크립트
서비스 파일 생성 스크립트
create-service.sh:
#!/bin/bash
# 사용법: ./create-service.sh tomcat /home/svcuser/apps/tomcat svcuser
SERVICE_NAME=$1
APP_HOME=$2
USER=$3
if [ -z "$SERVICE_NAME" ] || [ -z "$APP_HOME" ] || [ -z "$USER" ]; then
echo "사용법: $0 <서비스명> <앱경로> <사용자>"
echo "예시: $0 tomcat /home/svcuser/apps/tomcat svcuser"
exit 1
fi
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
echo "서비스 파일 생성 중: $SERVICE_FILE"
sudo tee $SERVICE_FILE > /dev/null <<EOF
[Unit]
Description=${SERVICE_NAME} Service
After=syslog.target network.target
[Service]
Type=forking
User=${USER}
Group=${USER}
ExecStart=${APP_HOME}/bin/startup.sh
ExecStop=${APP_HOME}/bin/shutdown.sh
WorkingDirectory=${APP_HOME}
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
echo "서비스 파일 생성 완료"
echo ""
# 권한 설정
sudo chmod 644 $SERVICE_FILE
sudo chown root:root $SERVICE_FILE
# systemd 리로드
echo "systemd 리로드 중..."
sudo systemctl daemon-reload
# 서비스 활성화
echo "서비스 활성화 중..."
sudo systemctl enable ${SERVICE_NAME}.service
echo ""
echo "========================================="
echo "서비스 등록 완료!"
echo "========================================="
echo "시작: sudo systemctl start ${SERVICE_NAME}.service"
echo "상태: sudo systemctl status ${SERVICE_NAME}.service"
echo "로그: sudo journalctl -u ${SERVICE_NAME}.service -f"
사용 예시:
chmod +x create-service.sh
./create-service.sh tomcat /home/svcuser/apps/tomcat svcuser
정리
핵심 요약
- systemd 서비스 등록으로 자동 시작
- 매번 수동 실행 불필요
- 서버 재부팅 후 자동 기동
- 서비스 파일 작성 위치
/etc/systemd/system/권장- Unit 파일 형식 (INI)
- 필수 섹션
[Unit]: 설명 및 의존성[Service]: 실행 방식 및 명령어[Install]: 활성화 타겟
- Type 선택
simple: 포그라운드 실행forking: 데몬 방식 (Tomcat, Nginx)
- 로그 관리
- journalctl로 통합 관리
- 실시간 모니터링 가능
체크리스트
서비스 등록 시 확인 사항:
- 서비스 파일 작성 (
/etc/systemd/system/) - 절대 경로 사용 (ExecStart, ExecStop)
- User/Group 설정 (보안)
- 환경 변수 설정 (JAVA_HOME 등)
- Type 올바르게 선택
- After 의존성 설정
- Restart 정책 설정
- Timeout 설정 (충분히)
- PID 파일 설정 (forking)
- daemon-reload 실행
- enable로 활성화
- start로 시작
- status로 확인
- 재부팅 테스트
AWS 비용 절감 효과
Before:
1. 서버 시작
2. SSH 접속
3. Tomcat 시작 스크립트 실행
4. Nginx 시작 스크립트 실행
5. 애플리케이션 시작 스크립트 실행
(매일 반복...)
After:
1. 서버 시작
(끝!)
추가 이점:
- 휴먼 에러 제거 (시작 깜빡함)
- 시간 절약 (하루 5분 × 20일 = 100분/월)
- 서비스 안정성 향상 (자동 재시작)
마지막으로
systemd 서비스 등록은 한 번 설정하면 평생 편하다.
AWS 비용 절감을 위해 서버를 껐다 켰다 하는 환경에서는 필수이며, 운영 환경에서도 서비스 관리를 체계적으로 할 수 있다.
오늘 설정하면 내일부터 자동이다.
댓글남기기