kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Envoy の EDS を REST API で体験する「API Based Dynamic Routing Configuration」を試した

今回は「Try Envoy」「API Based Dynamic Routing Configuration」を紹介する.前回紹介した「File Based Dynamic Routing Configuration」と関連した内容だけど,今回は API を使った「ディスカバリサービス (xDS)」を学べる.特に Envoy は RESTgRPC をサポートしているけど,今回のコンテンツだと REST を学べる.なお,前回の記事も載せておく.

kakakakakku.hatenablog.com

API Based Dynamic Routing Configuration

手順は以下の「計7種類」ある.今までの Katacoda と同じく文章だと理解しにくい点も多く,自分なりに理解を整理しながら進めた.

  • Step.1 「Introduction」
  • Step.2 「EDS Configuration」
  • Step.3 「Start upstream services」
  • Step.4 「Start EDS」
  • Step.5 「Add endpoint to EDS」
  • Step.6 「Delete Endpoint」
  • Step.7 「Disconnect EDS server」

www.envoyproxy.io

www.katacoda.com

Step.1 「Introduction」

今回は EDS (Endpoint Discovery Service) を REST API を使って実現することにより,エンドポイント情報を取得する.過去記事の内容を復習すると,エンドポイントはクラスタメンバーと表現することもできて,転送先のことを意味する.xDS API を実装する場合,現在だと Java と Go でコントロールプレーンの実装 (gRPC + Protocol Buffers) が Envoy から公開されていて参考になる.ただし,今回は独自実装の API を試す.

Step.2 「EDS Configuration」

envoy.yaml の設定は前回と似ている.今回異なるのは eds_configapi_config_source を設定している点で「File Based」のときは path を設定していた.さらに今回は EDS API として,もう1個 Cluster eds_cluster を追加している.なお,api_typeREST (v2) と REST_LEGACY (v1) があり,既に API v1 は deprecated になっている.API v2 の詳細は以下のドキュメントに載っている.

www.envoyproxy.io

clusters:
- name: targetCluster
  type: EDS
  connect_timeout: 0.25s
  eds_cluster_config:
    service_name: myservice
    eds_config:
      api_config_source:
        api_type: REST
        cluster_names: [eds_cluster]
        refresh_delay: 5s

- name: eds_cluster
  type: STATIC
  connect_timeout: 0.25s
  hosts: [{ socket_address: { address: 172.18.0.4, port_value: 8080 }}]

さっそく Envoy を起動する.ただし,EDS API はまだ存在してなく設定のみとなる.構成図は以下のようになる.

$ docker run --name=api-eds -d \
    -p 9901:9901 \
    -p 80:10000 \
    -v /root/:/etc/envoy \
    envoyproxy/envoy:latest

f:id:kakku22:20191226121518p:plain

Step.3 「Start upstream services」

今回も katacoda/docker-http-server を起動し,個別に接続確認をしておく.

$ docker run -p 8081:8081 -d -e EDS_SERVER_PORT='8081' katacoda/docker-http-server:v4

$ curl http://localhost:8081 -i
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 36
Server: Werkzeug/0.15.4 Python/2.7.16
Date: Thu, 26 Dec 2019 00:00:00 GMT

35c4aef0-e0df-46ac-bd3b-8b12cb3741c9

ただし,コンテナイメージのタグが v4 になっていて,サイズも大きく,何やら Python 環境が入っているけど,詳細は書かれていなかった.あと,環境変数 EDS_SERVER_PORT が追加されている点も異なる.

$ docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" katacoda/docker-http-server
REPOSITORY                    TAG                 SIZE
katacoda/docker-http-server   v4                  932MB
katacoda/docker-http-server   latest              7.59MB

Envoy のログを確認すると,まだ EDS API に接続できず,エラーになっている.

$ docker logs xxx
[2019-12-26 00:00:00.000][000008][warning][config] [bazel-out/k8-opt/bin/source/common/config/_virtual_includes/http_subscription_lib/common/config/http_subscription_impl.h:101] REST config update failed: fetch failure

構成図は以下のようになる.

f:id:kakku22:20191226182058p:plain

Step.4 「Start EDS」

次にやっと EDS API を起動する.今回は Python で実装されたプロトタイプを使うことになり,コードは GitHub に公開されている.ここで Envoy と EDS API の接続はできたけど,まだエンドポイントが設定されてなく,実際に Envoy にリクエストを送ると no healthy upstream と返ってくる.挙動としては期待値と言える.

$ docker run -p 8080:8080 -d katacoda/eds_server;

$ curl http://localhost
no healthy upstream

構成図は以下のようになる.

f:id:kakku22:20191226182113p:plain

Step.5 「Add endpoint to EDS」

次に EDS API に curl でリクエストを送り,エンドポイントを追加する.今回は既に起動済の katacoda/docker-http-server にルーティングする.

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{
  "hosts": [
    {
      "ip_address": "172.18.0.3",
      "port": 8081,
      "tags": {
        "az": "us-central1-a",
        "canary": false,
        "load_balancing_weight": 50
      }
    }
  ]
}' http://localhost:8080/edsservice/myservice

Envoy にリクエストを送ると,同じ結果が返ってくる.

$ curl http://localhost
35c4aef0-e0df-46ac-bd3b-8b12cb3741c9

$ curl http://localhost
35c4aef0-e0df-46ac-bd3b-8b12cb3741c9

次に katacoda/docker-http-server を4コンテナ追加し,計5コンテナになる.

$ for i in 8082 8083 8084 8085
  do
      docker run -d -e EDS_SERVER_PORT=$i katacoda/docker-http-server:v4;
      sleep .5
done

追加した4コンテナをエンドポイントとして EDS API に追加する.なお,設定している load_balancing_weight はドキュメントを読むと「合計値をエンドポイント数で割る」と書いてあるため,5コンテナに均等分散するという意味になる.

www.envoyproxy.io

$ curl -X PUT --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{
    "hosts": [
        {
        "ip_address": "172.18.0.3",
        "port": 8081,
        "tags": {
            "az": "us-central1-a",
            "canary": false,
            "load_balancing_weight": 50
        }
        },
        {
        "ip_address": "172.18.0.5",
        "port": 8082,
        "tags": {
            "az": "us-central1-a",
            "canary": false,
            "load_balancing_weight": 50
        }
        },
        {
        "ip_address": "172.18.0.6",
        "port": 8083,
        "tags": {
            "az": "us-central1-a",
            "canary": false,
            "load_balancing_weight": 50
        }
        },
        {
        "ip_address": "172.18.0.7",
        "port": 8084,
        "tags": {
            "az": "us-central1-a",
            "canary": false,
            "load_balancing_weight": 50
        }
        },
        {
        "ip_address": "172.18.0.8",
        "port": 8085,
        "tags": {
            "az": "us-central1-a",
            "canary": false,
            "load_balancing_weight": 50
        }
        }
    ]
    }' http://localhost:8080/edsservice/myservice

実際に Envoy にリクエストを送ると,うまく分散されている.エンドポイント数が少ないからかもしれないけど,たまに2回連続で同じコンテナからレスポンスが返ることもあった.

$ while true; do curl http://localhost; sleep .5; printf '\n'; done
dbc1fe2f-7bfa-44e6-8fff-52a57190de95
28959add-c2df-47cf-8045-c09911a9e913
8d366f05-18d8-4f72-936f-ab282615f6ca
f7f7599d-1be9-480c-96e9-6619b2843499
35c4aef0-e0df-46ac-bd3b-8b12cb3741c9

構成図は以下のようになる.

f:id:kakku22:20191226182130p:plain

Step.6 「Delete Endpoint」

今度は EDS API から5コンテナの情報を消す.すると,Step.4 と同じ no healthy upstream となった.

$ curl -X PUT --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{
  "hosts": [  ]
}' http://localhost:8080/edsservice/myservice

$ curl -v http://localhost
no healthy upstream

Step.7 「Disconnect EDS server」

最後は Step.5 と同じくもう1度 EDS API にエンドポイントを追加してから,EDS API を停止する.

$ docker ps -a | awk '{ print $1,$2 }' | grep katacoda/eds_server  | awk '{print $1 }' | xargs -I {} docker stop {};
$ docker ps -a | awk '{ print $1,$2 }' | grep katacoda/eds_server  | awk '{print $1 }' | xargs -I {} docker rm {}

一度 Envoy 側で EDS API からエンドポイント情報を取得した場合,Envoy 側には影響なしで使えることがわかる.

$ while true; do curl http://localhost; sleep .5; printf '\n'; done
35c4aef0-e0df-46ac-bd3b-8b12cb3741c9
dbc1fe2f-7bfa-44e6-8fff-52a57190de95
8d366f05-18d8-4f72-936f-ab282615f6ca
f7f7599d-1be9-480c-96e9-6619b2843499
28959add-c2df-47cf-8045-c09911a9e913

構成図は以下のようになる.

f:id:kakku22:20191226182144p:plain

まとめ

  • 「Try Envoy」のコンテンツ「API Based Dynamic Routing Configuration」を試した
  • EDS (Endpoint Discovery Service) API を REST で起動し,エンドポイントの dynamic な反映を体験できた
  • katacoda/docker-http-server:v4 の解説がなかったり,gRPC の xDS API の体験がなかったり,もう少し学びたかった点もある

引き続き,進めていくぞ!

Try Envoy 関連