Contents
see ListNode.js 서비스를 프로세스 하나로만 실행하면 생기는 문제
서버에서 Node.js 애플리케이션을 node server.js로 직접 실행하면 SSH 연결이 끊겼을 때 프로세스가 종료되기 쉽고, 예기치 않은 오류 뒤에 자동으로 다시 시작되지 않습니다. 재부팅 뒤에 실행을 잊거나, 환경변수와 작업 디렉터리가 운영자마다 달라지는 문제도 자주 발생합니다. Linux의 systemd는 서비스를 운영체제에 등록해 부팅 시 시작, 비정상 종료 후 재시작, 실행 권한 제한, 로그 조회를 한곳에서 관리하게 해 줍니다.
이 문서는 Ubuntu·Debian 계열을 예로 들지만 systemd를 사용하는 대부분의 배포판에서 같은 방식으로 적용할 수 있습니다. 애플리케이션 코드 수정 없이도 운영 안정성을 높일 수 있는 기본 구성을 다룹니다.
운영 계정과 배포 경로를 먼저 분리하기
root 계정으로 애플리케이션을 실행하지 않는 것이 원칙입니다. 서비스 전용 계정을 만들고, 소스와 빌드 산출물은 전용 디렉터리에 둡니다. 예를 들어 /srv/orders-api에 애플리케이션을 배포하고 nodeapp 계정만 이 경로를 소유하도록 구성합니다. 배포 담당자가 파일을 교체하는 경우에도 실행 계정의 권한과 비밀값이 불필요하게 넓어지지 않도록 그룹 권한을 별도로 설계해야 합니다.
sudo useradd --system --create-home --shell /usr/sbin/nologin nodeapp
sudo install -d -o nodeapp -g nodeapp /srv/orders-api
sudo install -d -o nodeapp -g nodeapp /etc/orders-api
sudo chown -R nodeapp:nodeapp /srv/orders-api환경변수 파일에는 데이터베이스 비밀번호나 API 키처럼 실행 시 필요한 값만 둡니다. 이 파일은 저장소에 커밋하지 말고, 소유자를 root로 하고 서비스 계정만 읽을 수 있게 제한합니다. 공백이 포함된 값은 따옴표로 감싸고, 셸 확장이 필요한 문법은 넣지 않는 편이 안전합니다.
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://app:[email protected]:5432/orders환경변수 파일은 root 소유, 서비스 그룹 읽기 권한으로 만들고 chmod 640을 적용합니다. 애플리케이션이 읽을 필요 없는 배포 계정에는 이 파일을 노출하지 않습니다.
서비스 유닛 파일의 핵심 설정
/etc/systemd/system/orders-api.service 파일을 만들고 서비스의 실행 파일, 현재 디렉터리, 계정, 환경변수 파일을 명시합니다. WorkingDirectory를 지정하지 않으면 상대 경로로 읽는 설정 파일이나 업로드 경로가 의도와 다르게 동작할 수 있습니다. Node.js 경로는 command -v node로 실제 설치 위치를 확인한 뒤 절대 경로를 사용합니다. nvm처럼 로그인 셸에 의존하는 설치 방식은 systemd에서 경로를 찾지 못하는 원인이 되므로 운영 환경에서는 특히 점검이 필요합니다.
[Unit]
Description=Orders API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=nodeapp
Group=nodeapp
WorkingDirectory=/srv/orders-api
EnvironmentFile=/etc/orders-api/orders-api.env
ExecStart=/usr/bin/node /srv/orders-api/server.js
Restart=on-failure
RestartSec=5
TimeoutStartSec=30
TimeoutStopSec=30
KillSignal=SIGTERM
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
[Install]
WantedBy=multi-user.targetRestart=on-failure는 오류 종료나 비정상 종료에서만 재시작합니다. 정상 배포를 위해 systemctl stop을 실행했을 때 무한히 다시 살아나는 것을 막을 수 있습니다. 다만 애플리케이션이 시작 직후 계속 실패하면 재시작이 반복될 수 있으므로, 오류 원인은 반드시 journal 로그에서 확인해야 합니다. HTTP 서버는 SIGTERM을 받았을 때 새 요청 수신을 중단하고 진행 중 요청과 데이터베이스 연결을 정리한 뒤 종료하도록 구현하는 것이 좋습니다.
등록·실행·상태 확인 순서
유닛 파일을 수정한 뒤에는 systemd 설정을 다시 읽혀야 합니다. 이후 enable은 재부팅 후 자동 시작을 등록하고, restart는 새 코드나 새 환경변수를 반영합니다. 환경변수 파일만 수정한 경우에도 restart가 필요합니다.
sudo systemctl daemon-reload
sudo systemctl enable --now orders-api.service
sudo systemctl status orders-api.service
sudo journalctl -u orders-api.service -n 100 --no-pager
sudo journalctl -u orders-api.service -f상태 화면에서 active (running)만 확인하지 말고 실제 포트가 열렸는지, 애플리케이션의 헬스 체크가 200을 반환하는지도 함께 확인합니다. 로컬 바인딩 서비스라면 외부에 포트를 노출하지 않고 reverse proxy가 요청을 전달하게 구성할 수 있습니다. 배포 직후에는 이전 프로세스가 남아 포트를 점유하지 않았는지, 새 로그에 연결 오류나 마이그레이션 오류가 없는지 검토합니다.
운영 중 자주 놓치는 제한과 보안
systemd의 보안 옵션은 애플리케이션 권한을 최소화하는 데 도움이 됩니다. NoNewPrivileges=true는 실행 중 추가 권한 획득을 막고, PrivateTmp=true는 다른 서비스와 임시 디렉터리를 분리합니다. 파일 업로드나 홈 디렉터리 접근이 필요한 서비스에서는 ProtectHome=true가 영향을 줄 수 있으므로 스테이징 환경에서 먼저 확인해야 합니다. 쓰기 경로가 명확하다면 ReadWritePaths=/srv/orders-api/uploads 같은 제한을 추가하는 방식도 유효합니다.
로그는 기본적으로 journald에 남습니다. 로그가 너무 많아지는 문제는 애플리케이션의 요청 본문·토큰·개인정보 기록을 줄이는 것부터 해결해야 합니다. 단순히 로그 보존 기간을 늘리는 것은 장애 분석에는 유리하지만 디스크 사용량과 민감 정보 노출 위험을 함께 키울 수 있습니다. 오류에는 요청 식별자, 처리 단계, 안전하게 마스킹한 오류 코드를 남기고 비밀값은 절대 기록하지 않습니다.
배포 전 체크리스트
- 서비스 전용 계정으로 실행하고 root 실행을 피했는가
- 실행 경로·작업 디렉터리·Node.js 절대 경로가 유닛 파일에 명시됐는가
- 환경변수 파일의 소유자와 권한이 최소 권한으로 제한됐는가
- 비정상 종료 재시작과 SIGTERM 기반 정상 종료를 모두 시험했는가
- systemctl 상태, journal 로그, 헬스 체크를 배포 검증에 포함했는가
systemd 유닛은 단순한 자동 시작 설정이 아니라 서비스의 실행 조건과 장애 대응 규칙을 코드 밖에서 명확히 하는 운영 계약입니다. 작은 API부터 이 기준을 적용하면 재부팅, 배포, 장애 상황에서 확인해야 할 항목을 일관되게 줄일 수 있습니다.
os
| No | 작성일 | Title |
|---|---|---|
| 320 | 2015. 10. 16. | [ linux ] tar 및 tar.gz 압축 , 압축해제 |
| 284 | 2015. 05. 29. | [ linux ] ps -ef 프로세서 상태 확인 |
| 245 | 2015. 03. 11. | [ centos ] centos /lib/ld-linux.so.2: bad ELF interpreter: 그런 파일이나 디렉터리가 없습니다 오류 |
| 244 | 2015. 03. 10. | [ centos ] centos 7 apache 설치 및 tomcat 연동 |
| 243 | 2015. 03. 10. | [ CentOS ] centos 7 console boot |
| 226 | 2015. 03. 09. | [ centos ] centos 7 에 tomcat 서비스등록 ( systemclt ) |
| 225 | 2015. 03. 08. | [ centos ] centos 7 samba 설치하기 공유 폴더 윈도우에서 사용하기 |
| 224 | 2015. 03. 08. | [ centos ] centos 7 service 관리 ( systemctl ) |
| 223 | 2015. 03. 08. | [ centos ] centos 7 firewall ( 방화벽 ) 설정 |
| 183 | 2015. 03. 08. | [ centos ] centos 7 nfs 설정 |