ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

英語進捗 2020 Jan.

とりあえず継続することができるようになってきたので、質と量を少しずつ伸ばしたい。

アメリカ人がなぜ英語喋れるかって1日中英語使ってるからやで理論に従うと、1日25分じゃ死んでも追いつけない。いや追いつけないんだろうけど、成長のスピードは遅い。3時間ぐらいにどうにか持っていきたい。

これ毎日捻出するの結構キツいな。

abceed は隙間時間と通勤電車といっても60分は行かないので、もっと隙間時間作るか、意図してやらないといけない。

シャドーイングに関しては、散歩しながらやる。痩せる気はしないけど一応健康意識が 1mm ぐらいはある。

最後技術学習と合わせてやらないとなので、まぁこれは英語というよりは普通に勉強と思えばできなくはない。

とにかく歩きと隙間時間を使って1時間ぐらい今まで浮いていた時間を活用しつつも、家で向き合ってやる時間はどうしても必要になってくる。たった3時間確保でもこれだけ難しいの厳しいな。

でも abceed すごい楽しいので TOEIC 受けるの楽しみになってきた。

シャドーイングの課題が、やっぱりいきなり聞くだけだとなかなか理解できないので、最初に script を読んでだいたい中身を理解してから聴くというのをやりたいが、それをやる前に移動してしまいできてないという状態になっていてよくない。なんとかしたい。

Business Trip 2020 Jan For Jakarta

Business Trip で Quipper の Jakarta Office に行ってきた。

10月に @yuya-takeyama が Enginering Manager になって、僕と rbmrclo が @sre-global という担当になって、yuya が「行ってみるのもありなんでは?」と言ってくれたのち robbie がいい感じの proposal を書いてくれて実現した。僕1人じゃこの提案はできなかったと思うので本当に2人に感謝している。

旅の前半の Jakarta 編が終わったので、次の Manila へ向かう飛行機の中で書き留めておく。

この旅の目的は SRE と Developer の関係性の再構築・強化だ。これだとざっくりしているが、より詳しく言うと、SREs は全員 Tokyo にいて、現状 PH/ID/MX Branch には SRE が存在しない。Developer と SRE は密接に Communication が求められるのに関わらず、おもな Communication 手段は Slack / GitHub Issue のみであり、頻繁に Communication を取れてるとはいいづらい。(関係が悪いとも思っていないが)

実施した Program 的なものは以下

  • Global SRE Missions for 2020
    • SRE Concepts (from robbie)
      • Emergency Response
      • Capacity Planning
      • Observability
    • Future of Global SRE (from robbie)
    • SLIs/SLOs (from me)
    • Delegation Infrastructure Management(Terraform) to Devs (from me)

最初に上記のようなトピックの Abstruct を説明するセッションを 1h とった。その後の Q/A をもとに、より詳細なセッションを別途持った。

  • SLO/SLI Engineering OKR alignment
  • How to handle too much request by online try-out
  • 1 on 2 / sre-global vs Engineering Managers
    • Explain Mission Control Program
    • What is your expectation for sre-global?
  • Sharing On-boarding processes

基本的な思想として、SREs は必要悪で、全員で Site Reliability Enginering ができるのが理想だと思っている。もちろん SREs の仕事は Platform 開発や OnCall(うちは OnCall Rotation を持っていないが)、Architect, Performance のような Production に関わることを含むので、すぐにはゼロにはできないが、SRE の思想や、やってる内容は少しずつ Product Team に委譲し、Product Team が Market に対して爆速で仮説検証をできるように(しかも信頼性も Keep しながら!Reliability is a feature である)していきたいと思っている。

そういった話をもとに、いままさに導入して試行錯誤の最中である SLI/SLO の導入説明や、robbie が提案する Mission Control Program - Google が提唱した、Developer が短期的に SRE Team に移動して知見を得て元の Team に戻っていく Program - の提案や実現性のヒアリングを行なった。また、 @suzuki-shunsuke が爆速で進めてきた Terrraform を state 分割して各チームにオーナーシップを持たせる話も説明した。

これらの説明を建設的に、前向きに彼らは受け取ってくれた。僕が1番驚いたのは Indonesia Team の Product Management だ。Product Manager の rofiqi が本当にしっかりしていて、Indonesia Branch の Product / Area ごとに、さらに細分化された Team ごとに 達成度合いを OKR を運用しており、その説明もとてもわかりやすかった。

SLI/SLO の導入の部分で、OKR にどう Align していけば良いか?という質問が行われたので、追加のセッションで Indonesia Team の OKR System をそもそもどのように運用しているのかを説明してもらったあと、基本的な SLI/SLO の話や原則、ポイントを簡単に説明したのち、最終的には「君らにまかせる」という回答をした。

彼らが OKR として採用している Key Result は Product Team であれば獲得した Paid User 数だったり、学習時間だったりするし、Enginering Team であれば 開発速度や、バグの数などがあった。Product によってはそこで SLI として採用している Availability(uptime) があったりした。

もちろん信頼性目標を組み込むのは悪くはないが、守れないと必ずしも"悪い"というものではないので、僕としては若干疑問は残るが、運用するのは彼らなので、彼らのやりやすい方法でやれるならそれが一番いい。

彼らが優秀であることと、これまでも試行錯誤を Indonesia の一部のチームではじめていたこともあり、わりと共通認識は取れていたと思う。SLI/SLO に関しての重要なポイントも説明し、理解してくれたようだったので、Indonesia に関してもまた SLI/SLO を「当たり前」のものとして運用できる未来は見えたのでよかった。

SLI/SLO に関しては僕が Lead しているのでそれもまた直接説明できてよかった。あと Coursera のコースとってるおかげがわりとぶつけで説明したけどうまく話せたのもよかった。

robbie が導入した Mission Control Program に関しても Enginering Manager のほぼ全員が(実際にそれをやれるかの How の部分には疑問が残るとはいえ、)方向性には同意してくれたようでよかった。

また、Enginering Manager との 1 on 2 をやる際に、せっかくのチャンスなので SRE(Global) に対する期待値を聞いてみた。

これは近々 Developer に向けて、SRE がやってきているいろいろの Feedback をもらうための Survey をやるつもりだが、その先駆けに Simple な質問を1つだけした。

目指す姿としては全 Product Team の全員が Site Reliability Enginering をできる、あるいは Team 内に mini-SRE がいてそれを Lead してくれるような形だが、少なくとも半年以上は先の話であり、現状は remote で Tokyo の SRE-global と各地の Developer で協力してやっていく必要がある。

それをもっとうまくやるためにも、期待値調整をしてみた。みんないろいろ思うところがあったみたいで、聞いてよかった。

例えば、あまり普段関わりのない Android Team の Enginering Manager と話したが、Staging 環境の安定性を1番にあげていた。あまり使ってない Endpoint があるからそれを調べられないか、みたいな話とか。話してみれば今すぐできることが出てくるので会話は大事だ。

またある Enginering Manager は Infrustructure / Platform の Interface をもっと多く開発してくれると嬉しいということだった。Learning Curve を下げつつ、かつ Secure にいろんなものを、SRE に頼まずに調達できる世界は目指している世界なので、そのあたりの認識が一致していることがわかってよかった。

これも思いつきだが、Global Team でも OnBoarding はちゃんと行われていて、日本での OnBoarding の状況を紹介できたのもよかった。たくさん質問がでてみんな興味を持ってくれたようだった。

半分冗談だが、初日の Dinner は「ここにいる全員 SRE だ!写真とるぞ!」って言って写真とったのすごいよかった。

f:id:take_she12:20200112112503j:plain
Global SRE class picture of 2020, we grew from 2 members to 17 members in 1 night. 🥰

たった3日間だったが、ふだんの仕事もしつつ(いろいろやむなしで Production の Operation もあった :sweat_smile: )多くの meeting を持ち(せっかくきてるからね!)非常に密度の濃い時間を過ごせたと思う。

こういう、「必要性を定量的に示すことは難しいが、やってみると確実に効果がある」類のことに、出張としていける組織でよかったと思う。

普段は Slack の向こう側で、slack id でしか認識していなかった彼らと直接話せて本当によかった。Lunch や Dinner にも連れ出してくれて本当にいいやつらだ。たくさん彼ら彼女らのことが好きになりました。


さてさて熱の冷めぬうちにやりたいなと思ったことを書いておく。

  • robbie との 1on1 を weekly か byweekly でもつ。彼とは行きで同じ便だったこともあって、わりと長い時間2人で仕事のことに関わらずいろんなことを話して、普段から隣に座ってはいるものの、もっと多くのことを話したほうがいいと感じた。(彼が東京にきてもう半年以上経ってるのにこのザマである!:sweat_smile:)
  • Devs との StudySession や Introduction の類を Global でも Zoom / Hangout を使ってやる。日本側ではカジュアルにやってるので同じことをできないわけがない。(いや、わけは英語力というものがあるんだけどね。まさに言い訳である。)
  • 今後は(現在議論中に事情により)仕事で日本と Global の Devs が関わる機会は徐々に減ってくる。しかし技術的な Knowledge Share が重要なことは変わりがない(むしろ相対的に重要度は増す)以前やっていた Technical な Study Session をもう一度復活させるもありかもなぁ、みたいな話をした。でもそれを僕が Lead するにはあと少し勇気が足りない。機運待ち。
  • Global の Blog ちゃんと書いていきたい。と3ヶ月前から言ってるんだができてない!

このへんのことは前期(2019/3Q)で立てた目標にも入っていたりするんだけど、その Motivation があらためて強化された機会だったということで、今Q も目標にしてやっていこうかな。


あとはこの旅で robbie が彼にしか出せない Value を出しまくってるわけで、自分はどうしていくかなぁと彼と最終日バーで飲みながら考えたりもした。もちろん今の SRE Team は全得意領域をそれぞれ持っていてそれぞれ Value を出している。「で、お前はどうなん?Value 出してるん?」ってもう1人の僕に聞かれている。

role 的な点で他のメンバーと比べて unique なのは global 担当でありながら日本人であるということで、その両者のシナジーを作るだとか、うまいこと応用を聞かせるとか、輸入したり輸出したりできるようにするとか、そういうところなのかなぁ、とぼんやり思ったりするところで終わっている。いや、Technical に突き抜けいよと言われる気もするが、いい意味でも悪い意味でも技術(の内容)にこだわりがないので半ば諦めている。Production Reliability に関する Culture Making の部分はこれからも引き続きやっていく。

やりたいことはたくさんあるので、いかにやらないことを決めるのが目下の課題かなぁなんてことも思ったりした。

とりとめない話でした。Manila もいい時間が過ごせるといいな。

LEARN ENVOY - Dynamic Configuration / Service Discovery Integration

www.envoyproxy.io

(ちゃんとした翻訳・要約ではなく、読みながらのメモです)

  • envoy は様々な Service Discovery Software と Integrate できる
  • envoy の core concept は data plane を controll plane から分離し、control plane から source of truth となる configuration を 変更できることである
  • まずは control plane が service discovery に接続できる必要があり、それは以下の 3 step に分けられる

  • Decide on a control plane implementation

  • Publish service definitions to Envoy clusters
  • Publish hosts/containers/instances to Envoy endpoints

いうても control plane まだないぞ、みたいな気持ちで続きを読んでいく。

いかなる control plane も Envoy v2 xDS APIs を実装する必要がある。

www.envoyproxy.io

istio の場合 pilot がそれに該当する。

その後の CDS / EDS の話ははいという感じ。

最後に、Service Discovery はめちゃくちゃたくさんの問い合わせがくるのでどう分割するかという話。データセンター、地域で分割したり、サービスのニーズによって分割したり。


さて、まだ Control Plane を持っていないのだが、現状 Kubernetes 上で Service Discovery は誰が担っているかというと、k8s service になる。

k8s service が 実際に転送先の pod の IP の list を持っていて、pod 生き死にすると、新しい endpoint を service が知るので、envoy cluster 側は 転送したい cluster IP だけ知っていればよく、かつそれは kube-dns で引ける。

そう考えるとこのパターンだと service discovery という感じではなく、load balancing は service が行うことになるので、envoy としては cluster が単一の宛先を覚えてるだけですね。

istio が入るとこのへんどう変わってくるんだろうか。なんか envoy だけまずは知ろうとしてるけど早くも control plane の話出てきたし順番間違ったかな。まぁこのまま進むけど。

LEARN ENVOY - Getting Started / Getting Help

www.envoyproxy.io

困ったときログレベル変えられるようにしとこうと。

これね。

-l <string>, --log-level <string> --log-path <path string>

何をいれりゃいいんだと思って、envoy の help みる。

root@d21849f0ad80:/# envoy -h

USAGE:

   envoy  [--disable-extensions <string>] [--use-fake-symbol-table <bool>]
          [--cpuset-threads] [--enable-mutex-tracing]
          [--disable-hot-restart] [--max-obj-name-len <uint64_t>]
          [--max-stats <uint64_t>] [--mode <string>]
          [--parent-shutdown-time-s <uint32_t>] [--drain-time-s <uint32_t>]
          [--file-flush-interval-msec <uint32_t>] [--service-zone <string>]
          [--service-node <string>] [--service-cluster <string>]
          [--hot-restart-version] [--restart-epoch <uint32_t>] [--log-path
          <string>] [--log-format-escaped] [--log-format <string>]
          [--component-log-level <string>] [-l <string>]
          [--local-address-ip-version <string>] [--admin-address-path
          <string>] [--reject-unknown-dynamic-fields]
          [--allow-unknown-static-fields] [--allow-unknown-fields]
          [--config-yaml <string>] [-c <string>] [--concurrency <uint32_t>]
          [--base-id <uint32_t>] [--] [--version] [-h]


Where:

   --disable-extensions <string>
     Comma-separated list of extensions to disable

   --use-fake-symbol-table <bool>
     Use fake symbol table implementation

   --cpuset-threads
     Get the default # of worker threads from cpuset size

   --enable-mutex-tracing
     Enable mutex contention tracing functionality

   --disable-hot-restart
     Disable hot restart functionality

   --max-obj-name-len <uint64_t>
     Deprecated and unused; please do not specify.

   --max-stats <uint64_t>
     Deprecated and unused; please do not specify.

   --mode <string>
     One of 'serve' (default; validate configs and then serve traffic
     normally) or 'validate' (validate configs and exit).

   --parent-shutdown-time-s <uint32_t>
     Hot restart parent shutdown time in seconds

   --drain-time-s <uint32_t>
     Hot restart and LDS removal drain time in seconds

   --file-flush-interval-msec <uint32_t>
     Interval for log flushing in msec

   --service-zone <string>
     Zone name

   --service-node <string>
     Node name

   --service-cluster <string>
     Cluster name

   --hot-restart-version
     hot restart compatibility version

   --restart-epoch <uint32_t>
     hot restart epoch #

   --log-path <string>
     Path to logfile

   --log-format-escaped
     Escape c-style escape sequences in the application logs

   --log-format <string>
     Log message format in spdlog syntax (see
     https://github.com/gabime/spdlog/wiki/3.-Custom-formatting)

     Default is "[%Y-%m-%d %T.%e][%t][%l][%n] %v"

   --component-log-level <string>
     Comma separated list of component log levels. For example
     upstream:debug,config:trace

   -l <string>,  --log-level <string>
     Log levels:
     [trace][debug][info][warning][error][critical][off]

     Default is [info]

   --local-address-ip-version <string>
     The local IP address version (v4 or v6).

   --admin-address-path <string>
     Admin address path

   --reject-unknown-dynamic-fields
     reject unknown fields in dynamic configuration

   --allow-unknown-static-fields
     allow unknown fields in static configuration

   --allow-unknown-fields
     allow unknown fields in static configuration (DEPRECATED)

   --config-yaml <string>
     Inline YAML configuration, merges with the contents of --config-path

   -c <string>,  --config-path <string>
     Path to configuration file

   --concurrency <uint32_t>
     # of worker threads to run

   --base-id <uint32_t>
     base ID so that multiple envoys can run on the same host if needed

   --,  --ignore_rest
     Ignores the rest of the labeled arguments following this flag.

   --version
     Displays version information and exits.

   -h,  --help
     Displays usage information and exits.


   envoy

はい。

起動オプションとして設定できるので、時がきたら使ってみる、でいいかな。

LEARN ENVOY - Getting Started / Routing Basics

www.envoyproxy.io

Routing components

Route

A route is a set of rules that match virtual hosts to clusters and allow you to create traffic shifting rules. Routes are configured either via static definition, or via the route discovery service (RDS).

仮想ホスト名を clusters に一致させるルールセット。static に設定することも、route discovery service を使うこともできる。

Cluster

A cluster is a group of similar upstream hosts that accept traffic from Envoy. Clusters allow for load balancing of homogenous service sets, and better infrastructure resiliency. Clusters are configured either via static definitions, or by using the cluster discovery service (CDS).

upstream の host group のこと。こちらも static に設定することも、cluster discovery service を使うこともできる。

Listener

A listener is a named network location (e.g., port, unix domain socket, etc.) that can accept connections from downstream clients. Envoy exposes one or more listeners. Listener configuration can be declared statically in the bootstrap config, or dynamically via the listener discovery service (LDS).

downstream からの通信を受け付ける君。static にも dynamic にもできる。

Defining Routes

前回の sample を参照する。domain がワイルドカードになっているが、 specific にしてもいい。foo.example.com みたいな。host header が残ってれば。

virtual_hosts:
  - name: backend
    domains:
    - "*"

clusters definition は特にコメントなし。

Configuring listeners

これもシンプルなので特に気になるところはないかな。

          http_filters:
          - name: envoy.router
            config: {}

ここには何が入るんだろ。

envoy.router はこれ。

www.envoyproxy.io

なるほど、timeout とか retry とかを header に埋めて設定できるんですね。

filter.network.HttpFilter は他にこんなやつがある。

envoy.buffer
envoy.cors
envoy.fault
envoy.http_dynamo_filter
envoy.grpc_http1_bridge
envoy.grpc_json_transcoder
envoy.grpc_web
envoy.health_check
envoy.lua
envoy.rate_limit
envoy.router

www.envoyproxy.io

おわり

おわり

とにかく公式ドキュメントが頼りになりすぎてよい。

LEARN ENVOY - Getting Started / On Your Laptop

www.envoyproxy.io

動かした。つまりこういうこと。

f:id:take_she12:20200105214208p:plain

補足

  • service[12]-container において、envoy process と flask process の group は表現できなかった
  • なんか最初の user -> front-envoy のフローで http スキーマのあとの // が消えて斜線になってしまった
  • front の cluster からの通信では、docker-compose の alias で service[12] で 名前解決している
  • listen してる port とか表現できてないけど通信の URL でわかってほしい
  • 肝心の match prefix service/1 の最後の数字が消えてる。。。
  • admin port は省略している。でも 8001 で browser でアクセスするといろんな情報みれて便利

感想

スッと動いて便利。最初の最初にこれやるのはよさそう。

UML

最近また plantuml 使いはじめた。たのしい。こういう Editor で。

PlantUML Editor

uml
@startuml

package "front-envoy-container" {
    [front-envoy-listener]
  [service1-cluster]
  [service2-cluster]
}

package "service1-container" {
    [service1-listener]
  [service1_local_service-cluster]
  [service1_flask]
}

package "service2-container" {
    [service2-listener]
  [service2_local_service-cluster]
  [service2_flask]
}

[user] --> [front-envoy-listener] : "http://localhost:8000/service/1 or http://localhost:8000/service/2"
[front-envoy-listener] --> [service1-cluster] : match: prefix: "/service/1"
[front-envoy-listener] --> [service2-cluster] : match: prefix: "/service/2"
[service1-cluster] --> [service1-listener] : http://service1/service/1:80
[service2-cluster] --> [service2-listener] : http://service2/service/2:80
[service1-listener] --> [service1_local_service-cluster] : match: prefix: "/service"
[service2-listener] --> [service2_local_service-cluster] : match: prefix: "/service"
[service1_local_service-cluster] --> [service1_flask] : http://127.0.0.1:8080
[service2_local_service-cluster] --> [service2_flask] : http://127.0.0.1:8080
@enduml

Service 間通信を Envoy 経由で行い、Metrics を取得する

はじめに

現職では Application は すべて Kubernetes 上で動いている。その場合、インターネットからの通信経路は以下のようになる。

Internet -> Reverse Proxy(Nginx) -> Service Router(Nginx) -> Kubernetes Service -> Pod

で、後半の Service Router から先が Kubernetes Cluster となっている。Type: LoadBalancer で 受けたあと、forwarding して service-router と呼んでいる Nginx に飛ばしている。

現状、一部で Microservices が動いていたり、Distributed Monolith を Microservices に切ろうとしていたりするが、Service Router 以降の、Pod(Application)同士の通信は Kubernetes Service 作成時に kube-dns に自動的に登録される domain 名で、直接通信している。

インターネット経由でのアクセスの場合は Reverse Proxy や Service Router での Metrics は取得できるが、そうではない、Service 間の通信は現状 Application Layer でしか Metrics を取得できないという問題がある。

それの何が問題かというと、Microservices 単位で SLI/SLO を設定するときに困る。NewRelic などの APM でも取得できるのだが、現状 SLI/SLO は DataDog Widget で管理しているので、なんにせよ Datadog のほうで SLI の計測は行いたいのだ。

どうする

直接通信する代わりに、間に Envoy を Proxy として挟む。

Before

Service A --(http://service_b)--> Service B

After

Service A --(http://localhost:10000)-->Envoy-->(http://service_b)-->Service B

Service A の Container と Envoy の Container は同一 Pod として動いている。いわゆる Sidecar である。

具体的な設定差分はこんな感じになる。

configmap でも Deployment の env でもなんでもいいが、(最悪直接書いてもいいが)Service A の Container の環境変数に、Service B への URL をいれる。

            - name: SERVICE_B_URL
              value: localhost:10000

Envoy の config は以下のようになる。

apiVersion: v1
kind: ConfigMap
metadata:
  name: "${SERVICE_NAME}-envoy-config"
data:
  envoy.yaml: |
    admin:
      access_log_path: /dev/stdout
      address:
        socket_address: { address: 127.0.0.1, port_value: 9901 }
    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
              codec_type: AUTO
              route_config:
                name: local_route
                virtual_hosts:
                - name: service1_grpc
                  domains: ["*"]
                  routes:
                  # Envoy admin endpoints
                  - match: { prefix: "/server_info" }
                    route: { cluster: envoy_admin }
                  - match: { prefix: "/stats" }
                    route: { cluster: envoy_admin }
                  # HTTP endpoint
                  - match: { prefix: "/v1/example/todo" }
                    route: { cluster: service_b_http }
                  - match: { prefix: "/v2/example/todo" }
                    route: { cluster: service_b_http }
              http_filters:
              - name: envoy.router
                config: {}
      clusters:
      - name: service_b_http
        connect_timeout: 5s
        type: STRICT_DNS
        lb_policy: ROUND_ROBIN
        dns_lookup_family: V4_ONLY
        load_assignment:
          cluster_name: service_b_http
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: service_b
                    port_value: 80
      - name: envoy_admin
        connect_timeout: 0.250s
        type: LOGICAL_DNS
        lb_policy: ROUND_ROBIN
        hosts:
        - socket_address:
            protocol: TCP
            address: 127.0.0.1
            port_value: 9901

実はこれ以外にも gRPC の Client Load Balancing としても Envoy を以前から利用していた。今回は http を proxy することで metrics を取得した。envoy の metris はこの設定のように /stats をあけておいて、annotation を設定すればよい。

docs.datadoghq.com

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: "${SERVICE_NAME}"
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: "${SERVICE_NAME}"
  template:
    metadata:
      name: "${SERVICE_NAME}"
      labels:
        app: "${SERVICE_NAME}"
        version: v0.1
      annotations:
        ad.datadoghq.com/${SERVICE_NAME}-envoy.check_names: |
          ["envoy"]
        ad.datadoghq.com/${SERVICE_NAME}-envoy.init_configs: |
          [{}]
        ad.datadoghq.com/${SERVICE_NAME}-envoy.instances: |
          [
            {
              "stats_url": "http://%%host%%:10000/stats"
            }
          ]

Envoy の config

以下のドキュメントを参考にすれば簡単に動いた。

www.envoyproxy.io

google.com へ https で proxy してる部分を http にしただけだ。

ただ、知っておくべき概念だけ簡単に復習しておく。

static_resources の対になるところは dynamic_resources だと思うが、それはまたそのうち。

Listeners

listeners (Listener) Static Listeners. These listeners are available regardless of LDS configuration.

はい。

www.envoyproxy.io

Listeners 以下の設定例はこれだが、そもそも何かというと、名前の通り、どういう基準で通信を受け付けるか、ということを記述するところである。

address のみが Required になっている。

Listener は複数設定できる。今回の例では address はこうなっている。

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

任意の IP Address の port 10000 で受け付ける。

さらに、条件を絞るために filter_chains という設定がある。

www.envoyproxy.io

filter_chain_match でその criteria を記載する。

www.envoyproxy.io

ここの apply ordering は重要そうである。

The following order applies:

  1. Destination port.
  2. Destination IP address.
  3. Server name (e.g. SNI for TLS protocol),
  4. Transport protocol.
  5. Application protocols (e.g. ALPN for TLS protocol).
  6. Source type (e.g. any, local or external network).
  7. Source IP address.
  8. Source port.

今回はこの filter_chains ではなく、 filters を使っている。

Order matters as the filters are processed sequentially as connection events happen とある通り順番重要。

さらに typed_config に潜っていきたいが、それ自体の説明はない。

もう一度 Getting Started の config を見てみると、

listeners:
- name: listener_0
  address:
    socket_address: { address: 0.0.0.0, port_value: 10000 }
  filter_chains:
  - filters:
    - name: envoy.http_connection_manager
      typed_config:
        "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
        stat_prefix: ingress_http
        codec_type: AUTO
        route_config:
          name: local_route
          virtual_hosts:
          - name: local_service
            domains: ["*"]
            routes:
            - match: { prefix: "/" }
              route: { host_rewrite: www.google.com, cluster: service_google }

http_connection_manager とやらを使っていて、そこの設定を使っているのだろう。

http_connection_manager とは何かというと

www.envoyproxy.io

やばい!飽きてきた。まぁこんな風に filters として使えるものがプロトコルによって異なるものを採用できる、プラガブルになっていることがわかった。

Network Filters の一覧はこのへん。

www.envoyproxy.io

例えば MySQL とか MongoDB とか Redis とかありますね。これらへの接続も Envoy Proxy 経由で行うことでいい感じにすることができそう。今は掘らない。

Clusters

Clusters は Listeners で受けたものを流す対象。Listener は Clusters のみを知っていればよく、実際の通信先は Clusters がいい感じに知っているという仕組みになっているようだ。Service Discovery を Clusters が担っている。

www.envoyproxy.io

基本的には通信を proxy する upstream やらをどうやって discovery するかを書けば良さそうだ。

もう一度 Getting Started の Example から。

clusters:
- name: service_google
  connect_timeout: 0.25s
  type: LOGICAL_DNS
  # Comment out the following line to test on v6 networks
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: service_google
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: www.google.com
              port_value: 443
  transport_socket:
    name: envoy.transport_sockets.tls
    typed_config:
      "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
      sni: www.google.com

ここで多分きっと重要な type を深掘りしておこう。

type (Cluster.DiscoveryType) The service discovery type to use for resolving the cluster.

Only one of type, cluster_type may be set.

service discovery type だと言っている。

さて詳細はこちらの Document にのっている。

www.envoyproxy.io

5種類の Type がある。

Static

Static is the simplest service discovery type. The configuration explicitly specifies the resolved network name (IP address/port, unix domain socket, etc.) of each upstream host.

その名の通り IP Address なり Range なりを Static に記述するタイプですね。具体的な設定例はこんな感じになりそう。

  clusters:
    - name: service_ipsum_echo_1
      connect_timeout: 0.25s
      http_protocol_options: {}
      lb_policy: ROUND_ROBIN
      type: STATIC
      hosts:
        - socket_address: {address: 172.16.221.30, port_value: 8000 }

Strict DNS

When using strict DNS service discovery, Envoy will continuously and asynchronously resolve the specified DNS targets. Each returned IP address in the DNS result will be considered an explicit host in the upstream cluster. This means that if the query returns three IP addresses, Envoy will assume the cluster has three hosts, and all three should be load balanced to. If a host is removed from the result Envoy assumes it no longer exists and will drain traffic from any existing connection pools. Note that Envoy never synchronously resolves DNS in the forwarding path. At the expense of eventual consistency, there is never a worry of blocking on a long running DNS query.

決められたタイミングで DNS 問い合わせを行い、その結果を採用する。3つの IP アドレスが解決されれば 3つホストがあると認識するし、いなくなればいなくなったと判断する。毎回の DNS Query にて service discovery を行うように読み取れる。ふむという感じ。

Logical DNS

Logical DNS uses a similar asynchronous resolution mechanism to strict DNS. However, instead of strictly taking the results of the DNS query and assuming that they comprise the entire upstream cluster, a logical DNS cluster only uses the first IP address returned when a new connection needs to be initiated. Thus, a single logical connection pool may contain physical connections to a variety of different upstream hosts. Connections are never drained. This service discovery type is optimal for large scale web services that must be accessed via DNS. Such services typically use round robin DNS to return many different IP addresses. Typically a different result is returned for each query. If strict DNS were used in this scenario, Envoy would assume that the cluster’s members were changing during every resolution interval which would lead to draining connection pools, connection cycling, etc. Instead, with logical DNS, connections stay alive until they get cycled. When interacting with large scale web services, this is the best of all possible worlds: asynchronous/eventually consistent DNS resolution, long lived connections, and zero blocking in the forwarding path.

非同期的に DNS 問い合わせを行う点は Strict DNS と同じだが、Logical DNS は新規接続時に DNS 問い合わせを行い、得た結果の最初の1つのみを採用して通信を行う。

ポイントは Connections are never drained というところで、一度 Connection を確立した場合、再度問い合わせを行ったりせずにずっとその IP アドレスを使うという点だろう。

イマイチ When interacting with large scale web services, this is the best of all possible worlds なポイントがしっくりきていない。Strict だと Member が変わった時一気に接続先が更新されて Gradually じゃない点がよくないってことなんだろうか、?

Original destination

Original destination cluster can be used when incoming connections are redirected to Envoy either via an iptables REDIRECT or TPROXY target or with Proxy Protocol. In these cases requests routed to an original destination cluster are forwarded to upstream hosts as addressed by the redirection metadata, without any explicit host configuration or upstream host discovery. Connections to upstream hosts are pooled and unused hosts are flushed out when they have been idle longer than cleanup_interval, which defaults to 5000ms. If the original destination address is not available, no upstream connection is opened. Envoy can also pickup the original destination from a HTTP header. Original destination service discovery must be used with the original destination load balancer.

when incoming connections are redirected to Envoy either via an iptables REDIRECT or TPROXY target or with Proxy Protocol

iptables REDIRECT か TPROXY target で受けた場合に機能すると言っている。のでそうなんだろう。Istio とかがやってるのはこれなのかもしれない。わからない。

Endpoint discovery service (EDS)

The endpoint discovery service is a xDS management server based on gRPC or REST-JSON API server used by Envoy to fetch cluster members. The cluster members are called “endpoint” in Envoy terminology. For each cluster, Envoy fetch the endpoints from the discovery service. EDS is the preferred service discovery mechanism for a few reasons:

xDS management server を使って dynamic に routing 先を取得したりするんだろうか。

今後

軽く公式ドキュメント追いかけながらメモしようと思ってはじめたが思いの外長くなったのでこのへんで終わる。

年末に http で通信している Service 間通信に Envoy をいれる PR を出したので年明け早々に Production に出すつもりである。

まだまだ Envoy は入門したばかりなので、今後も定期的に Official Document を見て機能を理解していきたい。具体的には、Learn Envoy をざっと見て、Envoy でできることをざっくり理解する。

www.envoyproxy.io

目下現実的に優先度高くやりたいと思っているのは Circuit Breaking 。さっさと当たり前の世界にしていきたい。あとは Dynamic Configuration もおさえておきたい。

あとは Kakku さんがやってる Try Envoy も一通りみておきたい。

kakakakakku.hatenablog.com

タイトル眺めた感じ。。。どれも見ておいたほうがよさそうだな。

最終的には全 Service 間通信に Envoy を経由するようにしたい。手元でやるには限界がくるので、そのタイミングでより上位の Service Mesh の Software を検討することになると思う。

Envoy でやれることをちゃんと理解した上で、Istio / AppMesh も動作検証していきたい。

追記

タイトルにある通り、ちゃんと Metrics はとれました。

代表的な HTTP での Availability / Latency の SLI となる Metrics がとれたので安心。

f:id:take_she12:20200105183137p:plain