kakakakakku blog

Weekly Tech Blog: Keep on Learning!

「Try Envoy」で Envoy を学ぼう!「Getting Started with Envoy」を試した

Envoy のサイトに「Try Envoy」という学習コンテンツがあり,現在は以下の「計11種類」のシナリオから選んで学べる.実際にはブラウザベースで進められる Katacoda の学習コンテンツが埋め込まれているため,特別な環境構築をせずに進められるのは便利.

  • Getting Started with Envoy(今回紹介する)
  • Migrating from NGINX to Envoy Proxy
  • Migrating from HAProxy to Envoy Proxy
  • Securing traffic with HTTPS and SSL/TLS
  • File Based Dynamic Routing Configuration
  • API Based Dynamic Routing Configuration
  • Detecting Down Services with Health Checks
  • Implementing Blue / Green Rollouts
  • Implementing Metrics and Tracing Capabilities
  • Debugging Envoy Proxy
  • Controlling load balancing policies

f:id:kakku22:20191117162057p:plain

学習コンテンツは「Try Envoy」からアクセスできるけど,当然ながら Katacoda にもある.個人的には画面レイアウトの広さという観点から Katacoda を使うと良いと思う.

www.envoyproxy.io

www.katacoda.com

「Try Envoy」のシナリオを数個試してみて,当然ながら手順通りには動くけど,Envoy 初学者には理解しにくそうな点もあった.そこで,学んだことを整理しつつ,イメージしにくそうな部分をまとめておこうと思う.

Getting Started with Envoy

今回は Envoy に入門するシナリオ「Getting Started with Envoy」を紹介する.Envoy を使ってリクエストを外部サービスに流したり,複数のアプリケーションにパスベースルーティングをする.

www.katacoda.com

手順は以下の「計4種類」ある.サイトに「Estimated Time: 10 minutes」と書いてあるけど,考えながら進めるとすぐに終わらないと思う.時間は参考レベルで見ておくと良さそう.

  • Step.1 「Create Proxy Config」
  • Step.2 「Start Proxy」
  • Step.3 「Admin View」
  • Step.4 「Route to Docker Containers」

Step.1 「Create Proxy Config」

最初は Envoy の挙動を設定する envoy.yaml を作成する.実際にエディタで envoy.yaml を書く場面はなく,手順上で Copy to Editor ボタンをクリックすると自動的にエディタに反映される仕組みになっている.Katacoda 便利!その前に基本的な用語を整理しておく.詳細はドキュメントを参照してもらえればと.

  • Listeners : リクエストを受ける設定
  • Filters : リクエストをフィルタする設定(複数のフィルタを設定できる)
  • Clusters : リクエストを転送する設定

そして envoy.yaml を読み解く前に構成図を整理しておく.コンテナとして起動した Envoy にリクエストを投げると,そのまま Google に転送される.Envoy を試す最も簡単な構成とも言える.

f:id:kakku22:20191117211922p:plain

envoy.yaml のポイントはザッと以下となる.

  • listeners
    • 10000 Port でリクエストを受ける
  • filter_chains
    • route_config にルーティング設定を書く
    • プレフィックスが / の場合に host_rewrite で HTTP Host Header 書き換える
    • service_google Cluster に転送する
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }

    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { host_rewrite: www.google.com, cluster: service_google }
          http_filters:
          - name: envoy.router

  clusters:
  - name: service_google
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    hosts: [{ socket_address: { address: google.com, port_value: 443 }}]
    tls_context: { sni: www.google.com }

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

Envoy で HTTP を制御するフィルタ envoy.http_connection_manager の詳細は以下のドキュメントに書いてある.

www.envoyproxy.io

Step.2 「Start Proxy」

さっそく envoy.yaml を指定して Envoy コンテナを起動する.80 Port にリクエストをすると,Google に転送される.ホスト内だと curl localhost でも良いし,Katacoda だと https://xxx.environments.katacoda.com という一時的な URL も使えるため,どちらも使える.

$ docker run --name=proxy -d \
  -p 80:10000 \
  -v $(pwd)/envoy/envoy.yaml:/etc/envoy/envoy.yaml \
  envoyproxy/envoy:latest

Step.3 「Admin View」

次に Envoy の「Administration interface」機能を試す.UI は直感的に理解しにくく感じたけど,メトリクス/統計情報/ログなど,Envoy の状態を確認するときに使える.

$ docker run --name=proxy-with-admin -d \
    -p 9901:9901 \
    -p 10000:10000 \
    -v $(pwd)/envoy/envoy.yaml:/etc/envoy/envoy.yaml \
    envoyproxy/envoy:latest

f:id:kakku22:20191117181412p:plain

詳しくは以下のドキュメントに書いてある.

www.envoyproxy.io

Step.4 「Route to Docker Containers」

最後は Python アプリケーションと Envoy を組み合わせて使う.docker-compose.yml から構成を読み取ると以下となる.リクエストの URL を判断して service1service2 にパスベースルーティングをしていて,アプリケーション側は環境変数 SERVICE_NAME を表示する程度になっている.

f:id:kakku22:20191117211948p:plain

なお service1service2envoyproxy/envoy-alpine:latest イメージをベースに Python と Flask をインストールしているため,Envoy でよく使われる「サイドカーパターン」になっていない点に注意する.とは言え,今回のように Envoy を試すことが目的の場合は問題ないと言える.

version: '2'
services:

  front-envoy:
    build:
      context: .
      dockerfile: Dockerfile-frontenvoy
    volumes:
      - ./front-envoy.yaml:/etc/front-envoy.yaml
    networks:
      - envoymesh
    expose:
      - "80"
      - "8001"
    ports:
      - "8000:80"
      - "8001:8001"

  service1:
    build:
      context: .
      dockerfile: Dockerfile-service
    volumes:
      - ./service-envoy.yaml:/etc/service-envoy.yaml
    networks:
      envoymesh:
        aliases:
          - service1
    environment:
      - SERVICE_NAME=1
    expose:
      - "80"

  service2:
    build:
      context: .
      dockerfile: Dockerfile-service
    volumes:
      - ./service-envoy.yaml:/etc/service-envoy.yaml
    networks:
      envoymesh:
        aliases:
          - service2
    environment:
      - SERVICE_NAME=2
    expose:
      - "80"

networks:
  envoymesh: {}

front-envoyfront-envoy.yaml を以下に載せる.ポイントは filtersroutes に2種類の prefix が設定されている点で,URL によって転送する Cluster を変えている.

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    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:
              - "*"
              routes:
              - match:
                  prefix: "/service/1"
                route:
                  cluster: service1
              - match:
                  prefix: "/service/2"
                route:
                  cluster: service2
          http_filters:
          - name: envoy.router
            config: {}
  clusters:
  - name: service1
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: service1
        port_value: 80
  - name: service2
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: service2
        port_value: 80
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001

実際に起動すると,期待した通りにリクエストをルーティングできた.

$ docker-compose -f ~/envoy/examples/front-proxy/docker-compose.yml up -d

$ curl localhost:8000/service/1
Hello from behind Envoy (service 1)! hostname: 211d6aaf214c resolvedhostname: 172.19.0.3

$ curl localhost:8000/service/2
Hello from behind Envoy (service 2)! hostname: 30ce9b84004a resolvedhostname: 172.19.0.2

まとめ

  • Envoy のサイトに「Try Envoy」という学習コンテンツがある
    • 現在は「計11種類」のシナリオから選べる
  • 今回は「Getting Started with Envoy」を試した
    • リクエストを外部サービスに流したり,複数のアプリケーションにパスベースルーティングをしたり,基本的な動作確認はできる
  • まだ「Envoy の良さ」を感じられる内容ではなく,引き続き「Try Envoy」を進めていく