이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Spring 애플리케이션을 어떤 Linux 사용자로 실행해야 하는가?
  • 권한 문제와 프로세스 문제를 장애 중 어떻게 분리하는가?
  • chmod 777, root 실행, kill -9 같은 대응이 왜 위험한가?

개요

운영 서버에서 Spring 애플리케이션은 결국 Linux 프로세스다.

프로세스는 특정 사용자와 그룹 권한으로 실행되고, 파일을 읽고 쓰며, 포트를 열고, signal을 받아 종료된다.

그래서 백엔드 개발자도 최소한 누가 실행 중인지, 어떤 권한으로 파일에 접근하는지, 어떤 PID가 CPU와 memory를 쓰는지를 읽을 수 있어야 한다.

기본 원칙

운영 애플리케이션은 보통 root가 아니라 별도 사용자로 실행한다.

예를 들어 app, spring, order-api 같은 전용 사용자를 둔다.

전용 사용자를 쓰면 서비스가 침해되거나 잘못된 파일을 수정하더라도 피해 범위를 줄일 수 있다.

id app
groups app
sudo -u app whoami

root로 실행하면 권한 오류가 줄어든 것처럼 보이지만, 실제로는 안전장치를 없애는 것이다.

Spring 애플리케이션은 필요한 디렉터리에만 쓰기 권한을 가져야 한다.

파일 권한 확인

배포 디렉터리, 로그 디렉터리, 환경 파일 권한을 확인한다.

ls -ld /opt/order-api
ls -l /opt/order-api/app.jar
ls -l /etc/order-api/order-api.env
ls -ld /var/log/order-api

운영에서 자주 보는 문제는 다음이다.

  • jar 파일을 읽을 권한이 없다.
  • log directory에 쓸 권한이 없다.
  • EnvironmentFile을 systemd 서비스 사용자가 읽지 못한다.
  • 배포 파일 소유자가 매번 달라진다.
  • 임시로 chmod 777을 해두고 되돌리지 않는다.

권한 문제를 해결할 때는 넓게 열기보다 필요한 대상만 바꾼다.

sudo chown -R app:app /opt/order-api
sudo chmod 750 /opt/order-api
sudo chmod 640 /etc/order-api/order-api.env

Secret이 들어 있는 환경 파일은 world-readable이면 안 된다.

프로세스 확인

장애 중에는 먼저 프로세스가 실제로 떠 있는지 확인한다.

ps -eo pid,user,stat,comm,args --sort=-%mem | head
pgrep -af 'order-api|java'

봐야 할 것은 단순 PID가 아니다.

  • 실행 사용자가 의도한 사용자와 같은가?
  • 같은 서비스가 중복 실행 중인가?
  • CPU나 memory를 과도하게 쓰는가?
  • process state가 D, Z처럼 비정상인가?
  • 실행 인자에 --spring.profiles.active=prod가 맞게 들어갔는가?

프로세스가 떠 있어도 Spring이 정상이라는 뜻은 아니다.

잘못된 profile로 뜨거나, DB 연결에 실패한 채 health check만 부분적으로 살아 있을 수 있다.

Signal과 종료

프로세스 종료는 signal을 이해해야 안전하다.

보통 먼저 SIGTERM으로 정상 종료를 요청한다.

kill -TERM <pid>

kill -9는 마지막 수단이다.

SIGKILL은 애플리케이션이 정리 코드를 실행할 기회를 주지 않는다.

Spring 애플리케이션에서 shutdown hook, connection 정리, in-flight request 마무리가 필요하다면 kill -9는 장애를 키울 수 있다.

systemd로 관리되는 서비스는 직접 PID를 죽이기보다 service 단위로 조작하는 편이 낫다.

sudo systemctl stop order-api
sudo systemctl start order-api

장애 신호

  • Permission denied가 systemd journal이나 애플리케이션 로그에 반복된다.
  • 배포 후 jar 소유자가 deploy 사용자로 남아 app 사용자가 읽지 못한다.
  • 로그 파일이 root 소유로 만들어져 다음 배포에서 쓰기 실패가 난다.
  • 같은 jar가 여러 PID로 떠 있어 포트 충돌이나 중복 batch 실행이 발생한다.
  • CPU 사용률이 높은 java 프로세스의 실행 인자가 기대한 서비스와 다르다.
  • kill -9 후 임시 파일이나 lock 파일이 남아 재시작이 실패한다.

안전한 완화 조치

권한 문제가 의심되면 먼저 읽기 명령으로 상태를 확인한다.

바로 chmod -R 777을 실행하지 않는다.

서비스 사용자, 대상 경로, 필요한 권한을 좁힌 뒤 변경한다.

프로세스가 중복 실행 중이면 어느 PID가 systemd가 관리하는 PID인지 확인한다.

systemctl status order-api
cat /proc/<pid>/cmdline | tr '\0' ' '

프로세스를 종료해야 한다면 SIGTERM, systemd stop, traffic drain 가능성을 먼저 검토한다.

DB migration, batch, file upload 처리 중인 프로세스라면 강제 종료 전에 영향 범위를 확인해야 한다.

인프라 담당자와 공유할 자료

서비스: order-api prod
시간대: 2026-06-30 14:10~14:25 KST
실행 사용자: app
PID: 18231
문제: /var/log/order-api 쓰기 Permission denied
확인 명령:
- ls -ld /var/log/order-api
- ps -eo pid,user,args | grep order-api
최근 변경: 14:00 배포 스크립트 사용자 deploy -> app 변경
요청: 배포 디렉터리와 로그 디렉터리 소유권 정책 확인

이 정도 정보를 주면 인프라 담당자는 사용자, group, 배포 스크립트, systemd unit을 빠르게 확인할 수 있다.

실전 팁

  • 서비스마다 실행 사용자를 명확히 둔다.
  • 애플리케이션 디렉터리와 로그 디렉터리 소유자를 배포 절차에 포함한다.
  • Secret 파일은 읽을 사용자와 group을 제한한다.
  • ps 출력은 PID보다 user, args, start time을 함께 본다.
  • 강제 종료 전에는 in-flight request, batch, migration 여부를 확인한다.

위험 신호!

  • Spring 애플리케이션을 root로 실행한다.
  • 장애 중 권한 문제를 chmod -R 777로 해결한다.
  • 배포 때마다 파일 소유자가 달라진다.
  • 어떤 사용자가 서비스를 실행하는지 팀원이 모른다.
  • systemd가 관리하는 PID와 수동 실행 PID가 섞여 있다.
  • 원인 확인 없이 kill -9를 반복한다.

확인 질문

확인 질문

  • 운영 Spring 프로세스를 root로 실행하면 위험한 이유는 무엇인가?
    • 서비스 침해나 명령 실수의 피해 범위가 서버 전체로 커지고, 파일 권한 문제가 숨겨져 나중에 더 큰 장애로 나타날 수 있기 때문이다.
  • 권한 문제를 해결할 때 가장 먼저 확인할 것은 무엇인가?
    • 실행 사용자, 대상 파일과 디렉터리의 owner/group/mode, systemd unit의 User, 환경 파일 권한이다.
  • kill -9를 마지막 수단으로 봐야 하는 이유는 무엇인가?
    • 애플리케이션이 정상 종료와 자원 정리를 할 기회를 잃어 요청 실패, lock 잔존, 데이터 처리 중단을 만들 수 있기 때문이다.

참고 문서