GitLab.com Database Incident - Feb 1, 2017  - GitLab

2017/01/31に発生したGitLabのインシデントについて、2017/02/01に[GitLab.com Database Incident](https://about.gitlab.com/2017/02/01/gitlab-dot-com-database-incident/)というブログ記事が投稿されました。
こちらの記事の英検3級的ゆるふわ和訳です。プルリク待ち。

公開から色々追記しているようで、時折時系列がおかしいところがあります。
時刻は全てUTCです。

# GitLab.com Database Incident

昨日、データベースに深刻な事故が発生した。
GitLab.comのデータ(issue、マージリクエスト、ユーザ、コメント、スニペット他)が6時間分失われたよ。
GitとWikiのリポジトリ、自己ホスティングサービスのインスタンスは影響を受けないよ。
データを失うことは容認できることではないので、近いうちに何故このような事態が発生したのか、再発させないための対策リストを発表するよ。

### Update 6:14pm UTC: GitLab.com is back online

この記事を書いてる時点では、6時間前のバックアップからデータベースを復元しようとしてるよ。
すなわち、午後5:20から11:25までのデータ(project、issue、マージリクエスト、ユーザ、コメント、スニペット他)は、GitLab.comが復旧した際には失われるよ。

### Git data (repositories and wikis) and self-hosted instances of GitLab are not affected.

要約についてはここから下に記載するよ。
[死刑宣告書](https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCxIABGiryG7_z_6jHdVik/pub)も読めるよ。

## First Incident

2017/01/31午後6時、あるスパマーがスニペットを使ってデータベースに大量アクセスし、そのせいでデータベースが不安定になってることを発見したよ。
我々はトラブルシューティングを行い、問題の内容と対策方法を調査したよ。
<img alt="01.png" src="https://qiita-image-store.s3.amazonaws.com/0/26088/d6c2d3c3-6d6b-38e5-b1ba-225bac95ad28.png">

2017/01/31午後9時、スパムのアクセスはさらにエスカレートし、データベース書き込みにロックアップが発生し、それがダウンタイムを引き起こしたよ。
<img alt="02.png" src="https://qiita-image-store.s3.amazonaws.com/0/26088/67bf0dfc-b9af-7579-cb12-44a04e0d1dc9.png">


### Actions taken

・スパマーのIPアドレスをブロックした。
・47000のIPアドレスを使って同一IDでアクセスしてきていたCDNを排除した。
・スニペットを作成して、スパムユーザを削除した。

## Second Incident

2017/01/31午後10時、DBのレプリケーションが遅すぎたせいで止まったよ。
大量のデータが登録されたから、セカンダリデータベースでリアルタイム処理しきれなかったせいだよ。
<img alt="03.png" src="https://qiita-image-store.s3.amazonaws.com/0/26088/40c73be4-2df6-693a-557c-6c48d09f73cc.png">
<img alt="04.png" src="https://qiita-image-store.s3.amazonaws.com/0/26088/04f3c29d-dfa2-4b57-17b4-b4bafbcbc2e3.png">

### Actions taken

・`db2`を修正しようと思ったけど、この時点ですでに4GBの遅れが出ていた。
・`db2.cluster`でレプリケーションがエラーになったから、再度クリーンなレプリケーションをしようと`/var/opt/gitlab/postgresql/data`を削除した。
・`db2.cluster`は`max_wal_senders`が少ないと`db1`に接続拒否された。`max_wal_senders`はスレーブサーバからの同時接続数上限を表す。
・チームメンバー1は`db1`で`max_wal_senders`を32に設定してPostgreSQLを再起動した。
・PostgreSQLは、セマフォの使用数が多すぎるとして再起動を拒否した。
・チームメンバー1は`max_connections`を2000から8000にしてPostgreSQLを再起動した。
・`db2.cluster`は接続エラーこそ出さなくなったが、レプリケーションは相変わらず動かない。何も言わずにハングアップする。
・不満が募り始める。チームメンバー1は、23時に帰る予定だったが、この問題が発生したせいでそれどころではなくなったと発言した。

## Third Incident

017/01/31午後11時、チームメンバー1は、`pg_basebackup`が失敗するのは、db2にデータディレクトリが存在するせいではないかと思いついたよ(すでに空だったけど)。
そこでデータディレクトリを削除しようと決意したよ。
彼はそのコマンドを実行した数秒後に、`db2.cluster.gitlab.com`ではなく`db1.cluster.gitlab.com`でそれを実行したことに気がついたよ。

017/01/31午後11:27分、チームメンバー1は削除コマンドを中断した。
だが、それはあまりにも遅かった。
300GBのデータのうち、わずか4.5GBしか残っていなかった。

我々はGitLab.comを停止しなければならず、Twitterでこの情報を共有した。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">We are performing emergency database maintenance, <a href="https://t.co/r11UmmDLDE">https://t.co/r11UmmDLDE</a> will be taken offline</p>&mdash; GitLab.com Status (@gitlabstatus) <a href="https://twitter.com/gitlabstatus/status/826572933304827904">2017年1月31日</a></blockquote>

## Problems Encountered

・LVMのスナップショットは、デフォルトでは24時間に一回実行される。
チームメンバー1は、データベースのロードバランシング作業を行っていたため、たまたま6時間前に手動でバックアップしてた。
・デフォルトのバックアップは24時間に一回動いてるように見えたが、チームメンバー1は、バックアップが何処にあるかわからなかった。
チームメンバー2が確認したところ、数バイトのファイルしか生成されておらず、まともに動いてるようには見えなかった。
・チームメンバー3の調査結果。
`pg_dump`がPostgreSQL9.6ではなく9.2で動作してるせいでバックアップに失敗しているようだ。
サーバ側のPostgreSQLは9.6で動作しているが、pg_dumpはそれを使わず自前の9.2でバックアップを実行しようとしている。
その結果バージョン違いにより`pg_dump`は何もエラーを出さずに失敗し、SQLダンプは生成されていなかった。
また[Fog gem](https://github.com/fog/fog)が古いバックアップを削除していた。
・[Azureのスナップショット](https://docs.microsoft.com/ja-jp/azure/backup/backup-azure-vms-introduction)は、NFSサーバはバックアップしてたけどDBサーバは対象外だった。
・同期プロセスは、データをステージングに同期させた後に削除する仕様。24時間以内に取り出さないと、過去のデータは失われる。
・レプリケーションの手順は問題が多く、エラーが発生しやすく、出鱈目なシェルスクリプトに依存していて、まともに文書化されていなかった。
・S3へのバックアップはうまくいってなかった。中身は空っぽだった。
・つまり、存在している5系統のバックアップ/レプリケーションは全て、現在、もしくは最初に設定して以来動作していなかった。
最終的に6時間前のバックアップを復元することにした。
・`pg_basebackup`は、マスタDBがレプリケーションを開始するまでは何も反応せずに待機するが、これには最大10分ほどかかる可能性がある。
このことを知らなければ、コマンドがフリーズしていると考えてもおかしくないだろう。
[strace](https://ja.wikipedia.org/wiki/Strace)を使っても、特に有用な情報は得られなかった。

## Recovery

現在、ステージングのバックアップデータを使って、復旧作業を進めているよ。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">We accidentally deleted production data and might have to restore from backup. Google Doc with live notes <a href="https://t.co/EVRbHzYlk8">https://t.co/EVRbHzYlk8</a></p>&mdash; GitLab.com Status (@gitlabstatus) <a href="https://twitter.com/gitlabstatus/status/826591961444384768">2017年2月1日</a></blockquote>

・2017/02/01 00:36　`db1.staging.gitlab.com`をバックアップした。
・2017/02/01 00:55　`db1.staging.gitlab.com`を`db1.cluster.gitlab.com`にマウントした。
・ステージングの`/var/opt/gitlab/postgresql/data/`から本番の`/var/opt/gitlab/postgresql/data/`にコピーした。
・2017/02/01 01:05　`nfs-share01`を`/var/opt/gitlab/db-meltdown`の一時記録場所に設定した。
・残った本番データ、`pg_xlog`を含む`20170131-db-meltodwn-backup.tar.gz`をコピーした。

以下はデータの削除と、その後のコピーの様子。
<img alt="05.png" src="https://qiita-image-store.s3.amazonaws.com/0/26088/6e761fe0-6606-6b51-67d4-6d97f04b875a.png">

Twitterや他の場所で[#hugops](https://twitter.com/i/moments/826818668948549632)で受けた素晴らしい助言について感謝します。


# メモ

自動バックアップは信用できないとかいって定期的に手動バックアップしてる顧客がいるのだが、あの運用が正しかったということが証明されたな(棒)

syncはバックアップ自体は取れている(その後消える)ように読めたのだが、何か読み違いがあるだろうか。

再発させないための対策を書くと書いてあるが、まだ無いようだった。

`webhooks`とか`omnibus`とか、どういう意味なのかよくわからなかった。

`synchronisation process`は`sync sync sync`のことだろうか？

# 参考

http://www.publickey1.jp/blog/17/gitlabcom56.html
全体的な概要。
http://tmegos.hatenablog.jp/entry/gitlab_backup_fail
pg_dumpが動かない実体験。