- メモリ使用量の削減
- ワーカータイムアウトの変更
- メモリに制約のある環境ではPumaクラスターモードを無効化
- RuggedでPumaを使用する際のパフォーマンス上の注意点
- SSLでリッスンするPumaの設定
- UnicornからPumaへの切り替え
- Puma のトラブルシューティング
- 関連するトピック
GitLab パッケージにバンドルされている Puma インスタンスを設定します。
PumaはRubyアプリケーション用の高速、マルチスレッド、高同時HTTP 1.1サーバです。GitLabのユーザー向け機能を提供するRailsアプリケーションのコアを実行します。
メモリ使用量の削減
メモリ使用量を削減するために、Pumaはワーカープロセスをフォークします。ワーカーが作成されるたびに、プライマリプロセスとメモリを共有します。ワーカーが追加のメモリを使うのは、メモリページを変更したり追加したりするときだけです。このため、ワーカーが追加のウェブリクエストを処理するにつれて、Puma ワーカーはより多くの物理メモリを使用するようになります。時間の経過とともに使用されるメモリ量は、GitLabの使用状況によって異なります。GitLabユーザーが使用する機能が多ければ多いほど、時間の経過とともに予想されるメモリ使用量は多くなります。
無秩序なメモリ増加を止めるために、GitLab Railsアプリケーションは監視スレッドを実行し、ワーカーが与えられた常駐セットサイズ(RSS) しきい値を一定時間超えると自動的に再起動します。
GitLabはメモリの上限をデフォルトで1200Mb
。デフォルト値をオーバーライドするには、per_worker_max_memory_mb
に新しい RSS リミットをメガバイト単位で設定します:
-
/etc/gitlab/gitlab.rb
を編集します:puma['per_worker_max_memory_mb'] = 1024 # 1GB
-
GitLab を再設定します:
sudo gitlab-ctl reconfigure
ワーカーが再起動されると、GitLabを実行する容量が短時間減少します。ワーカーが頻繁に入れ替わる場合は、per_worker_max_memory_mb
を高い値に設定してください。
ワーカー数はCPUコアに基づいて計算されます。ワーカーが頻繁に再起動される場合(1分間に1回以上)、4~8ワーカーの小規模なGitLabデプロイではパフォーマンスにイシューが発生する可能性があります。
サーバーに空きメモリがある場合は、1200
以上の高い値が有効です。
ワーカーの再起動を監視
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
で設定された 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 が 4 GB 未満のメモリ制約のある環境では、Pumaクラスタ化モードを無効にすることを検討してください。
workers
の数を0
に設定して、メモリ使用量を数百 MB 削減します:
-
/etc/gitlab/gitlab.rb
を編集します:puma['worker_processes'] = 0
-
GitLab を再設定します:
sudo gitlab-ctl reconfigure
デフォルトで設定されているクラスター化モードとは異なり、アプリケーションを処理する Puma プロセスは一つだけです。Pumaワーカーとスレッドの設定の詳細については、Pumaの要件を参照してください。
この設定で Puma を実行することの欠点はスループットが低下することですが、これはメモリに制約のある環境では公平なトレードオフと考えることができます。
(OOM) のメモリ不足を回避するために、十分なスワップを用意してください。詳細はメモリ要件を参照してください。
プーマ・シングルモードに関する既知の問題
Puma をシングルモードで実行すると、一部の機能がサポートされません:
詳細については、エピック5303を参照してください。
RuggedでPumaを使用する際のパフォーマンス上の注意点
Git リポジトリの保存に NFS が使用されているデプロイでは、GitLab はRugged を使用することでパフォーマンスを向上させるためにGit への直接アクセスを使用します。
機能フラグによって無効化されていない限り、Gitへの直接アクセスが利用可能でPumaがシングルスレッドで実行されている場合、Ruggedの使用は自動的に有効になります。
MRI RubyはGlobal VM Lock(GVL) を使用しています。GVL により、MRI Ruby はマルチスレッドになりますが、最大でもシングルコアで動作します。
Gitには集中的なI/Oオペレーションが含まれます。Ruggedがスレッドを長時間使用すると、リクエストを処理しているかもしれない他のスレッドが飢餓状態に陥る可能性があります。シングルスレッドモードで動作しているPumaでは、同時に処理されるリクエストはせいぜい一つなので、このイシューは発生しません。
GitLabはRuggedの使用を削除するよう取り組んでいます。現在ではRuggedを使わなくても十分なパフォーマンスが得られていますが、場合によってはRuggedを使ったほうが有益なこともあります。
マルチスレッドのPumaでRuggedを実行することの注意点と、Gitalyの許容可能なパフォーマンスを考慮し、Pumaのマルチスレッド(Pumaが複数のスレッドで実行されるように設定されている場合)が使用されている場合、Ruggedの使用を無効にします。
このデフォルトの動作は、状況によっては最適な設定ではないかもしれません。デプロイにおいてRuggedが重要な役割を果たす場合は、ベンチマークを実施して最適な設定を見つけることをお勧めします:
- 最も安全な選択肢は、シングルスレッドのPumaで開始することです。
- RuggedをマルチスレッドのPumaで強制的に使用するには、機能フラグを使用します。
SSLでリッスンするPumaの設定
LinuxパッケージインストールでデプロイされたPumaは、デフォルトでUnixソケットをリッスンします。代わりにHTTPSポートでリッスンするようにPumaを設定するには、以下の手順に従います:
-
PumaがリッスンするアドレスのSSL証明書キーペアを生成します。以下の例では、
127.0.0.1
。カスタム認証局(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
暗号化されたSSLキーの使用
GitLab 16.1 で導入されました。
Pumaは暗号化された秘密鍵の使用をサポートしており、実行時に復号化することができます。次の説明では、この設定方法を説明します:
-
キーをパスワードで暗号化します:
openssl rsa -aes256 -in /path/to/ssl-key.pem -out /path/to/encrypted-ssl-key.pem
暗号化されたファイルを書き込むには、パスワードを2回入力します。この例では、
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への切り替え
webservice
Chart ドキュメントを参照してください。GitLab 13.0から、Pumaがデフォルトのウェブサーバーとなり、Unicornは無効になりました。GitLab 14.0では、UnicornはLinuxパッケージから削除され、サポートされなくなりました。
Pumaはマルチスレッドアーキテクチャを採用しており、Unicornのようなマルチプロセスのアプリケーションサーバーよりもメモリ使用量が少なくなっています。GitLab.comでは、メモリ消費量が40%削減されました。Railsアプリケーションのリクエストには通常、I/O待ち時間が含まれます。
I/O待ち時間の間、RubyはGVLを他のスレッドにリリースします。そのため、マルチスレッドのPumaはシングルプロセスよりも多くのリクエストに対応することができます。
Pumaに切り替えた場合、2つのアプリケーションサーバーの違いにより、Unicornサーバーの設定は自動的に引き継が_れません_。
UnicornからPumaに切り替えるには:
- 適切な Pumaワーカーとスレッドの設定を決定します。
-
/etc/gitlab/gitlab.rb
で、Unicorn のカスタム設定を Puma に変換します。以下の表は、Unicornのどの設定キーがLinuxパッケージ使用時の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 のトラブルシューティング
502 Pumaが100%のCPUでスピンした後のゲートウェイのタイムアウト
このエラーは、Pumaワーカーからの応答がないままWebサーバーがタイムアウト(デフォルト:60秒)した場合に発生します。この処理中に CPU が 100% になった場合は、何か時間がかかっている可能性があります。
このイシューを解決するには、まず何が起こっているのかを知る必要があります。以下のヒントは、ユーザーがダウンタイムに影響されても構わない場合にのみお勧めします。そうでない場合は、次のセクションに進んでください。
- 問題のあるURLをロード
-
sudo gdb -p <PID>
を実行して 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 プロセスが終了すると、GDB はエラーを報告します。さらに時間を稼ぐには、Puma ワーカーのタイムアウトを上げることができます。Linux パッケージインストールユーザは、/etc/gitlab/gitlab.rb
を編集して、60 秒から 600 秒に増やすことができます:
gitlab_rails['env'] = {
'GITLAB_RAILS_RACK_TIMEOUT' => 600
}
セルフコンパイルインストールの場合は、環境変数を設定してください。Puma Worker timeoutを参照してください。
変更を有効にするために GitLab を再設定します。
他のユーザーに影響を与えないトラブルシューティング
前節では実行中のPumaプロセスにくっつけましたが、この間にGitLabにアクセスしようとしたユーザーには望ましくない影響があるかもしれません。本番システム中に他のユーザーに影響を与えるのが心配な場合は、別の Rails プロセスを実行してイシューをデバッグすることができます:
- GitLabアカウントにログインします。
- 問題が発生している URL をコピーします(例えば、
https://gitlab.com/ABC
)。 - ユーザーの個人アクセストークンを作成します([ユーザー設定]->[アクセストークン])。
- GitLab Rails コンソールを表示します。
-
Railsコンソールで実行します:
app.get '<URL FROM STEP 2>/?private_token=<TOKEN FROM STEP 3>'
使用例:
app.get 'https://gitlab.com/gitlab-org/gitlab-foss/-/issues/1?private_token=123456'
- 新しいウィンドウで
top
を実行します。このRubyプロセスが100%のCPUを使用していることが表示されるはずです。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 <PID of puma worker> -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
の出力が根本原因の診断に役立つかもしれません。