Config files vs. flags: story of pain in SREday London 2024の意訳

に公開

Qiita Advent Calendar 2025の海外SRE関連セッション意訳の3日目です。ひとりアドベントカレンダーですが、完走できるように頑張ります。

このアドベントカレンダーでは、海外のSRE関連のセッションを翻訳しながら、私の意見や疑問や補足を追加したものです。私の誤解から誤った説明になっている箇所は、コメントでご指摘お願いします。

このアドベントカレンダーでは、私の意見やコメントは可能な限り、下記の記法で要約と区別します。ただ、そこまで厳密にできないため、要約文中に私の思想が混じってしまうことはご容赦ください。(気がついたらご指摘ください)

前回は、SLO Mathという内容のセッションでした。特に低い信頼性の上に高い信頼性を構築するといった内容が解説されています。
SLO Math in SLOconf 2021の意訳

紹介するセッションについて

アドベントカレンダーの初日や2日目は、SREそれ自体やSLOに関する考え方などの少し抽象的な内容を扱ったので、3日目は具体的なものを選びました。

https://www.youtube.com/watch?v=fINZzoYfAQc

セッション詳細: SREday - London, UK, Sep 19-20, 2024

設定ファイルを使うべきか、コマンドライン引数を使うべきか。それはなぜなのか?といった内容です。かなり思想が強く、言葉も強いセッションになっているので、そこは注意した方が良さそうです。

要約

今回のセッションの例となる設定ファイル

今回のセッションのモチベーションを維持するために、このXMLによる設定ファイルを題材に話をする。
例

みてわかる通り、httpのエンドポイントが分割されて記述されていて、さらによくわからない <Details> ネストの中によくわからない設定項目が含まれている。

このセッションでいう設定ファイルは何か

このセッションにおける設定ファイル:

  • サービスバイナリや任意の実行可能ファイルの起動時に読み込まれる静的な設定
  • 実行中には変化しない
  • 変更するには再起動が必要

いわゆる「アプリ起動時に読む静的設定」の話であり、ランタイムで動的に変わるコンフィグの話ではない。

設定ファイルを使う理由とその問題

なぜ設定ファイルを使うのか

一般的な利点:

  • 1箇所に設定がまとまっており、人間にとっては確認しやすい
  • 設定ファイルをネストしてincludeするなどの構造化ができる
  • ディスクやメモリ容量の範囲でほぼ任意の設定を表現できる

設定ファイルの問題点

  • additional indirection
    アプリケーション起動時にファイルを経由するため、どの値が最終的に効いているかのオブザーバビリティが悪い
  • ファイルシステムへの依存
    パスのミス、権限のミス、マウントの失敗など、FS由来のトラブルが発生する
  • フォーマットが人の好み次第
    XML,YAML,JSON,独自フォーマットなどが乱立する
  • テンプレートレンダリングやその場書き換えが入りやすく、仕組みが複雑化する
  • 多くの場合スキーマが無く、静的な検証が効かない

登壇者自身の経験として

  • 権限やパスのミスで「起動時に設定ファイルを読み取れない」事故を何度も見てきた
  • 古いアプリでは「どの設定が実際に使われているのか」誰も分からない
  • 何年も放置されたゴミ設定が残り続け、どこを変えれば動作が変わるのか分からない

設定ファイルをシンプルにしていく

まずは既存の設定ファイルをシンプルにしてみる。

  • 不要なネストを減らす
  • フィールド名から意味が推測できないものを修正する
    意味不明なフィールドがあると、開発者に30分かけて説明してもらう羽目になる
  • デフォルト値で良いものまで全部を必須設定にしているケースを見直す
    しばしば全項目が"11"のような意味のない値で埋められている

中には、

  • かつて実験的に追加され、そのまま削除され忘れたフィールド
  • HTTP endpointの形式をわざわざ分割して持っているが、今は統一フォーマットで問題ないもの

などもあり、整理していくと設定自体がかなりスリムになる。
「XMLは古いので、今みんながなぜか好んで使っているYAMLにしてみる」という冗談交じえつつ。

改善例

設定ファイルvsフラグ:実行時の視点

設定をYAMLで書いて、それを指定してアプリを起動するコマンドと、同じ設定を全部フラグとして渡すコマンドを比較する。

結論:

  • 実際に書いてみると「設定ファイルを指定するコマンド」と「フラグを並べたコマンド」で複雑さはそれほど変わらない
  • むしろフラグにすると「設定が1箇所(コマンド)に集まる」利点がある

ファイル
フラグ

フラグとは何か

  • コマンドライン引数または環境変数
  • 共通のフォーマットを持つ:
    • ENVVAR=value
    • -f value
    • --flag=value

基本の動き:

  • 利便性のために「妥当なデフォルト値」を持ち、必要なものだけ上書きする

フラグを使う理由

利点:

  • ファイルシステムへの依存が無い
  • テンプレート化が最初からやりやすい
    シェルスクリプトやオーケストレーションツールでそのまま文字列生成できる
  • psコマンドなどでプロセスを見れば、どのフラグで起動されているかが観測できる
  • exportされた環境変数は子プロセスにそのまま伝播する
  • dead flagsがほぼ存在しない
    アプリケーションが読まないフラグは基本的に意味を持たないので、「設定はあるが誰も使っていない」という状況が起きにくい

フラグに対するよくある反論と回答

「今は設定ファイルを一度書くだけで済むのに、毎回フラグを指定するの?」

本番環境で設定ファイルを手で書いて置いているケースはほとんどないはず。
通常はオーケストレーションやデプロイツールが生成している。

ローカル環境でも、毎回手で入力する必要はなく、シェルスクリプトなどを使ってコマンドを1回書けばよい。

「うちの設定はフラットにできない」

  • 多くの設定ファイルは実際にはフラットにでき、そのままフラグにもできる。(特にマイクロサービスのような小さめのサービス)
  • どうしてもネストが必要な場合でも、フラグでdictやlistの表現は可能(多くのCLIツールが実際にやっている)

例:

  • --list=item1,item2,item3

のような形でフラットなキーに展開できることが多い。

「コマンドラインは4KB制限があるから無理では?」

4KB制限の話は誤解されがちで:

  • 現代のUnixシステムの典型的なARG_MAXは2MB程度
  • 4KBという数字は「標準入力で受け取る単一行」など別の制限の話と混ざっている

つまり、通常のサービス設定ならフラグでも十分に収まることが多い。

Kubernetes上でのconfig fileの問題

Kubernetes環境でconfig fileを使う場合の話。

主なやり方は2つ:

  1. コンテナイメージに設定ファイルを焼き込む

    • 一般的には最悪の方法:
    • 設定を変えるたびにコンテナイメージのビルドが必要
    • 設定の中身を見るためにデバッグコンテナをアタッチするなど、観測性が悪い
  2. ConfigMapとして渡す方法

    • ConfigMapには1MB制限があり、Linuxの実行引数上限(数MB)より小さい
    • ConfigMapはPodとは別のリソースで、更新してもアプリ側が監視していなければ再起動されない
    • ConfigMapの変更をトリガにpodではなくアプリを再起動するようにすると、全Podが一斉再起動しがち
    • 設定に常に後方互換性を保たないといけない、という制約もきつい

回避策の一例として:

  • ConfigMapの名前にバージョン番号を付与
  • アプリケーションバイナリと特定バージョンのConfigMapを対応付けて読む

Kubernetes上でフラグを使う場合

Kubernetesでフラグを使う場合:

  • Deploymentのspec(template部分)にコマンドやargsとして記述する
  • そのDeploymentが変更されると、RollingUpdateなどの戦略に従ってPodが順次入れ替わる(プログレッシブな再起動)

この方式だと:

  • 設定変更と同時に「どうロールアウトするか」もDeployment戦略に組み込める
  • ConfigMapのように「リソースは変わったがPodは動いたまま」という不整合を避けられる

いつ設定ファイルを使うべきか

  1. 大きくてネストされた設定が本当に必要な場合
    例:複雑なルーティング定義、大量のルールセットなど
  2. 3rd-partyアプリケーション
    例:NGINXなど、もともと設定ファイル前提で設計されているもの
  3. スキーマ付きのフォーマットが必要な場合
    この場合はTextproto形式のような、型のあるテキストフォーマットが推奨されていた

いつフラグを使うべきか

  1. 基本的には「可能な限りこちらを使うべき」
  2. 設定のサイズが小〜中程度
  3. 言語側に標準または実績あるflagsライブラリが存在する場合

現代的なマイクロサービスでは、多くの場合こちらに当てはまるはず。

一般的なアドバイス

最後に、ファイル, フラグによらない設定全般に対するアドバイス。

  1. 可能な限り妥当なデフォルト値を用意する
    「すべての設定はexplicitであるべき」という考え方もあるが、登壇者は「可能な限りオプショナルであるべき」という立場。
  2. dry-run機能をサポートする
    設定を変えた結果を実際に反映せず検証できるようにする。
  3. 設定をオーケストレーションツールのどこか1箇所に集約する
    あちこちに設定が散らばらないようにする。
  4. 設定の中にプログラムを入れない
    設定ファイルの中にロジックを書き始めると「設定のテスト」が必要になり、本末転倒になる。
    プログラムはアプリケーション側に置き、設定はあくまでデータとして扱うべき

全体を通してコメント

やはり抽象的なセッションと比較して、具体的な内容のセッションは、細かなところで思うところがある内容でした。
総論として、私も基本的にデフォルト値を持ちつつ、ファイルではなく環境変数などで設定する方が好みです。

一方で、昔からあるアプリケーションフレームワークでは、設定ファイルの読み込み機能が強力にサポートされていて
ここで説明されてるようなデメリットは、アプリケーションフレームワークが解決してしまっているケースも多いよな、となりました。
例えば、「設定がどこで読まれてどう使われているかわからない」みたいな話も、アプリケーションの設計で統一できる話な気がしています。

Discussion