- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け今ここ
- Nginx受け口
- アプリ処理今ここ
- 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.
戻せることと、戻し先が正しく動くことは別です。戻し先を試していなければ、戻す操作は試していない状態への移動です。
- ・分けたことで、戻す操作がコードを置き直さずに済む
- ・戻し先の状態が正しいかは、分けたこととは別に確かめる
同じプロセスのまま、フラグだけで挙動が変わる
小さな実演を用意しました。Python の HTTP サーバが、リクエストのたびにフラグのファイルを読みます。フラグは2本で、new_tax は税の計算を、new_receipt は明細の書き方を切り替えます。
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)}"
新しい明細は、新しい税計算が返す内訳つきの形を前提にしています。旧い税計算は数値だけを返します。フラグのファイルを書き換えながら、同じサーバに順に問い合わせた結果です。
== 置いた直後(新しいコードは入っているが、フラグは両方 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]== 戻す: 両方 offpid=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. です。「別にするのが正解」とは書かれていません。
ブルーグリーンで新環境に切り替えた後、不具合が出て向き先を旧環境へ戻しました。この「戻す」が保証するものはどれですか。
アプリ内の機能フラグ
コードは置いたまま、フラグで経路を切り替えます。戻すのはフラグを off にする操作です。フラグが複数あると、途中の組み合わせも本番に現れる状態になります。
new_tax new_receipt 応答off off 200 合計 1080(置いた直後・戻し先)off on 500 TypeErroron off 200 合計 1100on on 200 小計 1000 / 税 100(予定の状態)
プロキシの向き先切り替え(ブルーグリーン)
新しい版は待機側の環境に置き、向き先を切り替えて効かせます。戻すのは向き先を元へ戻す操作です。戻るのは経路で、戻し先の環境が動くことや、共有する DB の中身までは戻りません。
フラグの off と、向き先を戻す操作。2つに共通する「戻す」の性質は何でしょうか。
戻した後の状態を、誰がいつ確かめたかに着目してください。
この章のまとめ
- 置く(デプロイ)と効かせる(リリース)は別の操作。実演では置いた直後も旧い挙動のままで、同じ pid のままフラグだけで変わった。
- 分けると、効かせる操作を単独で取り消せる。壊れる前に用意する戻す手の最初の形。
- 戻し先も1つの状態。記事は予定の設定に加えて fall-back も試すよう勧める。
- フラグ2本では、両端が正常でも途中の組み合わせで落ちることがある。試験の対象は本番で通る状態の列。
- フラグは保持費用のある在庫。使うなではなく、少なく保つ。寿命の目安
a week or twoには例外が付く。 - ブルーグリーンで戻るのは経路。旧環境が動くことと、共有 DB の中身は別に確かめる。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
Hodgson の記事は、フラグ付きの成果物でどの設定を試すことを勧めていますか。
用語集(3語)
- デプロイとリリース
- 本教材では、コードを本番に置く操作をデプロイ、その挙動を利用者に効かせる操作をリリースと呼び分ける。Hodgson は分離を継続的デリバリーの原則として引く。
- 機能フラグ(Feature Toggle)
- コードを変えずに、設定で経路を切り替える仕組み。Release Toggle はその一種で、未完成の機能や、公開の時期を待つ機能を隠したまま置くためのもの。
- ブルーグリーン
- 新旧2つの環境を並べ、プロキシの向き先を切り替えて出す方式。戻すのも向き先の切り替えになる。