ツナワタリマイライフ

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

4月

3月やりたかったことはあんまりできてない。途中から本格自粛になっちゃったねえ。できてないことと関係ないけど。

あんまりやっていくことが定まらない。どこに向かうべきかちょっと見失ってしまった。英語もゾンビ英会話は続けてるし仕事でもちょいちょい英語ミーティングはあるがモチベーションを失いつつある。

4月は次の1年の方向を定める月としたい。

1年後に転するつもりで(するとは言っていない)いろいろ考えたい。カジュアル面談はぼちぼちしていきたい。少なくとも1年以内はないという前提でよければ keep in touch カジュアル面談声かけてください。

いのちだいじにしつつ、少しずつ、少しずつ。進む速度より進む方向をていねいに考えたい。

Envoy の Trafic Mirroring による負荷テストを考える

弊社には以前 Shadow Proxy というものがいた。

qiita.com

当時のモチベーションとして Database への Index 追加等をぶつけでなく事前にやりたい、ということだった。現状は Production database の Snapshot から Develop database を毎晩リストアしており、事前にそちらで実行するようにしている。

2020年の今やるなら Envoy かな、と。

で、ここでモチベーションとしてあげている負荷テストとは特に、Database の Migration のことを考えている。

現状 EC2 上に MongoDB をセルフホストしている。そのためインスタンスのスケールアップが必要なときにはいろいろあって1日ほどかかる。マネージドサービスの Atlas にすればスケールアップも容易で早くなるし、パフォーマンスに関する分析も提供してくれるようだ。

この Atlas に本番環境を移行する前に、Prodcution と同様の Query を実行しても Performance に問題がないことを事前に確認できればいい。


最近、一部のサービスで、Rails Application から MongoDB への接続の間に Envoy を経由するようにした。

    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: service_http
                  domains: ["*"]
                  routes:
                  # Envoy admin endpoints
                  - match: { prefix: "/ready" }
                    route: { cluster: envoy_admin }
                  - match: { prefix: "/stats" }
                    route: { cluster: envoy_admin }
              http_filters:
              - name: envoy.router
                config: {}
      - name: mongo_proxy
        address:
          socket_address: { address: 0.0.0.0, port_value: 27017 }
        filter_chains:
        - filters:
          - name: envoy.mongo_proxy
            typed_config:
              "@type": type.googleapis.com/envoy.config.filter.network.mongo_proxy.v2.MongoProxy
              stat_prefix: mongo_proxy
          - name: envoy.tcp_proxy
            typed_config:
              "@type": type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy
              stat_prefix: mongo_tcp_proxy
              cluster: mongo_proxy
      clusters:
      - name: mongo_proxy
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        load_assignment:
          cluster_name: mongo_proxy
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: mongodb-0
                    port_value: 27017
            - endpoint:
                address:
                  socket_address:
                    address: mongodb-1
                    port_value: 27017
      - 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

MongoProxy filter で metrics をとりつつ、基本的には TCP で proxy しているだけである。そもそも普段、アプリケーションやコマンドラインでは、MongoDB Wired Protocol で MongoDB と通信している。

docs.mongodb.com

Cluster を Load Balancing しているのは Primary と Secondary Node である。

さて、このような状態でどうするか?複数の Cluster を定義し、Mirror したものを別の Cliuster (Atlas) に送り、レスポンスは棄却する、ということがやりたい。

request mirror policies が使えそうだが、これは http だ。

www.envoyproxy.io

残念ながら TCP Layer でこれらは実現できそうにない。

Envoy Slack で "The upstream envoy filter is only for http right now" という回答を得た。やむなし。

とすると http でやるしかない。

こういう例。

blog.markvincze.com

どこでミラーするのかあらためて考える。

f:id:take_she12:20200404104139p:plain

graph TD
  A[Nginx] -->|http://service-a.quipper.com| B[k8s service]
subgraph Kubernetes
  B[Nginx] -->|http://service-a| C[k8s service]
  C -->D[Rails Application Pods]
subgraph Pod
  D -->|mongo://localhost:27017| E[Envoy]
end
end
  E -->|mongo://mongo-fqdn:27017| F[MongoDB]

Front Proxy 相当の動きをしている、Kubernetes 内の Nginx でやるしかあるまい。

その場合、この Nginx を Envoy にすぐに置き換えることは難しいので、これまた Sidecar として Envoy を使うことになるだろう。

こうなる。

f:id:take_she12:20200404105626p:plain

graph TD
  A[Nginx] -->|http://service-a.quipper.com| B[k8s service]
subgraph Kubernetes
subgraph Pod-Nginx
  B[Nginx] -->|http://localhost| C[Envoy]
end
  C[Envoy] -->|http://service-a| D[k8s service a]
  D -->E[Rails Application Pods]
subgraph Pod-Rails-a
  E -->|mongo://localhost:27017| F[Envoy]
end
  C -->|Mirroring| H[k8s service b]
  H -->I[Rails Application Pods]
subgraph Pod-Rails-b
  I -->|mongo://localhost:27017| J[Envoy]
end

end
  F -->|mongo://mongo-fqdn:27017| G[MongoDB]
  J -->|mongo://mongo-fqdn:27017| K[Managed MongoDB]

何が問題になりそうか

  • 外部サービスもすべて同様に用意する必要がある。Redis とか PostgreSQL とか。めんどい。
  • Mirroring をはじめるタイミング。どこかでうんしょと MongoDB のデータを同期した状態からはじめる必要があるが、Production のデータは常に変わりはじめるので、完全に同期はできない。そのためデータ不整合等のエラーが出る可能性は高い。割り切るしかない?

良い点

  • 何より現実のトラフィックと同じものを用意できる点。なんだかんだ自前で負荷テストをやろうとすると Application の知識も必要だし、「これは本当に実際の負荷を再現できているのか?」という永遠に解決しない悩みと向き合い続ける必要がある。それがなくなる点が大きい。
  • Kubernetes Cluster を別に作る必要がない点。単純に Deployment が増えているだけなので、下の Node が Cluster Autoscaler で倍増するだけのはずだ。
  • 段階的に試せる。ミラーリングはパーセンテージを指定できるので、最初は 1% とかで、本番リソース全体を枯渇させる、みたいなリスクを回避できる。

次回は具体的にどういう config でやるのかを試す。

LEARN ENVOY - Back Pressure

www.envoyproxy.io

Envoy を使えば rate limiting, retries, timeouts を実装しなくて済むようになります。この記事では client が障害と backpressure をどう扱うかを示します。

A Guide to Envoy’s Backpressure

まぁだいたいは Envoy での設定(rate limiting, retries, timeouts)でなんとかなるが、失敗し続けた際にどう振舞うかを client に実装する必要がある。

Envoy Failure Modes

へー。Envoy が Downstream(client)に、それらが失敗したときに返却する一覧。

  • Rate Limiting Failure: 429 を返す
  • Timeout Failure: 504 を返す
  • Empty Cluster: 503 を返す
  • Unresponsive Upstream: 503 を返す
  • Circuit Breaker Failed Open: 503 を返し、x-envoy-overloaded: true header をつける。
  • Maintenance mode:503 を返し、x-envoy-overloaded: true header をつける。
  • Domain or Route not found: 404 を返す
  • Upstream Connection Closed: downstream がデータを受信しはじめたかによって、reset か 503 になる
  • Envoy is Overloaded: エラーなしでコネクションがブロックされる

おわりに

Back Pressure って問題起きた時に Envoy が Downstream に何を伝えるかって意味だったのね。

まぁでもどういうレスポンスコードが帰るかを知れるのは有益だった。

「KnativeとIngress Gateway」メモ

techbookfest.org

Knative

  • Serving
    • Revision
    • Configuration
    • Route
    • Service(Kubernetes の Service とは違う)
  • Eventing
    • Sources
    • Broker

この時点ではよくわかんないけど、Kubernetes の上に Gateway 層(Istio とか他) がいて、その上に Knative がいるようだ。

Gateway

  • Ingress は L7 Load Balancer
    • Type LoadBalancer Service や
    • Type NodePort Service もクラスタ外に公開するが

Knative が利用する Gateway というのは Envoy とかでよく説明される Front Proxy - North / South traffic のこと。この手段がいくつかある。

  • Ambassador
  • Contour
  • Gloo
  • Kourier
  • Istio

いずれも Envoy を Proxy に利用している。

その代表として Istio の説明。

Istio

結局イマイチ Knative いれるのになんで Istio(Gateway?)がいるのかがよくわかってない。

Typo をみつけたけど PR 投げられない。

3.5 Istio のコンポーネント つぎは主要なコンポーネントのインストールです。ここでは Helm を使って kubectl で apply する YAML ファイルを生成して apply います。

Istio で gateway つくって、それを外部から Knative に流すためにいるんかな。そのために Istio はヘビーじゃない?と思うが。

Ambassador

基本的に north-south トラフィック を制御するためのプロダクトです。east-west トラフィックの制御は Istio、Consul、 Linkerd とのインテグレーションを提供しています。

ほーん

.... よくわからんかった

Contour

おぉ、これは試そうと思ってたやつ。

Ingress を HTTTPProxy というカスタムリソースで表現するらしい。

Kourier

Knative 用の Ingress らしい。

Gloo

エンタープライズ版の API Gateway

ほう

おわりに

うちは Type LoadBalancer Service で AWS の LB で受けて、Node Port で受けて中にある Nginx に流してそこから各 Service に Proxy するというアーキテクチャをとっている。

いわゆるここの Front Proxy が将来どうなるか。Ingress を使う、Envoy を使う可能性は十分あると思う。

例えばこの Front の部分で path base routing なり header みてどうなり、percent base で canary をやるなりするなら Service Mesh があったほうが多分楽なのかなあ。

それとも Canary とかはその先の、Istio だったら Virtual Service 層でできるのかな、多分。

いずれにしろ、今は Service Mesh Control Plane は決着がついてない状況なので、Solution Base で、最小の構成で試して価値を出しつつ、学んでいきたいと思う。

Knative はどうなんだろうなあ、今のところ Layer 増えるジャンっていうイメージしかもってなくて、それでどんだけ幸せになれるかピンときてないです。Platform やる側だからかな。

LEARN ENVOY - Health Checks

www.envoyproxy.io

  • Load Balancing をうまくやるために
    • Health Checking(active monitoring)
    • Outlier Detection(passive monitoring)

Health Checking

  • endpoint ごとに生きているかを確認する
  • cluster 単位で行う
  • とはいえ health check だけでじゃ十分でない場合があり、異常値(outlier detection) と組み合わせると良い

Outlier Detection

  • Health Check とは反対に、実際の Response から Healthy かどうかを判別する
  • 例えば3回連続の503が帰ったら cluster から外す、という挙動をとる
  • 使えないホストにトラフィックを流さない判断がさっさとできるのはよさそうだが、それで復帰するのかなあ

Implementing Health Checking

  • Health check はコードに手をいれず実現できて便利だが
  • check しているのは service の health ではなく、host の health だ
  • つまり、host が auto-healing しないと意味がない
  • サービスが十分なトラフィックを受けているなら、5回の5xxエラーなどの統計情報が十分 Outlier Detection として役に立つ
  • もし initialize 処理が終わる前にトラフィックが流れるような場合は、Outlier Detection が役立ちます(まぁそうならんようんするやろ)
  • トラフィックが少ないが重要な場合は、Active Helath Check が役に立ちます

おわりに

Active と Passive の Healthcheck があり、まぁ Passive Health check が場合によっては便利な場面があるんやなーと思いました。

LEARN ENVOY - Automatic Retries

www.envoyproxy.io

Retry も重要。このへんが Application によってしてくれたりしてくれなかったりするのを Envoy がしてくれると助かるよね。

  • Envoy は Resilience のために retry を system の外で実装できるぞ
    • Choose appropriate defaults
    • Limit retry-able requests
    • Consider the calling context

Choose appropriate defaults / A typical Envoy retry policy

まぁ simple な retry 設定はこんな感じ

retry_on: "5xx"
num_retries: 3
per_try_timeout_ms: 2000

ふむ。

それぞれ default 値がある。 per_try_timeout_ms は retry のときの timeout, default は通常の request のときの timeout。

Limit Retry-able Requests

  • Retry 可能であるものだけを Retry すべき
  • つまり non idempotent 冪等でないリクエストは Retry すべきではない
    • キャンセルできないリクエストも同様
    • http GET なんかはリトライしやすい好例

Consider the Calling Context

リトライを設定するときには呼び出し側がどのようにリクエストするかを考慮する必要がある。

parameter としては timeout_ms , per_try_timeout_ms , num_retries とある。 per_try_timeout_ms * num_retriestimeout_ms を超えられない。retry ははやめに fail させたほうがよい。

また、global timeout がない場合で、timeout も長く retry がある場合はパフォーマンスは大きく低下する。このような高ファンアウト処理は retry をするべきではない。

最後に、retry と合わせて circuit breaking を同時に設定したほうがいい。retry は cascading failure を招きかねない。それを防ぐためにも必要。


このへん、最終的にはチューニング必要なんだなと思いつつ、前提としての考え方はめっちゃ重要だと思った。

3月

もう10日も過ぎてしまっている。

1月いっぱいかなり忙しくて、よーし2月はゆっくりする!生活をする!となったはいいが、それはそれでいろいろ行き先迷子になってしまったので、せめて短期的に、今月何するかだけ羅列しておく。

英語

  • DMM 英会話は継続
    • Vocabulary Material が終わって、Discussion Material へ
    • 英会話では Speaking 重視で、教材を読む中心から、何もないところから自分が発するようなところに寄せていきたい
    • Describing Pictures / Advanced Describing Pictures とか Daily News が次の候補かな
  • TOEIC は3月は中止、4月に受けるので、abceed で予測スコア750 目指す
  • 英検準1級 5/31 も受ける。Vocabulary の教材を abceed でやる トク単 https://amzn.to/2TSi5SG
  • Podcast を Script を読んで聞いてブログにまとめる Kubernetes Podcast Kubernetes Podcast from Google
  • Coursera Hybrid Cloud Service Mesh with Anthos 動画は2、3週でシャドーイング Hybrid Cloud Service Mesh with Anthos | Coursera

写真

音楽

  • アルバムリリースする
  • mix 4曲
  • cubase 環境再構築

仕事

生活

  • 自炊
    • 回鍋肉とペペロンチーノをマスターしたので、別の中華料理1つと、パスタ 料理1つをレパートリーに増やす
  • 1日1回1時間以上散歩
  • お風呂で読書する
    • 月10冊は読みたい