kakakakakku blog

Weekly Tech Blog: Keep on Learning!

nginx と Envoy の設定を比較して学べる「Migrating from NGINX to Envoy Proxy」を試した

引き続き「Try Envoy」を使って Envoy を学ぶ.今回は nginx と Envoy を比較したコンテンツ「Migrating from NGINX to Envoy Proxy」を紹介する.nginx の nginx.conf を Envoy の envoy.yaml にどのようにマイグレーションするのか?を学べる.

Migrating from NGINX to Envoy Proxy

手順は以下の「計7種類」ある.今回もサイトの表記は「Estimated Time: 10 minutes」と書いてあるけど,気にせずに進める.

  • Step.1 「NGINX Example」
  • Step.2 「NGINX Configuration」
  • Step.3 「Server Configuration」
  • Step.4 「Location Configuration」
  • Step.5 「Proxy and Upstream Configuration」
  • Step.6 「Logging Access and Errors」
  • Step.7 「Launching」

www.envoyproxy.io

www.katacoda.com

Step.1 「NGINX Example」

nginx の設定には「ログ設定」「ポート設定」「ルーティング設定」などを書く.Envoy の場合は nginx と異なる仕組みとなり,大きく以下の4種類のコア設定から構成されている.詳しくは前回の記事にもまとめた.

  • Listeners
  • Filters
  • Routers
  • Clusters

Step.2 「NGINX Configuration」

まず nginx で「ワーカーコネクション」の設定をする場合,よく worker_processesworker_connections をチューニングする.worker_processes に CPU コア数を指定したり,auto を指定する.Envoy の場合はどのようにコネクションを管理するのだろうか?

worker_processes  2;

events {
  worker_connections   2000;
}

Envoy の場合は,ハードウェアスレッドに対してワーカースレッドを作成し,ワーカースレッドはノンブロッキング処理をすると書いてある.それ以上は書いてなく,詳しい解説は Envoy 公式ブログを参照する必要がある.例えば「Main / Worker / File Flush」という個別のスレッドがあったり,アクセスログを出力するときはロックを取得したり,スレッドをうまく活用する仕組みの名前が「Thread Local Storage (TLS)」だったり,Envoy のスレッドモデルに興味があれば読んでおくと良さそう.

blog.envoyproxy.io

Step.3 「Server Configuration」

以下の nginx.conf を読むと,server_name に指定されたドメインから 8080 Port でリクエストを受けることになる.

server {
  listen 8080;
  server_name one.example.com www.one.example.com;
}

もし同じ設定を Envoy に設定する場合,envoy.yamllisteners にポート番号を設定する.ドメインはここには設定せず,次の filters に設定する.前回と似ているため,envoy.yaml は見慣れてきたような気がする.

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }

Step.4 「Location Configuration」

引き続き nginx.conf を読み解いていく.以下の設定は / にリクエストをすると http://targetCluster/ に転送される.

location / {
  proxy_pass http://targetCluster/;
  proxy_redirect off;

  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
}

もし同じ設定を Envoy に設定する場合,envoy.yamlfiltersenvoy.http_connection_manager を使う.これは前回も出てきた HTTP を制御するフィルタで,ここにドメインとリクエストを受けるプレフィックスを設定している.転送する設定は次の clusters に設定するため,ここでは Cluster 名を設定している.

filter_chains:
- filters:
  - name: envoy.http_connection_manager
    config:
      codec_type: auto
      stat_prefix: ingress_http
      route_config:
        name: local_route
        virtual_hosts:
        - name: backend
          domains:
            - "one.example.com"
            - "www.one.example.com"
          routes:
          - match:
              prefix: "/"
            route:
              cluster: targetCluster
      http_filters:
      - name: envoy.router

前回は特に気にしなかったパラメータとして codec_type がある.ドキュメントを読むと「AUTO / HTTP1 / HTTP2」を設定できる.基本的にはデフォルトの AUTO で良さそうだけど,例えば HTTP1 を設定すると,通信を HTTP/1.1 に強制できる.

www.envoyproxy.io

次に domains で,これは virtual_hosts の中で必須のパラメータになっている.one.example.com のように正確なドメイン名を設定しても良いし,* を使って部分一致を設定することもできる.

www.envoyproxy.io

Step.5 「Proxy and Upstream Configuration」

nginx.confupstream を設定すると,クラスタを表現できる.今回は IP アドレスを2個設定している.

upstream targetCluster {
  172.18.0.3:80;
  172.18.0.4:80;
}

転送する設定は clusters を使う.clusters には「タイムアウト」「負荷分散」などを設定できる.

clusters:
- name: targetCluster
  connect_timeout: 0.25s
  type: STRICT_DNS
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  hosts: [
    { socket_address: { address: 172.18.0.3, port_value: 80 }},
    { socket_address: { address: 172.18.0.4, port_value: 80 }}
  ]

まず,type にサービスディスカバリの種類を設定する.デフォルトのパラメータは STATIC で,リスト全てを対象とする.今回は STRICT_DNS が設定されているため,名前解決をする.正直今回の構成だと STATIC で良いのではないか?と思うけど,STRICT_DNS になっている理由や,もう1個類似したパラメータとして LOGICAL_DNS もあり,このあたりは別途調べないとクリアにならなそう.

lb_policy には負荷分散のタイプを指定する.今回はデフォルトのパラメータ ROUND_ROBIN でラウンドロビンになっているけど,他にも LEAST_REQUESTRING_HASH など,気になるタイプが多く使えるようになっている.

www.envoyproxy.io

Step.6 「Logging Access and Errors」

最後はログ設定となり,今回は envoy.file_access_log を使って stdout に出力する.なお,ログ設定は filters に設定するため,抜粋すると以下のようになる(access_log 以外は省略している).format を設定すると項目をカスタマイズできるし,json_format を設定すると JSON 形式で出力できる.

filter_chains:
- filters:
  - name: envoy.http_connection_manager
    config:
      access_log:
      - name: envoy.file_access_log
        config:
          path: "/dev/stdout"
          json_format: {"protocol": "%PROTOCOL%", "duration": "%DURATION%", "request_method": "%REQ(:METHOD)%"}

www.envoyproxy.io

www.envoyproxy.io

Step.7 「Launching」

作成した envoy.yaml を試す.今回は以下の構成図のように Envoy コンテナと HTTP コンテナを2個起動する.HTTP コンテナは katacoda/docker-http-server となり,これは Go の net/http パッケージを使って,ホスト名を表示するだけの実装になっている.

$ docker run --name proxy1 -p 80:8080 --user 1000:1000 -v /root/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy
$ docker run -d katacoda/docker-http-server
$ docker run -d katacoda/docker-http-server

f:id:kakku22:20191201025819p:plain

実際に Envoy コンテナに Host Header 付きでリクエストをする.レスポンスのホスト名(コンテナ ID)を見ると,ラウンドロビンになっていることがわかる(date は修正している).

$ curl -H "Host: one.example.com" localhost -i
HTTP/1.1 200 OK
date: Sat, 30 Nov 2019 17:00:00 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 0
server: envoy

<h1>This request was processed by host: 1c7446d81170</h1>

$ curl -H "Host: one.example.com" localhost -i
HTTP/1.1 200 OK
date: Sat, 30 Nov 2019 17:00:00 GMT
content-length: 58
content-type: text/html; charset=utf-8
x-envoy-upstream-service-time: 0
server: envoy

<h1>This request was processed by host: 8c2ba95ccec2</h1>

さらに今回は envoy.yaml でドメインを設定しているため,Host Header なしでリクエストをすると,404 になった.

$ curl localhost -i
HTTP/1.1 404 Not Found
date: Sat, 30 Nov 2019 17:00:00 GMT
server: envoy
content-length: 0

まとめ

  • 「Try Envoy」のコンテンツ「Migrating from NGINX to Envoy Proxy」を試した
  • 個人的に慣れている nginx.conf の設定項目を envoy.yaml に書き換えていくことにより,理解を深められた
  • Envoy の設定はバリエーションが多く,ドキュメントを読みながらどんどん試したいと思う

Try Envoy 関連