Parity before cutover

Alex O'Callaghan

カットオーバー前にパリティを揃える

私たちは、複数のチームやサービスが依存するサービスを運用しています。長年、このサービスは二重に存在していました。オンプレミスのサーバーで稼働する Python 2 の本番バージョンと、クラウド移行によってようやく置き換えられる日を待ちながら、別ブランチで眠っていたモダンな Python 3 バージョンです。

多くのレガシーアップグレードは、こうした構図になります。目指すゴールは明らかです。新しい言語バージョン、新しいインフラ、そして古いシステムの撤廃です。だからこそチームは一直線にゴールを目指します。作業は長期間残り続けるブランチや「v2」という名の書き直しで進められ、本番から離れた場所で難しい作業を片付け、最後に一度で切り替えるという計画になります。

着地しないリライト

その計画が約束どおりに実現することは、めったにありません。新しいバージョンがリリースされないまま置かれている間、リスクが消えることはなく、むしろ積み重なっていきます。

緊急の修正はすべて、まず古いバージョンに適用しなければならず、そのうえで新しい方にも移植する必要があります。そうでなければ忘れ去られてしまいます。二つのバージョン間の挙動の違いは、意図的なものもそうでないものも、すべてカットオーバー後に下流のチームによって発見されるのを待つバグになります。しかも、原因の切り分けが最も難しいタイミングで見つかるのです。

長命なブランチは、すべてを一つのイベントにまとめられるため効率的に感じられます。しかし実際には、リスクを一点に集中させ、何かがおかしいことを教えてくれるはずのフィードバックを遅らせているに過ぎません。

前に進むために一度後ろへ下がる

私たちがこの移行を引き継いだとき、最初に考えたのはゴールを一気に目指すことでした。モダンなバージョンをクラウドで動かし、利用側の移行を始めるという発想です。私はまず一歩後ろに下がることを提案しました。ほかの何かを動かす前に、モダンなバージョンを古いインフラ上の本番環境で動かし、新旧を同一のコードにするという方法です。

レガシーなホストの OS は古く、サポートされる Python は 3.6 が上限でした。一方、モダンなバージョンは 3.7 を想定していました。回避策がなかったわけではありません。Python をソースからビルドする、そもそもコンテナを想定していないホストにコンテナを載せるといった方法です。しかし、そうした回避策は、すでに課題の多い移行の複雑さをさらに増やすだけでした。

そこで私たちは、サービスをあえて 3.6 にダウングレードし、オンプレミス向けのデプロイ設定を復活させました。設定は実行時に環境ごとに切り替わるようにしています。こうして、レガシーホストと Kubernetes のどちらにもデプロイできる、単一のコードベースができあがりました。

バージョンのダウングレードは、一見すると後退にしか見えません。しかし、それがあったからこそ、その後の移行は驚くほどシンプルになりました。

一度に変えるのは一つだけ

パリティが確保されたことで、すべてのリリースが両方の環境にデプロイされるようになりました。新旧の挙動の違いは、カットオーバー時に一気に噴き出すのではなく、小さな差分としてすぐに表面化します。何年にもわたって積み重なった二つのバージョン間の乖離は、まだ古いインフラをフォールバックとして使えるうちに、少しずつ解消されていきました。

クラウドへカットオーバーする頃には、それは純粋なインフラの変更になっていました。「プラットフォームの移行はうまくいったか?」「リライトはうまくいったか?」という二つの問いを同時に検証する必要はありません。もし何かが壊れても、コードは同一なのですから、原因はプラットフォームにあるとすぐに分かります。カットオーバーは何事もなく淡々と終わりましたが、それこそが理想的な姿です。これはマイクロフロントエンドで React を段階的にアップグレードするのと同じ原則です。リリースごとに変える要素が一つになるように、順序立てるのです。

codeinfrastructureversionbig bang: everything at oncePython 2on-premPython 3.6on-premPython 3.6KubernetesPython 3.10Kubernetes

ゴールではなく、そこに至る道のりをリリースする

移行が完了すると、何年も停滞していた Python のアップグレードも、通常のリリースとしてあっさり出荷できました。古いホストによるバージョンの上限はなくなり、アップグレードすべきコードベースは一つだけになっていたからです。ゴールを直接目指すのをやめたからこそ、ゴールにはむしろ早くたどり着けました。

もしブランチが何か月も開きっぱなしになっていたり、書き直したコードが他チームの移行待ちで出荷できずにいたりするなら、それは一度にやりすぎているサインかもしれません。今すぐリリースできる中間状態がないか探してみてください。たとえ一見後退に見えたとしてもです。難しいのはゴールそのものではありません。難しいのはそこに至る道のりであり、最も安全な道のりとは、一歩ずつリリースできる道のりのことです。

原文は Alex O'Callaghan により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。