Gitaly

Gitalyは、Gitリポジトリへの高レベルRPCアクセスを提供するサービスです。 これがなければ、GitLabコンポーネントはGitデータを読み書きできません。

Gitalyのドキュメントでは:

  • Gitalyサーバーとは、Gitaly自身を実行するノードを指します。
  • Gitalyクライアントとは、Gitalyサーバーにリクエストを行うプロセスを実行するノードを指します。 プロセスには以下のものが含まれますが、これらに限定されるものではありません:

GitLabのエンドユーザーはGitalyに直接アクセスすることはできません。 GitalyはGitLabのGitリポジトリへのアクセスのみを管理します。 他のタイプのGitLabデータはGitalyを使用してアクセスすることはできません。

注意:GitLab 13.0からNFSのGitalyサポートは廃止されました。 GitLab 14.0ではNFSのGitalyサポートは削除される予定です。 できるだけ早くGitalyクラスタにアップグレードしてください。

アーキテクチャ

以下は、Gitalyがどのように使用されているかについての高レベルのアーキテクチャの概要です。

Gitaly architecture diagram

Gitalyの設定

Gitalyサービス自体は、TOML設定ファイルによって設定されます。

Gitalyの設定を変更するには:

Omnibus GitLabの場合

  1. /etc/gitlab/gitlab.rb を編集し、Gitaly設定を追加または変更します。
  2. ファイルを保存し、GitLabを再設定します。

ソースからのインストールの場合

  1. /home/git/gitaly/config.toml を編集し、Gitaly設定を追加または変更します。
  2. ファイルを保存し、GitLabを再起動します。

以下の設定オプションもあります:

独自のサーバーでGitalyを実行します。

デフォルトでは、GitalyはGitalyクライアントと同じサーバー上で実行され、上記のように設定されています。 シングル・サーバー・インストールでは、このデフォルトの設定が最適です:

しかし、Gitalyは独自のサーバーにデプロイすることができるので、複数のマシンにまたがるGitLabのインストールに役立ちます。

注意:Gitalyサーバーが独自のサーバー上で動作するように設定されている場合、クラスター内のGitalyクライアントの前にGitalyサーバーをアップグレードする必要があります

Gitalyを独自のサーバーに設定する手順は以下の通りです:

  1. Gitalyをインストールします。
  2. 認証を設定します。
  3. Gitalyサーバーを設定します。
  4. Gitalyクライアントを設定します。
  5. 不要な場合はGitalyを無効にします(オプション)。

Gitalyを独自のサーバーで実行する場合、GitLabのバージョンに関して以下の点に注意してください:

  • GitLab 11.4から、GitalyはElasticsearchのインデクサーを除き、Gitリポジトリデータの共有NFSマウントを必要とせずにすべてのGitリクエストに対応できるようになりました。
  • GitLab 11.8から、ElasticsearchインデクサはデータアクセスにもGitalyを使うようになりました。 NFSはブロックレベルのGitデータの冗長性のためにまだ活用できますが、Gitalyサーバにマウントする必要があるだけです。
  • GitLab 11.8から12.2までは、NFSを使用しないGitalyのセットアップでElasticsearchを使用することが可能です。 これらのバージョンでElasticsearchを使用するには、GitLabの設定でリポジトリインデクサーを有効にする必要があります。
  • GitLab12.3以降、新しいインデクサはデフォルトであり、設定は必要ありません。

ネットワークアーキテクチャ

以下のリストは、Gitalyのネットワークアーキテクチャを表しています:

  • GitLab Railsはリポジトリをリポジトリストレージに分割します。
  • /config/gitlab.yml には、ストレージ名から(Gitaly address, Gitaly token) ペアへのマップが含まれています。
  • /config/gitlab.ymlstorage name ->(Gitaly address, Gitaly token) マップは、Gitaly ネットワーク・トポロジーの唯一の真実の情報源です。
  • (Gitaly address, Gitaly token) がGitalyサーバーに相当します。
  • Gitalyサーバーは1つ以上のストレージをホストしています。
  • Gitalyクライアントは、1つ以上のGitalyサーバーを使用することができます。
  • Gitalyアドレスは、すべてのGitalyクライアントに対して正しく解決されるように指定されなければなりません。
  • Gitalyのクライアントは以下の通りです:
    • PumaかUnicornか。
    • Sidekiq.
    • GitLab Workhorse。
    • GitLab シェル。
    • Elasticsearch インデクサー。
    • Gitalyそのものです。
  • Gitaly サーバは、/config/gitlab.ymlで指定されているように、自身の(Gitaly address, Gitaly token) ペアを介して自身に対してRPC 呼び出しを行うことができなければなりません。
  • 認証はGitalyとGitLab Railsノード間で共有される静的トークンを使って行われます。
危険:Gitalyのネットワークトラフィックは、デフォルトでは暗号化されていないため、Gitalyサーバーは公開インターネットにさらされてはなりません。 Gitalyサーバーへのアクセスを制限するために、ファイアウォールの使用を強くお勧めします。 別のオプションは、TLSを使用することです。

以下のセクションでは、2つのGitalyサーバーをシークレットトークンで構成する方法を説明しますabc123secret

  • gitaly1.internal.
  • gitaly2.internal.

GitLabのインストールには3つのリポジトリストレージがあると仮定します:

  • default.
  • storage1.
  • storage2.

必要であれば、1つのリポジトリストレージに1台のサーバーを使用することもできます。

注:Gitalyドキュメント全体で言及されているトークンは、管理者によって選択された任意のパスワードに過ぎません。 GitLab APIや他の同様のWeb APIトークンのために作成されたトークンとは無関係です。

Gitalyのインストール

各GitalyサーバーにGitalyをインストールするには、Omnibus GitLabを使用するか、ソースからインストールします:

  • Omnibus GitLabの場合は、必要なOmnibus GitLabパッケージをダウンロードしてインストールしますが EXTERNAL_URL=
  • ソースからインストールするには、InstallGitalyの手順に従ってください。

認証の設定

GitalyとGitLabは、認証に2つの共有秘密を使っています:

  • GitalyへのgRPCリクエストを認証するためのものです。
  • GitLab ShellからGitLab内部APIへの認証コールバック用のセカンド。

Omnibus GitLabの場合

Gitaly トークンを設定します:

  1. Gitalyクライアント上で、/etc/gitlab/gitlab.rbを編集します:

    gitlab_rails['gitaly_token'] = 'abc123secret'
    
  2. ファイルを保存し、GitLabを再設定します。
  3. Gitalyサーバー上で、/etc/gitlab/gitlab.rbを編集します:

    gitaly['auth_token'] = 'abc123secret'
    
  4. GitLabを再設定します。

GitLab Shell トークンの設定には 2 つの方法があります。

方法1:

  1. /etc/gitlab/gitlab-secrets.json をGitalyクライアントからGitalyサーバ(および他のGitalyクライアント)の同じパスにコピーします。
  2. Gitalyサーバー上でGitLabを再設定します。

方法2:

  1. Gitalyクライアント上で、/etc/gitlab/gitlab.rbを編集します:

    gitlab_shell['secret_token'] = 'shellsecret'
    
  2. ファイルを保存し、GitLabを再設定します。
  3. Gitalyサーバー上で、/etc/gitlab/gitlab.rbを編集します:

    gitlab_shell['secret_token'] = 'shellsecret'
    
  4. GitLabを再設定します。

ソースからのインストールの場合

  1. /home/git/gitlab/.gitlab_shell_secret を Gitaly クライアントから Gitaly サーバー (および他の Gitaly クライアント) の同じパスにコピーします。
  2. Gitalyクライアント上で、/home/git/gitlab/config/gitlab.ymlを編集します:

    gitlab:
      gitaly:
        token: 'abc123secret'
    
  3. ファイルを保存し、GitLabを再起動します。
  4. Gitalyサーバー上で、/home/git/gitaly/config.tomlを編集します:

    [auth]
    token = 'abc123secret'
    
  5. ファイルを保存し、GitLabを再起動します。

Gitalyサーバーの設定

Gitalyサーバー上で、ストレージパスを設定し、ネットワークリスナーを有効にする必要があります。

認証を有効にした際のダウンタイムリスクを軽減したい場合は、一時的に認証を無効にすることができます。 詳細については、Gitaly認証の設定に関するドキュメントをご覧ください。

Omnibus GitLabの場合

  1. /etc/gitlab/gitlab.rbを編集します:

    # /etc/gitlab/gitlab.rb
    
    # Avoid running unnecessary services on the Gitaly server
    postgresql['enable'] = false
    redis['enable'] = false
    nginx['enable'] = false
    puma['enable'] = false
    sidekiq['enable'] = false
    gitlab_workhorse['enable'] = false
    grafana['enable'] = false
    gitlab_exporter['enable'] = false
    
    # If you run a separate monitoring node you can disable these services
    alertmanager['enable'] = false
    prometheus['enable'] = false
    
    # If you don't run a separate monitoring node you can
    # enable Prometheus access & disable these extra services.
    # This makes Prometheus listen on all interfaces. You must use firewalls to restrict access to this address/port.
    # prometheus['listen_address'] = '0.0.0.0:9090'
    # prometheus['monitor_kubernetes'] = false
    
    # If you don't want to run monitoring services uncomment the following (not recommended)
    # node_exporter['enable'] = false
    
    # Prevent database connections during 'gitlab-ctl reconfigure'
    gitlab_rails['rake_cache_clear'] = false
    gitlab_rails['auto_migrate'] = false
    
    # Configure the gitlab-shell API callback URL. Without this, `git push` will
    # fail. This can be your 'front door' GitLab URL or an internal load
    # balancer.
    # Don't forget to copy `/etc/gitlab/gitlab-secrets.json` from Gitaly client to Gitaly server.
    gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
    
    # Make Gitaly accept connections on all network interfaces. You must use
    # firewalls to restrict access to this address/port.
    # Comment out following line if you only want to support TLS connections
    gitaly['listen_addr'] = "0.0.0.0:8075"
    
  2. 各Gitalyサーバーの/etc/gitlab/gitlab.rb

    gitaly1.internal

    git_data_dirs({
      'default' => {
        'path' => '/var/opt/gitlab/git-data'
      },
      'storage1' => {
        'path' => '/mnt/gitlab/git-data'
      },
    })
    

    gitaly2.internal

    git_data_dirs({
      'storage2' => {
        'path' => '/srv/gitlab/git-data'
      },
    })
    
  3. ファイルを保存し、GitLabを再設定します。
  4. sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.ymlを実行し、Gitaly が GitLab 内部 API へのコールバックを実行できることを確認します。

ソースからのインストールの場合

  1. /home/git/gitaly/config.tomlを編集します:

    listen_addr = '0.0.0.0:8075'
    
    internal_socket_dir = '/var/opt/gitlab/gitaly'
    
    [logging]
    format = 'json'
    level = 'info'
    dir = '/var/log/gitaly'
    
  2. 各Gitalyサーバーの/home/git/gitaly/config.toml

    gitaly1.internal

    [[storage]]
    name = 'default'
    path = '/var/opt/gitlab/git-data/repositories'
    
    [[storage]]
    name = 'storage1'
    path = '/mnt/gitlab/git-data/repositories'
    

    gitaly2.internal

    [[storage]]
    name = 'storage2'
    path = '/srv/gitlab/git-data/repositories'
    
  3. /home/git/gitlab-shell/config.ymlを編集します:

    gitlab_url: https://gitlab.example.com
    
  4. ファイルを保存し、GitLabを再起動します。
  5. sudo -u git /home/git/gitlab-shell/bin/check -config /home/git/gitlab-shell/config.ymlを実行し、Gitaly が GitLab 内部 API へのコールバックを実行できることを確認します。

Gitalyクライアントの設定

最後のステップとして、Gitalyクライアントを更新し、ローカルのGitalyサービスを使用することから、先ほど設定したGitalyサーバーを使用することに切り替える必要があります。

なぜなら、GitalyクライアントがGitalyサーバーに到達するのを妨げるものは、すべてのGitalyリクエストを失敗させるからです。 例えば、ネットワーク、ファイアウォール、名前解決の問題などです。

また、以前に手動で有効にした場合は、Ruggedを無効にする必要があります。

Gitalyは次のような前提を置いています:

  • あなたのgitaly1.internal Gitaly サーバは、Gitaly クライアントからgitaly1.internal:8075 にアクセスでき、その Gitaly サーバは/mnt/gitlab/default/mnt/gitlab/storage1に読み書きできます。
  • あなたのgitaly2.internal Gitaly サーバは、あなたの Gitaly クライアントからgitaly2.internal:8075 にアクセスすることができ、その Gitaly サーバは/mnt/gitlab/storage2に読み書きすることができます。
  • あなたのgitaly1.internalgitaly2.internal Gitaly サーバーは相互にアクセスできます。

Gitalyサーバは、ローカルGitalyサーバ(無gitaly_address)とリモートサーバ(有 gitaly_address)で定義することはできません。

Omnibus GitLabの場合

  1. /etc/gitlab/gitlab.rbを編集します:

    git_data_dirs({
      'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
      'storage1' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
      'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
    })
    
  2. ファイルを保存し、GitLabを再設定します。
  3. sudo gitlab-rake gitlab:gitaly:check を実行し、Gitaly クライアントが Gitaly サーバーに接続できることを確認します。
  4. ログをたどってリクエストを確認してください:

    sudo gitlab-ctl tail gitaly
    

ソースからのインストールの場合

  1. /home/git/gitlab/config/gitlab.ymlを編集します:

    gitlab:
      repositories:
        storages:
          default:
            gitaly_address: tcp://gitaly1.internal:8075
            path: /some/dummy/path
          storage1:
            gitaly_address: tcp://gitaly1.internal:8075
            path: /some/dummy/path
          storage2:
            gitaly_address: tcp://gitaly2.internal:8075
            path: /some/dummy/path
    
    注:/some/dummy/path は存在する内部フォルダに設定する必要がありますが、このフォルダにはデータは保存されません。このイシューが解決された後は、この設定は不要になります。
  2. ファイルを保存し、GitLabを再起動します。
  3. sudo -u git -H bundle exec rake gitlab:gitaly:check RAILS_ENV=production を実行し、Gitaly クライアントが Gitaly サーバーに接続できることを確認します。
  4. ログをたどってリクエストを確認してください:

    tail -f /home/git/gitlab/log/gitaly.log
    

GitalyサーバーのGitalyログを確認すると、リクエストが届いているはずです。 Gitalyリクエストをトリガーする確実な方法の1つは、HTTPまたはHTTPSでGitLabからリポジトリをクローンすることです。

危険:リポジトリごと、またはグローバルにサーバーフックを設定している場合は、Gitalyサーバーに移動する必要があります。 複数のGitalyサーバーがある場合は、すべてのGitalyサーバーにサーバーフックをコピーしてください。

混合構成

GitLabは多くのGitalyサーバーのうちの1つと同じサーバーに常駐させることができますが、ローカルとリモートの設定を混在させる設定はサポートしていません。 以下の設定は間違っています:

  • すべてのアドレスは、他のGitalyサーバーから到達可能でなければなりません。
  • storage1 には、gitaly_address の Unix ソケットが割り当てられます。これは、Gitaly サーバの一部では無効です。
git_data_dirs({
  'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
  'storage1' => { 'path' => '/mnt/gitlab/git-data' },
  'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
})

ローカルとリモートのGitalyサーバーを組み合わせるには、ローカルのGitalyサーバーに外部アドレスを使用します。 例えば、以下のようになります:

git_data_dirs({
  'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
  # Address of the GitLab server that has Gitaly running on it
  'storage1' => { 'gitaly_address' => 'tcp://gitlab.internal:8075', 'path' => '/mnt/gitlab/git-data' },
  'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
})

path 除外された場合、そのストレージ・シャードにはデフォルトのGitストレージ・ディレクトリが使用されます。

不要な場合はGitalyを無効にします(オプション)。

Gitalyをリモートサービスとして実行している場合は、GitLabサーバー上でデフォルトで実行されているローカルのGitalyサービスを無効にして、必要なところだけ実行するようにするとよいでしょう。

GitLabインスタンスでGitalyを無効にすることは、GitLabインスタンスとは別のマシンでGitalyを実行する、カスタムクラスタ構成でGitLabを実行する場合にのみ意味があります。 クラスタ内のすべてのマシンでGitalyを無効にすることは、有効な構成ではありません(いくつかのマシンはGitalyサーバとして動作します)。

GitLabサーバーでGitalyを無効にするには:

Omnibus GitLabの場合

  1. /etc/gitlab/gitlab.rbを編集します:

    gitaly['enable'] = false
    
  2. ファイルを保存し、GitLabを再設定します。

ソースからのインストールの場合

  1. /etc/default/gitlabを編集します:

    gitaly_enabled=false
    
  2. ファイルを保存し、GitLabを再起動します。

TLSサポートの有効化

GitLab 11.8 で導入されました

GitalyはTLS暗号化をサポートしています。 安全な接続をリッスンするGitalyインスタンスと通信するには、GitLab設定の対応するストレージ・エントリーのgitaly_addresstls:// URLスキームを使用する必要があります。

各Gitalyサーバーに対応する証明書をインストールする必要があります。

さらに、証明書(またはその作成者)は、すべてにインストールされている必要があります:

  • 証明書を使用しているGitalyサーバーを含むGitalyサーバー。
  • 通信するGitalyクライアント。

このプロセスはGitLabカスタム証明書設定に記載されており、以下でも繰り返し説明します。

以下に注意してください。

  • 証明書は、Gitalyサーバーにアクセスするために使用するアドレスを指定する必要があります。 その場合は、以下のようになります:
  • 暗号化されていないリスニング・アドレスlisten_addr と暗号化されたリスニング・アドレスtls_listen_addr の両方を持つ Gitaly サーバーを同時に設定することができます。 これにより、必要に応じて暗号化されていないトラフィックから暗号化されたトラフィックへと徐々に移行することができます。

GitalyをTLSで設定するには:

Omnibus GitLabの場合

  1. Gitalyサーバー用の証明書を作成します。
  2. Gitalyクライアント上で、証明書(またはその作成者)を/etc/gitlab/trusted-certsにコピーします:

    sudo cp cert.pem /etc/gitlab/trusted-certs/
    
  3. Gitalyクライアント上で、/etc/gitlab/gitlab.rbgit_data_dirs を以下のように編集してください:

    git_data_dirs({
      'default' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
      'storage1' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
      'storage2' => { 'gitaly_address' => 'tls://gitaly2.internal:9999' },
    })
    
  4. ファイルを保存し、GitLabを再設定します。
  5. Gitalyサーバー上で、/etc/gitlab/ssl ディレクトリを作成し、そこに鍵と証明書をコピーします:

    sudo mkdir -p /etc/gitlab/ssl
    sudo chmod 755 /etc/gitlab/ssl
    sudo cp key.pem cert.pem /etc/gitlab/ssl/
    sudo chmod 644 key.pem cert.pem
    
  6. すべてのGitalyサーバ証明書(またはその作成者)を/etc/gitlab/trusted-certs にコピーし、Gitalyサーバが自分自身または他のGitalyサーバに呼び出す際に証明書を信頼できるようにします:

    sudo cp cert1.pem cert2.pem /etc/gitlab/trusted-certs/
    
  7. /etc/gitlab/gitlab.rb を編集して追加してください:

    gitaly['tls_listen_addr'] = "0.0.0.0:9999"
    gitaly['certificate_path'] = "/etc/gitlab/ssl/cert.pem"
    gitaly['key_path'] = "/etc/gitlab/ssl/key.pem"
    
  8. ファイルを保存し、GitLabを再設定します。
  9. Gitaly接続のタイプを観察することで、GitalyトラフィックがTLSを介して提供されていることを確認してください。
  10. (オプション)以下の方法でセキュリティを向上させます:
    1. /etc/gitlab/gitlab.rbgitaly['listen_addr'] をコメントアウトまたは削除することで、非 TLS 接続を無効にします。
    2. ファイルの保存
    3. GitLabの再構成

ソースからのインストールの場合

  1. Gitalyサーバー用の証明書を作成します。
  2. Gitalyクライアント上で、証明書をシステム信頼済み証明書にコピーします:

    sudo cp cert.pem /usr/local/share/ca-certificates/gitaly.crt
    sudo update-ca-certificates
    
  3. Gitalyクライアント上で、/home/git/gitlab/config/gitlab.ymlstorages を以下のように編集してください:

    gitlab:
      repositories:
        storages:
          default:
            gitaly_address: tls://gitaly1.internal:9999
            path: /some/dummy/path
          storage1:
            gitaly_address: tls://gitaly1.internal:9999
            path: /some/dummy/path
          storage2:
            gitaly_address: tls://gitaly2.internal:9999
            path: /some/dummy/path
    
    注:/some/dummy/path は、存在するローカルフォルダに設定する必要がありますが、このフォルダにはデータは保存されません。 Gitalyissue #1282が解決された後は、この設定は必要なくなります。
  4. ファイルを保存し、GitLabを再起動します。
  5. Gitalyサーバー上で、/etc/default/gitlab を作成または編集し、追加します:

    export SSL_CERT_DIR=/etc/gitlab/ssl
    
  6. Gitalyサーバー上で、/etc/gitlab/ssl ディレクトリを作成し、そこに鍵と証明書をコピーします:

    sudo mkdir -p /etc/gitlab/ssl
    sudo chmod 755 /etc/gitlab/ssl
    sudo cp key.pem cert.pem /etc/gitlab/ssl/
    sudo chmod 644 key.pem cert.pem
    
  7. 全てのGitalyサーバ証明書(またはその作成者)をシステム信頼済み証明書フォルダにコピーし、Gitalyサーバが自身または他のGitalyサーバに呼び出す際に証明書を信頼するようにします。

    sudo cp cert.pem /usr/local/share/ca-certificates/gitaly.crt
    sudo update-ca-certificates
    
  8. /home/git/gitaly/config.toml を編集して追加してください:

    tls_listen_addr = '0.0.0.0:9999'
    
    [tls]
    certificate_path = '/etc/gitlab/ssl/cert.pem'
    key_path = '/etc/gitlab/ssl/key.pem'
    
  9. ファイルを保存し、GitLabを再起動します。
  10. Gitaly接続のタイプを観察することで、GitalyトラフィックがTLSを介して提供されていることを確認してください。
  11. (オプション)以下の方法でセキュリティを向上させます:
    1. /home/git/gitaly/config.tomllisten_addr をコメントアウトまたは削除することで、非 TLS 接続を無効にします。
    2. ファイルの保存
    3. GitLabを再起動します。

Gitalyコネクションの種類を見る

Prometheusは、Gitalyが本番環境でどのような接続を提供しているかを観察するために使用できます。 以下のPrometheusクエリを使用します:

sum(rate(gitaly_connections_total[5m])) by (type)

gitaly-ruby

GitalyはGitLabのRubyアプリケーションコードを置き換えるために開発されました。

時間を節約し、既存のアプリケーション・ロジックを書き直すリスクを避けるために、私たちはGitLabからGitalyにアプリケーション・コードをコピーすることにしました。

そのコードを実行できるように、gitaly-ruby 。これは、Gitaly Goのメイン・プロセスの「サイドカー」プロセスです。gitaly-ruby で実装されているものの例をいくつか挙げます:

  • ウィキを扱うRPC。
  • マージコミットなど、ユーザーに代わってコミットを作成する RPC。

gitaly-ruby ワーカー数の設定

Gitalygitaly-ruby サーバーが多くのリクエストを処理しなければならない場合、デフォルトの設定であるアクティブな gitaly-rubyサイドカーをgitaly-ruby 1つだけ持つだけ gitaly-rubyでは不十分かもしれません。

GitalyからResourceExhausted エラーが表示された場合、gitaly-ruby の容量が不足している可能性が非常に高いです。

Gitalyサーバーのgitaly-ruby プロセス数は、以下の設定で増やすことができます:

Omnibus GitLabの場合

  1. /etc/gitlab/gitlab.rbを編集します:

    # Default is 2 workers. The minimum is 2; 1 worker is always reserved as
    # a passive stand-by.
    gitaly['ruby_num_workers'] = 4
    
  2. ファイルを保存し、GitLabを再設定します。

ソースからのインストールの場合

  1. /home/git/gitaly/config.tomlを編集します:

    [gitaly-ruby]
    num_workers = 4
    
  2. ファイルを保存し、GitLabを再起動します。

RPCの同時実行を制限

クローンのトラフィックは、Gitalyサービスに大きな負担をかけます。 作業の大部分は、以下のRPCのいずれかで行われます:

  • SSHUploadPack (Git SSHの場合)。
  • PostUploadPack (Git HTTPの場合)。

このようなワークロードがGitalyサーバーを圧倒するのを防ぐために、Gitalyの設定ファイルで同時実行数の制限を設定することができます。 例えば、以下のような場合です:

# in /etc/gitlab/gitlab.rb

gitaly['concurrency'] = [
  {
    'rpc' => "/gitaly.SmartHTTPService/PostUploadPack",
    'max_per_repo' => 20
  },
  {
    'rpc' => "/gitaly.SSHService/SSHUploadPack",
    'max_per_repo' => 20
  }
]

これは、指定された RPC のインフライト RPC 呼び出し回数を制限します。 制限はリポジトリごとに適用されます。 上記の例では、次のようになります:

  • Gitaly サーバーが提供する各リポジトリは、最大 20 の同時PostUploadPack RPC 呼び出しが可能で、SSHUploadPackについても同様です。
  • 20のスロットを使い切ったリポジトリに別のリクエストが来た場合、そのリクエストはキューに入れられます。

GitalyのログとPrometheusを使って、このキューの動作を観察することができます:

  • Gitalyのログで、文字列(または構造化されたログフィールド)acquire_msを探してください。 このフィールドがあるメッセージは、同時実行リミッターについてレポーターしています。
  • Prometheusでは、以下のメトリクスを探してください:

    • gitaly_rate_limiting_in_progress.
    • gitaly_rate_limiting_queued.
    • gitaly_rate_limiting_seconds.
注意:Prometheusのメトリクス名にはrate_limitingが含まれていますが、これは並行性リミッタであり、レートリミッタではありません。Gitalyクライアントが1000リクエストを非常に速く連続して行った場合、並行性は1を超えず、並行性リミッタは何の効果もありません。

Gitaly認証トークンのローテーション

本番環境でクレデンシャルをローテーションする場合、ダウンタイムが必要になるか、機能停止が発生するか、あるいはその両方が発生することがよくあります。

Gitaly認証トークンのローテーションは、以下の手順で行います:

この手順は、GitLabを単一のサーバーで実行している場合にも有効です。 その場合、”Gitalyサーバー “と “Gitalyクライアント “は同じマシンを指します。

認証モニタリングの検証

Gitaly認証トークンをローテーションする前に、Prometheusを使用してGitLabインストールの認証動作を監視できることを確認してください。 以下のPrometheusクエリを使用します:

sum(rate(gitaly_authentications_total[5m])) by (enforced, status)

認証が正しく設定され、ライブトラフィックがあるシステムでは、次のように表示されます:

{enforced="true",status="ok"}  4424.985419441742

私たちが気にするのは0以外の数だけです。

ゼロでない唯一の数値はenforced="true",status="ok"であるべきです。他のゼロでない数値がある場合、設定に何か問題があります。

status="ok" の数字は、現在のリクエストレートを反映しています。上の例では、Gitalyは1秒あたり約4000リクエストを処理しています。

これで、GitLabインストレーションのGitaly認証の動作を監視できることがわかったので、残りの手順を始めましょう。

認証遷移」モードの有効化

以下のように、Gitalyサーバを “auth transitioning “モードにして、Gitaly認証を一時的に無効にします:

# in /etc/gitlab/gitlab.rb
gitaly['auth_transitioning'] = true

この変更を行うと、Prometheusクエリは次のような結果を返すはずです:

{enforced="false",status="would be ok"}  4424.985419441742

enforced="false"、新しいトークンの展開を開始しても大丈夫だからです。

Gitaly認証トークンの更新

新しいGitaly認証トークンに更新するには、各GitalyクライアントとGitalyサーバー上で行います:

  1. 設定を更新します:

    # in /etc/gitlab/gitlab.rb
    
    gitaly['auth_token'] = '<new secret token>'
    
  2. Gitalyを再起動してください:

    gitlab-ctl restart gitaly
    

この変更がロールアウトされている間にPrometheus クエリを実行すると、enforced="false",status="denied" カウンタにゼロ以外の値が表示されます。

認証に失敗しないようにします。

新しいトークンが設定され、関係するすべてのサービスが再起動された後、一時的に以下のものが混在して表示されます:

  • status="would be ok".
  • status="denied".

新しいトークンがすべてのGitalyクライアントとGitalyサーバーによってピックアップされた後、唯一のゼロでないレートはenforced="false",status="would be ok"

認証遷移」モードの無効化

Gitaly認証を再度有効にするには、”auth transitioning “モードを無効にしてください。 Gitalyサーバーの設定を以下のように更新してください:

# in /etc/gitlab/gitlab.rb
gitaly['auth_transitioning'] = false
注意:このステップを完了しないと、Gitaly認証が行われません。

認証の有効性を確認

Prometheusのクエリをリフレッシュしてください。 これで、最初と同じような結果が表示されるはずです:

{enforced="true",status="ok"}  4424.985419441742

enforced="true" は、認証が強制されていることを意味します。

Gitalyをバイパスして直接Gitにアクセスできます。

ディスクに保存されたGitalyリポジトリにGitクライアントで直接アクセスすることは可能ですが、Gitalyは継続的に改良され、変更されているため、お勧めできません。 これらの改良は仮定を無効にする可能性があり、その結果、パフォーマンスの低下、不安定性、さらにはデータの損失を引き起こす可能性があります。

Gitalyには、info/refs 広告キャッシュなどの最適化がありますが、これはGitalyが公式gRPCインターフェイスを介してリポジトリへのアクセスを制御および監視することに依存しています。 同様に、Praefectには、フォールトトレランスやディストリビューションリードなどの最適化がありますが、これはリポジトリの状態を決定するgRPCインターフェイスとデータベースに依存しています。

これらの理由から、リポジトリへの直接アクセスは自己責任で行ってください。

GitLabでGitに直接アクセス

Gitへの直接アクセスには、「Ruggedパッチ」として知られるGitLabのコードを使用します。

沿革

Gitalyが存在する以前は、現在のGitalyクライアントはGitリポジトリに直接アクセスしていました:

  • シングルマシンのOmnibus GitLabインストールの場合はローカルディスク
  • GitLab を水平にインストールする場合の NFS の使用。

プレーンなgit コマンドを実行する以外に、GitLabはRuggedと呼ばれるRubyライブラリを使っていました。Ruggedはlibgit2のラッパーで、Cライブラリの形式でGitをスタンドアロンで実装したものです。

そのうちに、Rugged、特にUnicornとの組み合わせが非常に効率的であることが明らかになりました。libgit2 はライブラリであり、内部プロセスではないため、その間のオーバーヘッドはほとんどありませんでした:

  • Gitリポジトリ内のデータを検索しようとしたGitLabアプリケーションコード。
  • Gitの実装そのもの。

RuggedとUnicornの組み合わせはとても効率的だったため、GitLabのアプリケーション・コードではGitオブジェクトの重複検索が多くなってしまいました。 例えば、master のコミットを1回のリクエストで何十回も検索するようなことです。 パフォーマンスが低下することなく、非効率的なコードを書くことができました。

これらのGitルックアップをGitalyの呼び出しに移行したとき、Gitルックアップあたりの固定コストが急に高くなりました。 Gitalyがすでに実行されているgit (たとえば、コミットをルックアップする)プロセスを再利用できる場合でも、まだコストがかかります:

  • Gitalyまでのネットワーク往復料金。
  • Gitaly内部では、Gitalyとgit プロセスを接続するUnixパイプの書き込み/読み取りラウンドトリップ。

GitLab.comを使って測定し、Ruggedの効率性の損失が感じられなくなるまで、リクエストあたりのGitaly呼び出しの回数を減らしました。 NFSマウントを経由するのではなく、Gitファイルセーバー上でGitaly自体を直接実行したことも役立ちました。 これにより、Ruggedを使わなくなったことによるマイナスの影響を打ち消すほどの速度向上が得られました。

残念ながら、GitLabの他のデプロイでは、私たちがGitLab.comで行ったようにNFSを削除することができませんでした:

  • NFSの遅さ。
  • Gitaly固有のオーバーヘッドの増加。

Gitaly移行プロジェクトでGitLabから削除されたコードは、これらのデプロイに影響を与えました。 これらのNFSベースのデプロイのパフォーマンス回避策として、私たちは古いRuggedコードの一部を再導入しました。 この再導入されたコードは、非公式に “Ruggedパッチ “と呼ばれています。

どのように動作するか

Gitへの直接アクセスを行うRubyのメソッドはfeature flagsの後ろにあり、デフォルトでは無効になっています。 最良のパフォーマンスを得るためにfeature flagsを設定するのは不便だったので、Gitへの直接アクセスを可能にする自動メカニズムを追加しました。

GitLabは “Rugged patch “を持つ関数を呼び出すと、2つのチェックを行います:

  • このパッチのfeatureフラグがデータベースに設定されていますか? もし設定されていれば、featureフラグの設定はGitLabが “Rugged patch “コードを使うかどうかをコントロールします。
  • featureフラグが設定されていない場合、GitLabはGitalyサーバーの下のファイルシステムに直接アクセスしようとします。 可能であれば、”Rugged patch “を使用します。

これら両方のチェック結果はキャッシュされます。

GitLabがリポジトリのファイルシステムに直接アクセスできるかどうかを調べるために、次のヒューリスティックを使います:

  • Gitalyは、ファイルシステムのルートにUUIDを持つメタデータファイルがあることを保証します。
  • GitalyはこのUUIDをServerInfo RPC経由でGitLabにレポーターします。
  • GitLab Railsはメタデータファイルを直接読み込もうとします。 メタデータファイルが存在し、UUIDが一致すれば、直接アクセスできたとみなします。

OmnibusのGitLabでは、GitLab設定ファイルconfig/gitlab.yml。 これでUUIDチェックが満たされるため、Gitへの直接アクセスはデフォルトで有効になっています。

Gitalyクラスタへの移行

複雑さを取り除くために、GitLabのGitへの直接アクセスを削除しなければなりません。 しかし、GitLabのインストールがNFS上のGitリポジトリを必要とする限り、削除することはできません。

GitLabでGitへの直接アクセスを削除する取り組みには二つの側面があります:

  • GitLabが行う非効率なGitalyクエリの数を減らします。
  • フォールトトレラントまたは水平にスケールされたGitLabインスタンスの管理者に、NFSから移行するよう説得。

そのために私たちはGitalyクラスターを開発しました。

Gitalyのトラブルシューティング

スタンドアロンのGitalyサーバーを使用する際のバージョンの確認

スタンドアロンのGitalyサーバーを使用する場合、完全な互換性を確保するために、それらがGitLabと同じバージョンであることを確認する必要があります。 GitLabインスタンスの管理エリア > Gitalyサーバーを確認し、すべてのGitalyサーバーがUp to date

Gitaly standalone software versions diagram

gitaly-debug

gitaly-debug コマンドは、GitalyとGitのパフォーマンスに関する “プロダクション・デバッグ “ツールを提供します。これは、プロダクション・エンジニアとサポート・エンジニアがGitalyのパフォーマンスの問題を調査するためのものです。

GitLab 11.6以降を使用している場合、このツールはGitLab / Gitalyサーバーに既にインストールされているはずです(/opt/gitlab/embedded/bin/gitaly-debug)。古いバージョンのGitLabを調査している場合は、このツールをオフラインでコンパイルし、実行ファイルをサーバーにコピーすることができます:

git clone https://gitlab.com/gitlab-org/gitaly.git
cd cmd/gitaly-debug
GOOS=linux GOARCH=amd64 go build -o gitaly-debug

サポートされているサブコマンドのリストについては、gitaly-debug のヘルプページを参照してください:

gitaly-debug -h

コミット、プッシュ、クローンは 401 を返します。

remote: GitLab: 401 Unauthorized

gitlab-secrets.json ファイルをGitalyクライアント(GitLabアプリノード)と同期する必要があります。

クライアント側gRPCログ

GitalyはgRPCRPCフレームワークを使用しています。 Ruby gRPCクライアントには独自のログファイルがあり、Gitalyのエラーが発生したときに有用な情報が含まれている場合があります。 gRPCクライアントのログレベルは環境変数GRPC_LOG_LEVEL で制御できます。デフォルトのレベルはWARNです。

でgRPCトレースを実行できます:

sudo GRPC_TRACE=all GRPC_VERBOSITY=DEBUG gitlab-rake gitlab:gitaly:check

gitaly-ruby トラフィックの観察

gitaly-ruby はGitalyの内部実装の詳細であるため、gitaly-ruby プロセスの内部で何が行われているかについては、それほど多くの可視性はありません。

gitaly-ruby GitalyプロセスをスクレイピングするようにPrometheusをセットアップしている場合、grpc_client_handled_total。厳密に言えば、このメトリクスはgitaly-ruby 他のRPCと gitaly-ruby区別gitaly-ruby しませんが、実際には(G gitaly-rubyitLabgitaly-ruby 11.9の時点では)Gitaly自身によって行われるすべてのgRPCコールは、メインのGitalyプロセスからその gitaly-rubyサイドカーのgitaly-ruby 1つ gitaly-rubyへの内部gitaly-ruby コール gitaly-rubyです。

grpc_client_handled_total カウンタが Gitaly のみを観測していると仮定すると、以下のクエリは、RPC がgitaly-rubyへの呼び出しとして内部実装されている(可能性が高い)ことを示しています:

sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0

リポジトリの変更は401 Unauthorized エラーで失敗します。

Gitalyを独自のサーバーで運用しており、ユーザーが(SSHとHTTPSの両方で)リポジトリのクローンと取得に成功しても、401 Unauthorized メッセージが表示されることなく、リポジトリへのプッシュやWeb UIでのリポジトリへの変更ができないことに気づいた場合、Gitalyが間違ったsecretsファイルを持っているためにGitalyクライアントとの認証に失敗している可能性があります。

以下がすべて正しいことを確認してください:

  • この Gitaly サーバー上のリポジトリに対してgit push を実行すると、次のようなエラーで失敗します (401 Unauthorizedに注意してください):

     remote: GitLab: 401 Unauthorized
     To <REMOTE_URL>
     ! [remote rejected] branch-name -> branch-name (pre-receive hook declined)
     error: failed to push some refs to '<REMOTE_URL>'
    
  • GitLab UI を使ってリポジトリからファイルを追加したり変更したりすると、赤い401 Unauthorized のバナーが表示されて即座に失敗します。
  • 新しいプロジェクトを作成し、READMEを付けて初期化すると、プロジェクトは正常に作成されますが、READMEは作成されません。
  • Gitalyクライアントでログを追跡し、エラーを再現すると、/api/v4/internal/allowed エンドポイントに到達する際に、401 エラーが発生します:

     # api_json.log
     {
       "time": "2019-07-18T00:30:14.967Z",
       "severity": "INFO",
       "duration": 0.57,
       "db": 0,
       "view": 0.57,
       "status": 401,
       "method": "POST",
       "path": "\/api\/v4\/internal\/allowed",
       "params": [
         {
           "key": "action",
           "value": "git-receive-pack"
         },
         {
           "key": "changes",
           "value": "REDACTED"
         },
         {
           "key": "gl_repository",
           "value": "REDACTED"
         },
         {
           "key": "project",
           "value": "\/path\/to\/project.git"
         },
         {
           "key": "protocol",
           "value": "web"
         },
         {
           "key": "env",
           "value": "{\"GIT_ALTERNATE_OBJECT_DIRECTORIES\":[],\"GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE\":[],\"GIT_OBJECT_DIRECTORY\":null,\"GIT_OBJECT_DIRECTORY_RELATIVE\":null}"
         },
         {
           "key": "user_id",
           "value": "2"
         },
         {
           "key": "secret_token",
           "value": "[FILTERED]"
         }
       ],
       "host": "gitlab.example.com",
       "ip": "REDACTED",
       "ua": "Ruby",
       "route": "\/api\/:version\/internal\/allowed",
       "queue_duration": 4.24,
       "gitaly_calls": 0,
       "gitaly_duration": 0,
       "correlation_id": "XPUZqTukaP3"
     }
    
     # nginx_access.log
     [IP] - - [18/Jul/2019:00:30:14 +0000] "POST /api/v4/internal/allowed HTTP/1.1" 401 30 "" "Ruby"
    

この問題を解決するには、Gitalyサーバー上のgitlab-secrets.json ファイルがGitalyクライアント上のものと一致していることを確認してください。一致していない場合は、Gitalyサーバー上のsecretsファイルをGitalyクライアントと一致するように更新し、再設定してください。

コマンドラインツールがGitalyに接続できません。

コマンドライン(CLI) ツールを使ってGitalyサーバーに接続する際に問題があり、特定の操作で14: Connect Failed エラーメッセージが表示される場合、それはgRPCがGitalyサーバーに到達できないことを意味します。

TCP経由でGitalyにアクセスできることを確認してください:

sudo gitlab-rake gitlab:tcp_check[GITALY_SERVER_IP,GITALY_LISTEN_PORT]

TCP接続が失敗する場合は、ネットワーク設定とファイアウォールルールを確認してください。 TCP接続が成功する場合は、ネットワーク設定とファイアウォールルールが正しいことを示しています。

Bashなどのコマンドライン環境でプロキシサーバを使用している場合、gRPCトラフィックを妨害する可能性があります。

Bashまたは互換性のあるコマンドライン環境を使用している場合は、以下のコマンドを実行してプロキシサーバーが設定されているかどうかを確認します:

echo $http_proxy
echo $https_proxy

これらの変数のいずれかに値がある場合、Gitaly CLI接続はGitalyに接続できないプロキシを経由している可能性があります。

プロキシ設定を削除するには、以下のコマンドを実行します(どの変数に値があるかによって異なります):

unset http_proxy
unset https_proxy

再設定後、Gitalyが新しいアドレスをリッスンしません。

gitaly['listen_addr'] またはgitaly['prometheus_listen_addr']の値を更新する際、Gitalyはsudo gitlab-ctl reconfigureの後も古いアドレスでリッスンを続けることがあります。

この問題が発生した場合、sudo gitlab-ctl restart を実行することで問題が解決します。この問題が解決された後は、この操作は必要なくなります。

スタンドアロンのGitalyサーバーからリポジトリにアクセスする際、Gitalyログに権限拒否エラーが表示される問題

ファイルの権限が正しいにもかかわらずこのエラーが発生する場合は、Gitalyサーバーでクロックドリフトが発生している可能性があります。

Gitalyクライアントとサーバーが同期していることを確認し、可能であればNTPタイムサーバーを使用して同期をとってください。

総評

Praefectは、Gitaly用のルータおよびトランザクションマネージャで、Gitalyクラスタを実行するために必要なコンポーネントです。 詳細については、Gitalyクラスタを参照してください。