DeNA GitHub Enterprise Server

by taniguti | November 21, 2019
github enterprise server | #github

IT基盤部のたかのりです。

DeNAの様々な開発はもちろんのこと、当ブログの原稿のレビューや個人的なメモ書きに至るまで様々な活動がGitHub Enterprise Server上で行われています。そんな弊社のGitHub Enterprise Serverの運用についてご紹介したいと思います。

GitHub Enterprise Server とは

GitHub Enterprise ServerはGitHubサービスアプライアンスサーバです。以前は単にGitHub Enterpriseと呼ばれていましたが、GitHub.comのbusiness cloudサービスを拡充し、クラウド側の GitHub.com business cloudとオンプレミス側のGitHub Enterpriseを合わせて、GitHub Enterpriseと呼ぶようになりました。そしてクラウド側に限定する場合はGitHub Enterprise Cloud、そしてここで取り上げるオンプレミス側をGitHub Enterprise Serverと呼ぶようになりました。昨今は単に「GitHub Enterprise」で検索などすると、GitHub Enterprise Cloudな記事が多くなったような気がして寂しい限りです。

「GitHub Enterprise Server」はオンプレミス用のアプライアンスサーバです。データセンターなどで適当なサーバハードウエアにインストールして利用することもできますし、AWSをはじめ主要なクラウド環境用にもインスタンスイメージが提供されています。いずれにせよサーバインスタンス全体を管理運用してサービスを提供するというものです。

DeNAではデータセンター内に設置したIAサーバで運用していますが、近い将来にAWSに移行して利用を継続することが決定しています。

GitHub Enterprise Serverの構成と使用状況

弊社のGitHub Enterprise Server(以下、GHES)の構成は以下のようなものです。

弊社の仮想基盤ではVMwareは主流ではありませんのでVMware vSphere Essentialsを利用した小規模な環境で運用しています。production環境のみインスタンスイメージは共有ディスクストレージではなくサーバハードウエアのDASストレージ(NVMe)に置いています。

この記事を書いている2019年11月下旬現在で、production環境にはおよそ1800+くらいの有効なユーザアカウントがあり、oraganizationは531個ありました。リポジトリの総数はフォークされているものを含めて22,508個あり、その半分強がorganizationのリポジトリでした。

ある1日(24時間)のGitリクエスト(clone/fetch/push)の総数は230,854回でした。Gitリクエストの多い上位20個のリポジトリには91,642回(リクエスト総数の約39.7%)のリクエストがありました。 ストレージの使用量は3.8TB程度で日々増えています。

下図はGHESのネットワークアクセスの24時間の推移です。DeNAは超朝型な企業のように見えますが図中の時刻はUTCです(+9時間すると日本標準時です)。エンジニアの勤務状況の傾向としても健全な風に見えますね。

Number of network clients

Number of network clients

GitHub Enterprise Serverリソース割り当てについて

以下の表はGHESのユーザ数に基づくハードウエア構成の推奨例です。

ユーザライセンス vCPUs メモリ アタッチされたストレージ ルートストレージ
トライアル、デモ、あるいは10人の軽量ユーザ 2 16 GB 100 GB 200 GB
10-3000 4 32 GB 250 GB 200 GB
3000-5000 8 64 GB 500 GB 200 GB
5000-8000 12 96 GB 750 GB 200 GB
8000-10000+ 16 128 GB 1000 GB 200 GB

ルートストレージは提供されているインスタンスイメージのルートなのでここは気にしなくて良いです。余談ですがルートストレージは二分割されていて、バージョンアップをするとパーティションを切り替えて起動してきます。なので運用中のルートストレージは半分の100GB程度です。

弊社のケースをこれに当てはめると、ユーザライセンス数は1800+ですので、CPUは4、メモリは32GB、アタッチするストレージは250GBと言うことですが実際はそうではありません。そもそもすでに3.8TBのストレージを消費していることはご紹介した通りですし、上記の表は最小の推奨構成です。実運用で活発に利用すると上記の表にあるよりも多くのリソースが必要になります。本格的に実運用を継続している場合には、この表はもはや意味を無さないのかもしれません。

さらに公式ドキュメントにはこのように書かれています。

CPUリソースを増加させる場合には、GitHub Enterprise Serverインスタンスに追加されるCPUごと(最大で16CPU)に少なくとも6.5GBのメモリを追加することをおすすめします。 When you use more than 16 CPUs, you don’t need to add 6.5 GB of memory for each CPU, but you should monitor your instance to ensure it has enough memory.

後半はまだ日本語訳されていませんが、CPUが16を超える場合は1CPUあたり6.5GBのメモリをを足す必要はないけれども、十分なメモリリソースがあるか注視せよ、となんとも曖昧な説明です。規模が大きくなると実態にあわせて検討する要素も増えるので仕方がないところかもしれません。

GHESでは大きくバックグラウンドで動作しているResqueというworkerプロセスと、ユーザーとのインタラクティブな動作を行うRailsのアプリケーションサーバのUnicorn workerに大別できます。その他細々としたプロセスもありますがリソースの見積もりという観点では無視してよいです。

バックグラウンドの様々な処理を行っているResque workerプロセスは1CPUに対して1個生成され、その上限は18個です。デフォルトの制限でResque workerは18個以上には増えないので、16個以上にCPUを増やしていく場合に単純に1CPUあたり6.5GBを足すという計算ではなくなることの一因かもしれません。 Unicorn wokerはもう少し複雑です。基本的にはCPU数の1.5倍が生成されるのですが、加えてメモリ総量の25%に基づいても調整されています。

  • 総割当メモリの25%が18GBを超える場合は、30個から増加
  • 総割当メモリの25%が16GBから18GBまでの場合は、24個から増加
  • 総割当メモリの25%が8GBから16GBまでの場合は、16個から増加
  • 総割当メモリの25%が4GBから8GBまでの場合は、8個から増加
  • それ以外(25%が4GB未満)の場合は4個まで

GHESインスタンスへの割り当てメモリをあまり増やさないまま、CPU数を大きく割り当てているところにアクティビティが急激に大きくなるようなことがあると、workerが増えていき突然メモリが不足してしまうことがあります(ありました)。「十分なメモリリソースがあるか注視せよ」とはそう言うことのようです。

Git LFSはGHESにも優しい

Git LFSという仕組みがあります。

Gitはその実装として、大きなファイルやバイナリーファイルを扱うことが得意ではないことはよく知られているところです。大きなファイルはリポジトリの外に置いておいて、その時々のバージョンに対応する実体へのポインターのみをリポジトリに含めるようにする、というのが乱暴なGit LFSの仕組みの説明です。その利点や仕組みなどを解説する記事を見つけることは難しくありませんので検索してみてください。

ということで、Git LFSを使うことでリポジトリを「扱いやすい大きさに維持すること」が利用者側だけでなくGHESを管理運用する側にも大きな助けになります、という話です。

GHESは利用者の多数のリポジトリを保持していますから、それらのリポジトリを効率的に保存したり、また利用できるように、保存されているリポジトリに対して常にバックグラウンドで様々なメンテナンスを行っています。この点が利用者側にあるローカルリポジトリと大きく異なる特性です。

前回のメンテナンスを行った時からの変化量がメンテナンスで消費されるインスタンスのリソースに影響します。大きなリポジトリであることはそうしたメンテナンス間の変化量も大きくなる傾向があります。活発に開発行われているリポジトリであれば、尚更であることは想像に難くないと思います。大きなバイナリファイルがリポジトリに含まれたままの場合、それらもメンテナンスの対象として扱われるため、想定以上にリソースを消費してしまいます。本来なら影響しないはずのGHESのパフォーマンスに影響がおよぶ事態となってしまうわけです。

このような問題を起こすリポジトリは多くの場合、歴史ある古いリポジトリで尚且つ現在も活発に開発が継続しているものだったりします。Git LFSが世に出る前からあるリポジトリだったりもするので、止むを得ない結果とも言えるかもしれません。

基本的にメンテナンスは前回と今回の差分について行われるので、リポジトリ運用の途中からGit LFSを利用してもらってもバックグラウンドメンテナンスの負荷軽減が期待できます。 また、昨日作られたような新しいリポジトリもこれから長い歴史をもつものになるかもしれません。利用者に積極的にGit LFSを使ってもらうことで、リポジトリサイズを「扱いやすい大きさ」であるように誘導することは、将来このような問題を回避するために重要なことと思います。

そんなわけでGit LFSの使用を推奨しています。リポジトリに大きなバイナリーファイルがないと、バックグラウンドのメンテナンスは速やかに実施されGitHubサービス全体も快適なものになります。つまりそれは、利用者もGHESの運用管理者も嬉しい、ということになります。

Git LFSを推奨し始めてからなんだかGHESのストレージの消費速度がやや加速しているような気がしますが、きっとそれは気にしすぎと「目を閉じて」日々過ごしています。

ダウンタイムを最短に

GHESの運用では不定期にバージョンアップを行う必要に迫られます。最新版にはセキュリティ対策が含まれているほか、より便利で魅力的な機能が追加されています。最新版を利用者に提供できるかどうかは利用者の満足度に直接的に大きく影響すると思っているので、常に念頭に置いていることでもあります。

バージョンアップのメンテナンス、例えば2.18.xから2.19.xへのバージョンアップでは再起動が必要であり、またアップデート中はサービス提供を止める必要があります。 このダウンタイムの発生は仕方のないことなので、これについては利用者に事前に告知を行い、そう言うものだと理解してもらっています。 原則的に1週間前にメールとGitHubのページにバナーを出す ことで周知をしています。

ダウンタイムの発生は仕方のないこと、とは言え、メンテナンスの作業時間を短縮するために手順の確認や定型作業のスクリプト化をしています。アップデートはStaging環境で先行して行い、アップデート手順やアップデート後に問題が発生しないかの確認を事前に行っています。

バージョンアップが失敗したり、バージョンアップ中になんらかの問題が起きた場合に備えて、バージョンアップの開始前にレプリケーションを一時的に停止しています。これは不測の事態にはレプリカからできるだけ早く復旧できるように備えるためです。 手順の概略としては以下のような流れで実施しています。アップデート作業には30から40分ほど現状かかっています。

  1. GHESをメンテナンスモードに変更
  2. レプリケーションの完了を確認
  3. レプリケーションの停止。
  4. プライマリー側バージョンアップ&再起動。
  5. 動作確認作業(アクセス元IPアドレスを限定し、メンテナンスモードを解除)。
  6. アクセス元の制限を解除しサービスを再開。
  7. レプリカ側バージョンアップ。
  8. レプリケーション再開。

最近は同じシリーズ内、例えば2.18.1から2.18.3のようなバージョンアップでは再起動することなくバージョンアップできるようにもなっています(時々再起動が必要な場合もありますが)。この場合ような場合ダウンタイムなしでバージョンアップを実施しています。

また仮想環境のサーバのメンテナンスなどで長時間のダウンタイムが必要な場合は、レプリカ側で先行して作業を行い、計画的にレプリカをプライマリーに昇格させることでサービスとしてのダウンタイムが最短になるようにしています。高可用性構成は主にはインスタンスの障害に備えてのものですが、弊社の実績的には大きな構成変更を実施する時のダウンタイムの短縮に大変役立っています。

GitHub社のサポートと良い関係を

ここ数年は大きな問題を発生させずに運用できている弊社のGHESですが小さな問題は度々発生しています。大きな問題はもちろん小さな問題でも頼りになるのがGitHub社のサポートチームです。

GitHub社のサポートには世界中のGHES利用者からのフィードバックや障害事例が集まっています。そのため「調べる」より「聞く」方が速い一つの例と思います。障害発生時はもちろん、ちょっとした、不具合とは行かないけれども運用上で気になることの相談にも気軽に答えてくれます。バージョンアップのような大きな変化を加える場合に、不安であればサポートにアップデート作業の日時を伝えておくと、GitHub社のサポートチーム内でも不測の事態の発生に備えて情報共有もしてくれているようです。

トラブルを回避するためにGHESに「手を入れる必要がある」場合もあります。実際そのようなカスタマイズが弊社のGHESにも加えられています。GHESはアプライアンスサーバとして提供されているので、原則としてカスタマイズを加えることは許されていません。しかしながら、このようなカスタマイズもサポートチームの助言を元に加えればなんのお咎めもありません。というよりも、問題解決のための方法として「修正」することを提案されるわけです。加えた修正がバージョンアップで消えてしまうのか、それとも永続的なものかについても助言を得ることができますし、何より自分たちでGHESを勝手にハックして改変するよりは公式お墨付きの改修の方が断然安心感があります。サポート側の記録にも改変状況が記録されるので、将来のサポートもそれを考慮した支援を得ることができます。ある種の改変のアイディアを思いついた場合はサポートチームとその改変について議論するとよいでしょう。

障害や問題が発生した場合は、その原因調査や解決手順をサポートから得ながら障害対応を進めるわけですが、障害を取り除き動作が正常に戻った後でもやりとりを続けることが度々あります。障害の発生機序からGHESの動作について突っ込んで聞いてみたりもしますし、使用した管理用のコマンドツールの使い方などを質問することがあります。 単にトラブル対応に関する質問だけでなく、それから派生した質問を続けていくと、より良い運用の仕方やあるいは利用者の使い方の提案のネタを得ることができます。実は今回紹介したCPUとメモリ量とworker数の決定の仕組みもトラブル対応のやり取りの中で教えてもらったことだったりします。 上手にGHESを運用しようという姿勢を見せるとサポート側もより一層情報を出して支援してくれますよ(筆者の希望的見解です)。こうしたやり取りで得た知見は弊社の日々の通常運用に役立っています。GitHub社のサポートを積極的に利用して、サポートチームと良好な関係を持つと日々の運用も安心して進められると思います。

DeNAのGHES管理運用の現場からは以上です。