Dev Study
← 出して、戻して、直す — 変更と復旧の実務
レッスン 1 / 12·約 9 分·第24期 出して、戻して、直す

置くことと効かせることは別の操作

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け今ここ
  5. Nginx受け口
  6. アプリ処理今ここ
  7. DB保存

このコースの芯は、壊れたときに打てる手は壊れる前に用意しておいた分しかない、です。初回は、戻す手をあらかじめ用意する最初の形として、置く操作と効かせる操作の分離を見ます。

まず結論

新しいコードを本番に置く操作(デプロイ)と、それを利用者に効かせる操作(リリース)は分けられます。機能フラグは同じプロセスのまま挙動を切り替え、ブルーグリーンはプロキシの向き先を切り替えます。

分けると「戻す」も1つの操作になります。ただし戻し先の状態も、試していなければ未検証です。フラグが2本あれば、予定の状態と戻し先の状態がどちらも正常でも、途中の組み合わせで落ちることがあります。

? 読む前に予測

新機能のフラグが2本あります。両方 on(予定の状態)と両方 off(戻し先)を試し、どちらも正常でした。本番では1本ずつ別の日に on にする予定です。言えることはどれでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

2つの操作を分けると、戻す手が1つ増える

「デプロイした」と言うとき、実は2つの操作が混ざっています。新しいコードを本番の機械に置く操作と、その挙動を利用者に効かせる操作です。

両者を分ける方法が機能フラグです。Hodgson の記事は、公開を隠すためのフラグ(Release Toggle)を次のように位置づけます。

Using Release Toggles in this way is the most common way to implement the Continuous Delivery principle of “separating [feature] release from [code] deployment.”

分ける利点は、効かせる操作を単独で取り消せることです。コードを置き直さずに、効かせた挙動だけを止められます。壊れたときに打てる手を、壊れる前に1つ用意する最初の形です。

ただし、取り消した先も1つの状態です。記事は、予定の設定を試すのが最も重要だとしたうえで、戻し先も試すよう勧めます。

It's also wise to test the fall-back configuration where those toggles you intend to release are also flipped Off.

戻せることと、戻し先が正しく動くことは別です。戻し先を試していなければ、戻す操作は試していない状態への移動です。

置く・効かせる・戻す
置く(デプロイ)新しいコードが本番にある。挙動は旧いまま
フラグを on / 向き先を切り替え
効かせる(リリース)利用者に新しい挙動が届く
フラグを off / 向き先を戻す
戻し先これも1つの状態。試していなければ未検証
  • ・分けたことで、戻す操作がコードを置き直さずに済む
  • ・戻し先の状態が正しいかは、分けたこととは別に確かめる
具体実物

同じプロセスのまま、フラグだけで挙動が変わる

小さな実演を用意しました。Python の HTTP サーバが、リクエストのたびにフラグのファイルを読みます。フラグは2本で、new_tax は税の計算を、new_receipt は明細の書き方を切り替えます。

実演アプリの該当部分(app.py より抜粋)設定ファイル
def tax(price, fl):
if fl["new_tax"]:
return {"rate": 0.10, "amount": int(price * 0.10)} # 新しい税計算は内訳つきの dict を返す
return int(price * 0.08) # 旧い税計算は数値だけ
def checkout(price, fl):
t = tax(price, fl)
if fl["new_receipt"]:
return f"小計 {price} / 税 {t['amount']} (税率 {t['rate']})" # 新しい明細は dict 前提
return f"合計 {price + (t['amount'] if isinstance(t, dict) else t)}"

新しい明細は、新しい税計算が返す内訳つきの形を前提にしています。旧い税計算は数値だけを返します。フラグのファイルを書き換えながら、同じサーバに順に問い合わせた結果です。

本教材の実演の出力(スクリプト非掲載。python:3.13-slim、Docker 28.4.0、macOS。2回実行して同一)コマンド出力
== 置いた直後(新しいコードは入っているが、フラグは両方 off)
pid=1 flags={'new_tax': False, 'new_receipt': False} -> 合計 1080
[200]
== new_tax=false new_receipt=false(再起動なし・同じプロセス)
pid=1 flags={'new_tax': False, 'new_receipt': False} -> 合計 1080
[200]
== new_tax=false new_receipt=true(再起動なし・同じプロセス)
pid=1 flags={'new_tax': False, 'new_receipt': True} -> TypeError: 'int' object is not subscriptable
[500]
== new_tax=true new_receipt=false(再起動なし・同じプロセス)
pid=1 flags={'new_tax': True, 'new_receipt': False} -> 合計 1100
[200]
== new_tax=true new_receipt=true(再起動なし・同じプロセス)
pid=1 flags={'new_tax': True, 'new_receipt': True} -> 小計 1000 / 税 100 (税率 0.1)
[200]
== 戻す: 両方 off
pid=1 flags={'new_tax': False, 'new_receipt': False} -> 合計 1080
[200]

置いた直後: 新しいコードはあるのに、応答は旧い税率 8% の「合計 1080」です。置いただけでは何も効いていません。その後も pid=1 のまま、再起動なしで応答が変わります。

4通りのうち、明細だけ新しく税計算は旧いままの1通りが 500 です。新しい明細が数値に ['amount'] を当てています。予定の状態(両方 on)も戻し先(両方 off)も 200 です。

✓ ここまでの確認

実演で、新しいコードを置いた直後の応答が「合計 1080」(旧い税計算の結果)だったことから言えるのはどれですか。

抽象原理

組み合わせは増え、在庫は残る

フラグが1本なら状態は on と off の2つです。記事は、複数になると組み合わせが爆発する(combinatoric explosion)と書いたうえで、すべてを試す必要はないとも書きます。多くのフラグは互いに干渉せず、1回のリリースで2本以上を動かすことは少ないからです。

実演は、その「少ない」側に入った例です。2本を別の日に on にする、片方だけ戻す、という運用をすれば、途中の組み合わせが本番に現れます。試験の対象は、本番で実際に通る状態の列です。

分けておくことにも費用があります。記事はフラグを在庫として扱います。

Savvy teams view the Feature Toggles in their codebase as inventory which comes with a carrying cost and seek to keep that inventory as low as possible.

使うなではなく、少なく保てです。Release Toggle の寿命については a week or two を目安にしつつ、製品側の理由で長く残るものがあると例外を添えています。

具体実物

プロキシの向き先を切り替える(ブルーグリーン)

分ける場所はアプリの中だけではありません。ブルーグリーンでは、新旧2つの環境を並べておき、前段のプロキシやロードバランサの向き先を切り替えて出します。

  • 置く: 待機側の環境に新しい版を入れる。利用者のリクエストはまだ来ない
  • 効かせる: 向き先を待機側へ切り替える
  • 戻す: 向き先を元の環境へ戻す

戻すのが切り替えだけで済む点は、フラグの off と同じ構造です。同じ問いも残ります。戻し先の旧環境は、いまも正しく動く状態で残っているか。

もう1つ、両環境が同じ DB を見ている場合、新環境が書いた行は向き先を戻しても残ります。このデータの側は第16期で扱います。

設定の配り方にも同じ分離が現れます。SRE Book 第8章は、設定をバイナリとは別のパッケージにすれば we retain the ability to change each package independently. と書きます。ただしこれは配り方の4通りの1つとして並べられたもので、結論は decide which works best on a case-by-case basis. です。「別にするのが正解」とは書かれていません。

✓ ここまでの確認

ブルーグリーンで新環境に切り替えた後、不具合が出て向き先を旧環境へ戻しました。この「戻す」が保証するものはどれですか。

⇄ 2つを見比べる
具体例 A

アプリ内の機能フラグ

コードは置いたまま、フラグで経路を切り替えます。戻すのはフラグを off にする操作です。フラグが複数あると、途中の組み合わせも本番に現れる状態になります。

実演の4通り(new_tax / new_receipt)表
new_tax new_receipt 応答
off off 200 合計 1080(置いた直後・戻し先)
off on 500 TypeError
on off 200 合計 1100
on on 200 小計 1000 / 税 100(予定の状態)
具体例 B

プロキシの向き先切り替え(ブルーグリーン)

新しい版は待機側の環境に置き、向き先を切り替えて効かせます。戻すのは向き先を元へ戻す操作です。戻るのは経路で、戻し先の環境が動くことや、共有する DB の中身までは戻りません。

フラグの off と、向き先を戻す操作。2つに共通する「戻す」の性質は何でしょうか。

戻した後の状態を、誰がいつ確かめたかに着目してください。

この章のまとめ

  • 置く(デプロイ)と効かせる(リリース)は別の操作。実演では置いた直後も旧い挙動のままで、同じ pid のままフラグだけで変わった。
  • 分けると、効かせる操作を単独で取り消せる。壊れる前に用意する戻す手の最初の形。
  • 戻し先も1つの状態。記事は予定の設定に加えて fall-back も試すよう勧める。
  • フラグ2本では、両端が正常でも途中の組み合わせで落ちることがある。試験の対象は本番で通る状態の列。
  • フラグは保持費用のある在庫。使うなではなく、少なく保つ。寿命の目安 a week or two には例外が付く。
  • ブルーグリーンで戻るのは経路。旧環境が動くことと、共有 DB の中身は別に確かめる。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

Hodgson の記事は、フラグ付きの成果物でどの設定を試すことを勧めていますか。

用語集(3語)
デプロイとリリース
本教材では、コードを本番に置く操作をデプロイ、その挙動を利用者に効かせる操作をリリースと呼び分ける。Hodgson は分離を継続的デリバリーの原則として引く。
機能フラグ(Feature Toggle)
コードを変えずに、設定で経路を切り替える仕組み。Release Toggle はその一種で、未完成の機能や、公開の時期を待つ機能を隠したまま置くためのもの。
ブルーグリーン
新旧2つの環境を並べ、プロキシの向き先を切り替えて出す方式。戻すのも向き先の切り替えになる。