microservices-infrastructure-with-envoy-consul-nomad

 microservices-infrastructure-with-envoy-consul-nomad

Envoy Meetup Tokyo #1 の発表スライドです。
発表後から誤字の修正のほか次の修正が入っています。

P32. スライドが一枚抜けていたので追加
P26. 設定ミスとなる正しい理由をご指摘いただいたので正しい対処方法を記載

96bb87f5017cc4190a544bc040995f45?s=128

Kentaro Maeda

January 08, 2020
Tweet

Transcript

  1. Envoy・Consul・Nomad で作るマイクロサービス基盤 2020/1/8 Kentaro Maeda @ LINE at Envoy Meetup

    Tokyo #1 1
  2. About Me ・Kentaro Maeda (前多賢太郎) ・@kencharos (twitter) ・サーバーサイドエンジニア at LINE株式会社

    フィナンシャル開発センター 2
  3. LINE証券とマイクロサービス基盤の概要 3

  4. About LINE証券 ・2019/08 LINE証券 開始 ・2019/10 かんたん本⼈確認(eKYC)開始 ・2019/11 投資信託の取り扱い開始 ・⼀部機能をマイクロサービス基盤で運⽤開始

    ・新機能を継続開発中 4
  5. LINE証券のインフラ 本番環境はLINE本体と独⽴したネットワーク上で運⽤ ・本番環境はサーバー・RDBMSがインフラチームから提供 ・サーバーセットアップは⾃分たちで実施、いわゆるオンプレ ・開発環境・運⽤監視基盤はLINE本体のリソースを使⽤可能 ・プライベートクラウド ・運⽤監視基盤(ログサーバー、内製監視ツール・デプロイツールなど) ・開発基盤(GHE,Jenkins, Dockerレジストリ、Mavenリポジトリなど) 5

  6. 開業時のサービス構成 機能ごとに分割したサービスが10以上 サービスごとに以下のリソース・作業が必要 ・2-10台のサーバー ・セットアップスクリプト(Ansible Playbook) ・デプロイスクリプト(シェルスクリプト + 社内デプロイツール) ・ロードバランサー、IP、DNS申請

    ・SSL証明書申請 ・Databaseスキーマ(申請) 6
  7. サービスが増えるたびに運⽤負荷が増える ・サーバーリソースが有効活⽤できない ・複数サービスの同居が難しい ・特定のタイミングでしか使わないサービスにもサーバーを⽤意 ・職⼈芸と化しているデプロイスクリプト ・熟練のエンジニアがサーバー構築に1-2週間かかることも ・申請作業が多く、時間と調整の⼿間が多い ・申請作業の抜け漏れがあった際の⼿戻りと時間のロスが多い サービス開発にビジネスロジックとインフラ両⽅を気にしないといけない。 ビジネスロジックの開発だけに集中できるようなインフラ基盤を導⼊し、サービス開

    発を加速したい 7
  8. インフラ基盤に求めるもの マイクロサービス化を推進する機能 ・セットアップとデプロイの単純化 ・あらゆるサービスがどのサーバーでも動くように ・サーバーリソースの共有 ・複数サービスを同居する場合でもポート管理を⾃動化する ・スケールアップ/スケールアウトを容易にする ・サービスの追加や変更に動的に対応する ・サービスを再起動することなく構成を変更する ・どこでどのサービスが動いているのかを把握できるようにする

    ・多部署への申請が必要な作業をなるべく減らす ・サービスごとにLBを設定せずクライアントサイドLBを導⼊ ・ログや運⽤監視など⾮機能要件の標準を設定する 8
  9. Before/After 9

  10. マイクロサービス基盤を構成するもの ・Docker ・Consul ・Nomad ・Envoy + ControlPlane ・Vault 10

  11. (参考)Kubernetes について k8sクラスタの⾃前構築と運⽤が必要な状況だったため⾒送り ・k8sの運⽤経験を持つメンバーがおらず、時間をかけて検証する余裕もなかった ・k8sのすべての機能を必要としていない ・docker-composeがマルチノードで動けばいい程度のものがあればいい ・実績のある⼩さなツールを組み合わせることで、⾃分たちで構築・改善ができ、 また⾃信をもって運⽤ができると判断した ・将来的にk8sに移⾏するかもしれないが、それでもサービスをコンテナ化してお けば無駄にはならない

    ・コンテナ化よりもサービスメッシュによるAPI統合の⽅が重要 ・各ツールの採⽤事例や個⼈で作ったプロトタイプ(※最終⾴参照)で良さげな感触を 得た 11
  12. マイクロサービス基盤の構成要素 12

  13. Docker Docker Engineをインストールしたサーバーならどこでも、Dockerイメージを実⾏ でき、サーバーのセットアップ、デプロイ共に単純化できる ・サービスと実⾏基盤・ライブラリをDockerイメージでパッケージング ・Docker Registry で Docker イメージを各サーバーに配布

    13
  14. (参考)サービスの技術構成 コンテナ化することで⾔語やライブラリの⾃由度は上がるが、チームとして標準的な 技術構成を決めている ・Java (AdoptOpenJDK LTS)/Kotlin 1.3 ・Spring Boot 2.1/2.2

    ・Spring Cloud Consul ・Spring Cloud Sleuth ・REST or gRPC ・micrometer ・Jib 技術トレンドに合わせて随時⾒直していく 14
  15. HashiCorp社のサービス管理ツール 各サーバーへインストールするエージェントと、エージェントの情報を収集する管理 サーバーで構成 ・シングルバイナリでインストールが楽 ・Service Discorvery ・各サーバーで実⾏しているサービスのホスト・ポート、死活状態を監視・収集 ・KVS ・各サービスで使⽤する設定情報を⼀元管理 15

  16. HashiCorp社のデプロイツール 各サーバーにエージェントをインストールし、リソースが空いているサーバーに Dockerコンテナを起動できる ・シングルバイナリでインストールが楽 ・複数Dockerイメージ、起動するコンテナ数、⾃動復旧、ローリングアップデートな どを宣⾔的な設定ファイルで記述する ・k8s の deployment set

    相当 ・動的ポートを扱えるので、複数サービスの共有やスケールアウトも問題なし ・起動したDockerコンテナのホスト・ポートの管理やヘルスチェックはConsulに移 譲する 16
  17. サービスのルックアップ Consul + Nomadで簡易なコンテナクラスタは組めるが、サービス間通信では通信先 サービスの明⽰的なルックアップが必要、かつ負荷分散も⾃分で実装が必要 通信先のアドレスを得るには次のような⽅法がある ・Consul REST API ・

    curl http://localhost:8500/v1/health/service/service-a ・Consul 組み込みDNS ・動的ポートへはsrvレコードへの対応が必須 ・Spring Cloud Consul などのライブラリ なるべく、サービスが基盤の構成を意識しないといけない実装は避けたい 基盤がサービス間通信で相⼿先サービスへの通信を⾃動的に解決するようにする 17
  18. サービスメッシュ サービス間通信をフックし、アドレス解決・負荷分散・ロギング・エラー対応などの 共通処理を後から設定する⼿段 サービスの前段にプロキシとしてenvoyなどのプロキシを起動し、ingress/egressの 通信にプロキシを介して実現する プロキシを管理するControlPlaneというプロセスも必要となる サービスが別のサービスと通信する場合(egress)、どのサービスであっても⾃分のプ ロキシに向けて通信しておけばよくなる ・サービスのスケールアップなどで通信先が増えた場合も、何もしなくても良い ・環境変数などでegress向けのアドレスを変更可能にしておく

    ・gRPCで単⼀の接続のみで複数のRPCや負荷分散を⾏う場合には特に重要 18
  19. サービスメッシュの実現⼿段 ・Consul Connect ・consul サービス間でmTLS通信を envoy か組み込みプロキシで⾏う ・開発当時はL4プロキシのみ、かつ nomad と連携していなかった

    ・今の最新バージョンならL7プロキシも nomad 連携も可能(※最終⾴参照) ・envoyのすべての機能は使えないのでまだ様⼦⾒ ・Istio with nomad ・「多分動くけどテストしてない」という注意書きと⼀緒に書きかけっぽいページ がIsitio公式にあった → 最新バージョンで消えた ・nomad でenvoyをどう設定するかなどの考えたかは踏襲した ・envoy + ⾃作 ControlPlane ・実装難度は⾼いが、独⾃の要件やインフラ・ネットワーク制約に対応しやすい 19
  20. L7プロキシサーバー xDSプロトコルにより設定ファイルの⼀部を外部サーバー(ControlPlane)から取得・ ホットリロードできる Nomadのジョブ定義ファイルにサービスと⼀緒に起動するように設定 job "service-a" { count=2 group "service-a-group"

    { task "service-a-app" { driver = "docker" config { image = "service-a" } env {//egres host/port} } task "service-a-sidecar" { driver = "docker" config { image = "envoy-proxy" } env { // xDS ホスト} } } 20
  21. xDSプロトコルで設定可能な内容 v2時点で主要なものはLDS,RDS,CDS,EDS,SDSの5つ。envoy の設定ファイルと対応 している 設定ファイルのBootstrapにxDSサーバーを記述して、envoy起動時にenvoyがxDSサ ーバーに接続する 21

  22. xDSサーバー(ControlPlane)の実装 envoy公式でJava,GolangのxDS実装があり、これを使⽤して実装(今回はJava) https://github.com/envoyproxy/java-control-plane xDS gRPCサーバーの実装、およびxDSの設定内容を保持するキャッシュを提供 キャッシュのxDSのデータを更新すると、xDSサーバーに接続しているenvoyにデー タを反映する 22

  23. xDSキャッシュ更新処理の実装 Consul のServiceDiscovery, KVSを使⽤してxDSキャッシュを更新する ・ServiceDiscovery ・Nomadで起動したDockerコンテナのアドレスがConsulに登録されるので、その 変更がある度にEDSのデータとして設定 ・KVS ・変更頻度の低いLDS,RDS,CDS,SDSの設定をKVSに登録 ・KVSに変更を監視し、変更がある度にキャッシュを更新する

    ・KVSに登録する内容はLDS,RDS,CDS,SDSの内容をそのままYAMLにした ・envoyの全機能が使え、新バージョンにも対応しやすい ・機能が多く、記述量も多いので慣れるまでは苦労した 23
  24. 設定ファイルの例 version_info: 1 service_name: edge-router resources: #lds - "@type": type.googleapis.com/envoy.api.v2.Listener

    name: egress address:{ socket_address: {address: 0.0.0.0, port_value: 3101 } } filter_chains: - filters: - name: envoy.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager rds: route_config_name: egress_route config_source: {ads: {}} http_filters: - name: envoy.router #rds - "@type": type.googleapis.com/envoy.api.v2.RouteConfiguration name: egress_route virtual_hosts: - name: router_route domains: ["*"] routes: - match: { prefix: "/hi_a" } route: { cluster: service-a } - match: { prefix: "/hi_b" } route: { cluster: service-a } #cds - "@type": type.googleapis.com/envoy.api.v2.Cluster name: service-a <<: *cluster // 割愛 - "@type": type.googleapis.com/envoy.api.v2.Cluster name: service-b <<: *cluster // 割愛 24
  25. ControlPlane実装の困りごと(1) Consulのヘルスチェックが通過したサービスのみをEDSに登録するようにしても、 Nomadでサービスを起動すると、初期化処理中なのに接続してしまいエラーになる ConsulのヘルスチェックはデフォルトではNodeCheck(ホストの死活監視)のみ。 サービスレベルのHTTPヘルスチェックを追加し、初期化処理やDB接続などが済んで からヘルスチェックが通るようにした ・SpringBoot の場合、SpringBoot Actuator を使うとDBやRedisの接続が通るま

    でのヘルスチェックエンドポイントを追加してくれる 25
  26. ControlPlane実装の困りごと(2) 1でサービスヘルスチェックを追加したが、チェックOKまでに時間がかかると、⼀時 的にConsulでチェックOKのサービスが0になり、その瞬間にenvoyが起動しなくなる envoy は設定ミスがあると、起動/ホットリロードができない。 この場合、CDS(クラスター)にぶら下がるEDS(エンドポイント)が0になるので設定ミ ス扱い 通信不可能なホストであっても、EDS上の定義があれば問題ないので、 Consulのサービスがない場合はダミーのEDSを追加するようにして回避した (発表後に訂正)

    CDS(クラスター)にぶら下がるエンドポイントがない場合にEDSを作成していなくて 設定ミスとなっていた。 エンドポイントが0のEDSを作成するようにすると問題が起きなくなった。 26
  27. ControlPlane実装の困りごと(3) Consulのサービス変更/KVS変更の反映が遅い 当初はConsulの変更検知を⼀定間隔でAPIコールしていた 間隔が⻑いと反映が遅れるし、短いとリソースを消費する Consulのブロッキングクエリという、Consulで変更があるまでAPIのコネクションを 貼り続ける⽅式を⽤いて、変更を即時で検知できるようにした。 ・この⽅式はenvoyとxDSサーバーの接続⽅式ともよく似ている ・ControlPlaneとConsulの間で多量の⻑期間接続を⾏うので、スレッド枯渇が起きな いようにノンブロッキングIO(Vert.x Consul

    Clinet)を⽤いている 27
  28. ⾮機能要件 28

  29. Systemd Docker,Consul,Nomad,Vault,Prometheus Exporter など基盤を構成するホストプ ロセスは全てSystemdにデーモンとして登録し⾃動起動するように設定 Hypervisor障害によりOSリブートするトラブルがあったが、特に何もしなくてもサ ービス継続していた ・systemdによりホストプロセス⾃動起動 ・Consul起動でリブートしたサーバーが⾃動的にクラスタに復帰 ・リブートしたOSで実⾏していたNomadのサービスは別のサーバーで⾃動的に再起

    動していた 29
  30. セキュリティ mTLSによりサービス間通信をクライアント/サーバTLS証明書必須にした HashiCorp Vault のPKI機能を⽤いて、Nomad起動時にTLS証明書をenvoyに埋め込 んでいる (VaultはPKIの他、DBパスワードなどの機密情報を⼀元管理している重要な構成要素) 将来的には、envoyの外部認証やRBACを使って、証明書の詳細なVerifyや業務レベ ルの認可処理ができないかを検討している 30

  31. ログ コンテナログはJSONで標準出⼒に出すようにルール化 Journald と td-agent を⽤いて、コンテナログとデーモンのログを全て elasticsearchなどのログ基盤やエラー速報⽤のSlackに転送している 31

  32. ノード追加・基盤の更新 基盤の構成要素は全てAnsible RoleとPlaybookでまとめている 新しいサーバーリソースを追加する際は、そのサーバーにAnsible Playbookを実⾏す れば、⾃動的にConsulクラスタに参加してすぐ利⽤可能になる 今後、基盤のツールのアップデートなどがある際もPlaybookを実⾏すれば更新完了に なるようにしていく 32

  33. 運⽤監視 現時点ではPrometheusと社内の監視ツールを使⽤している ・コンテナで実⾏しているenvoyとサービスはPrometheusエンドポイントを公開 ・SpringBoot ではmicrometerを使⽤ ・node exporter, consul exporterなど ホストプロセスでも各種expoterを使⽤

    ・リソース監視のほか、Consulのヘルスチェック失敗、Systemdのステータス異常、 エラー発⽣なども監視している 引き続き、最適な運⽤監視の⼿段を検討している 33
  34. 分散トレーシング envoy のトレーシングに加えて、サービス側もトレーシングを⾏うようにしている ・DBクエリ、Redis実⾏などenvoyを介さない通信のトレーシングを取りたい ・envoyだけではサービス間通信のトレーシングを実現できず、サービス側でトレー ス情報の伝播(Context Propagation)が必須 ・Spring Cloud Sleuth

    によりサービス内のトレースと伝播を実現している 34
  35. まとめ オンプレ上にサービスメッシュ × コンテナクラスタの基盤を構築できた ・サービスメッシュによりサービス間のAPI統合が柔軟になった ・コンテナクラスタにによりデプロイとサーバーセットアップが格段に楽になった ・⾃分たちのチームで運⽤可能な⼿頃なサイズ感 適⽤範囲の拡⼤と開発⽀援・運⽤⽀援が今後の改善ポイント ・作成済みの既存サービスや、ストレージにも対応させていきたい ・xDS設定ファイルの作成⽀援、システム全体の設定の整合性チェックなど、便利に

    していきたいところが多数ある 35
  36. ※参考資料 ・Yakst - 多分あなたにKubernetesは必要ない ・https://yakst.com/ja/posts/5455 ・クックパッド開発者ブログ - Service Mesh and

    Cookpad ・https://techlife.cookpad.com/entry/2018/05/08/080000 以下、発表者によるもの ・Spring, consul, envoy, ControlPlane Java, gRPC プロトタイプ ・https://github.com/kencharos/envoy-service-discovery-spring ・consul connect, L7 traffic manager, nomad consul connect を試す ・http://kencharos.hatenablog.com/entry/2019/12/17/010151 36
  37. Thank You! 37