Joonmook 프로필 사진

@Joonmook

Blog

혼자 운영하는 서버의 배포 스크립트에 넣어둔 안전장치들

이 블로그의 배포는 오랫동안 "서버에 ssh로 들어가서 git pull 하고 pm2 restart"였다. 손으로 하는 배포는 잘 되는 날에는 문제가 없다. 문제는 급할 때 순서를 빼먹는 날이다. 그래서 npm run deploy 한 줄로 끝나는 스크립트를 만들었고, 만들면서 넣은 안전장치를 적어둔다. 전부 한 번씩 데인 뒤에 넣은 것들이다.

구조

순서는 네 단계다. 접속 확인, 파일 전송, 의존성 설치와 DB 마이그레이션, pm2 reload. 전송은 rsync를 쓴다. 바뀐 파일만 보내고, 로컬에서 지운 파일은 서버에서도 지운다.

rsync -az --delete --itemize-changes \
  -e "ssh -i $DEPLOY_KEY" \
  --exclude-from=deploy/rsync-exclude.txt \
  ./ "$REMOTE:$DEPLOY_PATH/"

--delete 는 무섭다

--delete 가 없으면 지운 파일이 서버에 영원히 남는다. 있으면 경로를 잘못 잡았을 때 서버 폴더를 통째로 비운다. 그래서 두 가지를 걸었다.

첫째, 제외 목록. .env, uploads/, node_modules/, .git/ 은 로컬에 없거나 달라도 서버 것을 건드리지 않는다. rsync는 exclude 된 경로를 삭제 대상에서도 제외하기 때문에, 서버에만 있는 업로드 파일이 안전하다. 이걸 빼먹으면 첫 배포에서 사용자 업로드가 전부 날아간다.

둘째, 경로 검사. 배포 경로가 홈 디렉터리나 루트면 스크립트가 거부한다. 그리고 대상 폴더가 비어 있거나 package.json이 있을 때만 진행한다. 오타로 엉뚱한 폴더를 가리켰을 때 그 폴더 안의 남의 파일을 지우지 않기 위해서다.

case "$DEPLOY_PATH" in
  ""|/|/root|/home|"/home/$DEPLOY_USER") echo "위험한 경로"; exit 1 ;;
esac
remote "[ -z \"\$(ls -A '$DEPLOY_PATH')\" ] || [ -f '$DEPLOY_PATH/package.json' ]" || exit 1

비대화형 ssh 에서는 nvm 이 없다

스크립트에서 ssh로 명령을 보내면 .bashrc 가 초반에 return 해버려서 nvm 이 로드되지 않는다. 터미널에서는 되는 node 가 스크립트에서는 command not found 가 된다. 이건 거의 모든 사람이 한 번씩 겪는다. 해결은 원격 스크립트 첫 줄에서 nvm 을 직접 읽는 것이다.

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

지금 서버는 apt Node 라 PATH에 있지만, 언젠가 nvm 으로 옮길 걸 알기 때문에 미리 넣어뒀다.

마이그레이션은 배포의 일부다

댓글 테이블을 추가하면서 CREATE TABLE IF NOT EXISTS 를 실행하는 migrate 스크립트를 만들고, 배포 스크립트가 npm install 다음에 항상 실행하도록 했다. 여러 번 실행해도 안전하니까 매번 돌린다. "코드는 배포됐는데 테이블이 없어서 500" 은 손으로 배포하던 시절에 실제로 겪은 일이다.

restart 가 아니라 reload

pm2 restart 는 프로세스를 죽이고 다시 띄운다. 그 사이 요청은 502다. reload 는 새 프로세스를 띄운 뒤 옛 프로세스를 내린다. fork 모드라도 잠깐의 끊김이 훨씬 짧다. 스크립트는 reload 를 쓰고, 프로세스가 아예 없으면 그때만 start 한다.

if pm2 describe "$PM2_TARGET" >/dev/null 2>&1; then
  pm2 reload "$PM2_TARGET" --update-env
else
  pm2 start server.js
fi

덤: root 소유 node_modules

배포 때마다 npm 이 EACCES 경고를 잔뜩 냈다. 예전에 sudo npm install 을 한 번 한 탓에 node_modules 안에 root 소유 폴더가 열몇 개 남아 있었다. 설치는 되지만 정리가 안 된다. chown 한 줄이면 끝나는 일인데, 이런 건 스크립트가 알려주지 않으면 몇 달을 모른 채 지나간다. 배포 로그를 경고까지 읽게 만드는 게 다음 과제다.

그래서

배포 스크립트의 가치는 빠른 게 아니라 순서를 잊지 않는 것잘못된 입력에 멈추는 것에 있다. 혼자 운영할수록 그렇다. 리뷰해 줄 사람이 없으니 스크립트가 리뷰어 역할을 해야 한다.

Comments 0

No comments yet. Be the first!

← Back to Blog