Daylogs/Nginx

Passenger 이해하기

ohgyun 2014. 8. 2. 00:31
발생일: 2014.01.18

키워드: Passenger, 패신저, 노드, node, nginx, 엔진엑스

문제:

이번 프로젝트에는 노드로 서버를 띄우고 앞 단에 엔진엑스를 두려고 한다.
알아보니, 패신저라는 모듈이 있고, 이 모듈로 nginx와 node를 효율적으로 관리할 수 있다고 한다.

아래는 패신저에 대해 리서치하면 정리해둔 노트이다.


해결책:


https://github.com/phusion/passenger/wiki/Phusion-Passenger%3A-Node.js-tutorial


## 특징

Phusion Passenger 는 앱과 웹서버를 겸하고 있다.
즉, node.js 와 nginx의 기능을 모두 제공한다.


- Multitenancy
     한 개의 서버에서 손쉽게 여러 개의 노드앱을 실행할 수 있다.

- Process management and supervision
     노드 프로세스들은 자동으로 시작되고, 크래시됐을 때에 역시 자동으로 재시작된다.

- Statistics and inshing
     패신저가 애플리케이션 상태를 확인할 수 있는 도구를 제공한다.
     지금 처리하고 있는 요청이라던가, 몇 개의 요청을 처리했는지 등등

- Scaling and load balancing
     패신저가 현재 트래픽을 판단해 노드 프로세스의 개수를 관리하다.
     트래픽이 많아지면 더 많은 앱을 실행하고 적은 경우라면 줄이기도 한다.
     트래픽을 각 노드 프로세스로 로드밸런싱 한다.

- I/O Security
     노드는 자동으로 웹서버의 백엔드에 위치하게 된다.
     Slowloris 같은 slow-client attack 의 공격을 방어할 수 있고, (?)
     불필요한 헤더를 제거해 앱의 HTTP 파서의 취약점을 보완한다.

- System security
     노드는 앱의 소유자(owner) 권한에서 시작된다.
     따라서 한 개의 앱이 다른 앱에 영향을 끼치지 않는다.
     프로세스도 별도의 유저로 실행되나?

- Static file acceleration
     정적 파일은 노드가 아닌 웹서버에서 서빙한다.


## 장점

- 프록시 룰, 초기화 스크립트, 관리 스크립트를 짤 필요가 없다.
- nginx에서 리버스 프록싱해주는 것보다 더 많은 걸 해준다.
     예를 들어, 로드 밸런싱이나 response buffering 같은 거.
- nginx 프록시 모듈로는 시스템 상태를 정확히 파악할 수 없다.
     커넥션이 정상적인지, 노드 프로세스들은 잘 돌아가고 있는지.
     패신저는 간단하고 강력한 어드민 툴을 제공한다.


## 어떻게 동작하나?

노드를 직접 사용하는 대신, 노드를 관리할 수 있는 패신저를 써라.

패신저와 엔진엑스는 완전히 통합되어 있어서, 사용자 입장에서는 패신저와 엔진엑스를 같은 거라 생각하면 된다.
이렇게 함으로써 시스템을 상당히 쉽게 관리할 수 있다.
엔진엑스 설정 파일을 변경하는 걸로 패신저를 설정할 수 있고,
패신저를 시작하고 중지하는 걸로 앤진엑스를 시작하고 중지할 수 있다.

패신저는 엔진엑스의 요청을 받아서 노드로 전달한다.
내부적으로는 엔진엑스의 프록시 룰을 사용하고, 이 리버스 프록시 레이어는 지저분한 HTTP 헤더를 정리하고,
노드를 느린 클라이언트로부터 방어하는 버퍼링 기능을 제공한다.

패신저는 여러 개의 노드 프로세스를 관리하고 로드밸런싱 기능을 제공한다.
한 개의 프로세스가 죽으면, 다시 시작한다.
패신저가 노드를 여러 개의 프로세스로 운영하기 때문에, 노드가 싱글 스레드임에도 불구하고 멀티 CPU 코어의 이점을 활용할 수 있다. (한 개의 프로세스는 한 개의 코어를 사용할 수 있다)

정적인 파일은 엔진엑스에서 바로 제공하기 때문에 노드까지 요청되지 않는다.
엔진엑스가 정적 파일을 서빙하는데 최적화되어 있기 때문에 노드는 동적인 작업만 처리해주면 된다.

일반적으로 노드앱을 모니터링하기가 쉽지 않다.
지금 처리하고 있는 요청이 뭔지. CPU와 메모리 사용량은 얼마인지. 중지된 건 아닌지. 과부하 상탠 아닌지.
패신저가 이런 것들은 모니터링하고 관리할 수 있는 도구를 제공한다.

어떤 독자들은 패신저가 유닉스라고 인지할 수도 있다.
실제로 전통적인 유닉스의 프로세스 지휘 아키텍처이기도 하고, 실제로 강력한 유닉스 기능을 많이 활용한다.
예를 들어, 패신저와 노드는 TCP 소켓 대신, 고성능의 유닉스 도메인 소켓으로 통신하고 있고,
관리 도구는 POSIX 프로세스 매니지먼트 API를 활용한다.


## 패신저가 하지 않는 것

패신저는 ‘최소한의 것만 한다’는 유닉스의 철학을 따른다.

- 운영체제에 서버를 설치하지 않는다.
- Node.js를 인스톨하지 않는다.
- 애플리케이션 코드를 서버로 전송하지 않는다. (배포 기능을 제공하지 않는다)
- 애플리케이션의 의존성을 설치하지 않는다.
- 데이터베이스를 관리하지 않는다.
- 애플리케이션이 데몬이나 서비스를 필요로 해도 이에 대한 관리를 하진 않는다. 예) Memcahed나 Redis 같은






# 설치하기


## 앱 준비하기

패신저의 철한 가운데 하나는, “Convention over configuration” 이다.

패신저의 컨벤션은 다음과 같다.

application directory
  |
  +-- app.js
  |
  +-- public/
  |
  +-- tmp/


- app.js : 앱의 시작 포인트이다. 패신저는 이 파일을 로딩해 시작시킨다.
- public/ : 정적 파일을 제공하는 디렉토리이다.
     만약, `public/foo.jpg` 파일이 있다면, `/foo.jpg` 요청으로 이 파일을 응답한다.
- tmp/ : 패신저가 애플리케이션을 재시작할 때 사용한다.
     `tmp/restart.txt` 파일을 생성하면, 패신저는 다음 요청 때 애플리케이션을 재시작한다.
     이 매커니즘이 약간 이상하게 보일 수도 있지만, SSH 대신 FTP 로만 접근할 수 있는 호스트도
     사용할 수 있도록 한 의도적인 디자인이다.







# 실행하기

모두 인스톨했으면, 아래 명령으로 실행할 수 있다.

$ passenger start


## 포트 바인딩

일반적으로 노드가 `listen()`에 특정 포트를 전달하는 식으로 실행하지만,
패신저를 통해 실행할 땐 이 설정이 적용되지 않는다.

- 패신저의 기본 포트는 따로 설정하지 않으면 3000번이다.
- 패신저가 노드를 실행할 땐, 랜덤으로 특정 포트를 할당한다. (노드에서 명시적으로 `listen`하는 게 아니라)
- 패신저가 여러 개의 노드 프로세스를 띄우고, 로드 밸런싱을 수행한다.


## 포트 변경

$ passenger start —port 3001


## Process spawning

패신저는 노드 앱을 시작/중지하고 각 노드 프로세스를 매니징한다.
노드 프로세스는 `passenger-status` 명령으로 확인할 수 있다.


## 프로세스 개수 제한

$ passenger start —min-poo-size 8

(기본값은 6이다)

- 각 프로세스는 다른 CPU 코어를 활용할 수 있다. 프로세스를 생성하면 여러 코어를 활용할 수 있다.
- CPU 코어보다 많은 수의 프로세스를 생성하는 건 의미가 없다.
- 프로세스를 너무 많이 생성하면 메모리가 부족해서 느려지거나 중지될 수 있다.
     메모리 사용량을 잘 보고 프로세스의 개수를 조절해야 한다.


## 특정 인스턴스를 중지시키려면

`passenger-status`로 랜덤 인스턴스의 PID를 얻은 후 `kill`한다.


## 멀티 프로세스에서의 특징

프로세스는 다른 프로세스와는 독립적이기 때문에, 메모리를 공유할 수 없다.
스레드는 한 개의 프로세스 내에서 구동되기 때문에,
한 스레드가 어떤 값을 변경하면 같은 프로세스 내의 다른 스레드도 변경된 값을 갖게 된다.

따라서, 프로세스 간 통신을 하려면 다른 데이터 저장소를 사용해야 한다.
(데이터베이스나 레디스, 멤캐시디 같은 것들)


또 다른 특징은, 한 개의 프로세스가 죽었을 때 다른 프로세스에 영향을 끼치지 않는다는 점이다.
스레드의 경우, 스레드가 크래시 된 후에 복구되지 못하고 있으면, 전체 프로세스가 해당 크래시로부터 영향을 받을 수 있다.

이런 이유 때문에 패신저는 멀티 프로세스를 선택했다.


## NODE_ENV

많은 노드 웹 프레임워크가 `NODE_ENV` 환경 변수를 사용한다.
패신저에서도 이 변수를 설정할 수 있고, 기본값은 `development`이다.

$ passenger start —environment production


## 추가 설정

설정은 통합 모드마다 조금씩 다르다. (예: nginx integration, apache integration mode)

stand alone 모드에 대한 건 아래 링크에서 확인할 수 있고,

통합 모드는 아래 링크를 확인한다.








# 배포하기

## Standalone 배포하기

한 번에 한 개의 노드 앱 밖에 지원하지 못하는 단점이 있지만, 엔진엑스나 아파치를 몰라도 된다.


$ sudo passenger start —port 80 —user someusername —environment production —daemonize



## 엔진엑스 또는 아파치와 배포하기

여기선 엔진엑스를 기준으로.

1. 엔진엑스 통합 버전을 다운로드 받는다.

2. 문서의 루트는 `pubic` 디렉토리를 바라보고 있어야 한다.

````
server {
    server_name www.foo.com;
    root /webapps/foo/public;
    passenger_enabled on;
}
````


### 첫 번째 요청은 느리다

첫 번째 요청이 느린 건, 첫 번째 요청이 들어오기 전까진 애플리케이션이 시작되지 않기 때문이다.

미리 시작하게 하려면 아래 링크를 참고한다.
 

### NODE_ENV 값의 기본값이 production 이다.

엔진엑스와 아파치 통합 모드에서는 `NODE_ENV`의 기본값은 `production`이다.

`passenger_app_env development`

처럼 해당 값을 변경할 수 있다.


### 설정

standalone 모드는 커맨들 라인 옵션으로 설정했지만,
통합 모드는 각 웹서버의 설정 파일을 통해 설정한다.

프로세스 개수나 메모리 제한 등도 설정 파일에서 가능하며,
자세한 건 아래 링크를 확인한다.


### 자동 사용자 전환

엔진엑스와 아파치는 일반적으로 `www` 유저로 실행된다.
그렇다고 노드 앱이 `www` 유저 권한으로 실행되는 건 아니다.

노드앱이 모두 동일한 사용자 권한으로 실행되면,
한 노드앱이 다른 노드앱(프로세스가 아니라 별도의 앱을 의미한다)의 파일을 수정할 수 있다.
따라서 노드 애플리케이션을 웹서버와 동일한 유저 권한으로 두는 건 굉장히 취약하다.

패신저는 `Automatic User switching`이라는 기능을 제공한다.
이 값을 설정하면, 노드 앱은 간단히 `app.js`의 유저 권한으로 실행된다.
물론 각 앱의 소유자를 적절히 잘 구분해놓아야 한다.


### 재배포/재시작하기

`tmp/restart.txt` 파일을 생성한다.
패신저는 다음 요청 때 기존 프로세스를 죽이고 새 프로세스를 생성한다.






# 개발 환경에서 패신저 사용하기

### 개발에서 패신저를 사용해야 하나?

단순히 노드로 개발하고, 패신저로 배포해도 된다.
그래도 가능하면 개발환경과 운영환경을 일치시키는 게 좋다.


### 매 요청마다 재시작하기

`tmp/always_restart.txt` 파일이 있으면, 패신저는 매 요청마다 새로운 프로세스로 실행한다.






# 주의

패신저는 애플리케이션과 커뮤니케이션할 때 `stdout`과 `stderr`를 사용한다.
로그를 표준출력으로 내보내는 건 괜찮지만, 파일을 내보내지 않도록 한다.




# Management and inspection

`passenger-status` 명령을 활용하면 좋다.

지금 처리하고 있는 요청까지 보려면 `--show=requests` 옵션을 사용하면 된다.

$ passenger-status --show=requests


### `passenger-memory-stats`

패신저와 관련된 모든 프로세스의 메모리를 확인할 수 있다.
(웹서버와 패신저, 노드앱을 모두 포함)

`passenger-status`와 다른 점:
- 웹서버의 메모리를 확인할 수 있다.
- 패신저 내부 프로세스의 메모리를 확인할 수 있다.
- 패신저가 백그라운드로 돌리는 프로세스의 메로리를 확인할 수 있다. (내부적으로 `ps`를 사용한다)
  패신저의 내부 프로세스가 맛이 갔어도 `passenger-memory-stats`으로 확인할 수 있다.



## 로그 확인

엔진엑스의 경우, 글로벌 `error_log` 지시자에서 설정한 파일에 로깅된다.
(`http`에서도 `error_log` 지시자를 사용할 수 있지만, 글로벌과는 다르다)





반응형