開発環境にデプロイしたつもりが本番環境にデプロイされちゃった話 @minakawa-daiki
皆さんは予期せぬ事態が起きた時どうしますか?
特に、予期せずやらかしてしまった時は冷静さを欠いてしまい、あらぬ方向へ進んでしまう人は多いと思います。
大事なのは事が起きしまった後に如何に対処できる事後処理能力と振り返り、そもそも事件が起こってしまわないようにするための事前対策です。
今回は私が参加していたとあるプロジェクトにて、私の事前調査が不十分だったせいで起きてしまった話をご紹介いたします。今回の事故が少しでも皆さんの役に立てればと思います。
開発背景
私が関わっているとあるプロジェクトの一部はFirebaseにて運用されています。特にフロントエンドはFirebase Hostingで配信されており、デプロイはGitHub上で特定のリリースタグを切ると自動でリリースされます。
Firebase Hostingはとても簡単にデプロイできます。どのくらい簡単かと言うと、firebase deploy というコマンドを実行するだけです。
今回起きてしまった事件は、私がFirebase Hostingのデプロイ挙動を理解していなかったために起きてしまいました。
その顛末を紹介していきます。
何をしたつもりで、何をやらかしたか
開発環境にデプロイしたつもりでいました。結果、本番環境にデプロイされました。
実行したコマンドはfirebase deployです。
なぜ本番環境にデプロイされてしまったのかをじっくりと紐解いていきましょう。
惨劇はなぜおこってしまったのか
なぜ本番環境にデプロイされたのか紐解いていくには、Firebase Hostingにおけるデプロイの仕組みを理解する必要があります。
まずFirebaseのプロジェクトはWebの場合、.firebasercに「プロジェクト エイリアス」が保存されます。デフォルトでは下記のような形になっています。
{ "projects": { "default": "<project-id>" }}
そして、firebase use –addを実行する事で、他のプロジェクトをエイリアスとして割り当てる事ができます。
これによって複数のFirebaseプロジェクトを同じプロジェクト ディレクトリに関連付けることができます。
例えばstagingとして登録した場合は.firebasercは以下のようになります。
{ "projects": { "default": "<project-id>", "staging": "<project-id>" }}
これによって、デプロイ先や参照先を firebase use コマンドで簡単に切り替えられます。便利ですね。
皆さんはここで「あーなるほどね、手元のプロジェクト エイリアスが本番に向いてたってことね。」って思うかもしれません。
流石の私もそんなヘマはしません。というか、本番の設定を手元に残しておくのは怖すぎるのでできません。
原因を理解するにはもう少しFirebase Hostingの機能を深掘る必要があります。
Firebase Hostingはデプロイするとデフォルトでは<project-id>.web.appというドメインが自動的に付与され、アクセスする事ができます。仮にプロジェクトIDが推測可能だった場合、URLも推測されてしまうのがポイントです。
また、Firebase Hostingは「マルチサイト機能」というものが存在します。これは1つのFirebaseプロジェクトに複数のHostingを設定できるという代物です。
この機能によって、複数のHostingでドメインが分かれてたとしても同一の認証情報が簡単に利用できたりするので便利です。
そしてデプロイ先はfirebase.jsonという設定ファイルで管理する事が可能です。
{ "hosting": { "site": "<deploy先マルチサイトID>", "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] }}
これで必要な知識は全てで揃いました。
では、なぜ今回本番環境に反映されてしまったのでしょうか?そのカギはfirebase.jsonにあります。
私たちが開発していたプロジェクトは過去、クローズドアルファ版を経て公開されています。そして当時、アルファ版をクローズにしたかったため、ドメインを推測し辛いものにしなければいけませんでした。
その背景があるため、先ほど紹介した自動で利用可能な <project-id>.web.app をどうしても使いたくありませんでした。
なので「マルチサイト機能」を利用して推測ができないドメインに変更しました。
そして、firebase.jsonの設定でデプロイ先を新しく作成したドメインの方に向け、CI/CDに組み込んで自動リリース環境を整えていました。
そして悲劇が起こります。。。
事件が起きた時の .firebaserc と firebase.json は下記のような状態になっていました。
{ "projects": { "dev": "<project-id>", "staging": "<project-id>" }}
firebase.json{ "hosting": { "site": "<alphaの推測し辛いマルチサイトID>", "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] }}
そして私が手元で開発し、開発環境へのデプロイをするために実行したコマンドは以下です。
$ firebase use devNow using alias dev (<devのID>)
なんでや
そうです。私はfirebase useで設定した方にデプロイされると勝手に勘違いしており、firebase.jsonの方に設定したsiteを全く気にしておりませんでした。ですが、デプロイの優先度はsiteで指定した方が高いです。(だったら「Deploying to ‘devのID’」もalphaのIDって表示にしてくれよって思うが、そこは今回は無視で。。。)
結果、リリースが本番環境に直撃しました。気軽なノリで開発環境が本番環境にリリースされしまうという事態になってしまったのです。
問題の対処
開発環境が本番環境にデプロイされてしまうと、DBやAPIの向き先、未公開機能のUIなど様々なものがユーザーに知られてしまいます。
リリース当時、(当たり前だが)シェルをずっと眺めていたおかげで本番環境にリリースされてしまったことにはすぐ気付けました。
なので、二次災害を防ぐという目的でも手元から再デプロイはせず、いつも通りのGitHubのタグ経由から本番環境に再デプロイしました。
そしてFirebaseで使用していた開発環境用のキーは全て無効化し、新しいキーを再発行しました。(ここ重要)
幸い、当時の開発環境のデータで見られてはいけない情報はなかったため、被害は大きくなく済みました。
二度と惨劇を起こさないために
悲劇はなぜ起きてしまったのでしょうか?
開発環境のデプロイを手元から行っていたから?本番環境の情報をfirebase.jsonに記述してしまったため?
それらも関係あるとは思いますが、大きな理由としては、私の注意散漫だと思います。気軽にリリースできるのを良い事に、被害予測を予めできなかった点に落ち度があると思っています。
ちょっとでも考えれば優先度の問題は手元で検証できたはずです。そこを怠ってしまったために起きてしまった被害。プロダクションで利用する機能は、手元での検証を念入りに行いましょう。
そして、悲劇が起こってしまった後も慎重になる事が重要です。特に二次災害には気をつけなければいけません。人はミスをしてしまった際にまた次のミスを誘発してしまう可能性が高いです。慎重に、落ち着いて対処しましょう。
所感
まさか正直こんな形で本番環境にデプロイされるとは思いませんでした。
ぶっちゃけると、firebase useで指定したエイリアスとfirebase.jsonで指定したsiteが異なってた場合はデプロイ拒否して欲しいってムカついてた時期もありました。(というか今でもそう思ってるが)
皆さんもFirebase Hostingを使う時は注意して欲しいという思いもありますが、実際に事件に遭遇した際でも慎重に対処するよう心がけましょう。焦るとより多くのミスを生むので。
そしてミスをしてしまった際は、包み隠さず共有し、振り返りをして、今後同じ悲劇を繰り返さないよう共有することが大切です。あなたの失敗は必ず組織を強くします。
(余談)それはそうとFirebase Hostingの体験はすごくよい。デプロイ一瞬で終わるし、CDNはFastlyだし。是非みんなにも使って欲しい。
単語メモ
- デプロイ:作ったプログラムを使える状態にすることです。
- Firebase:Googleが提供しているモバイル・Webアプリケーション向けのプラットフォームサービスです。
- Firebase Hosting:静的コンテンツと動的コンテンツ、およびマイクロサービス向けのフルマネージド・ホスティングサービスです。
- エイリアス:ファイルの実態を別名で参照するシンボル及び機能のことです。エイリアスを削除しても本体に影響はなく、複数作成することが可能です。
- CDN:コンテンツデリバリーネットワークの略で、大容量のデジタルコンテンツをインターネット上で大量配信するためのネットワークです。
- Fastly:高速で安全なデジタル体験を実験できます。コンテンツ配信ネットワークが強力でエッジクラウドプラットフォームサービスです。