ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node&Nginx를 PM2로 배포하기
    Tech/Backend 2020. 3. 20. 21:22

    처음 노드 앱을 만들어 배포하려 했던 때가 생각난다. 단순히 AWS EC2 인스턴스에서 node 명령어로 실행시키면 짜잔! 하면서 배포가 완료되는 줄만 알았는데, 전혀 아니었다. 포그라운드(Foreground)로 실행되었기 때문에 터미널을 통해 다른 일을 할 수 없었고, 터미널을 닫으니 서버 전체가 종료되었다. 사실 내 입장에서는 전혀 이해가 가지 않았다. 그러다가 주변에서 PM2나 Forever와 같은 프로세스 관리자를 사용해보라 하였고, 구글링하며 적용시켜보더니 잘 실행이 되었다. 더구나 터미널을 종료해도 서버가 살아있어서 조금 신기했던 기억이 있다. 그러나 지금까지 왜 이렇게 되는지 조차도 몰랐기에. 이번 기회에 글을 작성하며 제대로 배워보고자 했다.


    1. 프로세스의 동작방식

    1-1. 포그라운드(Foreground)

    포그라운드 프로세스는 사용자가 입력한 명령이 실행되어 결과가 출력될 때까지 기다려야 하는 포그라운드 방식으로 처리되는 프로세스이다. 

    자, AWS EC2 인스턴스에 ssh로 접속한 터미널 화면이다. 해당 화면에서 node 앱을 단순 실행시킨다면. 포그라운드 방식으로 처리가 되어 다른 명령을 입력할 수 없고 기다려야한다. 이러한 포그라운드 방식으로 작업하게 된다면, 다른 작업을 할 수 없을뿐더라 서비스하기 위해선 매일 24시간 터미널을 켜놔야할 것이다.

    1-2. 백그라운드(Background)

    다음은, 백그라운드 프로세스이다. 포그라운드 작업은 명령을 한 번에 하나씩 실행하므로 동시에 여러 개의 프로세스를 실행할 수 없었다. 그러나, 백그라운드 기능을 사용하면 앞에서 프로세스가 실행되는 동안 뒤에서 다른 프로세스가 실행될 수 있으므로 한 터미널에서 여러 개의 프로세스를 동시에 실행시키고 작업을 할 수 있게된다.

    그렇다면, 똑같은 터미널에서 node 앱을 백그라운드로 실행시켜보자. 포그라운드 방식과는 다르게 백그라운드에서 실행시키기 위해서는 명령어 마지막에 '&'을 넣어주어야 한다. '&'는 프로세스가 실행될 때 백그라운드에서 실행되도록 만드는 용어이다. 다음 명령어를 넣어 실행하였다면, 결과 값으로 [1986]이라는 프로세스 ID가 출력되는 것을 볼 수 있다. 그렇다면 node 앱이 정말로 백그라운드에서 돌아가고 있는지 확인해보도록 하자. 

    'ps -ef' 명령어를 통해서 위의 사진과 같이 현재 실행 중인 프로세스의 목록을 확인할 수 있다. 목록을 살펴보면, node 앱이 백그라운드에서 정상적으로 실행되고 있는 것을 확인할 수 있다.

    # 프로세스 목록 출력
    $ ps -ef # 프로세스의 부모/자식 관계 볼 때 사용
    $ ps -aux # 프로세스의 상태를 볼 때 사용
    
    # 프로세스 찾기
    $ ps -ef | grep [process]
    
    # 프로세스 종료
    $ kill [process id]

     

    그러나, 백그라운드 실행은 터미널과 상호작용을 하며 별도의 부모 프로세스를 가진다. 이에 부모 프로세스와 세션이 공유되기 때문에 부모 프로세스가 받는 Signal의 영향을 받는다. 즉, 부모 프로세스가 종료되면 자식인 프로세스에 종료 신호가 전달되어 같이 종료되는 것이다. 즉, 터미널을 종료하게 된다면 백그라운드 프로세스도 종료가 된다.

    1-3. 데몬(Daemon)

    데몬은 백그라운드 프로세스이기도 하다. 왜냐하면, 데몬 프로세스는 백그라운드 프로세스 중에서 부모 프로세스(PPID)가 1 혹은 다른 데몬 프로세스인 프로세스를 말하기 때문이다. 이러한 특징 때문에, 데몬은 터미널을 갖지 않으며 항상 실행될 수 있다. 따라서 터미널을 종료해도 프로세스를 유지할 수 있다.

     

    아래에서는 PM2라는 프로젝트 관리자를 통해 데몬으로 서버를 띄우는 방법을 알아볼 것이다. 그러나, 그 전에 PM2와 같은 패키지 없이 간단하게 데몬처럼 실행시킬 수 있는 방법도 다루어 보고자 한다. 그 방법은 바로 리눅스의 'nohup' 명령어를 사용하는 것이다. nohup이라는 리눅스에서 제공해주는 프로그램을 이용하면 부모 프로세스 종료 시 자식 프로세스에게 'hup(hang up)' 신호를 전달하지 않을 수 있다. 따라서 부모 프로세스(터미널)이 종료된다 하더라도 자식 프로세스가 종료되지 않게 할 수 있다.

    # nohup
    $ nohup node node/app.js &

    따라서 위의 명령어를 사용해서 노드 앱을 실행하게 된다면, 터미널 세션이 끊어지더라도 프로세스의 실행을 멈추지 않고 동작하도록 할 수 있다. 간혹가다가 명령어에 '&'만을 넣어 실행하기만 해도 터미널이 끊어진 상태에서 서버가 돌아간다는 사람도 있다. 기본적으로 '&'는 백그라운드에서 실행시킨다는 말이며, nohup이 아닐 경우 터미널이 끊어지면 실행도 끊어진다. 하지만, 요즘에는 옵션에 nohup과 같은 동작을 하게 설정이 되어 있어서 '&'만으로도 nohup과 같은 동작을 보이니 이 점 참고바란다. 또한, nohup으로 실행할 파일은 퍼미션 755 이상인 상태여야 한다.


    2. PM2 사용하기

    데몬으로 실행시킬 수 있는 프로그램들은 많다. 위에서 말한 nohup을 비롯하여 tmux, screen 등이 있다. 그 중에서도 우리는 node에서 사용할 수 있는 PM2 패키지를 사용하여 프로세스 관리를 하고자 한다. PM2는 여러가지 기능을 지원한다. 예를 들어, 프로세스를 백그라운드에서 실행하는 것은 물론, 실행 중인 프로세스를 모니터링할 수 있고, 오류로 인해 종료된 프로세스를 다시 실행시키고, 클러스터링까지 지원하기도 한다.

    2-1. PM2 설치하기

    아래의 간단한 명령어로 설치할 수 있다.

    # pm2 global 설치
    $ npm install --g pm2

    2-2. PM2 사용하기

    PM2 실행을 위한 명령어는 다음과 같다. 시작하고자 하는 파일명을 지정하여 실행할 수 있고, 만약 이름을 별도로 지정해 주고 싶다면, 두 번째 코드처럼 원하는 App의 이름을 지정해주면 된다.

    # pm2 실행하기
    $ pm2 start node/app.js
    $ pm2 start node/app.js --name=<name>

    해당 명령어를 입력하면 아래 화면과 같이 정상적으로 작동되는 모습을 볼 수 있다.

    이 외에도 종료하거나 삭제할 수 있는 명령어도 있다.

    # pm2 App 중지하기
    $ pm2 stop <id|name>
    
    # pm2 App 삭제하기
    $ pm2 delete <id|name>
    
    # pm2 App 재시작하기
    $ pm2 restart <id|name>

    PM2로 실행된 App의 로그를 확인할 수 있는 명령어도 있다.

    # pm2 로그 확인하기
    $ pm2 log
    $ pm2 log <id|name>

    다음으로는, App의 상태를 확인해볼 수 있는 명령어도 존재한다. 이를 통해서 실행 중인 App의 간략한 정보를 볼 수 있는데, 로그 파일에 대한 경로 등도 볼 수 있다.

    # pm2 App 상태 확인하기
    $ pm2 show <id|name>

    이 외에도 PM2에 등록된 관리 리스트를 볼 수 있는 list 옵션이나 PM2로 관리되는 프로세스별 상태와 실시간 로그까지 확인할 수 있는 monit 옵션 등 다양한 옵션들이 있다. 시간이 난다면 한 번 확인해보도록 하자.

    2-3. PM2로 분산처리 하기

    PM2를 통한 로드밸런싱은 RR(Round-Robin) 방식으로 작동하며, 간단한 옵션 하나로 진행할 수 있다. PM2의 start 명령어에 '-i <숫자>' 옵션을 달아주어 해당 숫자만큼의 인스턴스를 생성해 분산처리를 할 수 있다. 또한, 숫자에 '0'이나 'max'를 입력하여 사용 가능한 CPU 갯수만큼 실행할 수도 있다.

    * RR방식이란, 시분할 시스테믈 위해 설계된 선점형 스케쥴링의 하나로서, 프로세스들 사이에 우선순위를 두지 않고, 순서대로 시간단위로 CPU를 할당하는 방식의 CPU 스케쥴링 알고리즘이다. 보통 시간 단위는 10ms ~ 100ms 정도인데, 시간 단위 동안 수행한 프로세스는 준비 큐의 끝으로 밀려나게 된다. Context Switching의 오버헤드가 큰 반면, 응답시간이 짧아지는 장점이 있어 실시간 시스템에 유리하다.

    # pm2 클러스터 모드
    $ pm2 start node/app.js -i 10

    아래 사진처럼 지정된 갯수만큼의 노드를 PM2에 등록하고 로드밸런싱을 통해 부하를 분산시켜줄 수 있다.

    또한 운영 도중에 클러스터의 갯수를 조정하여 scale in/out이 가능하다. 설정을 위한 명령어는 다음과 같다.

    # pm2 scale in/out
    $ pm2 scale <name> <숫자>

    위의 사진을 보면, 10개 였던 클러스터의 갯수가 5개로 줄어든 것을 확인할 수 있다. 

     

    마지막으로, 클러스터 모드를 사용할 때는 start 대신에 reload를 사용하여야 한다. restart의 경우에는 모든 프로세스를 종료하고 재시작하는 것이나, reload를 사용한다면 다운타임 없이 서버를 재기동할 수 있기 때문이다.

    # pm2 클러스터 모드 재시작
    $ pm2 reload <id|name>

    2-4. PM2 설정파일 ecosystem.config.js

    다음의 명령어를 통해서 PM2의 설정 파일을 생성할 수 있다.

    # pm2 설정파일 생성하기
    $ pm2 ecosystem

    설정파일을 보면, 다음과 같이 구성되어 있는 것을 확인할 수 있다.

    module.exports = {
      apps : [{
        name: 'app',
        script: 'app.js',
    
        // Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
        args: 'one two',
        instances: 1,
        autorestart: true,
        watch: false,
        max_memory_restart: '1G',
        env: {
          NODE_ENV: 'development'
        },
        env_production: {
          NODE_ENV: 'production'
        }
      }],
    
      deploy : {
        production : {
          user : 'node',
          host : '212.83.163.1',
          ref  : 'origin/master',
          repo : 'git@github.com:repo.git',
          path : '/var/www/production',
          'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production'
        }
      }
    };
    

    name의 경우, 실행한 프로세스의 이름을 설명하며, script는 실행할 파일의 경로, watch는 파일 변경 시 자동 재실행 여부이다. exec_mode는 fork/cluster와 같은 실행모드이며, instances는 cluster로 실행할 때의 인스턴스 갯수이다. env는 환경변수를 의미한다.

     

    이러한 설정을 담은 파일과 함께 PM2를 실행시키고 싶다면, 다음과 같은 명령어를 입력하면 된다.

    # 배포모드
    $ pm2 start ecosystem.config.js --env production
    
    # 개발모드
    $ pm2 start ecosystem.config.js

    3. 결론

    이처럼, 프로세스의 종류에 대해서 알아보았고, PM2를 통해 할 수 있는 것들에 대해 알아보았다. PM2를 이용해 서비스한다면 얻을 수 있는 장점이 많다. 우선, 오류로 인한 중단을 방지하고, 재시작 시 다운타임을 최소화할 수 있는 등 프로세스 관리 함에 있어 최적화되어 있다. 이에 앞으로 노드 앱을 서비스할 때, PM2를 요긴하게 써보고자 한다.

    'Tech > Backend' 카테고리의 다른 글

    Nginx 탐구하기  (0) 2020.03.19
    Cache  (0) 2019.12.27
    Proxy  (2) 2019.12.24

    댓글 0

Designed by Tistory.