インフラストラクチャのロールバックがアプリケーションのロールバックと全く異なる理由

アプリケーションのアップデートに問題があり、ユーザーがエラーを目にし始めたとします。チームはロードバランサーを以前のバージョンに切り替えるか、パイプラインで古いアーティファクトを再デプロイします。数分以内に、アプリケーションは古いコードで動作し始めます。データはそのまま、データベースは変更されず、サーバーも以前と同じマシンです。問題は解決しました。

これがアプリケーションのロールバックです。アプリケーションはほとんどがステートレスであるため、機能します。コードを差し替えれば、以前の動作が戻ってきます。永続的な副作用はありません。

では、ファイアウォールルールを変更したり、データベースのディスクをリサイズしたり、サブネットの設定を更新した後に問題が発生した場合を考えてみてください。元に戻したいと思ったとき、単に古い設定を再適用すればすべてが正常に動作するでしょうか?

おそらく、そうはいきません。

インフラストラクチャのロールバックは、まったく異なる種類の問題です。関わるリソースはデータを保持し、ネットワークパスを管理し、他のシステムが依存する基盤サービスを提供します。インフラを変更するということは、単にコードを差し替えるのではなく、何年ものデータや数十のサービスとの接続、あるいはその上のすべての基盤となる役割を持つものの状態を変えることなのです。

状態が最初の問題

アプリケーションは使い捨て可能になるように設計されています。コンテナを停止し、古いコードで新しいものを起動すれば、アプリケーションは新しく始まります。以前のバージョンの記憶も、失敗したデプロイの残存データもありません。

インフラストラクチャはその逆です。データベースは設定を変更してもデータを保持し続けます。ディスクボリュームはリサイズしてもファイルを保持します。これらのリソースはステートフルです。設定変更のライフサイクルを超えて持続する状態を保持しています。

アプリケーションをロールバックするときは、コードを復元するだけです。インフラをロールバックするときは、リソース内に蓄積されたデータを破壊せずに設定を復元しなければなりません。それが常に可能とは限りません。

具体的な例を挙げます。トラフィック増加により、データベースのインスタンスタイプをスモールからラージにアップグレードしたとします。新しいインスタンスタイプに問題があり、スモールに戻したいと考えます。しかし、ラージインスタンスが動作していた間に、データベースにさらに多くのデータが書き込まれました。古いスモールインスタンスにはそのデータが収まりきりません。ロールバックは安全ではありません。データを失わずにインスタンスを縮小することはできず、元に戻した場合にデータを保持することもできません。

この違いは、2つのパスを並べて比較すると明確になります。

flowchart TD A[問題のある変更がデプロイされる] --> B{アプリケーションか?} B -->|はい| C[LBまたはパイプライン経由でコードを差し替え] C --> D[古いコードが動作、副作用なし] D --> E[ロールバック成功] B -->|いいえ、インフラだ| F[ステートフルなリソースを特定] F --> G{状態を保持できるか?} G -->|いいえ| H[ロールバックでデータが失われる可能性] G -->|はい| I[依存関係を確認] I --> J[順序付けられたロールバック手順] J --> K[部分的または壊れた状態] K --> L[回復が必要、単なるロールバックではない]

これはツールの問題ではありません。ステートフルなリソースの根本的な制約です。新しい設定を実行するという行為自体が、古い設定では対応できない方法でリソースを変更してしまうのです。

依存関係がリスクを増大させる

インフラストラクチャのリソースが単独で存在することはほとんどありません。1つの変更が、VPC、サブネット、セキュリティグループ、ロードバランサー、複数のインスタンス、データベースといった、相互に接続された10ものリソースに影響を与える可能性があります。各リソースは特定の方法で互いに依存しています。

1つのリソースをロールバックすると、それに依存するリソースが影響を受けます。古いセキュリティグループを復元すると、ロードバランサーとインスタンス間の通信が切断される可能性があります。サブネットを復元すると、データベース接続が切れる可能性があります。ロールバックは単一の操作ではありません。それは注意深く順序付けられなければならない一連の処理であり、その順序はリソースが元々どのように作成され、その後どのように変更されたかに依存します。

実際には、これは古いステートファイルに対して terraform apply を実行して終わり、というわけにはいかないことを意味します。古いステートは、ロールバックされなかった他のリソースの現在のステートと競合する可能性があります。結果として、インフラが壊れた状態のまま部分的な復旧に終わることがよくあります。

冪等な適用は安全なロールバックを意味しない

インフラストラクチャのパイプラインは冪等になるように設計されています。同じ設定を何度実行しても、同じ結果が得られます。これは変更を適用する際にはうまく機能します。しかし、冪等な適用が安全なロールバックを意味するわけではありません。

ディスクサイズを考えてみましょう。100 GB のディスクを宣言し、適用するとディスクが作成されます。同じ設定を再度実行しても、何も変わりません。これが冪等です。次に、設定を 200 GB に変更して適用します。ディスクは拡張されます。その後、設定を 100 GB に戻して再度適用します。何が起こるでしょうか?

ほとんどのインフラツールは、操作を拒否するか、ディスクを破棄して新しいものを作成します。データ損失のリスクなしにディスクを縮小することはできません。設定は理論上冪等ですが、実際のリソースは元に戻せない方法で変更されています。

これはステートドリフトと呼ばれます。コード内の設定はあることを示していますが、クラウドやサーバー上の実際のリソースは異なっています。ロールバックしようとするとき、単にコードを元に戻すのではありません。もはや現実と一致しない設定を調整しようとしているのです。そして、現実が勝つことがよくあります。

これがチームにとって意味すること

インフラストラクチャのロールバックには、アプリケーションのロールバックとは異なる種類の準備が必要です。同じパイプラインや同じ考え方に頼ることはできません。どのリソースが安全にロールバックでき、どのリソースができず、どのような順序で行うべきかを把握する必要があります。

一部の変更は元に戻せます。ロードバランサーのヘルスチェック設定の変更は、通常、安全にロールバックできます。データベースのパラメータグループの変更は、新しいパラメータが保存データを変更していなければ安全かもしれません。しかし、インスタンスサイズ、ディスクサイズ、ネットワークトポロジー、ストレージエンジンの変更は、データ損失やダウンタイムなしには元に戻せないことがよくあります。

最も安全なアプローチは、単なるロールバックではなく、回復を計画することです。回復とは、古い設定がもはや有効ではない可能性があることを受け入れ、後退する代わりに前進する道を構築することを意味します。これには、古い設定で新しいリソースを作成してデータを移行することや、修正が開発される間、機能低下した状態を受け入れることが含まれるかもしれません。

インフラストラクチャ変更のための実践的チェックリスト

インフラストラクチャの変更を適用する前に、以下の質問を自問してください。

  • このリソースは状態を保持しますか? もしそうなら、設定を元に戻した場合に状態を保持できますか?
  • このリソースに依存する他のリソースは何ですか? ロールバックによってそれらの接続が切断されますか?
  • その変更は元に戻せますか? ツールはデータを破壊せずにディスクを縮小したり、インスタンスをダウングレードしたり、ネットワークパスを復元したりできますか?
  • 変更が失敗した場合の実際の回復計画は何ですか? それはロールバック、移行、それとも再構築ですか?
  • 非本番環境で回復パスをテストしましたか?

まとめ

アプリケーションのロールバックはコードの差し替えです。インフラストラクチャのロールバックは状態の調整問題です。これらを同じように扱うと、システムの破損、データ損失、長い復旧時間につながります。単なるロールバックではなく、回復を計画してください。どの変更が元に戻せるかを把握し、必要になる前に回復パスをテストしてください。