8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2024

Day 6

IstioとEnvoy Filterを試してみた|ローカル環境でアクセスログ&Luaを試す

Last updated at Posted at 2024-12-05

はじめに

Envoy Proxyは、サービスメッシュの中核となるプロキシで、トラフィック管理や監視、セキュリティ機能を提供する強力なツールです。特に、Istioと組み合わせることで、トラフィックの制御やカスタマイズを柔軟に行えます。

Istioでは、Envoy Proxyの動作をカスタマイズするために「Envoy Filter」と呼ばれるカスタムリソースを使用できます。このリソースを利用することで、Envoy Proxyのトラフィック処理をさらに細かく制御できるようになります。

本記事では、ローカル環境にKubernetesクラスターを構築し、Istioを使って以下の2つを試してみます。

  1. アクセスログの有効化
    Envoy Proxyにリクエスト・レスポンスの詳細を記録する設定を追加します。

  2. LuaフィルターでHTTPヘッダーを動的に付与
    リクエストのパラメータを基にカスタムHTTPヘッダーを追加する方法を解説します。

今回のテーマ

  1. ローカル環境でKindクラスタを構築する
  2. Istioのインストール
  3. Envoy Filterでアクセスログの有効化
  4. LuaフィルターでHTTPヘッダーを付与

1. ローカル環境でKindクラスタを構築する

まずはローカル環境にKubernetesクラスターを構築します。
今回使用するのは、Dockerコンテナ上で簡単にクラスターを作成できるツール「Kind」です。

Kindを使ってローカル環境にKubernetesクラスターを構築する記事はネットで調べると沢山出てきますので、既にご存知の方も多いと思います。
そのためテーマ1、2については、必要に応じて読み飛ばしてください:pray:

必要なツール

以下のツールがインストールされていることを確認してください。

Kindクラスタの構築

専用のディレクトリを作成(任意)

mkdir -p ~/kind-envoy
cd ~/kind-envoy

Kindクラスタ設定ファイルを作成

kind-config.yaml という名前で以下の内容を保存します。

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30000
        hostPort: 30000
        protocol: TCP

今回は、単純な動作確認なので、コントロールプレーンノードのみを作成します。本番環境に近い構成を試したい場合や、スケジューリングや分散処理をテストしたい場合は、ワーカーノードを追加する設定に変更してください。

クラスタを作成

kind create cluster --name envoy-testing --config kind-config.yaml

動作確認

正常に作成されたか確認します。

kubectl cluster-info --context kind-envoy-testing

2. Istioのインストール

Envoy Filterを適用するには、Istioのサイドカー(Envoy)を利用します。
以下の手順でIstioをインストールします。

Istioctlのインストール

curl -L https://istio.io/downloadIstio | sh -
cd istio-*
export PATH=$PWD/bin:$PATH

Istioのデフォルトプロファイルをインストール

istioctl install --set profile=demo -y

動作確認

Istioコントロールプレーンが正常に動作しているか確認します。

kubectl get pods -n istio-system

サイドカー自動注入の有効化

デフォルトのネームスペースにIstioサイドカー(Envoy)を自動注入します。

kubectl label namespace default istio-injection=enabled

サンプルアプリケーションをデプロイ

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/sleep/sleep.yaml
kubectl get pods

3. Envoy Filterでアクセスログを有効化

お待たせしました!ここからは実際にEnvoy Filterを使って設定を進めていきます。
まずは、Envoy Proxyにリクエストやレスポンスの詳細を記録するアクセスログを有効化する設定を作成してみましょう。

Envoy Filterの作成

以下は、アクセスログを標準出力に記録するためのEnvoy Filter設定例です。

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: enable-access-logs
  namespace: default
spec:
  # Workload Selectorを使用して、このフィルターを適用する対象のPodを指定
  workloadSelector:
    labels:
      # このフィルターは、ラベル "app: sleep" を持つPodにのみ適用される
      app: sleep
  configPatches:
    - applyTo: HTTP_FILTER
      # フィルターの適用対象をHTTP_FILTER(リクエスト/レスポンス処理)に指定
      match:
        context: ANY
        listener:
          filterChain:
            filter:
              # EnvoyのHTTP Connection Managerフィルターを対象とする
              name: "envoy.filters.network.http_connection_manager"
      patch:
        # "MERGE"は既存の設定を変更または追加する操作
        operation: MERGE
        value:
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            access_log:
              # アクセスログの設定を追加
              - name: envoy.file_access_log
                typed_config:
                  # アクセスログの形式としてファイルログを選択
                  "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                  path: /dev/stdout

この設定により、app: sleepラベルを持つPodのトラフィックに対して、アクセスログが標準出力に記録されます。

詳細な設定ポイント解説

  • workloadSelector: このセクションでは、このEnvoy FilterがどのPodに適用されるかを指定します。
    labelsに指定したキーと値(例: app: sleep)が対象Podのラベルと一致する場合に適用されます。
    この設定がないと、デフォルトでネームスペース内のすべてのPodに適用されます。

  • configPatches: Envoyの設定を変更または追加するセクションです。

  • applyToフィールドで、どの設定部分にパッチを適用するかを指定します。ここでは、HTTPのリクエストやレスポンスを処理するHTTP_FILTERを対象としています。

  • match.context: Envoy Filterが適用されるトラフィックの方向やコンテキストを指定します。
    ANY を指定することで、リクエスト(INBOUND)とレスポンス(OUTBOUND)の両方を対象にします。

  • listener.filterChain.filter.name: Envoyの内部で動作するフィルターを指定します。
    ここでは envoy.filters.network.http_connection_manager を選択し、HTTPトラフィックを処理するフィルターを対象としています。
    patch.operation: MERGEは既存の設定を変更または追加する操作です。
    ADDで新しい設定を追加したり、REMOVEで既存の設定を削除することも可能です。

  • access_log: このセクションでアクセスログの出力先やフォーマットを指定します。
    ここではenvoy.file_access_logを利用して、ログを標準出力(/dev/stdout)に出力する設定を行っています。詳細は、以下をご参照ください。

フィルターの適用

作成したマニフェストを適用します。

kubectl apply -f enable-access-logs.yaml

動作確認

1. 対象Podにリクエストを送信

kubectl exec -it <sleep-pod-name> -- curl http://httpbin.org/get

<sleep-pod-name>は、kubectl get podsコマンドで確認したsleepPodの名前に置き換えてください。
ここではcurlを使用して、httpbin.org/getエンドポイントにHTTPリクエストを送信します。

2. Envoy Proxyのログを確認

サイドカーコンテナ(istio-proxy)が出力するログにアクセスします。

kubectl logs <sleep-pod-name> -c istio-proxy
出力例
[2024-11-30T12:00:00.123Z] "GET /get HTTP/1.1" 200 - "-" "-" 0 123 45 42 "-" "curl/7.68.0" "abcd1234-5678-90ef-ghij-klmnopqrstuv" "localhost" "127.0.0.1:8080"

アクセスログが出力されました!
このようにして、アクセスログが記録されていることで、リクエストのトラフィックの流れを把握しやすくなり、デバッグや監視が容易になります。

このログはEnvoyのアクセスログフォーマットに基づいています。必要に応じて、Envoy Filterの設定を変更することで、ログフォーマットをカスタマイズできます。

4. LuaフィルターでHTTPヘッダーを付与

Luaフィルターとは?

Envoyには、リクエストやレスポンスの処理を行うためのさまざまな組み込みHTTPフィルターが用意されています。
しかし、一部の特殊な操作や要件については、これらの組み込みフィルターだけでは対応できない場合があります。
たとえば、動的なリクエスト処理や、特定の条件に基づいてトラフィックを操作するカスタムロジックが必要な場合です。

こうしたカスタムロジックを実現するために、Envoyは汎用的なLuaフィルターを提供しています。Luaフィルターを使用すると、以下のような操作が可能です。

  • 特定の条件に基づいてHTTPヘッダーを追加または変更
  • リクエストやレスポンスの内容を操作
  • トラフィックの動的なリダイレクトやフィルタリング

Luaは軽量で柔軟性が高いため、Envoyの動作を動的に拡張する際に適しています。具体的には、Luaスクリプトを利用してEnvoyのトラフィック処理をカスタマイズすることができます。

詳細は以下をご参照ください。

以下のようにHTTPヘッダーを追加する簡単なスクリプトを試してみます。

Envoy Filterの修正(Luaフィルターを付与)

以下は、前述のアクセスログを有効にした上で、Luaフィルターを適用する設定です。

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: enable-access-logs
  namespace: default
spec:
  workloadSelector:
    labels:
      app: sleep
  configPatches:
    # アクセスログの有効化
    - applyTo: HTTP_FILTER
      match:
        context: ANY
        listener:
          filterChain:
            filter:
              name: "envoy.filters.network.http_connection_manager"
      patch:
        operation: MERGE
        value:
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            access_log:
              - name: envoy.file_access_log
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                  path: /dev/stdout

    # Luaフィルターの追加 ----------------------------------------------------------
    - applyTo: HTTP_FILTER
      match:
        # OUTBOUND(サイドカーから外部への通信)時に適用
        context: SIDECAR_OUTBOUND
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              # HTTP Routerフィルターの直前にLuaフィルターを挿入
              subFilter:
                name: envoy.filters.http.router
      patch:
        # INSERT_BEFOREは指定したフィルターの前に新しいフィルターを追加する操作
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.lua
          typed_config:
            # Luaスクリプトの設定
            "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
            # default_source_code を使用
            default_source_code:
              inline_string: |
                -- リクエストのパスから "user_id" クエリパラメータを抽出
                function envoy_on_request(request_handle)
                  local path = request_handle:headers():get(":path")  -- リクエストパスを取得
                  local user_id = path:match("user_id=([^&]+)")  -- "user_id" の値を抽出
                  if user_id then
                    -- 抽出した "user_id" をカスタムHTTPヘッダー "X-Custom-UserID" に追加
                    request_handle:headers():add("X-Custom-UserID", user_id)
                  end
                end

詳細な設定ポイント解説

  • subFilter: envoy.filters.http.router
    Luaフィルターを挿入する位置を指定します。ここでは envoy.filters.http.router(リクエストをルーティングするフィルター)の直前に挿入しています。

  • patch.operation: INSERT_BEFORE
    INSERT_BEFORE を指定することで、指定したフィルターの前に新しいフィルターを追加します。
    typed_config: Luaフィルターの設定を行います。
    default_source_codeの使用: inline_codeは非推奨となっているので、default_source_codeを利用し、inline_stringフィールドでLuaコードを定義します。詳細は以下をご参照ください。

フィルターの適用

修正したマニフェストを適用してみます。

kubectl apply -f enable-access-logs.yaml

動作確認

Luaフィルターを適用したことで、HTTPリクエストのURLからクエリパラメータ user_id を抽出し、それを新たなカスタムHTTPヘッダー X-Custom-UserID に追加する動作を確認します。

1. 対象Podにリクエストを送信

Luaフィルターを適用したPodに対して、以下のようなリクエストを送信します。

kubectl exec -it <sleep-pod-name> -- curl "http://httpbin.org/headers?user_id=12345"
  • URLにはクエリパラメータ user_id=12345 を含めています。この user_id の値をLuaフィルターが抽出し、HTTPヘッダーに追加します。

2. レスポンスにカスタムHTTPヘッダーが追加されていることを確認

応答として返されるJSONに、新たに追加されたカスタムHTTPヘッダー X-Custom-UserID が含まれることを確認します。

出力例
{
  "headers": {
    "X-Custom-UserID": "12345",
    ...
  }
}

このレスポンスは、httpbin.org/headers エンドポイントが現在のHTTPリクエストヘッダーを返すことで得られます。
Luaフィルターによって追加された X-Custom-UserID ヘッダーには、リクエストURLのクエリパラメータ user_id の値が反映されます(この場合は 12345)。

まとめ

今回の記事では、Envoy ProxyとIstioを使用して、トラフィックの観測や制御を行う方法を解説しました。具体的には、以下の2つの機能を試しました。

1. アクセスログの有効化

Envoy Filterを利用して、トラフィックの詳細を記録するアクセスログの設定方法をご紹介しました。

2. LuaフィルターでHTTPヘッダーを付与

Luaスクリプトを使い、リクエストのURLから情報を抽出し、新たなカスタムHTTPヘッダーを動的に追加する方法を実践しました。
また、今回は特定のPod全体に対してフィルターを適用しましたが、特定のルートに対して適用することも可能です。詳細はこちらを参照ください。

これらの設定を通じて、Envoy ProxyとIstioの柔軟性を体験できることが分かります。Envoy Filterはカスタマイズの幅が広く、さまざまなシナリオで活用可能です。

例)

  • トラフィックのフィルタリングやリダイレクト
  • 動的なデータ操作や監視の強化
  • セキュリティポリシーの細分化

Envoy ProxyとIstioは、本番環境でのトラフィック管理やセキュリティ強化に役立つツールです。ぜひ、この記事を参考に、皆さんも新たな機能やシナリオを試してみてください。

この記事が、皆さんのシステム構築やトラフィック管理の参考になれば幸いです。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?