Configuration Design and Best Practices
- システム設定は一般的なSREのタスク
- システム設定は、面倒で苛立たしいタスクになることがある
- 設定しているシステムに詳しくない場合
- 設定が使いやすさを念頭に置いて設計されていない場合
- 設定は一般的に2つのシナリオがある
- 初期セットアップ中で十分に時間がある場合
- インシデントに対処するための緊急変更をする場合
- この章では、インフラを設計及び保守する人の観点から設定を考える
- 安全で持続可能な設定を設計するには
What Is Configuration?
-
ソフトウェアシステムは常に変化する
- ビジネスニーズ、インフラ要件、etc ...
-
システムの動作をすばやく変更する必要がある
-
変更に費用と時間のかかるプロセスが必要であれば、コード修正という手段だけでは不十分
-
設定(※)はシステムを変更するための低オーバーヘッドな手段を提供する
- (※)システムの動作を変更するためのヒューマンコンピュータインターフェースとして大まかに定義できるもの
-
SREはこれを定期的に利用する
-
システムには3つの重要な要素があると考えることができる
- ソフトウェア
- システムが動作するデータセット
- システム設定
-
3つを明確に区別することは難しい
- システム設定のためにプログラミング言語を使用したり
- システム設定からプログラミング言語を参照したり
-
例
- mod_lua
- XMonadやHaskell-based configuration
- データセット中のSQLストアド・プロシージャ
-
これらは複雑なアプリケーションになる可能性がある
-
優れた設定インターフェースは、迅速に自信の持てる、テスト可能な設定変更を可能にする
-
ユーザが設定を更新する直接的な方法を持っていない場合、間違いが起こりやすい
- 認知負荷と大きな学習曲線が増える
Configuration and Reliability
- 私達のシステムは、最終的には人間によって管理されているので、設定も人間が担当する
- システム設定の品質は、それを確実に実行するための組織の能力に影響を与える
- 設定のインターフェース設計の影響は、保守性に対するコード品質の影響と似ている
- ただし、設定はいくつかの点でコードとは有意に異なる傾向がある
- コードによるシステムの変更:通常、少しずつ。テストを含む長く複雑なプロセス
- 設定によるシステムの変更:機能が大幅に変更される場合があり、テストされていない(できない)環境で行われることがある
- インシデント中は、簡単かつ安全に調整できる設定システムが必要
- 初期の航空機では、混乱したコントロールとインジケーターが事故につながった
- パイロットのスキルや経験にかかわらず、操作ミスが頻繁に発生していた
- Kim Vicente, The Human Factor (New York: Routledge, 2006), 71–6.
- ユーザビリティと信頼性
- コントロールスティクとインジゲータを、.confファイルとモニタグラフに変えるとどうなるだろう
Separating Philosophy and Mechanics
-
- 我々は、新しいソフトウェアを設計するとき、または既存のコンポーネントから新しいシステムを組み立てるとき、設定についても話し合う
- どのように設定するか、どのように読み込まれるか
- 設定の包括的なトピックを2つに分ける
- フィロソフィー
- メカニズム
- フィロソフィー
- 言語や他の仕組みから完全に独立した設定の側面
- どのように設定を構成するか
- どのようにして正しい抽象化レベルを作るか
- 多様なユースケースをシームレスにサポートするか
- メカニズム
- 言語設計・デプロイ戦略・他システムとの相互作用など
- この章ではあまり焦点を当てない
- 言語の選択などのトピックが業界ですでに議論されているため
- フィロソフィーとメカニズムを別々に議論することで、設定についてより明確に考えることができる
- 設定項目が膨大でも、インターフェースが優れていれば問題にならない
Configuration Philosophy
- このセクションでは、実装から完全に独立した設定の側面について説明する
- フィロソフィーに基づいた理想的なシステムは設定がないこと
- 勝手に正しく再設定されていくシステム
- もちろん現実には難しいが、これが設定の望ましい方向を指し示している
- 多数の(変更可能な)パラメータから離れて、より単純になるようにする
- 歴史的にミッションクリティカルなシステムは大量の制御(設定に相当)を提供
- オペレータにかなりの訓練を必要とした
- 図14-1はNASAの宇宙船管制センターのもの
- 現代のコンピュータシステムでは、多くの場合でそんな訓練は必要とされない
- この理想はシステムに対して行える制御の量を減らすが、エラーのための表面積とオペレータへの認知負荷の両方を減らす
- システムの複雑さが増すについてれ、オペレータの認知負荷がますます重要になる
- Googleではこれらの原則を社内で実践した
- 多くの場合、簡単で幅広い採用と内部のユーザサポートコスト減が実現した
Configuration Asks Users Questions
- 設定内容や方法に関係なく、人間とコンピュータのやり取りは最終的に次の形になる
- ユーザに質問をし、システムの動作方法に関する入力を要求する
- この概念モデルはユーザがXMLを編集しているのか、GUIを操作しているのかに関係なく当てはまる
- 現代のソフトウェアシステムでは2つの異なる方法からこのモデルにアプローチできる
- インフラ中心の考え方
- できるだけ多くの設定項目を用意する
- 項目が多いほどシステムはより完璧に調整できるため、より優れている
- ユーザ中心の考え方
- 設定のため、インフラの質問に対してユーザに回答してもらう
- 質問に回答するのは面倒なので、項目は少ないほど優れている
- インフラ中心の考え方
- ユーザ入力を最小にするという最初の哲学によって、私達は後者を支持する
- このソフトウェアデザインの影響は設定を超えて広がる
- ユーザによる設定へ焦点を当てることは、ソフトウェアが主要な顧客のための特定のユースケースのセットで設計される必要があるということを意味する
- これにはユーザ調査が必要
- これとは対照的に、インフラ中心のアプローチはソフトウェアが効率的に基本インフラストラクチャを提供することを意味する
- 実用的なシステムに変えるには、ユーザによるかなりの設定が必要
- これらのモデルは厳密には対立していないが、それらを一致させようとするのは非常に困難
- おそらく直感に反して、限られた設定オプションは、非常に用途の広いソフトウェアよりも優れた採用につながる可能性がる
- ソフトウェアはほとんど「箱から出して」動作するため
- オンボーディングの努力は大幅に少なくなる
- インフラ中心のアプローチから始まるシステムは、いくつかの設定項目を削除することで、ユーザ中心のアプローチに向かって移動することができる
- いくつかについては以降のセクションで説明
Questions Should Be Close to User Goals
- ユーザ中心の設定という哲学に従うため、ユーザが質問に容易に回答できるようにする
- より少ない設定項目:自分の言葉で自分のニーズを記述
- より多くの設定項目:ユーザはどのようにシステムが構成されるかを正確に記述
- お茶を作ることを考える
- より少ない設定オプション:ユーザは「熱いお茶」で大体欲しいものが手に入る
- より多くの設定オプション:ユーザは水量、沸騰温度、お茶のブランドと風味、浸漬時間、ティーカップの種類、そしてカップの中のお茶の量を指定してより完璧なものが手に入る
- 詳細を指定するのに必要な努力は、飲み物の限界利益より、多くの費用になるかも
- この例えは、設定システムに取り組んでいるユーザと開発者の両方にとって有益
- ユーザが正確なステップを指定するとき、システムはそれらに従う必要がある
- 代わりにユーザが自分の高水準の目標を説明すると、システムは時間の経過とともに進化し、これらの目標の実装方法を改善できる
- システムのユーザ目標を十分に理解しておくことが、ここで必要な最初のステップ
- ジョブスケジューリングを考える
- 状況:1回限りの分析プロセスを実行する必要がある
- KubernetesやMesosのようなシステムを使用すると、分析プロセスをどの物理マシンで実行するかを決定するなどの詳細な作業に煩わされることなく、分析を実行するという実際の目標を達成できる
Mandatory and Optional Questions
-
構成設定は2タイプの質問を含むかもしれない
- 必須と任意
-
ユーザ中心で採用しやすいようにするためには、必須の質問を西行源に抑える必要がある
-
簡単ではないが重要
- 1つか2つの小さなステップを追加することはほとんどコストをかけないと思うかもしれない
- しかし、エンジニアの人生はしばしば個々に小さなステップの無限の連鎖
- これらの小さなステップの削減は、生産性を劇的に向上させることができる
-
最初の一連の必須質問には、システム設計時に考えた質問が含まれていることがよくある
- 必須の質問を減らす最も簡単な方法は、質問を任意の質問に変換すること
- つまり、ほとんどのユーザに安全かつ効果的に適用されるデフォルトの回答を提供する
- たとえば、実行をdry-runするかどうかをユーザーに定義させるのではなく、dry-runをデフォルトの動作にする
-
デフォルト値は、多くの場合、静的なハードコードされた値だが必ずしもそうである必要はない
- システムの他の特性に基づいて動的に決定できる
- 動的決定を利用すると、構成をさらに単純化できる
-
動的デフォルトの例を検討してみる
- computationally intensive system(訳注:計算量が支配的なシステム)は、通常、構成制御を介してデプロイするスレッド数を決定する
- その動的デフォルトは、システム(またはコンテナ)の実行コアと同じ数のスレッドをデプロイする
- この場合、単一の静的デフォルトは役に立たない
- 動的デフォルトとは、システムがデプロイ毎に正しいスレッド数を決定するようにユーザーに依頼する必要がないことを意味する
- 同様に、コンテナ内に単独でデプロイされたJavaバイナリは、コンテナ内で使用可能なメモリーに応じて自動的にそのヒープ制限を調整する可能性がある
-
リソースの使用量を制限する必要がある場合は、設定内の動的デフォルトを上書きできるようにすると便利
-
実装された動的デフォルトは、すべての人にとってうまくいくとは限らない
-
時間が経つにつれて、ユーザはさまざまなアプローチを好み、動的デフォルトをより細かく制御したくなることがある
-
ユーザの大部分が動的デフォルトの問題を報告している場合は、決定ロジックが現在のユーザ要件と一致しなくなっている可能性がある
-
追加設定無しで動的デフォルトを実行できるようにするため、広範な実装することを検討する
-
ごく一部のユーザーしか不幸ではない場合は、手動で設定オプションを設定したほうがよい
- より複雑なものを実装することは、ユーザにとってより多くの仕事を生み出す(例えば、文書を読むための認知的負荷の増大)。
-
任意項目についてデフォルト値を用意する場合、静的・動的にかかわらず、選択の影響について慎重に検討する必要がある
- 経験上、ほとんどのユーザがデフォルト値を使用することになるので、これはチャンスでもあり責任でもある
- 間違ったデフォルトを指定することは多くの害を及ぼす
- 例えば、デフォルト値の影響がコンピュータサイエンスの範囲外にある場合
- 臓器提供者のデフォルトがオプトインである国々は、臓器提供者のデフォルトがオプトアウトである国よりも、劇的に臓器提供者の割合が高い
- 特定のデフォルトを選択するだけで、システム全体の医療オプションに大きな影響がある
-
いくつかのオプションの質問は明確な使用例なしで始まる
-
これらの質問を完全に削除したいと思うかもしれない
-
多数のオプションの質問がユーザを混乱させるかもしれないので、本当に必要性を感じたときだけ項目を足すべき
-
最後に、設定言語が継承の概念を使用している場合は、リーフ設定内の任意の質問に対してデフォルト値に戻すことができると便利
Escaping Simplicity
- これまではシステム設定を最も単純な形に設計することについて説明した
- ただし、設定システムではパワーユーザも考慮する必要がある
- お茶の例に戻ると、本当に特定の期間、お茶を急がなければならない場合はどうなるだろうか
- パワーユーザに対応するための1つの戦略
- => 一般ユーザとパワーユーザが必要とする設定の最小公約数を見つけ、デフォルトとしてそのレベルの複雑さを決定すること
- 欠点は、この決定がすべての人に影響を与えるということ
- 最も単純なユースケースでも、低レベルの用語で検討する必要がある
- デフォルトの動作をオプションでオーバーライドするという観点から設定を検討する
- ユーザは「緑茶」を設定してから「5分間浸す」を追加する
- このモデルにおいて、デフォルト設定は依然として高レベルでユーザの近くにありつつも、低レベルの側面を調整できる
- このアプローチは目新しいものではない
- C++やJavaなどの高水準プログラミング言語と類似する
- これらの言語では、コードに機械語(またはVM)命令を含めることができる
- 組織全体の設定に費やした時間の合計を最適化することを検討すると便利になる
- 設定にかけた時間だけでなく、
- 多くの選択肢が提示されたときにユーザが経験する可能性がある思考停止・間違った方針転換をした後の設定の修正にかかる時間・信頼性の低下による変更速度の低下なども考慮
- 設定そのものは非常に難解であっても、最も一般的なユースケースのサポートを簡単にできれば、それが良い場合もある
- 設定にかけた時間だけでなく、
- 一部のユーザが複雑な設定を必要としていることがわかった場合は、一般的な使用例を誤って特定している可能性がある
- システムの初期の製品仮定を再検討し、追加のユーザー調査を実施しよう
Mechanics of Configuration
- このセクションでは、設定の仕組みに焦点を当てます
Separate Configuration and Resulting Data
- 避けられない問題:どんな言語で設定を記述するか
- INI、YAML、XML
- もしくはより高水準な言語
- Q. 設定はコードかデータか
- 経験的に、どちらでもあるが分離するのが最適
- システムインフラストラクチャは、プレーンな静的データを操作すべき
- Protocol Buffers、YAML、またはJSONのような形式
- ただし、これはユーザにそれらを直接操作してもらうことを求めるわけではない
- 設定を生成するより高級なインターフェースを使ってよい
- インターフェースは何でも良い
- PythonベースのDSL、Lua、Jsonnet(15章参照)
- または言語要素のないWebUI
- データ表現とは意図的に分離された設定UIから始めると、システムは柔軟に展開することができる
- さまざまな組織が、異なる文化的規範や要件(社内の特定の言語を使用する場合やエンドユーザに設定を外部化する必要がある場合など)を持つ
- 特別な要件を持つシステムでも複数のアプローチを持つことができる
- 図14-2
- 図にある分離はユーザに見えない
- ユーザは予め定められたアプローチで設定ファイルを変更するだけ
- 設定ファイルのコンパイルは裏側で自動的に行われる
- ユーザは予め定められたアプローチで設定ファイルを変更するだけ
-
データとして保存しておくと分析も容易
-
設定を反映するに際して、設定がどう取り込まれたかを記録したメタデータを保存すると便利
- どこ経由の変更か
- 誰が変更したか
-
設定言語が、データであっても変わらない
- インフラが記録するものとインターフェースの両方でJSONが使われることもある
- ただし、内部的な形式とインターフェースの形式を密結合させない
- 不要な情報(実装固有のデータなど)を提供することになってしまう
Importance of Tooling
- 設定においてツールは非常に大事
- ツールがあるかどうかで、悪夢のような混沌をもたらすか、持続可能なシステムになるか、それが変わる
Semantic validation
- ほとんどの言語は構文チェックを提供するが、セマンティック(意味的な)検証を無視してはならない
- 文法的に正しくても意味のある挙動をするか
- 指定したパスに、対象はある?
- RAMの量は適切?
- 文法的に正しくても意味のある挙動をするか
- 設定が意味ある値になっているかを検証することは機能停止の帽子と運用コストの削減に役立sつ
- 変更の反映前に、設定ミスが見つかる仕組みがあるか
Configuration syntax
- 設定は、ユーザが望むものを確実に達成することが大事
- しかし、文法による曖昧さの除外も大事
- 重要な3要素
- エディタ内による構文の強調表示
- 既存言語にならうとだいたい解決する
- リンター
- 一般的な記述上の矛盾を判定する
- 構文フォーマッター
- 標準化することで、重要でない議論を回避できる
- clang-format, autopep
- 大規模な組織では、非推奨となった広く再利用された設定に注釈を付けることができると便利
- 自動書き換えツールによって一元的な変更が容易になり、従来の問題を回避できる
- 標準化することで、重要でない議論を回避できる
- エディタ内による構文の強調表示
- ツールを使用することで、ユーザは自分の記述が正しいことを確信し作業できる
Ownership and Change Tracking
- 設定は企業や機関の重要なシステムに影響を与える可能性がある
- ユーザを確実に分離し、システムにどのような変更が行われたのかを把握することが重要
- 第10章で述べたように、効果的なポストモーテムの文化は個人を非難することを回避できる
- ただし、インシデントの発生時とポストモーテム作業の両方で、設定変更者と変更の影響を把握することが大事
- これは事故が事故によるものか悪意のある行為者によるものかにかかわらず当てはまる
- 設定コードには明確なオーナーが必要
- ファイルの所有者が一人だけの場合は、誰が変更したかを追跡するよりはるかに簡単
- バージョニングは、ある時点の設定がどうなっているかを見ることを可能にする
- SubversionやGitを使って設定ファイルを管理することは今日一般的
- このやり方はWebUIやAPIによって取り込まれる設定についても同様に有効
- 関連情報として、設定変更とそれによって得られるアプリケーションを記録すると便利(ときには必ずそれが求められる)
- 設定を記録することとそれが反映されることは必ずしもイコールではない
- インシデント対応において、設定変更が原因と疑われる場合、変更自体が履歴管理されていると自身を持ったロールバックが可能になる
Safe Configuration Change Application
- 設定はシステムに大きな変更を加える簡単な方法だが、簡単にテストできないことがある
- 安全な設定変更の要素とは何かを考える
- 3つの主なプロパティが必要
- all or nothingの変更を回避しながら、徐々に展開する機能
- 変更が危険であると判明した場合に変更をロールバックする機能
- 変更によってオペレータの制御が失われる場合は、自動ロールバック(または少なくとも進行を停止する機能)
- 新しい設定を反映するときは、広範囲での一括展開を回避することが重要
- 新しい設定を徐々に展開する
- 100%停止を引き起こす前に問題を検出し、問題のある展開を中止することができる
- Kubernetesなどのツールがすべてのポッドを一度に更新するのではなく、ソフトウェアまたは設定を更新するためにローリングアップデート戦略を使用する理由の1つ(関連する議論については第16章を)
- ロールバック機能は、インシデント期間を短縮するために重要
- パッチを適用するよりもはるかに迅速に機能停止を緩和できる
* 設定をロールフォワードおよびロールバックできるようにするためには、外部に依存しない形になっていなければならない
* 変化する可能性のある外部リソースを必要とする構成は、ロールバックするのが非常に難しい場合がある
* (例)リモートCVSの中にあるデータを参照する設定
- 最後に重要なこと
- システムとして、制御を失う可能性のある変更を処理するときには注意が必要
- デスクトップでは、画面解像度の変更はカウントダウンを促し、ユーザーが変更を確認しないとリセットされることがよくある
- 誤った設定によってユーザが変更をもとに戻せなく可能性があるため
- システム管理者は、現在設定中のシステムから誤って自分自身をシステムからファイアウォールで外すことがある
- これらの原則は設定固有のものではなく、あらゆるシステム更新についても同じことが言える
Conclusion
- ちょっとした設定変更でも本番システムに劇的な影響を与える可能性がある
- これらのリスクを軽減するために慎重に設定を設計する必要がある
- 設定の設計は、APIとUI設計の両方の側面を持っている
- システム実装のちょっとした一部ではなく、意図的に設計されるべき
- 設定をフィロソフィーとメカニズムに分けることで、内部システムを設計するときに議論の範囲を正しく設定することができる
- 推奨事項を適用するには時間と注意が必要
- 原則を実際に適用した例:Canary Analysis Serviceに関するACM Queueの記事
- 必須の質問を減らし、任意の質問に対する良い答えを見つけることに、およそ1ヶ月を費やした
- 努力は、簡単な設定システムを生み出した
- 使いやすかったので、それは広く内部で採用されれた
- ユーザーサポートはほとんど必要ない
- ユーザーはシステムを容易に理解できるため、安心して変更を加えることができる
- もちろん、設定ミスやユーザーサポートを完全に排除したわけでも、期待することもない