프로세스·서비스 관리 (systemd)
5. 자주 하는 실수 (Tip)
❌ 실수 1: 유닛 파일을 고치고 daemon-reload 를 안 한다
restart 를 해도 옛 설정으로 뜹니다. 바뀐 JAVA_OPTS 가 안 먹는다고 한참 헤맵니다. ✅ 유닛 파일이나 EnvironmentFile 을 고친 뒤에는 항상 systemctl daemon-reload 를 먼저 합니다.
sudo vi /etc/systemd/system/myapp.service
sudo systemctl daemon-reload && sudo systemctl restart myapp❌ 실수 2: kill -9 를 습관적으로 먼저 쓴다
DB 커넥션이 반납되지 않고 처리 중 요청이 끊깁니다. 파일 쓰기 중이면 깨진 파일이 남습니다. ✅ kill PID 로 TERM 을 먼저 보내고 30초 기다립니다. systemd 서비스면 systemctl stop 이 이 과정을 대신합니다.
❌ 실수 3: ExecStart 에 셸 문법을 쓴다
ExecStart=cd /opt/myapp && java -jar app.jar 는 cd 라는 실행 파일을 찾다 실패합니다. ✅ WorkingDirectory 로 폴더를 정하고 ExecStart 는 절대 경로 하나만 씁니다. 복잡하면 /bin/bash -c '...' 나 실행 스크립트로 뺍니다.
❌ 실수 4: SuccessExitStatus=143 을 빠뜨린다
systemctl stop 을 할 때마다 상태가 failed 로 남고 Restart=on-failure 가 다시 띄웁니다. ✅ 자바 앱은 TERM 에 143 을 돌려주므로 이 값을 성공으로 등록합니다.
❌ 실수 5: start 만 하고 enable 을 잊는다
잘 돌다가 몇 달 뒤 패치 재부팅 이후 서비스가 안 떠 있습니다. ✅ systemctl enable --now myapp 으로 둘을 한 번에 하고 systemctl is-enabled myapp 으로 확인합니다.
❌ 실수 6: root 로 서비스를 띄운다
User= 를 빼면 root 로 뜹니다. 앱 취약점이 서버 전체 장악으로 이어집니다. ✅ 전용 계정을 만들고 User=myapp 을 적습니다. 배포 폴더와 로그 폴더 소유권도 그 계정으로 맞춥니다.
❌ 실수 7: nohup & 로 운영 서비스를 띄운다
재부팅 후 안 뜨고, 죽어도 아무도 모르고, 누가 어떤 옵션으로 띄웠는지 기록이 없습니다. ✅ 임시 테스트가 아니면 유닛 파일을 씁니다. 20줄이면 됩니다.
❌ 실수 8: ps | grep java 결과에 grep 자신이 섞인다
스크립트에서 grep -c 로 세면 항상 1 이상이 나와 "떠 있다" 고 오판합니다. ✅ grep '[j]ava' 트릭이나 pgrep -f, 또는 systemctl is-active 를 씁니다.
ps -ef | grep -c java # 항상 1 이상 (grep 자신)
pgrep -fc 'app.jar' # 실제 개수❌ 실수 9: EnvironmentFile 에 셸 문법을 쓴다
export JAVA_OPTS="-Xmx1g" 처럼 쓰면 export 까지 키 이름에 들어가 변수가 안 잡힙니다. ✅ JAVA_OPTS=-Xmx1g 처럼 키=값 만 적습니다. 공백이 있는 값은 큰따옴표로 감싸되 이스케이프는 셸과 다르니 단순하게 둡니다.
❌ 실수 10: TimeoutStopSec 이 앱의 완전 종료 시간보다 짧다
앱이 요청을 마무리하는 중에 KILL 을 받아 결국 요청이 끊깁니다. ✅ Spring 의 timeout-per-shutdown-phase 보다 TimeoutStopSec 을 넉넉히 크게 둡니다. 예를 들어 앱 20초, systemd 30초입니다.