GitLab 패키지의 번들 Puma 인스턴스 구성
Offering: GitLab Self-Managed
Puma는 Ruby 애플리케이션을 위한 빠른 멀티스레드 고동시성 HTTP 1.1 서버입니다. 메모리 사용량을 줄이기 위해 Puma는 워커 프로세스를 포크합니다. 통제되지 않은 메모리 증가를 막기 위해 GitLab Rails 애플리케이션은 감시 스레드를 실행하여, 워커가 일정 시간 동안 지정된 상주 세트 크기(RSS) 임계값을 초과하면 자동으로 워커를 재시작합니다.
Puma는 Ruby 애플리케이션을 위한 빠른 멀티스레드 고동시성 HTTP 1.1 서버입니다. GitLab의 사용자 기능을 제공하는 핵심 Rails 애플리케이션을 실행합니다.
메모리 사용량 튜닝#
메모리 사용량을 줄이기 위해 Puma는 워커 프로세스를 포크합니다. 워커가 생성될 때마다 기본 프로세스와 메모리를 공유합니다. 워커는 메모리 페이지를 변경하거나 추가할 때만 추가 메모리를 사용합니다. 이로 인해 Puma 워커는 추가 웹 요청을 처리하면서 시간이 지남에 따라 더 많은 물리 메모리를 사용하게 될 수 있습니다. 시간이 지남에 따라 사용되는 메모리의 양은 GitLab 사용 방식에 따라 다릅니다. GitLab 사용자가 더 많은 기능을 사용할수록 시간이 지남에 따라 더 높은 메모리 사용량이 예상됩니다.
통제되지 않은 메모리 증가를 막기 위해 GitLab Rails 애플리케이션은 감시 스레드를 실행하여, 워커가 일정 시간 동안 지정된 상주 세트 크기(RSS) 임계값을 초과하면 자동으로 워커를 재시작합니다.
GitLab은 메모리 제한의 기본값으로 1500Mb를 설정합니다. 기본값을 재정의하려면 per_worker_max_memory_mb를 새 RSS 제한값(메가바이트)으로 설정하세요:
-
/etc/gitlab/gitlab.rb를 편집합니다:puma['per_worker_max_memory_mb'] = 1200 # 1.2 GB -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure
워커가 재시작되면 짧은 시간 동안 GitLab 실행 용량이 줄어듭니다. 워커가 너무 자주 교체된다면 per_worker_max_memory_mb를 더 높은 값으로 설정하세요.
워커 수는 CPU 코어 수를 기준으로 계산됩니다. 4~8개의 워커가 있는 소규모 GitLab 배포는 워커가 너무 자주 재시작되는 경우(분당 1회 이상) 성능 문제가 발생할 수 있습니다.
서버에 여유 메모리가 있는 경우 per_worker_max_memory_mb 값을 높이는 것이 도움이 될 수 있습니다.
데이터베이스 연결 계획#
Puma 워커나 스레드를 늘리기 전에 PostgreSQL max_connections 설정에 미치는 데이터베이스 연결 영향을 고려하세요.
자세한 연결 계획 및 계산 방법은 PostgreSQL 튜닝 페이지를 참조하세요.
워커 재시작 모니터링#
GitLab은 워커가 높은 메모리 사용량으로 인해 재시작되는 경우 로그 이벤트를 내보냅니다.
다음은 /var/log/gitlab/gitlab-rails/application_json.log에서 이러한 로그 이벤트 중 하나의 예시입니다:
{
"severity": "WARN",
"time": "2023-01-04T09:45:16.173Z",
"correlation_id": null,
"pid": 2725,
"worker_id": "puma_0",
"memwd_handler_class": "Gitlab::Memory::Watchdog::PumaHandler",
"memwd_sleep_time_s": 5,
"memwd_rss_bytes": 1077682176,
"memwd_max_rss_bytes": 629145600,
"memwd_max_strikes": 5,
"memwd_cur_strikes": 6,
"message": "rss memory limit exceeded"
}
memwd_rss_bytes는 실제 소비된 메모리 양이고, memwd_max_rss_bytes는 per_worker_max_memory_mb를 통해 설정되거나 DEFAULT_PUMA_WORKER_RSS_LIMIT_MB로 정의된 RSS 제한값입니다.
워커 타임아웃 변경#
기본 Puma 타임아웃은 60초입니다.
puma['worker_timeout'] 설정은 최대 요청 지속 시간을 설정하지 않습니다.
워커 타임아웃을 600초로 변경하려면:
-
/etc/gitlab/gitlab.rb를 편집합니다:gitlab_rails['env'] = { 'GITLAB_RAILS_RACK_TIMEOUT' => 600 } -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure
메모리 제한 환경에서 Puma 클러스터 모드 비활성화#
이 기능은 실험적 기능으로 통보 없이 변경될 수 있습니다. 이 기능은 프로덕션 사용 준비가 되어 있지 않습니다. 이 기능을 사용하려면 먼저 프로덕션 외부에서 테스트해야 합니다. 자세한 내용은 알려진 문제를 참조하세요.
사용 가능한 RAM이 4GB 미만인 메모리 제한 환경에서는 Puma 클러스터 모드를 비활성화하는 것을 고려하세요.
workers 수를 0으로 설정하여 메모리 사용량을 수백 MB 줄이세요:
-
/etc/gitlab/gitlab.rb를 편집합니다:puma['worker_processes'] = 0 -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure
기본적으로 설정된 클러스터 모드와 달리, 단일 Puma 프로세스만 애플리케이션을 제공합니다. Puma 워커 및 스레드 설정에 대한 자세한 내용은 Puma 요구 사항을 참조하세요.
이 구성으로 Puma를 실행하는 단점은 처리량이 줄어든다는 것인데, 메모리 제한 환경에서는 적절한 트레이드오프로 볼 수 있습니다.
메모리 부족(OOM) 상태를 방지하려면 충분한 스왑이 있는지 확인하세요. 자세한 내용은 메모리 요구 사항을 참조하세요.
Puma 단일 모드 알려진 문제#
Puma를 단일 모드로 실행할 때 일부 기능이 지원되지 않습니다:
자세한 내용은 에픽 5303을 참조하세요.
SSL로 수신하도록 Puma 구성#
Puma는 Linux 패키지 설치와 함께 배포될 때 기본적으로 Unix 소켓을 통해 수신합니다. Puma가 HTTPS 포트를 통해 수신하도록 구성하려면 다음 단계를 따르세요:
-
Puma가 수신할 주소에 대한 SSL 인증서 키 쌍을 생성합니다. 아래 예시에서는
127.0.0.1입니다.[!note] 사용자 지정 인증기관(CA)의 자체 서명 인증서를 사용하는 경우, 설명서에 따라 다른 GitLab 구성 요소에서 신뢰할 수 있게 만드세요.
-
/etc/gitlab/gitlab.rb를 편집합니다:puma['ssl_listen'] = '127.0.0.1' puma['ssl_port'] = 9111 puma['ssl_certificate'] = '<path_to_certificate>' puma['ssl_certificate_key'] = '<path_to_key>' # Disable UNIX socket puma['socket'] = "" -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure
Unix 소켓 외에도 Puma는 Prometheus에서 스크레이핑할 메트릭을 제공하기 위해 포트 8080에서 HTTP를 통해 수신합니다. Prometheus가 HTTPS를 통해 스크레이핑하도록 만드는 것은 불가능하며, 이에 대한 지원은 이 이슈에서 논의 중입니다. 따라서 Prometheus 메트릭을 잃지 않고 이 HTTP 리스너를 끄는 것은 기술적으로 불가능합니다.
암호화된 SSL 키 사용#
히스토리
- GitLab 16.1에서 도입되었습니다.
Puma는 런타임에 복호화할 수 있는 암호화된 개인 SSL 키 사용을 지원합니다. 다음 지침은 이를 구성하는 방법을 보여줍니다:
-
아직 암호화되지 않은 경우 비밀번호로 키를 암호화합니다:
openssl rsa -aes256 -in /path/to/ssl-key.pem -out /path/to/encrypted-ssl-key.pem암호화된 파일을 작성하기 위해 비밀번호를 두 번 입력합니다. 이 예시에서는
some-password-here를 사용합니다. -
비밀번호를 출력하는 스크립트나 실행 파일을 만듭니다. 예를 들어,
/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password에 비밀번호를 출력하는 기본 스크립트를 만듭니다:#!/bin/sh echo some-password-here비밀번호를 디스크에 저장하지 말고, Vault와 같은 안전한 메커니즘을 사용하여 비밀번호를 가져오세요. 예를 들어, 스크립트는 다음과 같을 수 있습니다:
#!/bin/sh export VAULT_ADDR=http://vault-password-distribution-point:8200 export VAULT_TOKEN=<some token> echo "$(vault kv get -mount=secret puma-ssl-password)" -
Puma 프로세스가 스크립트를 실행하고 암호화된 키를 읽을 수 있는 충분한 권한이 있는지 확인합니다:
chown git:git /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password chmod 770 /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password chmod 660 /path/to/encrypted-ssl-key.pem -
/etc/gitlab/gitlab.rb를 편집하고puma['ssl_certificate_key']를 암호화된 키로 교체하고puma['ssl_key_password_command']를 지정합니다:puma['ssl_certificate_key'] = '/path/to/encrypted-ssl-key.pem' puma['ssl_key_password_command'] = '/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password' -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure -
GitLab이 성공적으로 시작되면 GitLab 인스턴스에 저장된 암호화되지 않은 SSL 키를 제거할 수 있습니다.
Unicorn에서 Puma로 전환#
Helm 기반 배포의 경우 webservice 차트 설명서를 참조하세요.
Puma는 기본 웹 서버이며 Unicorn은 더 이상 지원되지 않습니다.
Puma는 Unicorn과 같은 멀티 프로세스 애플리케이션 서버보다 메모리를 덜 사용하는 멀티스레드 아키텍처를 갖추고 있습니다. GitLab.com에서 메모리 소비가 40% 감소했습니다. 대부분의 Rails 애플리케이션 요청에는 I/O 대기 시간의 비율이 포함됩니다.
I/O 대기 시간 동안 MRI Ruby는 다른 스레드에게 GVL을 해제합니다. 따라서 멀티스레드 Puma는 단일 프로세스보다 더 많은 요청을 처리할 수 있습니다.
Unicorn에서 Puma로 전환할 때, 두 애플리케이션 서버 간의 차이로 인해 Unicorn 서버 구성이 자동으로 이전되지 않습니다.
Unicorn에서 Puma로 전환하려면:
-
적합한 Puma 워커 및 스레드 설정을 결정합니다.
-
/etc/gitlab/gitlab.rb에서 사용자 지정 Unicorn 설정을 Puma로 변환합니다.아래 표는 Linux 패키지를 사용할 때 어떤 Unicorn 구성 키가 Puma 키에 해당하는지, 그리고 대응하는 키가 없는 것은 무엇인지를 요약합니다.
Unicorn Puma unicorn['enable']puma['enable']unicorn['worker_timeout']puma['worker_timeout']unicorn['worker_processes']puma['worker_processes']해당 없음 puma['ha']해당 없음 puma['min_threads']해당 없음 puma['max_threads']unicorn['listen']puma['listen']unicorn['port']puma['port']unicorn['socket']puma['socket']unicorn['pidfile']puma['pidfile']unicorn['tcp_nopush']해당 없음 unicorn['backlog_socket']해당 없음 unicorn['somaxconn']puma['somaxconn']해당 없음 puma['state_path']unicorn['log_directory']puma['log_directory']unicorn['worker_memory_limit_min']해당 없음 unicorn['worker_memory_limit_max']puma['per_worker_max_memory_mb']unicorn['exporter_enabled']puma['exporter_enabled']unicorn['exporter_address']puma['exporter_address']unicorn['exporter_port']puma['exporter_port'] -
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure -
선택 사항. 멀티 노드 배포의 경우 로드 밸런서가 준비 상태 확인을 사용하도록 구성합니다.
Puma 트러블슈팅#
Puma가 CPU 100%에서 스핀 후 502 Gateway Timeout#
이 오류는 웹 서버가 Puma 워커에서 응답을 듣지 못한 후 타임아웃될 때(기본값: 60초) 발생합니다. 이 과정에서 CPU가 100%로 스핀하면 예상보다 오래 걸리는 작업이 있을 수 있습니다.
이 문제를 해결하려면 먼저 무슨 일이 일어나고 있는지 파악해야 합니다. 다음 팁은 사용자가 다운타임의 영향을 받아도 상관없는 경우에만 권장됩니다. 그렇지 않은 경우 다음 섹션으로 건너뛰세요.
-
문제가 있는 URL을 로드합니다
-
sudo gdb -p를 실행하여 Puma 프로세스에 연결합니다. -
GDB 창에서 다음을 입력합니다:
call (void) rb_backtrace() -
이렇게 하면 프로세스가 Ruby 백트레이스를 생성하도록 강제됩니다. 백트레이스에 대한
/var/log/gitlab/puma/puma_stderr.log를 확인하세요. 예를 들어, 다음과 같은 내용이 표시될 수 있습니다:from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' -
현재 스레드를 보려면 다음을 실행합니다:
thread apply all bt -
gdb로 디버깅을 완료한 후에는 프로세스에서 분리하고 종료하세요:detach exit
이 명령을 실행하기 전에 Puma 프로세스가 종료되면 GDB에서 오류가 보고됩니다. 시간을 더 벌기 위해 Puma 워커 타임아웃을 항상 늘릴 수 있습니다. Linux 패키지 설치 사용자의 경우 /etc/gitlab/gitlab.rb를 편집하여 60초에서 600초로 늘릴 수 있습니다:
gitlab_rails['env'] = {
'GITLAB_RAILS_RACK_TIMEOUT' => 600
}
자체 컴파일 설치의 경우 환경 변수를 설정하세요. Puma 워커 타임아웃을 참조하세요.
변경 사항이 적용되도록 GitLab을 재구성하세요.
다른 사용자에게 영향을 주지 않고 트러블슈팅#
이전 섹션에서는 실행 중인 Puma 프로세스에 연결했는데, 이 시간 동안 GitLab에 액세스하려는 사용자에게 바람직하지 않은 영향이 있을 수 있습니다. 프로덕션 시스템에서 다른 사용자에게 영향을 미치는 것이 우려된다면 별도의 Rails 프로세스를 실행하여 문제를 디버깅할 수 있습니다:
-
GitLab 계정에 로그인합니다.
-
문제가 있는 URL을 복사합니다(예:
https://gitlab.com/ABC). -
사용자에 대한 개인 액세스 토큰을 만듭니다(사용자 설정 -> 액세스 토큰).
-
GitLab Rails 콘솔을 엽니다.
-
Rails 콘솔에서 다음을 실행합니다:
app.get '/?private_token='예를 들어:
app.get 'https://gitlab.com/gitlab-org/gitlab-foss/-/issues/1?private_token=123456' -
새 창에서
top을 실행합니다. 이 Ruby 프로세스가 CPU 100%를 사용하는 것을 볼 수 있습니다. PID를 기록합니다. -
이전 섹션의 GDB 사용에 관한 2단계를 따릅니다.
GitLab: API에 액세스할 수 없음#
이것은 GitLab Shell이 내부 API(예: http://localhost:8080/api/v4/internal/allowed)를 통해 권한을 요청하려고 할 때 확인에 실패하여 자주 발생합니다. 이 문제는 다음과 같은 이유로 발생할 수 있습니다:
- 데이터베이스(예: PostgreSQL 또는 Redis) 연결 타임아웃
- Git 훅 또는 푸시 규칙의 오류
- 리포지터리 액세스 오류(예: 오래된 NFS 핸들)
이 문제를 진단하려면 문제를 재현한 다음 top을 통해 스핀하는 Puma 워커가 있는지 확인하세요. 이전에 문서화된 gdb 기술을 사용해 보세요. 또한 strace를 사용하면 문제를 격리하는 데 도움이 될 수 있습니다:
strace -ttTfyyy -s 1024 -p -o /tmp/puma.txt
어떤 Puma 워커가 문제인지 격리할 수 없다면, 모든 Puma 워커에 strace를 실행하여 /internal/allowed 엔드포인트가 어디서 막히는지 확인해 보세요:
ps auwx | grep puma | awk '{ print " -p " $2}' | xargs strace -ttTfyyy -s 1024 -o /tmp/puma.txt
/tmp/puma.txt의 출력이 근본 원인 진단에 도움이 될 수 있습니다.
