매번 FTP 프로그램을 열어 파일을 올리고, 서버에 접속해서 프로세스를 재시작하는 배포 방식에 지치지 않으셨나요? 작은 수정 사항 하나 반영할 때마다 반복되는 단순 작업에 시간을 뺏기고 있다면, 혹은 한순간의 실수로 서비스가 중단될까 봐 마음 졸이고 있다면 이 글이 바로 당신을 위한 해결책입니다. git push 단 한 번으로 모든 배포 과정이 마법처럼 자동으로 처리되는 세상을 상상해 보세요.
바로 GitLab의 강력한 CI/CD 기능과 Node.js의 든든한 동반자 PM2의 deploy 기능이 만나면 현실이 됩니다. 이 조합은 단순히 편리함을 넘어, 개발 생산성을 극대화하고 서비스 안정성을 획기적으로 높여주는 최고의 시너지를 발휘합니다. 하지만 막상 시작하려고 하면 어디서부터 손대야 할지 막막하게 느껴질 수 있습니다. SSH 키 설정은 왜 이렇게 복잡하고, ecosystem.config.js 파일은 어떻게 작성해야 하며, .gitlab-ci.yml 파이프라인은 또 어떻게 연결해야 할까요?
괜찮습니다. 오늘 이 글 하나로, 여러분의 프로젝트에 날개를 달아줄 GitLab과 PM2를 이용한 배포 자동화 시스템 구축의 모든 것을 A부터 Z까지, 실제 현업에서 바로 적용할 수 있는 꿀팁까지 담아 완벽하게 알려드리겠습니다. 더 이상 수동 배포의 악몽에 시달리지 마세요. 이제 우리도 똑똑하고 우아하게 배포할 시간입니다.
H2: 왜 PM2와 GitLab의 조합이 환상적인가요?
본격적인 설정에 앞서, 왜 수많은 개발자들이 이 두 가지 도구의 조합에 열광하는지 이해할 필요가 있습니다. 각각의 역할과 시너지를 알면 앞으로의 과정을 더 쉽게 이해하고 응용할 수 있게 됩니다.
- GitLab: 단순한 Git 저장소 그 이상입니다. 프로젝트 관리, 이슈 트래킹은 물론, 강력한 CI/CD(지속적 통합/지속적 배포) 파이프라인 기능을 내장하고 있어 별도의 도구 없이도 코드 푸시와 동시에 테스트, 빌드, 배포까지의 모든 과정을 자동화할 수 있습니다.
- PM2: Node.js 애플리케이션을 위한 프로세스 매니저입니다. 단순히
node app.js로 실행하는 것과 차원이 다릅니다. 예기치 못한 에러로 프로세스가 종료되면 자동으로 재시작해주고, 여러 CPU 코어를 활용하는 클러스터 모드를 지원하며, 무엇보다 무중단 리로드(Zero-Downtime Reload) 기능을 통해 서비스 중단 없이 업데이트를 반영할 수 있게 해줍니다.
이 둘이 만나면, 개발자가 GitLab에 코드를 push하는 순간, GitLab의 CI/CD 기능이 이 변경 사항을 감지하여 미리 정의된 스크립트를 실행합니다. 그리고 이 스크립트는 서버에 접속하여 PM2의 deploy 명령어를 호출하고, PM2는 소스 코드를 가져와(pull) 필요한 패키지를 설치(install)한 뒤, 서비스 중단 없이 새로운 버전으로 애플리케이션을 재시작(reload)해주는 완벽한 자동화 파이프라인이 완성되는 것입니다.
H2: PM2 Deploy, 핵심 개념부터 파헤치기
PM2 배포 자동화의 심장은 바로 ecosystem.config.js 파일에 있습니다. 이 파일 하나에 배포에 필요한 모든 정보를 담아두고, 우리는 pm2 deploy라는 간단한 명령어로 이 모든 설정을 불러와 실행하게 됩니다.
H3: PM2와 Deploy 기능 소개
PM2는 Node.js 생태계에서 가장 유명한 프로세스 매니저입니다. 단순히 앱을 실행하는 것을 넘어, 성능 모니터링, 로그 관리, 클러스터링 등 운영에 필요한 필수 기능들을 제공합니다. 그중에서도 deploy 기능은 내장된 배포 도구로, SSH를 통해 원격 서버에 접속하여 Git 저장소로부터 코드를 업데이트하고 애플리케이션을 재시작하는 일련의 과정을 표준화해줍니다.
핵심은 ecosystem.config.js 파일의 deploy 섹션에 모든 정보를 정의한다는 것입니다. 이를 통해 배포 과정이 특정 개발자의 PC나 환경에 종속되지 않고, 누가 어디서 실행하든 동일한 프로세스로 배포가 진행되도록 보장합니다.
H3: ecosystem.config.js 설정: 모든 것의 시작
프로젝트의 루트 디렉토리에 ecosystem.config.js 파일을 생성하고 아래와 같이 작성해 봅시다. 이 파일은 배포 시나리오의 설계도와 같습니다.
module.exports = {
apps: [{
name: 'my-app', // PM2에서 관리할 앱 이름
script: './app.js', // 실행할 스크립트 파일
instances: 1, // 클러스터 모드 인스턴스 수
exec_mode: 'cluster', // 실행 모드
env: {
NODE_ENV: 'development',
},
env_production: {
NODE_ENV: 'production',
}
}],
// 배포 설정
deploy: {
production: {
user: 'ubuntu', // 서버에 접속할 유저 이름
host: 'YOUR_SERVER_IP', // 서버 IP 주소
ref: 'origin/main', // 배포할 Git 브랜치
repo: 'GIT_REPOSITORY_URL', // Git 저장소 주소 (e.g., git@gitlab.com:user/repo.git)
path: '/home/ubuntu/my-app', // 서버에 코드가 배포될 경로
'pre-setup': '', // 서버에 처음 배포할 때 실행할 명령어
'post-setup': 'npm install && pm2 start ecosystem.config.js --env production', // 초기 설정 후 실행할 명령어
'pre-deploy-local': '', // 로컬에서 배포 전에 실행할 명령어
'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production --update-env', // 배포 후 실행할 명령어
'ssh_options': 'StrictHostKeyChecking=no' // SSH 옵션
}
}
};
각 항목을 자세히 살펴보겠습니다.
이 파일을 프로젝트에 추가하고 Git에 커밋하는 것만으로도 배포 준비의 절반은 끝난 셈입니다.
H2: GitLab CI/CD 파이프라인 연동: 자동화의 화룡점정
이제 ecosystem.config.js라는 설계도를 만들었으니, GitLab이 이 설계도를 읽고 실행하도록 자동화 파이프라인을 구축할 차례입니다.
H3: GitLab Runner 설정: 든든한 실행 조력자
GitLab CI/CD는 실제로 작업을 수행할 '일꾼'이 필요합니다. 이를 GitLab Runner라고 부릅니다. Runner는 우리가 정의한 스크립트(예: pm2 deploy)를 실행하는 역할을 합니다. Runner는 별도의 서버에 설치할 수도 있고, 배포 대상 서버에 직접 설치할 수도 있습니다. 소규모 프로젝트라면 배포 서버에 직접 Runner를 설치하는 것이 관리가 편리합니다.
GitLab Runner 설치 및 등록 과정은 공식 문서를 참고하면 간단하게 진행할 수 있습니다. 등록 과정에서 shell executor를 선택하면, Runner가 일반 터미널 환경처럼 명령어를 실행하게 되어 PM2 연동이 매우 쉬워집니다.
H3: .gitlab-ci.yml 작성: 배포 시나리오 정의
프로젝트 루트 디렉토리에 .gitlab-ci.yml 파일을 생성합니다. 이 파일은 GitLab CI/CD 파이프라인의 동작 방식을 정의하는 파일입니다. 특정 브랜치에 코드가 푸시되면 어떤 작업들을 순서대로 실행할지 알려주는 레시피와 같습니다.
stages:
- deploy
deploy-production:
stage: deploy
script:
# 1. SSH 개인 키를 CI/CD 변수에서 가져와 파일로 저장
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
# 2. 개인 키 파일의 권한 설정 (필수!)
- chmod 600 ~/.ssh/id_rsa
# 3. SSH Agent를 사용하여 키 등록 (비밀번호 없는 키의 경우)
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/id_rsa
# 4. SSH Known Hosts 설정 (최초 접속 시 호스트 키 확인 생략)
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
# 5. PM2 설치 (Runner에 PM2가 설치되지 않은 경우)
- npm install -g pm2
# 6. 드디어 배포 명령 실행!
- pm2 deploy production --force
only:
- main # 'main' 브랜치에 푸시될 때만 이 작업을 실행
tags:
- my-runner # 이 작업을 실행할 Runner를 태그로 지정
이 YAML 파일이 조금 복잡해 보일 수 있지만, 핵심은 script 섹션에 있는 pm2 deploy production --force 명령어입니다. 나머지 부분은 GitLab Runner가 배포 서버에 SSH로 안전하게 접속하기 위한 사전 준비 작업입니다.
H3: SSH 키 설정: GitLab과 서버의 안전한 악수
가장 많은 분들이 어려움을 겪는 부분이 바로 SSH 키 설정입니다. GitLab Runner가 PM2 deploy 명령을 내리면, PM2는 ecosystem.config.js에 정의된 repo 주소에서 소스 코드를 가져오고, host 서버로 접속을 시도합니다. 이 모든 과정이 비밀번호 입력 없이 자동으로 이루어지려면 SSH 키를 이용한 인증이 필수적입니다.
총 2단계의 키 설정이 필요합니다.
- GitLab Runner -> 배포 서버 접속용 키:
- Runner가 설치된 서버(또는 로컬 PC)에서
ssh-keygen명령으로 키 쌍(공개키, 개인키)을 생성합니다. - 생성된 **공개키(
id_rsa.pub)**의 내용을 복사하여 배포 서버의~/.ssh/authorized_keys파일에 추가합니다. - 생성된 **개인키(
id_rsa)**의 내용을 복사하여 GitLab 프로젝트 > Settings > CI/CD > Variables에SSH_PRIVATE_KEY라는 이름의 변수로 등록합니다. 이때, 변수 타입을 'File'로 지정하는 것이 더 안전하고 편리합니다..gitlab-ci.yml파일의 스크립트가 바로 이 변수를 사용합니다.
- Runner가 설치된 서버(또는 로컬 PC)에서
- 배포 서버 -> GitLab 저장소 접근용 키:
- 배포 서버에서
ssh-keygen명령으로 새로운 키 쌍을 생성합니다. - 생성된 **공개키(
id_rsa.pub)**의 내용을 복사하여 GitLab 프로젝트 > Settings > Repository > Deploy Keys에 등록합니다. 이렇게 하면 배포 서버가 해당 프로젝트의 소스 코드를 읽어올(pull) 권한을 갖게 됩니다.
- 배포 서버에서
이 두 가지 방향의 '신뢰 관계'를 SSH 키로 맺어주면, 모든 인증 과정이 자동으로 처리되어 마법 같은 배포 자동화가 완성됩니다.
H2: PM2 Deploy vs. 경쟁자: 무엇이 다를까?
PM2 Deploy는 훌륭한 도구지만, 모든 상황에 맞는 만능 해결책은 아닙니다. 다른 배포 자동화 방식들과 비교하여 어떤 장단점이 있는지 파악하면, 여러분의 프로젝트 규모와 상황에 가장 적합한 기술을 선택하는 데 도움이 될 것입니다.
결론적으로, Node.js 기반의 단일 서비스나 몇 개의 서비스를 빠르고 가볍게 자동 배포하고 싶다면 PM2 Deploy + GitLab CI/CD 조합이 최고의 선택입니다. 반면, 여러 언어와 프레임워크가 혼합된 마이크로서비스 환경이라면 처음부터 Docker와 Kubernetes를 도입하는 것이 장기적으로 더 나은 선택일 수 있습니다.
H2: 실전 배포 성공률 100%를 위한 전문가 팁
기본적인 설정을 마쳤다면, 이제 운영 환경에서 발생할 수 있는 문제들을 예방하고 배포 시스템을 더욱 견고하게 만들 몇 가지 팁을 알려드립니다.
- 팁 1: 무중단 배포의 핵심,
reload를 사용하세요pm2 restart my-app은 프로세스를 완전히 껐다가 켜기 때문에 아주 짧은 시간 동안 서비스 중단이 발생합니다.pm2 reload my-app은 새로운 프로세스를 먼저 띄우고 기존 프로세스를 종료하는 방식으로, 서비스 중단 없이 업데이트를 반영합니다.post-deploy스크립트에는 반드시reload를 사용하세요.
- 팁 2: 환경 변수(.env) 똑똑하게 관리하기
- DB 접속 정보 등 민감한 정보는
.env파일에 저장하고.gitignore에 추가하는 것이 일반적입니다. - 하지만 이 경우 배포 서버에는
.env파일이 존재하지 않아 앱이 실행되지 않습니다. - GitLab CI/CD Variables에
DOTENV와 같은 이름으로.env파일의 내용을 통째로 저장한 뒤,.gitlab-ci.yml의script부분에서echo "$DOTENV" > .env명령어를post-deploy이전에 실행해주면 안전하고 효율적으로 환경 변수를 관리할 수 있습니다.
- DB 접속 정보 등 민감한 정보는
- 팁 3: 만일을 위한 롤백 전략
- 배포 후에 심각한 버그가 발견되었다면 빠르게 이전 버전으로 돌아가야 합니다.
pm2 deploy production revert 1명령어를 사용하면 이전 배포 버전으로 간단하게 롤백할 수 있습니다. 이 명령어를 미리 숙지해두면 비상 상황에 침착하게 대응할 수 있습니다.
- 팁 4: 로그는 생명줄입니다
- 배포가 실패했거나 앱 실행 중 에러가 발생했다면 가장 먼저 로그를 확인해야 합니다.
- 서버에서
pm2 logs my-app명령어를 통해 실시간으로 출력되는 로그를 확인하는 습관을 들이세요. 에러의 원인을 찾는 가장 빠른 길입니다.
H2: PM2와 GitLab으로 한 단계 더 성장하기
지금까지 우리는 git push만으로 Node.js 애플리케이션을 자동으로, 그리고 중단 없이 배포하는 강력한 시스템을 구축하는 전 과정을 살펴보았습니다. 처음에는 SSH 키와 설정 파일들이 낯설게 느껴질 수 있지만, 한번 구축해두면 반복적인 배포 작업에서 해방되어 핵심적인 코드 개발에만 집중할 수 있게 됩니다.
이 시스템은 단순히 시간을 절약해주는 것을 넘어, 사람의 실수를 원천적으로 차단하여 서비스의 안정성을 높이고, 누구든 동일한 프로세스로 배포할 수 있게 만들어 팀의 개발 문화를 한 단계 발전시키는 계기가 될 것입니다.
오늘 배운 내용을 바탕으로 여러분의 작은 토이 프로젝트부터 실제 운영 중인 서비스까지, 점진적으로 자동 배포 시스템을 도입해 보세요. 수동 배포의 번거로움과 작별하고, 개발의 즐거움에 더 깊이 빠져드는 경험을 하게 될 것이라 확신합니다. 궁금한 점이나 막히는 부분이 있다면 주저하지 말고 댓글로 질문해주세요. 여러분의 성공적인 자동화 여정을 응원하겠습니다!