障害のきっかけとしていちばん身近なのが、本番へのリリースです。
Google の SRE Workbook の「Canarying Releases」の章も、同社ではインシデントの過半がバイナリや設定の反映から起きていると書いていました。
変更を止められない以上、工夫できるのは出し方のほうです。
ブルーグリーン、ローリングアップデート、カナリアのどれを選ぶか、迷う場面は多いのではないでしょうか?
三つの違いは、新旧のバージョンをどう並べ、どう切り替えるかに集約されます。
ブルーグリーンは2つの本番環境を切り替える
ブルーグリーンは、Martin Fowler が bliki で紹介した手法です。
できるだけ同じ構成の本番環境を 2 つ用意し、待機側のグリーンで新版の最終テストを済ませます。
問題がなければルーターを切り替え、すべてのリクエストをグリーンへ向けます(Martin Fowler の Blue Green Deployment)。
何かあればルーターをブルーへ戻すだけで切り戻せるのが利点です。
一方 SRE Workbook は、従来型の 2 倍のリソースを使う点を欠点に挙げていました。
ローリングとカナリアは新旧を混ぜて動かす
残りの二つは、同じ本番に新旧を同居させる方式です。
ローリングアップデートは少しずつ入れ替える
Kubernetes の Deployment の戦略は Recreate と RollingUpdate の 2 つで、既定は後者になっています。
RollingUpdate は古い ReplicaSet を縮めつつ新しい方を増やす方式で、進み方は次の 2 つで決まります。
- maxUnavailable … 更新中に減ってよい Pod 数(割合は切り捨て)
- maxSurge … 希望数を超えて増やせる Pod 数(割合は切り上げ)
既定値はどちらも 25% で、両方を 0 にはできません。
Pod 4 つなら次の書き方で、4 つを保ったまま最大 5 つに増やして入れ替えます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # 4 × 25% = 1(切り上げ)
maxUnavailable: 0 # 使える Pod を 4 未満にしない
# selector と template は省略
kubectl rollout undo で戻せますが、古い ReplicaSet は Pod 数 0 で残っているだけなので、戻しは Pod の起動からやり直しになります。
カナリアは一部だけ新版へ流す
カナリアの定義や評価の考え方は、次の記事にまとめました。
基盤から見ると、カナリアはトラフィックの配分を操れることが前提の仕組みです。
SRE Workbook は手段として、ロードバランサーの重み、プロキシ設定、ラウンドロビン DNS を挙げています。
Kubernetes のドキュメント(Managing Workloads)には、stable と canary の Deployment を共通ラベルの Service で束ね、レプリカ数 3:1で流す例がありました。
戻すときは利用者を旧版へ振り直すだけ、と Fowler のサイトの記事(Canary Release)にあります。
使い分けは3つの軸で決める
軸は、戻しの速さ、リソース量、新旧の同時稼働への耐性の 3 つです。
- ブルーグリーン … 戻しが速い/リソース 2 系統分/全量が一度に移る
- ローリングアップデート … 戻しは起動から/追加は maxSurge 分/更新中は混在
- カナリア … 戻しは配分を戻す/追加はカナリア分/広げる間は混在
速く戻したくてリソースに余裕があるならブルーグリーン、2 倍の環境が重いならローリングが候補でしょう。
本番の反応を少量で確かめたい変更には、カナリアの出番です。
ただ新旧を並べるほど、複数バージョンを同時に管理する手間が増えると Canary Release の記事も書いていました。
落とし穴はデータベースのスキーマ変更
アプリは切り替えられても、データベースは新旧で共有されがちです。
AWS のホワイトペーパーもデータストアの共有を基本とし、リレーショナル DB はスキーマがずれうるので配慮が要ると説明しています。
同資料は、列やテーブルを削ると旧版のアプリは動かなくなるとも指摘していました。
つまりスキーマを新版専用にした瞬間、ルーターを戻しても旧版は動かないということです。
新旧が同じ DB を同時に読み書きするローリングやカナリアでは、なおさら避けられません。
対策が Parallel Change(expand and contract)というパターンです。
- expand … 新旧の両方に対応できる形へ広げる(列を足すだけ、など)
- migrate … 使う側をすべて新しい形へ移す
- contract … 旧版向けの部分を消す
Fowler のブルーグリーンの記事も、先にスキーマを新旧両対応にして戻れる地点を作ってからアプリを出す順番を示していました。
最後に
違いは、新旧を並べる時間の長さと戻し方に表れます。
どれを選んでも、スキーマが新旧両対応でなければ戻すという選択肢が消える点は共通でした。
デプロイを自動で流す仕組みは、CD 入門の記事で扱っています。
私も DevOps を学ぶ途中なので、まずは maxSurge と maxUnavailable を手元で確かめてみたいと思います。
以上です。











コメントを残す