Puma의 keepalive 관련 지연 문제를 해결하기 위한 Heroku의 공식적인 권장 사항은 Puma 내부 설정을 조정하는 것입니다. 여기에는 Puma의 연결 keepalive를 완전히 비활성화하거나, max_fast_inline
설정을 비활성화하는 방법이 포함됩니다. 그러나 이러한 방법들은 잠재적으로 keepalive가 제공하는 성능 이점을 상쇄할 수 있습니다. 본 문서에서 제안하는 세 번째이자 더 효과적인 대안은 ‘Thruster’를 활용하는 것입니다. Thruster는 Go 언어로 작성된 프록시 서버로, Heroku 라우터와 애플리케이션 dyno 사이에서 연결을 중계하는 역할을 합니다. 이 접근 방식의 핵심은 다음과 같습니다:
- Heroku 라우터와 Thruster 간의 연결: Heroku 네트워크 상에서 Heroku 라우터와 애플리케이션 dyno 내에서 실행되는 Thruster 간의 요청은 연결 keepalive를 사용할 수 있습니다. Thruster는 Go의
net/http
라이브러리를 기반으로 하며, 기본적으로 keepalive를 지원하므로 이 부분에서 성능 이점을 유지할 수 있습니다. - Thruster와 Puma 간의 로컬 연결: 애플리케이션 dyno 내부에서 Thruster와 Puma 간의 로컬 요청은 연결 keepalive를 비활성화할 수 있습니다. 이 연결은 모두 동일한 dyno 내에서 발생하므로 TCP 핸드셰이크로 인한 네트워크 지연이 발생하지 않아 성능에 미치는 영향이 미미합니다. 이로써 Puma의 keepalive 관련 문제를 우회하면서도 전체적인 성능 저하를 방지할 수 있습니다.
Thruster를 Puma와 함께 Heroku에 통합하는 과정은 비교적 간단합니다. 다음 세 단계를 따르면 됩니다:
thruster
gem 추가: Gemfile에thruster
gem을 추가합니다.- Procfile 업데이트:
Procfile
을web: HTTP_PORT=$PORT TARGET_PORT=3001 bundle exec thrust bin/rails server
와 같이 업데이트하여 Thruster가 Puma 앞에 위치하도록 합니다. - Puma keepalive 비활성화: Puma 설정 파일에서
enable_keep_alives false
를 추가하여 Puma의 keepalive를 비활성화합니다.
저자는 이미 x-sendfile
지원의 이점 때문에 Heroku에서 Puma와 함께 Thruster를 사용하고 있었음을 밝히며, Thruster가 추가 프로세스임에도 불구하고 자원 사용량이 매우 미미하다고 강조합니다. 실제 측정 결과, Rails 앱이 Puma에서 200MB를 사용하는 반면 Thruster는 약 13MB만을 사용하여 리소스 오버헤드가 작음을 입증합니다. 이는 Thruster가 성능 문제를 해결하면서도 시스템 자원에 큰 부담을 주지 않는 효율적인 솔루션임을 시사합니다.