Slide 1

Slide 1 text

Consul/Spring Cloud Consul ⼊⾨ 2020/4/8 Kentaro Maeda @ LINE #JSUG 1

Slide 2

Slide 2 text

About Me ・Kentaro Maeda (前多賢太郎) ・Twitter @kencharos ・LINE株式会社 フィナンシャル開発センター サーバーサイドエンジニア 2

Slide 3

Slide 3 text

今⽇の内容 ・Consul ⼊⾨ ・Spring Cloud Consul 紹介 3

Slide 4

Slide 4 text

Spring Cloud Consul https://spring.io/projects/spring-cloud-consul より、 Spring Cloud Consul は HashiCorp Consul と連携して、Spring Boot に⼤規模分 散システムを構築するための機能として Service Discovery, Distributed Configuration, Control Bus を提供する。 Spring Cloud Consul で提供される機能は Consul の機能であり、 Spring Cloud Consul を活⽤するには、Consul の理解が⽋かせない。 4

Slide 5

Slide 5 text

HashiCorp Consul について https://www.consul.io 分散システム上のサービス管理を⾏うソフトウェア ・Service Discovery - どのノードに何のサービスがあるかカタログ化 ・Health Check - ノード・サービスの死活監視と通知 ・Key/Value Storage - サービス横断で設定を参照・更新する ・Multi Datacenter - 別のDCにあるConsulクラスタと接続・同期する ・Service Mesh - サービス間のセキュアな通信を管理する 5

Slide 6

Slide 6 text

Consul の特徴 ・Go⾔語製のシングルバイナリと設定ファイルのみで動く ・DBなどは不要。インストールとノードの追加が楽 ・Server/Agent によるクラスタ構成 ・Agent は⾃ノードの情報をServerに伝え、ヘルスチェックを⾏う ・Server はAgentの情報を収集・同期する ・Server/Agentはどちらも同じバイナリで設定内容が異なる ・⾼可⽤性 ・Server の過半数が⽣存していればクラスタを維持できる ・Agentの追加が容易であり、⼤規模なクラスタを構築しやすい ・WebAPI, CLI, DNS, UI など複数のインタフェースがある 6

Slide 7

Slide 7 text

Consul クラスタの構成 奇数のConsul Serverとスケール可能なAgent nodeからクラスタを構成 各nodeのサービスの情報をAgentに登録し、Server経由でクラスタ内で共有する 7

Slide 8

Slide 8 text

Service の登録 Consul へのサービス登録・削除は⾃分でAPI経由で⾏う。⾃動収集はしない。 ・例) service1 のアドレス、HTTPヘルスチェックを設定して登録 { // service1.json "ID": "service1", // consul 全体でユニークなID "Name": "service1", // 同じ種類のサービスをグルーピングするサービス名 "Address": "127.0.0.1", "Port": 8080, "Check": { "DeregisterCriticalServiceAfter": "60m", "HTTP": "http://127.0.0.1:8080/actuator/health", "Interval": "5s" } } # 同⼀ノードのagent に対してAPI 呼び出し curl -X PUT http://localhost:8500/v1/agent/service/register -d @service1.json 8

Slide 9

Slide 9 text

サービス情報の取得 登録したサービスはAPI, CLI, DNS経由で参照できる。 別ノードのAgentで登録されたサービスの情報も参照できるので、ノードやサービス が増えても常にローカルAgentに問い合わせれば良い。 #DNS $ dig @127.0.0.1 -p 8600 service1.service.dc1.consul. SRV ;; ANSWER SECTION: service1.service.dc1.consul. 0 IN SRV 1 1 8080 myhost.node.dc1.consul. ;; ADDITIONAL SECTION: myhost.node.dc1.consul. 0 IN A 127.0.0.1 myhost.node.dc1.consul. 0 IN TXT "consul-network-segment=" // API 呼び出し。レスポンスは⼀部省略 $ curl http://localhost:8500/v1/health/service/service1 [ { "Node": { "ID": "xxx", "Node": "myhost", "Address": "127.0.0.1",}, "Service": { "ID": "service1", "Service": "service1","Address":"127.0.0.1", "Port": 8080 }, "Checks": [//] } ] 9

Slide 10

Slide 10 text

Key/Value Storage consul クラスタ間で設定の登録・参照を⾏う 登録された値はクラスタ内で共有 # key1=value1 で登録 $ curl -X PUT http://localhost:8500/v1/kv/key1 -d "value1" # common/key2=value2 で登録 $ curl -X PUT http://localhost:8500/v1/kv/common/key2 -d "value2" # key1 をJSON で取得。value は base64 エンコード $ curl http://localhost:8500/v1/kv/key1 [{ "Key": "key1", "Value": "dmFsdWUx","LockIndex": 0, "Flags": 0, "CreateIndex": 22,"ModifyIndex": 22 }] # common/key2 を RAW 形式で取得 curl http://localhost:8500/v1/common/key2 value2 10

Slide 11

Slide 11 text

Watch/Event サービスの状態の変化やKey/Valueの変化、ヘルスチェック、任意のイベントについ て通知を受け取ることができる。 # service1 サービスの状態を監視し、変化したら hoge.sh を実⾏する consul watch -type service -service "service1" ./hoge.sh # 任意のイベント発⾏ consul event -name=test value1 # イベント通知が来たら cat で内容を出⼒ consul watch -type=event -name=test cat Web APIの場合、Blocking Query というロングポーリングの⼿段がある # 特定の時点(index=83) 以降 key1 の値が変化するまで10 秒待つ $ curl http://localhost:8500/v1/kv/key1?index=83&wait=10s 11

Slide 12

Slide 12 text

Consul のユースケース Consul はクラスタ内のサービスの状態管理とKey/Valueの値の保持に専念 これを使って何をするかは実装者次第。 Consul Connect, Spring Cloud Consul はサービス間通信をConsulで⾏う ・例1) サービス情報を使⽤して、動的な構成変更に追従するサービス間通信を⾏う ・Consul Connect, Spring Cloud Consul ・例2) Key/Value Storage でクラスタ全体で共有する設定を⼀元管理する ・Spring Cloud Consul ・例3) 負荷分散対象のサービスをwatchで監視して、変化があればロードバランサー の設定を変更して再起動 ・例4) Multi Datacenterで クラウド/オンプレミス/k8s を横断するサービス⼀覧を 作る 12

Slide 13

Slide 13 text

Consul Connect (Service Mesh) Consul に標準で組み込まれている、サービス間通信を管理する機能 ・L4プロキシ か L7プロキシ(envoy) をサービスごとにサイドカーとして起動し、サ ービス間通信で使⽤するローカルポートを提供 ・サービス間、負荷分散、死活監視はConsul側で提供 ・通信相⼿のサービスがどのノード・ポートにあってもローカルポートに繋げば Consulが解決する ・通信経路は mTLSで暗号化、Consul クラスタ外からのアクセスは不可 ・Consul APIで通信許可を⾃由に設定できる ・L7 Traffic Management, Mesh Gateway など機能強化により、⾼度なルーティン グや複数DC間での通信もできるようになった 13

Slide 14

Slide 14 text

Consul Connect の設定 サービス登録時にサイドカープロキシの設定で通信先とポートの設定を⾏う { "ID": "service1", "Name": "service1", "Address": "127.0.0.1", "Port": 8080, "Connect": { "SidecarService":{ "Proxy":{ "upstreams": [{ // service2 に 9000 ポートで接続したい! "destination_name": "service2", "local_bind_port": 9000 }]}}} サイドカープロキシをサービスごとに起動する consul connect proxy -sidecar-for service1 アプリケーションは service2呼び出しを locahost:9000 経由で⾏う @FeignClient(name = "service2", url = "${service2.url}") // 設定ファイル or 環境変数 public interface Service2Client { @GetMapping(value="/hello", produces = "text/plain") String hello(); } 14

Slide 15

Slide 15 text

Demo - Consul, Consul Connect ・⼿動でSpring Bootサービスの登録、Connectによる通信 ・https://github.com/kencharos/consul-spring- tutorial/blob/master/consulservice1/tutorial_consul_basic/readme.md ・ローカルで試すには consul agent --dev で全設定有効のServer兼Agentが起動 15

Slide 16

Slide 16 text

Consul のまとめ ・複数ネットワークを横断してサービス情報を集約・管理するソフトウェア ・どう使うは⾃分次第 ・⼩規模でも⼤規模でも同じように使える ・全てのAPIはローカルagentに対して実⾏ ・node追加やサービスの増減に⾃動的に追従してくれる ・全ての機能にAPIがあり、API設計が綺麗 ・クラウドでもオンプレでも、⼀度導⼊してみても損はない ・最初の導⼊(consul serverの準備や設定ファイル作成)はまあまあ⼤変 ・nodeのagentインストールまでを⾃動化できれば、serverとagentは⾃動的にク ラスタを維持するので運⽤は難しくない 16

Slide 17

Slide 17 text

Spring Cloud Consul Consul と連携して、以下の機能を提供する ・設定ファイル,アノテーションにより Consulへのサービス登録・削除を⾃動化する ・Consul で管理しているサービスに対して Service Discovery とクライアントサイ ドロードバランシングを提供し、ライブラリとしてサービス間通信を⾏う ・Consul Key/Value Storage の内容を Spring Boot のプロパティとして使⽤でき る(Distributed Configuration) ・Spring Cloud Bus のバックエンドとしてConsulを使⽤し、Spring Boot アプリケ ーションの⼀括管理を⾏う(はず) ・動作しなかったので紹介しません 17

Slide 18

Slide 18 text

(参考)Spring Cloud Consul の代替 同様の機能を提供するライブラリは他にもあるので、使⽤しているミドルウェアに合 わせて選択可能 Service Discovery ・Spring Cloud Zookeeper ・Spring Cloud Netflix(Eureka) ・Eurekaは今でもSpring Cloudでサポート中 Distributed Configuration ・Spring Cloud Zookeeper ・Spring Cloud Config 18

Slide 19

Slide 19 text

(参考)サービスメッシュとライブラリ どちらもサービス間通信のための機能だが、機能をアプリケーションの外と中のどち らに置くかが異なる 19

Slide 20

Slide 20 text

(参考)サービスメッシュ とライブラリの⽐較 システムの規模、構成に合わせてどちらを採⽤するかを検討 項⽬ サービスメッシュ ライブラリ 主な製品 Istio, Consul Connect, Dapr Spring Cloud Consul/Eureka 実現⽅法 Sidecar Proxyによる通信仲介 プロセス中に組み込み メリット サービスと疎結合 他⾔語に対応できる プロキシに接続すれば通信可能 導⼊が容易 サービス間直接通信 デメリット サービスメッシュ基盤が必要 分散トレースの伝播不可 ホップ数が増える ⾔語・フレームワークが固定 ライブラリのAPIを使う必要がある サービスごとに設定や導⼊が必要 20

Slide 21

Slide 21 text

Spring Cloud Consul を始める Spring Cloud Consulの依存性を追加する ext { set('springCloudVersion', "Hoxton.SR1") } dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } dependencies { // 全ての機能を使⽤する場合 implementation 'org.springframework.cloud:spring-cloud-starter-consul-all' // 機能を個別に選択する場合 implementation 'org.springframework.cloud:spring-cloud-starter-consul-config' implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery' //implementation 'org.springframework.cloud:spring-cloud-starter-consul-bus' } 21

Slide 22

Slide 22 text

Spring Cloud Consul を設定する bootstrap.ymlと spring.application.name が必須 spring: application: name: springcloudservice1 # 以下はデフォルト設定なので省略可能 cloud: consul: host: localhost port: 8500 依存性と上記設定だけで以下の処理が⾃動的に⾏われる。 ・Spring Boot 起動時に spring.application.name の値でConsulにサービス登録 ・Spring Boot Actuatorの healthエンドポイントに対してヘルスチェック設定 ・Spring Boot 終了時に Consulからサービス削除 ・bootstrap.ymlの設定でカスタマイズ可能 22

Slide 23

Slide 23 text

Service Discovery - Discovery Client サービス名を与えるとアドレス⼀覧を取得する DiscoveryClient が使⽤可能。 Consulからアドレスを取得していて、Consul Watch により最新の状態に同期され る。 @Autowired DiscoveryClient discovery; public void getService() { // springcloudservice2 サービスのアドレス⼀覧を取得 discovery.getInstances("springcloudservice2") .forEach(srv -> System.out.println(srv.getHost() +":" + srv.getPort())); } 23

Slide 24

Slide 24 text

Spring Cloud LoadBalancer(1) DiscoveryClient を使⽤してクライアントサイドロードバランシングを⾏う、 Spring Cloud LoadBalancerも使⽤できる。 RestTemplate, WebClient, OpenFeignなどに組み込み、URLにサービス名を与える ことで当該サービスのアドレス⼀覧に対してラウンドロビンで通信を⾏う OpenFeign など 宣⾔型HTTPクライアントの場合は、 @LoadBalancerClient を使 い、URL設定にサービス名を付与 @LoadBalancerClient // DiscoveryClient を組み込むアノテーション @FeignClient("springcloudservice2") // URL の代わりにサービス名 public interface Service2Client { @GetMapping(value = "/hello", produces = "text/plain") String hello2(); } 24

Slide 25

Slide 25 text

Spring Cloud LoadBalancer(2) RestTemplate/WebClient の場合は @Bean と⼀緒に @LoadBlanced を付与し、 HTTPアクセス時にホスト名にサービス名を設定する @LoadBalanced @Bean public WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder(); } @Autowired WebClient.Builder builder; public void fetch() { // ホスト名にサービス名を与えると、DiscoveryClient からアドレス解決する builder.build().get() .uri("http://springcloudservice2/hello2").exchange(); } さらに、 @LoadBalancerClient を使⽤してWebClinetインスタンスを事前に構築す る⽅法もある(https://spring.io/guides/gs/spring-cloud-loadbalancer/) 25

Slide 26

Slide 26 text

Distributed Configuration Consul の Key/Value Storage を Spring Boot のプロパティとして使う機能 config/< サービス名>/< プロパティ> に値を設定すると、起動時にConsulから取得する # springcloudservice2 サービスの greeting.service2 にB-1 を設定 consul kv put config/springcloudservice2/greeting/service2 B-1 @Value や ConfigurationPropertiesから参照可能 @RestController public class Hello2Controller { // B-2 がインジェクション @Value("${greeting.service2}") String greeting; @GetMapping(value = "/hello", produces = "text/plain") public String greeting() { return greeting; } } 26

Slide 27

Slide 27 text

application, プロファイル, 優先度 サービス名=applicationのプロパティは全サービスで共通で参照可能 ローカルのプロパティをconsulの設定で上書きできるので、クラウドなどの実⾏環境 固有の設定をJarに含めなくてよくなる。 プロパティの優先度 1. 環境変数 2. consul - config/<サービス名>,<プロファイル>/ 3. consul - config/<サービス名>/ 4. consul - config/application,<プロファイル>/ 5. consul - config/application/ 6. ローカル - allication.yaml 27

Slide 28

Slide 28 text

Key/Value Storageのフォーマット bootstrap.yaml の以下のformat設定で、値に設定するフォーマットを変更できる spring.cloud.consul.config.format: YAML|PROPERTIES|FILES|KEY_VALUE(default) YAML, PROPERTIES config/< サービス名><,PROFILE>/data キーに、yaml またはproperties 形式で複数 の値をまとめて設定できる FILES config/< サービス名><-PROFILE>.yml(.properties) のキーで設定ファイルの内容その ものを値に設定できる。 Key/Value Storageに設定する内容をgitで管理し、デプロイ時に反映する場合などに 便利 28

Slide 29

Slide 29 text

リフレッシュスコープ Consulから取得したプロパティは、 @RefreshScope を付与したBeanについて、 Key/Value Storage の値を更新するとBeanのリフレッシュ(DIのやり直し)する ・⾃動的にリフレッシュするか、明⽰的に actuator/refresh を実⾏するか、無効に するか設定で選択できる ・ConfigurationProperies は暗黙的に RefreshScope に含まれる @RestController @RefreshScope public class Hello2Controller { // @RefreshScope により、KVS の変更時にリフレッシュされる @Value("${greeting.service2}") String greeting; @GetMapping(value = "/hello", produces = "text/plain") public String greeting() { return greeting; } } 29

Slide 30

Slide 30 text

Spring Cloud Gateway + Service Discovery Spring Cloud Gateway の DiscoveryClient 連携を使⽤すると、 Spring Cloud Gateway から < サービス名>/< 各サービスのエンドポイント> で、各サービ スに接続できる。 API Gatewayとして各サービスの接続の統合や認証の⼀元化などに期待できる spring: application: name: sprnigcloudgateway cloud: gateway: discovery: # use consul service discovery locator: enabled: true 30

Slide 31

Slide 31 text

Demo2 - Spring Cloud Consul ・ServiceDiscovery によるサービス間接続 ・Key/Value Storage によるプロパティ設定とリフレッシュ ・Spring Cloud Gateway 連携 ・https://github.com/kencharos/consul-spring- tutorial/tree/master/springcloudservice1 31

Slide 32

Slide 32 text

まとめ ・Consul で分散システムのサービス情報を管理できる ・Spring Cloud Consul で動的なサービス間通信と、設定の管理ができる ・分散システムやマイクロサービスの設計を始めるときに、導⼊を検討してみません か 32

Slide 33

Slide 33 text

(おまけ) Consul を本番で使うためには ・エージェント間通信やAPIを暗号化するためにTLS必須 ・Consulが使⽤するポートは結構多いのでFirewallの設定に抜けがないように ・デフォルトでは全ての操作は認証無しで利⽤可能 ・ACL(ポリシーとトークン)を有効にして、使⽤可能なAPIを最⼩限にする ・パスワードなどの機密情報を Key/Value Storage に置かない ・機密情報管理のためのソフトウェア Hashicorp Valut などを検討する ・機密情報を安全に配布する他、 Consul ACL を AWS IAM などの他の認証機 構と連携するなど、より⾼度なポリシー制御ができる ・Spring Cloud Valut という Valut 連携ライブラリもある 33