entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

ちゃんと復旧できる、GitLabのバックアップ運用方法 ─ GitLab meetup #01レポート

2017年3月2日(木)、都内では初のGitLabのイベント「GitLab meetup in Tokyo #1」が開催されました。記念すべき第一回のイベントは、ピクシブのオフィスで開催され、約140名の参加があるという大盛況っぷり。ピクシブからは、2名のエンジニアが登壇しGitLabに対する知見を発表しています!

そのうちの一人。2013年にピクシブへ入社した金子 達哉、ニックネームはcatatsuy。彼が同イベントで語った、GitLabのバックアップや、バージョンアップの運用に関する知見を、本記事にてご紹介します。

GitLabのインストールは難しい?

GitLabとは、プライベートなgitリポジトリの管理画面やレビュー画面を提供してくれるWebアプリケーションです。GitHubBitbucketのようなことが、自社のサーバで行えます。自社のサーバーにインストールして運用することができます。

GitLabは、インストールや運用が難しいと言われています。しかし私は、そこまで難しいとは考えていません。

まずはインストールの前提について。これは公式ドキュメント通りにインストールを進める上で抑えるべきポイントです。

GitLabには、色んな運用方法がありまして、例えばDockerを活用した運用方法なんかもあります。しかし今回はそういうものを使わず、ローカル環境にインストールすることを前提としてお話します。

GitLabは、グローバルIPアドレスを持つサーバー上に構築するというのが前提です。gitリポジトリをSSH経由でcloneできるようにする必要があるからです。ただし、SSHをport forwardingしてHTTPをproxyすれば、グローバルIPアドレスを持たないサーバーでも構築することができます。SSHをプライベートネットワーク内でしか使わないのであれば、もちろんport forwadingの設定は不要です。

また、GitLabを動かすサーバーは1台であることが前提です。gitリポジトリがアプリケーションサーバーと同じサーバーにある必要があるので、複数台構成は現実的ではありません。

もちろん、PostgreSQLやMySQLみたいなデータベースについては、別サーバー上で運用することができます。Redisについても同様です。一般的なRailsのアプリケーションは、メモリやCPUリソースを食いやすいので、パワーがあるサーバーを使うことをオススメします。

バックアップすべきデータはどれか?

GitLabのデータバックアップ失敗問題については、ここ最近ネット上でも大きく取り上げられ話題になりました。

ただ、問題になっていたのはGitLab.comというサービスであって、GitLabのソースコードやバックアップの仕組みに問題があるわけではありません。GitLabのバックアップの仕組みをちゃんと使えば、今回のような問題は起きません。

まず、GitLabの持っているデータのバックアップ対象についてご紹介しましょう。

GitLabのデータは、データベースとgitリポジトリとアップロードファイルなどがあります。他にもRedisが管理しているデータもありますが、キャッシュとセッションとresqueのキューが入っている程度なので、消えたとしても大きな問題にはなりません。

社内向けに公開するのであれば、再びログインしてもらうだけで解決される、些細な問題でしょう。GitLab側もRedisのバックアップを作成するための方法は提供していません。

Redis以外のデータが、バックアップ対象です。

DBについては、公式側ではPostgreSQLを推奨しています。MySQLも一応は使えますが、非推奨である旨が明記されています。GitLabの公式ブログでもPostgreSQL前提の話しかしていません。公式ドキュメントにも、MySQLからPostgreSQLへの移行手順はあっても、その逆は書いていません。

そもそもRailsは、事実上PostgreSQL推奨です。RailsとMySQLの相性問題について、詳しくは「Rails MySQL kamipo」で検索すれば情報はみつかります。

また、MySQLを使う場合の注意点などは、GitLabの公式ドキュメントにも書かれています。ただ、RailsでMySQLを扱うプロダクトを運営してきたような人にとっては、常識的なことばかりが書かれているようにみえます。

いくつか例を挙げてみましょう。

MySQLではutf8_mb4を使わないと、絵文字などBMP外の文字、つまりUTF-8で4バイトになる文字が保存できません。なおかつ、5.7.7より前のMySQLを使っている場合は、デフォルト設定のままだとインデックスサイズに課題があり、Railsのマイグレーションを管理するschema_migrationsテーブルが作れない、といった問題が発生します。

また、RailsにはMySQL周りのデフォルト設定には問題があります。

デフォルトの照合順序utf8_unicode_ciを使うと、「ハハパパ問題」が起きます。このあたりは、GitLabはutf8_general_ciに設定されるなど、問題が起きないようにするための対応されています。

GitLabでは、Ruby on RailsでMySQLを扱う知識やノウハウを持っていないというなら、PostgreSQLを使うべきです。ピクシブでは、ECサービスの「BOOTH」やマンガアプリの「pixivコミック」などで運用実績があり知見があったので、MySQLを採用しています。

ここから先も、MySQLを前提にお話します。

GitLabの実践的のバックアップ方法

最近世の中を騒がせた、バックアップのお話をします。

バックアップは、 bundle exec rake gitlab:backup:create で実行されます。内部的には、MySQLの場合 mysqldump 、PostgreSQLの場合 pg_dump 、gitは git bundle create でバックアップファイルを作っているだけです。

GitLabのバックアップはrakeタスクになっているので、その使い方は、ソースコードを読むのが一番わかりやすいです。backup.rakeの内容を覗いてみましょう。

backup.rakeの中には、こんな風に実行できるコマンドがタスクとして並んでいます。上から順に、読み解いてみましょう。 gitlab:backup:db:create (DBのバックアップ実行)について、dump周りを読んでみると…

実際に mysqldump のコマンドを呼び出しているのが確認できますね。そして、mysqldumpをした結果が、gzipされるようにパイプされていることも、ここで確認できます。

gitlab:backup:repo:create (リポジトリのバックアップ実行)については、git bundle create を叩いてるのが確認できます。それを指定のディレクトリに出力しているだけですね。

gitlab:backgup:uploads:create (アップロードファイルのバックアップ実行)については、アップロードファイルをディレクトリごと、tarで固めているだけですね。

こんな感じで、どのバックアップについても外部のコマンドを実行しているだけなんだ、というのがわかるかと思います。

こうして作られたバックアップファイルは、最終的には1つのtarファイルとしてまとめられます。 config/gitlab.ymlbackup: 以下、 upload: 以下に設定を書くと、s3などのさまざまなストレージやサーバーへ、バックアップファイルを送ることができます。rsyncなどで、出来上がったtarファイルを送るだけでも大丈夫です。

バックアップは、cronで定期的に実行すればいいのですが、環境変数で CRON=1 を設定すれば、標準出力を抑えることもできます。もともとcronは、出力の内容をメールで送ってくれるのですけれど、この設定をすれば、エラーがあった場合のみ、そのログがメールで送られてくるようになります。便利ですね。

mysqldumpについては、データベースのサイズが大きくなると、時間がかかるので別の手段でバックアップすることをオススメします。環境変数 SKIP=db を渡せば、データベースのバックアップをスキップできるので、この設定をした上で別の方法を使ってバックアップするといいでしょう。

MySQLのバックアップは、mysqldumpに限らず色んな方法がありまして。例えば、MySQLを停止させて、data_dirごとコピーするというは、ピクシブでもやっている運用方法です。slaveを準備しておけば、masterを停止することなくこの作業が行えます。

リストアについて。

さきほど、バックアップファイルについてはひとつのtarファイルに入っているとお伝えしましたが、このファイル名にはタイムスタンプと日付が入っています。環境変数 BACKUP にタイムスタンプと日付を渡し、 bundle exec rake gitlab:backup:restore を実行すれば、バックアップファイルからデータがリストアされます。

データベースのバックアップファイルを別にしている場合は、さきほどと同じく環境変数に SKIP=db を渡しておけば、そこだけ無視されます。

GitLabの実践的なバージョンアップ

バージョンアップ時は、バックアップが必須です。バックアップをとるように、ドキュメントに記述されています。ただ、DB以外は基本的に変更が加わることがないので、DB以外はバックアップの対象から外しても問題にならないと考えています。

ある程度一気にバージョンをあげることもできます。GitLabはよくあるRuby on Railsのコマンドによってバージョンアップできるのですが、一部のバージョンで例外的な手順がありますので、見落とさないようにしなくてはいけません。

バージョンアップの作業は、無停止で行うのはほぼ無理です。公式ドキュメント通りの手順でやろうと想うと、ある程度のダウンタイムを許容するしかありません。

まず、DBについて。 db:migration (DBに対する変更)が実行されます。slaveに db:migration して、master昇格させるという手もありますが、非常に手間がかかるため、停止させたほうが楽です。

また、 bundle installassets:precompile といったコマンドによる変更が、運用中のファイルに対して直接行われることになります。Capistranoのようなデプロイシステムが提供してくれる、シンボリックリンクによる変更の一括切り替えのようなことはしてくれないため、アプリケーション起動中に反映するのは難しいでしょう。

最後に

GitLabはドキュメントが充実していて、セットアップ・バージョンアップ・バックアップなどの手順のドキュメントも揃っています。加えて、ソースコードはとてもシンプルなので、動きを追うのは容易ですので、そちらを参考に運用していくこともできるでしょう。

かつてGitLabは、貧者のGHEなんて言われていた時代もありました。しかし、ソースコードが読めて、柔軟なインフラ構成をつくれるといった、他にはないメリットもあります。みなさんも一緒に、GitLabを盛り上げていきましょう!

ありがとうございました。

20191219010141
catatsuy
1991年3月生まれ。東京工業大学情報工学科卒業。2013年ピクシブ株式会社入社。 現在はpixiv運営本部所属エンジニア。pixivの技術的な改善や、自社の広告配信サーバーのメンテナンス・機能改善などを主に担当。