Spring Cloudシリーズの第3回です。今回はService Discoveryです。
概要
今回は、Eureka(Spring Cloud Netflix Eureka )でService Discoveryのクラスタを立てます。
Spring Cloudではここ最近で方針転換があり、Spring Cloud Netflix関連プロダクトがメンテナンスモードになり、代替のプロダクトへの移行段階にあります。
Spring Cloud Netflix Projects Entering Maintenance Mode
今回は、その中でも次の世代に生き残った Eurekaを使ってService Discovery
のクラスタをdockerで立てていきます。
(最もよく知られている Service Discovery に consul がありますが、SpringBootとConsulの連携については、後の楽しみにとっておきましょう。)
環境
以下が動く環境を前提とします。
- docker & docker-compose
- JDK11
前回の投稿の続きですので、前回のゴール地点のソースを手元に用意し、それをベースとします。
作業ステップ
本記事では以下のステップで進めていきます。
- Eurekaのひな型作成
- 実装(アノテーションの付加と設定ファイルの記述)
- Dockerイメージ化とdocker-componentへの追加
- APIを多重化し、Serivce Discoveryに登録する
- Eurekaのクラスタリング
ステップ1:Eurekaのひな型作成
Spring Initializr の出番です。
Artifact と Name だけdiscovery
に指定し、あとは
Dependencies にEureka Server
とSpringBoot Actuator
を追加しましょう。
ActuatorはEurekaに必須ではありませんが、何かと必要になるものなので追加しておきます。
あとはGENERATE
するとスケルトンのdiscovery.zipがダウンロードできます。
ステップ2:実装(アノテーションの付加と設定ファイルの記述)
今回はあまりやることがありません。2つだけ。
まず1つ目。Applicationクラスにアノテーションを1つ付けます。
package com.example.discovery;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
// ★ ↑ これを追加
@SpringBootApplication
@EnableEurekaServer // ★ これを追加
public class DiscoveryApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryApplication.class, args);
}
}
2つ目は、設定ファイルを書きます。
server:
port: ${PORT:8761}
eureka:
shouldUseDns: false
instance:
hostname: ${CONTAINER_NAME:localhost}
preferIpAddress: false
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: ${ZONE:http://localhost:8761/eureka}
server:
enableSelfPreservation: true
spring:
main:
banner-mode: "off"
cloud:
loadbalancer:
ribbon:
enabled: false
management:
endpoints:
web:
exposure:
include: "*"
いくつかわざとらしく後で環境変数で書き換えられるようにしていますが、基本はデフォルト値で動くようにしています。
※discovery/src/main/resources/application.properties
は不要になるので、削除してください。
さて、ここまでくればEurekaサーバを起動できます。
$ cd discovery
$ ./mvn clean spring-boot:run
http://localhost:8761 にアクセスしてみてください。Eurekaのホーム画面が出ればOKです。
ステップ3:Dockerイメージ化とdocker-componentへの追加
この段階でさっさとDockerイメージ化をしてdocker-composeで操れるようにしておきましょう。
前回のディレクトリ構造に以下のようにdiscover
ディレクトリを配置します。
その上で、Dockerfileを作成し、とdocker-compose.ymlに追記します。
spring-msa
├── account
│ └── ・・・
├── db
│ └── init
├── docker-compose.yml <-- ★追記する
└── sd <-- ★配置する
├── Dockerfile <-- ★作成する
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ └── resources
└── test
FROM openjdk:11-jdk-slim
ADD target/discovery-*.jar /discovery.jar
ENV CONTAINER_NAME=localhost \
PORT=8761 \
OPTS_ARGS=''
ENTRYPOINT ["java", "-jar", "/discovery.jar", "${OPTS_ARGS}"]
version: '3'
・・・
services:
db:
・・・
adminer:
・・・
sd:
image: spring-msa/eureka
container_name: sd
build:
context: ./discovery
dockerfile: Dockerfile
environment:
- CONTAINER_NAME=sd
- PORT=8761
- ZONE=http://sd:8761/eureka/
ports:
- "8761:8761"
account:
・・・
これで準備OK.
mvn wrapperでJARを作ったあとに、Dockerイメージを作成します。
$ cd discovery
$ .mvnw clean package
$ cd ..
$ docker-compose build sd
これでdocker-composeで起動・停止できるようになりました。
(起動)
$ docker-compose up -d sd
(停止)
$ docker-compose stop sd
コンテナの8761ポートをホストの8761ポートにbindしているので、
http://localhost:8761 で先ほどのEurekaのダッシュボードが見れるはずです。
何かがおかしければdokerのログをみてください。
$ docker-compose logs -f sd
ステップ4:APIを多重化し、Serivce Discoveryに登録する
このままだとService Discoveryは立ち上がったものの、何もサービスが登録されていません。
そこで、前回作ったaccount
サービスを登録してみましょう。
pom.xml
と application.yml
に手をいれます。
@@ -15,6 +15,9 @@
<description>Demo project for Spring Boot</description>
<properties>
+ <spring-cloud.version>Hoxton.SR7</spring-cloud.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>
@@ -47,6 +50,10 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+ </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -65,6 +72,18 @@
</dependency>
</dependencies>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>${spring-cloud.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
<build>
<plugins>
<plugin>
spring:
・・・
r2dbc:
url: ${DB_URL:r2dbc:postgresql://localhost:5432/db}
username: ${DB_USER:postgres}
password: ${DB_PASSWD:postgres}
+ cloud:
+ loadbalancer:
+ ribbon:
+ enabled: false
management:
endpoints:
@@ -22,3 +26,16 @@ management:
endpoint:
health:
show-details: always
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: ${DISCOVERY:http://localhost:8761/eureka/}
+ should-use-dns: false
+ instance:
+ instanceId: ${CONTAINER_NAME:account}
+ hostname: ${CONTAINER_NAME:localhost}
+ preferIpAddress: false
+
services:
・・・
account:
image: spring-msa/account-api
container_name: account
build:
context: ./account
dockerfile: Dockerfile
ports:
- "9001:9001"
depends_on:
- db
environment:
- CONTAINER_NAME=account
- PORT=9001
- DB_USER=${DB_USER:-postgres}
- DB_PASSWD=${DB_PASSWORD:-postgres}
- DB_URL=${DB_URL:-r2dbc:postgresql://db:5432/db}
- DISCOVERY=http://sd:8761/eureka # ★ここを追加
networks:
- default
JARを作ってdocker-composeでdokcerイメージのビルドを含めて一気に起動してみましょう。
Service Discoveryも起動しておきます。
$ docker-compose up -d sd
$ cd account
$ ./mvnw clean package
$ cd ..
$ docker-compose up -d --build account
http:/localhost:8761 にアクセスしてしばらくすると、ACCOUNT-APIが1インスタンス登録されているのがわかると思います。
では、さらにACCOUNT-APIサービスのインスタンスを2つ追加しましょう。
docker-compose.ymlのaccountサービスの記載をコピーして、サービス名などの名前やポート番号を書き換えます。
同じDockerイメージを使って、3つのDockerコンテナを動かします。
ポート等はdocker-compose.ymlの環境変数で外から指定することで、重複しないようにします。
サービス名 | コンテナ名 | ポート番号 | |
---|---|---|---|
account | account | 9001 | |
account2 | account2 | 9002 | ←これを追加 |
account3 | account3 | 9003 | ←これを追加 |
・・・
services:
・・・
account:
・・・
account2:
image: spring-msa/account-api
container_name: account2 # ★
ports:
- "9002:9002" # ★
depends_on:
- db
environment:
- CONTAINER_NAME=account2 # ★
- PORT=9002 # ★
- DB_USER=${DB_USER:-postgres}
- DB_PASSWD=${DB_PASSWORD:-postgres}
- DB_URL=${DB_URL:-r2dbc:postgresql://db:5432/db}
- DISCOVERY=http://sd:8761/eureka
networks:
- default
account3:
image: spring-msa/account-api
container_name: account3 # ★
ports:
- "9003:9003" # ★
depends_on:
- db
environment:
- CONTAINER_NAME=account3 # ★
- PORT=9003 # ★
- DB_USER=${DB_USER:-postgres}
- DB_PASSWD=${DB_PASSWORD:-postgres}
- DB_URL=${DB_URL:-r2dbc:postgresql://db:5432/db}
- DISCOVERY=http://sd:8761/eureka
networks:
- default
追加したacocunt2、account3のサービスを起動します。
$ docker-compose up -d account2 account3
http://localhost:8761 にアクセスするとインスタンス数が3になっているのが確認できると思います。
ステップ5:Eurekaのクラスタリング
さて、Service Discoveryのサービスを立てることができましたが、1プロセスだと心もとないので、冗長化を試します。
これもdocker-compose.ymlの編集のみで対応可能です。
sdサービスのエントリをコピーして、ポート番号等を調整します。
サービス名 | コンテナ名 | ポート番号 | |
---|---|---|---|
sd | sd | 3001 | |
sd2 | sd2 | 3002 | ←これを追加 |
version: '3'
・・・
services:
db:
・・・
adminer:
・・・
sd:
image: spring-msa/eureka
container_name: sd
build:
context: ./discovery
dockerfile: Dockerfile
environment:
- CONTAINER_NAME=sd
- PORT=8761
- ZONE=http://sd2:3001/eureka/ # ★自分以外のEurekaインスタンスをカンマ区切りで記載する
ports:
- "3001:3001"
sd2:
image: spring-msa/eureka
container_name: sd2
environment:
- CONTAINER_NAME=sd2
- PORT=3001
- ZONE=http://sd:3001/eureka/ # ★自分以外のEurekaインスタンスをカンマ区切りで記載する
ports:
- "3002:3002"
account:
・・・
environment:
・・・
- DISCOVERY=http://sd:3001/eureka,http://sd2:3002/eureka # ★すべてのEurekaインスタンスをカンマ区切りで羅列する
・・・
account2:
・・・
environment:
・・・
- DISCOVERY=http://sd:3001/eureka,http://sd2:3002/eureka # ★すべてのEurekaインスタンスをカンマ区切りで羅列する
・・・
account3:
・・・
environment:
・・・
- DISCOVERY=http://sd:3001/eureka,http://sd2:3002/eureka # ★すべてのEurekaインスタンスをカンマ区切りで羅列する
これでsdとsd2を起動しましょう。
localhost:3001 と localhost:3002 にアクセスすると、両方とも「DS Replicas」の欄に他方のEurekaサーバへのリンクが表示されていて、
ACOCUNT-APIアプリが、accout,account2,account3の3件登録されているのが確認できると思います。
さいごに
わりとシンプルにService Discovery
のクラスタができました。
ここまでのソースは以下に置いています。
次回はService Discoveryを利用してAPIに分散アクセス(ロードバランシング)する
「API Gateway」を追加しましょう。
ではでは。