Envoy Proxy に入門してポチポチ勉強を進めてます。 Envoy Proxy は動的な設定と xDS API が魅力的ですが、手始めに静的な設定ファイルで動かしてみましょう。 この記事では HTTP ロードバランサーを Envoy で構築します。 完成図は以下の図になります。
HTTP ロードバランサーの後ろには、目的の異なる 2 つのクラスタがあります。
今回は NGINX と Apache httpd を使っています。
それぞれのクラスタはnginx.local
とhttpd.local
というホスト名(バーチャルホスト)で経路を分岐します。
- 2019-02-06 追記 : DEPRECATED なプロパティを修正しました。
Envoy の設定ファイル
Envoy をどういう環境で利用するにもBootstrap Configurationが必要です。 静的な設定ファイルで Envoy を利用する場合も必要ですし、xDS API を利用する場合も xDS サービスの設定を書きます。 Bootstrap Configuration には他にも、ノードの識別子や管理画面を設定できます。
Envoy の設定の仕様は公式ドキュメントにあります。
Envoy の設定は v1 API と v2 API の 2 つのバージョンがあります。 公式ドキュメントも v1 API に関する項目が削除され、将来使えなくなる可能性があるので、本記事も v2 APi のみを対象とします。
クラスタの起動
実験のためにまずは HTTP サーバーを建てます。NGINX と httpd は Docker Hub の nginx を使います。
これらのコンテナイメージはdocker run
するだけで HTTP サーバーが起動します。
また HTTP にアクセスするとログにアクセスログが出ます。
それぞれ 4 つのターミナル上で立ち上げてログを眺めましょう。
$ docker run --rm --name nginx1 nginx
$ docker run --rm --name nginx2 nginx
$ docker run --rm --name httpd1 httpd
$ docker run --rm --name httpd2 httpd
どういう結果を確かめるには、Docker コンテナに直接アクセスしてみたらわかるでしょう。
$ curl $(docker inspect nginx1 | jq -r '.[].NetworkSettings.IPAddress')
$ curl $(docker inspect httpd1 | jq -r '.[].NetworkSettings.IPAddress')
設定を書く
Bootstrap config を書きましょう。
以下の設定を /tmp/envoy/envoy.yaml
に保存します。
# /tmp/envoy/envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
http_filters:
- name: envoy.router
route_config:
name: route
virtual_hosts:
- name: nginx_service
domains: ["nginx.local"]
routes:
- match:
prefix: "/"
route:
cluster: nginx_cluster
- name: nginx_service
domains: ["httpd.local"]
routes:
- match:
prefix: "/"
route:
cluster: httpd_cluster
clusters:
- name: nginx_cluster
type: STRICT_DNS
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: nginx_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: nginx1, port_value: 80 }
- endpoint:
address:
socket_address: { address: nginx2, port_value: 80 }
- name: httpd_cluster
type: STRICT_DNS
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: httpd_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: httpd1, port_value: 80 }
- endpoint:
address:
socket_address: { address: httpd2, port_value: 80 }
Envoy を起動
Envoy は公式イメージが Docker Hub で配布されてるものを使います。
この Docker イメージは/etc/envoy/envoy.yaml
から設定をロードします。
手元で記述した設定ファイルはコンテナ内にマウントします。
また先程の HTTP サーバーを名前解決するために --link
オプションでそれぞれの Docker コンテナ名を指定します。
$ docker run \
--name envoy --rm --publish 80:80 \
--link nginx1 --link nginx2 --link httpd1 --link httpd2 \
-v /tmp/envoy:/etc/envoy \
envoyproxy/envoy:v1.9.0
ここまでで Envoy によるロードバランサーが完成しました。
バーチャルホスト名で Envoy に到達できるように、/etc/hosts
に以下の2つのエントリを追加します。
# /etc/hosts
127.0.0.1 nginx.local
127.0.0.1 httpd.local
Envoy が起動したら、nginx.local
と httpd.local
にアクセスしてみましょう。
$ curl nginx.local
$ curl httpd.local
nginx.local
でアクセスすると「Welcome to nginx!」が表示され、httpd.local
でアクセスすると「It works!」が表示されると思います。
設定ファイルを眺める
それでは順を追って設定ファイルを読んでいきましょう。 上記の設定は必要最低限なフィールドのみ埋めています。 それぞれのフィールドの定義は適宜ドキュメントへのリンクを貼ってあるので、必要に応じて参照してください。
1 行目のstatic_resources
はその名の通り静的なリソースを記述できます(定義はStaticResourcesにあります)。
ほかにもdynamic_resources
などがあります。
static_resources:
listeners
にはListenerの設定を記述します。
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 8000 }
filter_chains:
- filters:
Listener の設定には待ち受けるアドレス・ポートと、受け取ったパケットをどう処理するかを決めるフィルターを記述します。 Listener はFilterChainの設定を持ち、さらに FilterChain がFilterの設定を持ちます。
ここでは 1 つのフィルターを定義しています。
- name: envoy.http_connection_manager
config:
http_filters:
- name: envoy.router
stat_prefix: ingress_http
route_config:
name: route
virtual_hosts:
name
フィールドは利用するフィルターの種類です。
envoy.http_connection_manager
は Envoy の組み込みフィルターで、HTTP (L7 レイヤー) の情報に基づいて処理します。
envoy.http_connection_manager
を指定する場合は、config
フィールドにHttpConnectionManager を指定します。
http_filters
フィールドは HTTP connection manager がどういう HTTP フィルタを行うかを指定します。
ここでは envoy.router
という経路制御のための HTTP フィルタを利用します。
stat_prefix
はモニタリング用途に使うメトリクス名のプレフィクスです。
envoy.router
では route_config
フィールドでRoute Configurationを設定します。
その中の virtual_hosts
フィールドでVirtualHostを設定します。
ここではバーチャルホストに基づいて 2 つのクラスタに経路を設定します。
他にも URL などに基づいて経路を制御可能です。
- name: nginx_service
domains: ["nginx.local"]
routes:
- match:
prefix: "/"
route:
cluster: nginx_cluster
- name: nginx_service
domains: ["httpd.local"]
routes:
- match:
prefix: "/"
route:
cluster: httpd_cluster
最後にClusterを定義します。 各 Cluster にはそれぞれ 2 つの Endpoint があり、ラウンドロビンで接続先を決定します。
以前は Cluster のhosts
フィールドが利用できてましたが、現在は DEPRECATED になり、かわりにload_assignment
を使います。
load_assignment
フィールドは Endpoint Discovery Service (EDS) が返す値と同じ構造をしており、hosts
フィールドより細やかな設定ができます。
いまはひとまず、2 つのエンドポイントにロードバランシングします。
clusters:
- name: nginx_cluster
type: STRICT_DNS
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: nginx_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: nginx1, port_value: 80 }
- endpoint:
address:
socket_address: { address: nginx2, port_value: 80 }
- name: httpd_cluster
type: STRICT_DNS
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: httpd_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: httpd1, port_value: 80 }
- endpoint:
address:
socket_address: { address: httpd2, port_value: 80 }
まとめ
この記事では Envoy で L7LB を構築しました。 これだけだと NGINX とどう違うんだと感じるかも知れません。 Envoy の真髄はクラウドネイティブなアプリケーションで利用できる、柔軟性や可観測性です。 それらの記事については追々書いていきたいと思います。