コンテンツにスキップ

Network 周りの設定は App 側でやるべきか、Infra 側でやるべきか

はじめに

マイクロサービスや Kubernetes を用いたコンテナ基盤を運用するとき、タイムアウトやリトライ、認証などのネットワーク処理をどこで担うかが設計上の課題になります。各サービスのアプリケーションコード内でも、Gateway や Service Mesh などのインフラ(ミドルウェア)側でも実装できるため、適した実装方法は状況に応じて変わります。

本記事の焦点は、インフラ側で設定するメリットです。あわせてアプリケーション側の長所と短所も比較し、どの処理をアプリケーション側に残すべきかを含めて設計時の判断基準をまとめます。

App 側で設定する場合

アプリケーション側でネットワークを設定する場合、ライブラリを用いて通信ロジックをコード内に直接記述するか、環境変数で設定値を渡します。こうすれば、ビジネスロジックに合わせて柔軟に制御できます。たとえば、冪等性が保証されていない HTTP の POST メソッドでは、リトライしてよいかどうかの判断が困難です。業務ロジックを知るアプリケーションだけが、リトライの可否を安全に判断できます。

一方で、アプリケーション側で設定すると次の問題が生じます。

  • 設定がプログラミング言語やフレームワークに依存し、複数の言語が混在する環境では言語ごとにライブラリを選定して実装するため、設定の統一や移植が難しい
  • 全社共通のタイムアウト値のような組織の推奨設定を徹底しにくく、準拠できているかどうかも各サービスの実装を深掘りしないと確かめられない
  • 設定を変更するたびに、アプリケーションの再ビルドや再デプロイ、再起動が必要になる

Infra 側で設定する場合

Kubernetes の Gateway API やリバースプロキシなどのインフラ側で管理すれば、通信制御をアプリケーションコードから切り離せます。その結果、次のメリットが得られます。

  • 各言語やフレームワークの実装に依存せず、全社で一貫したポリシー(接続タイムアウトや TLS 終端など)を適用できる
  • 言語やフレームワークをまたいで、設定を移植しやすくなる
  • 設定をうまく切り出せていれば、設定を変えてもコンテナイメージを作り直す必要がなく、アプリケーションの再デプロイや再起動も回避できる

ただし、インフラ側で設定する場合は次の点に注意してください。

  • Service Mesh などで Sidecar パターンを採用すると、Sidecar を再起動するためにアプリケーション用の ReplicaSet も再デプロイや再起動が必要になる
  • Sidecar パターンでは、コンテナの起動順序への配慮や、追加のリソース消費への注意も必要になる
  • プロキシやミドルウェアを採用すると、学習コストや運用負荷を伴う可能性がある
  • ミドルウェア自体が単一障害点になる懸念がある

Gateway API による設定例

Kubernetes Gateway API のHTTPRouteを使うと、アプリケーションコードを変更せずに設定をインフラ側へ切り出せます。HTTPRoute のtimeoutsフィールドは、Gateway API v1.2.0 で Standard Channel に加わりました。次のようにtimeouts.requestを指定すると、api-serviceへのリクエストにタイムアウトを設定できます。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
spec:
  rules:
    - timeouts:
        request: 5s
      backendRefs:
        - name: api-service
          port: 8080

どちらを選択すべきかの判断基準

要件や組織体制に応じて、責任分界点を次の基準で整理してください。

観点 アプリケーション側が適しているケース インフラ側(Gateway 等)が適しているケース
対象の通信処理 冪等性の判断が必要なリトライ、詳細な業務フォールバック 共通接続タイムアウト、レートリミット、全社統一の TLS 終端
言語・システム構成 単一言語、モノリス、シンプルな構成 複数言語が混在するマイクロサービス
運用の関心事 アプリケーションの単体テスト内で通信挙動を完結させたい アプリケーションの再デプロイなしで動的に通信ポリシーを変更したい

まとめ

アプリケーション側で設定すると、言語やフレームワークに依存するため統一や移植が困難です。インフラ側へ切り出せば統一や移植が容易になり、構成によっては設定変更時の再デプロイや再起動も回避できます。ただし、ミドルウェアが単一障害点になりうるうえ、採用に伴って学習コストや運用負荷が生じる可能性もあります。

責務の分離を意識すると、接続タイムアウトやトラフィック制御といった横断的関心事は、インフラ側へ集約することになります。業務ロジックと密結合する処理、たとえば冪等性の判断が必要なリトライやきめ細かなエラーハンドリングは、アプリケーション側で担保するのが適切です。チームの開発規模や運用体制に合わせて、境界線を検討してください。