デバッグのヒント
ここでは、本番環境でイシューをデバッグする際のヒントをいくつか紹介します。
Railsコンソールセッションの開始
GitLabインスタンスのトラブルシューティングやデバッグには、Railsコンソールが必要になることがよくあります。
オムニバス・インストール用
sudo gitlab-rails console
ソースからのインストールの場合
sudo -u git -H bundle exec rails console -e production
Kubernetes: コンソールはタスクランナーポッドにあります。詳細はKubernetesチートシートを参照してください。
アクティブレコードのロギングの有効化
を実行すると、RailsのコンソールセッションでActive Recordのデバッグログの出力を有効にできます:
ActiveRecord::Base.logger = Logger.new(STDOUT)
これにより、コンソールで実行したRubyコードによってトリガーされたデータベースクエリに関する情報が表示されます。 ロギングを再びオフにするには、次のように実行します:
ActiveRecord::Base.logger = nil
データベース文のタイムアウトの無効化
を実行すると、現在のRailsコンソールセッションのPostgreSQLステートメントのタイムアウトを無効にできます:
ActiveRecord::Base.connection.execute('SET statement_timeout TO 0')
この変更は現在のRailsコンソールセッションにのみ影響し、GitLabの本番環境や次のRailsコンソールセッションでは永続化されないことに注意してください。
Railsコンソールセッション履歴の出力
Railsコンソールのコマンド履歴をコピーしやすい形式で出力し、将来の参照用に保存したい場合は、次のように実行します:
puts Readline::HISTORY.to_a
Rails Runnerの使い方
GitLabの本番環境のコンテキストでRubyコードを実行する必要がある場合は、Rails Runnerを使って実行できます。 スクリプトファイルを実行する場合は、スクリプトにgit
ユーザーがアクセスできる必要があります。
オムニバス・インストール用
sudo gitlab-rails runner "RAILS_COMMAND"
# Example with a two-line Ruby script
sudo gitlab-rails runner "user = User.first; puts user.username"
# Example with a ruby script file
sudo gitlab-rails runner /path/to/script.rb
ソースからのインストールの場合
sudo -u git -H bundle exec rails runner -e production "RAILS_COMMAND"
# Example with a two-line Ruby script
sudo -u git -H bundle exec rails runner -e production "user = User.first; puts user.username"
# Example with a ruby script file
sudo -u git -H bundle exec rails runner -e production /path/to/script.rb
メール不通
よくあるトラブルが、何らかの理由でメールが送信されないというものです。 SMTPサーバーを設定したのに、メールが配信されないとします。 設定を確認する方法を説明します:
-
Railsコンソールを実行します。
-
ActionMailer
delivery_method
を見て、意図したものと一致していることを確認してください。 SMTP を設定した場合は、:smtp
と表示されるはずです。 Sendmail を使用している場合は、:sendmail
と表示されるはずです:irb(main):001:0> ActionMailer::Base.delivery_method => :smtp
-
SMTPを使用している場合は、メール設定を確認してください:
irb(main):002:0> ActionMailer::Base.smtp_settings => {:address=>"localhost", :port=>25, :domain=>"localhost.localdomain", :user_name=>nil, :password=>nil, :authentication=>nil, :enable_starttls_auto=>true}
上記の例では、SMTPサーバーはローカル・マシンに設定されています。これが意図的なものであれば、ローカルのメール・ログ(例:
/var/log/mail.log
)で詳細を確認する必要があるかもしれません。 -
コンソールからテストメッセージを送信します。
irb(main):003:0> Notify.test_email('youremail@email.com', 'Hello World', 'This is a test message').deliver_now
メールが届かない、またはエラーメッセージが表示される場合は、メールサーバーの設定を確認してください。
高度なイシュー
より高度なイシューのデバッグには、gdb
。
GNUプロジェクトデバッガ (gdb)
Ubuntu/Debianにインストールするには:
sudo apt-get install gdb
CentOSの場合:
sudo yum install gdb
アールブトレース
GitLab 11.2にはrbtraceが同梱されており、Rubyコードのトレース、実行中の全スレッドの表示、メモリダンプの取得などが可能です。 ただし、デフォルトでは有効になっていません。有効にするには、環境変数にENABLE_RBTRACE
。 例えば、Omnibus:
gitlab_rails['env'] = {"ENABLE_RBTRACE" => "1"}
その後、システムを再設定し、UnicornとSidekiqを再起動します。 Omnibusでこれを実行するには、rootとして実行します:
/opt/gitlab/embedded/bin/ruby /opt/gitlab/embedded/bin/rbtrace
よくある問題
以下、問題を診断するためのヒントの多くは、さまざまな状況に当てはまります。 具体的な例を挙げて、何が問題なのかを知るために何ができるかを説明します。
502 Unicorn が 100% CPU でスピンした後のゲートウェイのタイムアウト
このエラーは、Unicornワーカーからの応答がないままWebサーバーがタイムアウト(デフォルト:60秒)したときに発生します。 この処理中にCPUが100%になった場合は、何か時間がかかっている可能性があります。
このイシューを解決するには、まず何が起こっているのかを知る必要があります。 以下のヒントは、ダウンタイムによるユーザーの影響を気にしない場合にのみお勧めします。 そうでない場合は、次のセクションに進んでください。
- 問題のあるURLをロード
-
sudo gdb -p <PID>
を実行して Unicorn プロセスにアタッチします。 -
gdbウィンドウで次のように入力します:
call (void) rb_backtrace()
-
これはプロセスにRubyのバックトレースを生成させます。
/var/log/gitlab/unicorn/unicorn_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
これらのコマンドを実行する前にUnicornプロセスが終了すると、gdbがエラーを報告することに注意してください。 より多くの時間を稼ぐには、Unicornのタイムアウトをいつでも上げることができます。 omnibusユーザーの場合は、/etc/gitlab/gitlab.rb
を編集して60秒から300秒に増やすことができます:
unicorn['worker_timeout'] = 300
ソース・インストールの場合は、config/unicorn.rb
を編集してください。
変更を有効にするために GitLab を再設定します。
他のユーザーに影響を与えないトラブルシューティング
前節では実行中のUnicornプロセスにアタッチしましたが、この間にGitLabにアクセスしようとするユーザーには望ましくない影響があるかもしれません。 本番システム中に他の人に影響が及ぶことを心配する場合は、別のRailsプロセスを実行してイシューをデバッグすることができます:
- GitLabアカウントにログインします。
- 問題のあるURLをコピーしてください(例:
https://gitlab.com/ABC
)。 - ユーザーの個人アクセストークンを作成します(プロフィール設定 -> アクセストークン)。
- GitLabRailsコンソールを表示します。
-
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
経由でスピンしている Unicorn ワーカーがあるかどうかを調べます。 上記のgdb
テクニックを使ってみてください。 さらに、strace
を使うと、問題の切り分けに役立つかもしれません:
strace -ttTfyyy -s 1024 -p <PID of unicorn worker> -o /tmp/unicorn.txt
どの Unicorn ワーカーがイシューか特定できない場合は、すべての Unicorn ワーカーでstrace
を実行し、/internal/allowed
エンドポイントがスタックしている場所を確認してください:
ps auwx | grep unicorn | awk '{ print " -p " $2}' | xargs strace -ttTfyyy -s 1024 -o /tmp/unicorn.txt
/tmp/unicorn.txt
の出力は、根本原因の診断に役立つかもしれません。