Azure Spring Cloud Enterprise の Service Registry の利用方法について

この記事では、Azure Spring Cloud Enterprise で VMware Tanzu® Service Registry を使用する方法について説明します。

Tanzu Service Registry は、商用 VMware Tanzu コンポーネントの 1 つです。 これにより、マイクロサービス・アーキテクチャの主要な考え方の 1 つである Service Discovery パターンを適用したアプリケーションの開発がとても簡単になります。 各サービスのクライアントを、手動で構成したり、特定の形式のアクセス規則を適用することは困難で、特に本番環境においては不安になります。 その代わりに Tanzu Service Registry を使用すると、登録済みのサービスをアプリケーションで動的に検出し、呼び出すことができます。

通常、Spring Boot のアプリケーションで、Service Registry や Discovery の機能を利用するためには、通常、[Service Registration and Discovery](https://spring.io/guides/gs/service-registration-and-discovery/) に記載されているよう手順で行います。  
基本的には、Eureka Service Registry のサーバを自身で作成して起動し、起動した後 Eureka Client からサーバに接続を行います。 この際クライアントから Eureka Server にアクセスする事で登録されている Eureka Client の情報を取得しアクセスすることになります。

Azure Spring Cloud を利用すると、自分自身で Eureka Server (Service Registry) のサービスを作成したり起動しなくてもよく、Azure Spring Cloud  Enterprise の作成時に Service Registry にチェックする事で簡単に利用できるようになります。

## 前提条件

 事前に、Tanzu Service Registry を有効化して Azure Spring Cloud Enterprise　をプロビジョニングしておいてください。
詳細については、「[Quickstart: Provision an Azure Spring Cloud service instance using the Enterprise tier](https://docs.microsoft.com/en-us/azure/spring-cloud/quickstart-provision-service-instance-enterprise?tabs=azure-portal)」を参照してください。


![Azure Spring Cloud Enterprise VMWare Tanzu settings](https://live.staticflickr.com/65535/51912418645_d1bd5b0502_c.jpg)

> [!NOTE]
> Tanzu Service Registry を使用するためには、Azure Spring Cloud サービス インスタンスをプロビジョニングする際に有効にする必要があります。 現在は、プロビジョニング後に有効化することはできません。

以降の説明時では、Azure Spring Cloud Enterprise の作成時に使用したリソース・グループ名、インスタンス名をそれぞれ下記に定義します。ご自身の作成した環境に合わせて適宜変数の内容を書き換えてご利用ください。

|  項目  | 変数名  |
| ---- | ---- |
|  リソース・グループ名  |  $RESOURCE-GROUP  |
|  インスタンス名  |  $AZURE_SPRING_CLOUD_NAME  |


## How to Guide の進め方

本ガイドでは、Azure Spring Cloud の Service Registry に対してサービスを登録する方法、そして別サービスから参照する方法を下記に従い説明していきます。

![How to guide Story](https://live.staticflickr.com/65535/51912418685_b05f589d54_c.jpg)

1. Service A を作成し基本的な実装方法について紹介します
2. 作成した Service A を Azure Spring Cloud にデプロイし、Service Registry に登録します
3. 別の Service B を作成し Service A を呼び出すように実装します
4. Service B をデプロイし、Service Registry に登録します
5. Service B を通じて Service A を呼び出します

## Service Registry を利用したアプリケーションの作成

### 1. Spring Boot でサービス A を実装

コチラの 「[Create Sample Service A](https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.6.4&packaging=jar&jvmVersion=11&groupId=foo.bar&artifactId=Sample%20Service%20A&name=Sample%20Service%20A&description=Demo%20project%20for%20Spring%20Boot&packageName=foo.bar.Sample%20Service%20A&dependencies=web,cloud-eureka)」 のリンクを押下してください。すると下記のような画面が表示されます。

![Spring Initializr](https://live.staticflickr.com/65535/51911801461_1c309e74a2_c.jpg)

ここで、`GENERATE` ボタンを押すと、下記のような Spring Boot のサンプル・プロジェクをが入手できます。

```text
.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── foo
    │   │       └── bar
    │   │           └── Sample
    │   │               └── Service
    │   │                   └── A
    │   │                       └── SampleServiceAApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── foo
                └── bar
                    └── Sample
                        └── Service
                            └── A
                                └── SampleServiceAApplicationTests.java
```

#### 1.1 Service Registry Client (Eureka Client) の依存ライブラリの設定を確認

Spring Cloud Service Registry を使用して　Service Registry と Discovery を管理できるようにするには、アプリケーションの `pom.xml` ファイルに依存関係を追加する必要があります。 `spring-cloud-starter-netflix-eureka-client` の依存関係が pom.xml に含まれている事を確認してください。追加されていない場合は、依存関係を追加してください。

```xml
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
```

#### 1.2 Eureka Client として実装

`SampleServiceAApplication.java` に対して `@EnableEurekaClient` アノテーションを追加し Eureka Client として作成します。

```java
    package foo.bar.Sample.Service.A;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class SampleServiceAApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SampleServiceAApplication.class, args);
    	}    
    }
```

この設定だけで、この Spring Boot のサービスは Eureka Client として動作するようになります。

#### 1.3 テスト用 REST Endpoint の作成

上記で、Service Registry にサービスを登録する事はできますが、実際のサービスが何も実装されていなければ検証ができません。そこで、他の外部サービスから呼び出しができるように、簡単な RESTful エンドポイントを一つ作成します。下記のコードを実装してください。

```java
package foo.bar.Sample.Service.A;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceAEndpoint {

    @GetMapping("/serviceA")
    public String getServiceA(){
        return "This is a result of Service A";
    }

    @GetMapping("/env")
    public Map<String, String> getEnv(){
        Map<String, String> env = System.getenv();
        return env;
    }
}
```

#### 1.4 Spring Boot アプリケーションのビルドと動作確認

以上で、簡単なサービスが出来ましたので、ソースコードをコンパイルしビルドしてください。下記の `mvn` コマンドを実行してください。

```bash
mvn clean package
```

### 2. サービス A をデプロイし Service Registry に登録

次に、上記で作成したサービス A を Azure Spring Cloud Enterprise にデプロイし Service Registry に登録する方法について紹介します。

#### 2.1 Azure Spring Cloud アプリケーションの作成

まず、Azure Spring Cloud にアプリケーションを作成します。
アプリケーションを作成するためには、`az spring-cloud app create` コマンドを使用します。
今回は、検証用に `--assign-endpoint` を指定しパブリック IP を付与しています。これにより、外部ネットワークからのアクセスが可能になります。


```azurecli
 az spring-cloud app create \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME  \
    --name serviceA \
    --instance-count 1 \
    --memory 2Gi \
    --assign-endpoint
```

#### 2.2 Service Registry への接続方法について　（バインドについて）

ここまでで Spring Boot でサービスを作成し、Azure Spring Cloud にアプリケーション作成しました。
これから、アプリケーションをデプロイして動作確認を行ってまいりますが、Service Registry を利用するために一つやらなければならない事があります。それは、Service Registry に対してアプリケーションをバインドし、Service Registry への接続情報を取得できるようにすることです。

通常、Eureka Client の実装では、Spring Boot アプリケーションの `application.properties` 設定ファイルに、下記のような接続情報の設定を記述し、サーバと接続できるようにします。

```text
eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
```
しかし上記の設定をアプリケーション内に直接記述した場合、Service Registry のサーバが変わるたびに、再編集し再度プロジェクトをビルドする必要があります。こうした手間を防ぐために、Azure Spring Cloud では Service Registry を参照するために、「[Service Registry にアプリケーションをバインド](https://docs.microsoft.com/en-us/cli/azure/spring-cloud/service-registry?view=azure-cli-latest)」 する方法で、Service Registry への接続情報を取得できるようにしています。

具体的には、Service Registry にアプリケーションをバインドする事で、Service Registry の接続情報(`eureka.client.service-url.defaultZone`) を Java の環境変数にを追加します。これにより、アプリケーションの起動時に環境変数の内容を読み込む事で Service Registry に接続ができるようになります。

実際には、JAVA_TOOL_OPTIONS に対して、下記の環境変数が追加されます。

```text
-Deureka.client.service-url.defaultZone=https://$AZURE_SPRING_CLOUD_NAME.svc.azuremicroservices.io/eureka/default/eureka 
```

#### 2.3 Service Registry にサービスをバインド

Azure Service Registry にサービスをバインドするために、`az spring-cloud service-registry bind` コマンドを実行してください。これにより、アプリケーションを Service Registry にバインドし、サーバに接続できるようになります。

```azurecli
az spring-cloud service-registry bind \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    --app serviceA
```

アプリケーションのバインドは、Azure Portal からも設定できます。

![spring-cloud-service-registry-bind-app](https://live.staticflickr.com/65535/51911899908_298442ba3c_c.jpg)

> [!NOTE]
> Service Registry に対する変更が、全てのアプリケーションに伝搬するためには数分かかります。

> [!NOTE]
> アプリケーションにおいて、バインド/アンバインドのステータスを変更する場合、アプリケーションを再起動、もしくは再デプロイする必要があります。

#### 2.4 Azure Spring Cloud にアプリケーションのデプロイ

アプリケーションをバインドしたので、次に、Spring Boot の成果物ファイル (`Sample-Service-A-0.0.1-SNAPSHOT.jar`) を、Azure Spring Cloud 上にデプロイをします。デプロイするためには、下記の `az spring-cloud app deploy` コマンドを実行してください。

```azurecli
az spring-cloud app deploy \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    --name serviceA \
    --artifact-path ./target/Sample-Service-A-0.0.1-SNAPSHOT.jar \
    --jvm-options="-Xms1024m -Xmx1024m"
```

デプロイが成功しているかを確認するため、下記の `az spring-cloud app list` コマンドを実行してください

```azurecli
az spring-cloud app list \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    -o table
```

実行すると下記のような結果が表示されます。

```azurecli
Name                      Location       ResourceGroup           Public Url                                                           Production Deployment    Provisioning State    CPU    Memory    Running Instance    Registered Instance    Persistent Storage    Bind Service Registry    Bind Application Configuration Service
------------------------  -------------  ----------------------  -------------------------------------------------------------------  -----------------------  --------------------  -----  --------  ------------------  ---------------------  --------------------  -----------------------  ----------------------------------------
servicea                  southeastasia  $RESOURCE-GROUP         https://$AZURE_SPRING_CLOUD_NAME-servicea.azuremicroservices.io      default                  Succeeded             1      2Gi       1/1                 N/A                    -                     -                        -
```

#### 2.5 サービス A アプリケーションの動作確認

上記コマンドの実行結果より、`Public Url` に記載されている URL に、実装した RESTful エンドポイント(`/serviceA`) を付け加えてアクセスしてください。成功すると下記のような文字列が返ってきます。

```bash
curl https://AZURE_SPRING_CLOUD_NAME-servicea.azuremicroservices.io/serviceA
This is a result of Service A
```

サービス A では、環境変数の一覧を表示する RESTful エンドポイントも作成しました。環境変数を確認するために、エンドポイント (`/env`) にアクセスしてください。
すると環境変数の一覧が表示されます。

```bash
curl https://$AZURE_SPRING_CLOUD_NAME-servicea.azuremicroservices.io/env
```

表示された結果を確認すると、Java の起動オプションに (`JAVA_TOOL_OPTIONS`)、`eureka.client.service-url.defaultZone` が追加されている事を確認できます。これにより、アプリケーションは Service Registry にサービスを登録し、他のサービスから利用できるようになります。

```text
"JAVA_TOOL_OPTIONS":"-Deureka.client.service-url.defaultZone=https://$AZURE_SPRING_CLOUD_NAME.svc.azuremicroservices.io/eureka/default/eureka
```

以上で、サービスを Azure Spring Cloud の Service Registry (Eureka Server) に登録できました。これで他のサービスから、Service Registry を使用して該当サービスにアクセスできるようになります。

### 3. サービス B から Service Registry 経由でサービス A にアクセスするプログラムの実装

#### 3.1 Spring Boot でサービス B を実装

1.1 と同様、コチラの 「[Create Sample Service B](https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.6.4&packaging=jar&jvmVersion=11&groupId=foo.bar&artifactId=Sample%20Service%20B&name=Sample%20Service%20B&description=Demo%20project%20for%20Spring%20Boot&packageName=foo.bar.Sample%20Service%20B&dependencies=web,cloud-eureka)」 のリンクから `GENERATE` ボタンを押下し、新しいサービス B のプロジェクトを作成してください。

#### 3.2 サービス B を Eureka Client として実装

Service A と同様に Service B も `@EnableEurekaClient` のアノテーションを追加し Eureka Client として実装します。

```java
package foo.bar.Sample.Service.B;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;


@SpringBootApplication
@EnableEurekaClient
public class SampleServiceBApplication {

	public static void main(String[] args) {
		SpringApplication.run(SampleServiceBApplication.class, args);
	}
}
```

#### 3.3 Service B のエンドポイントを実装

次に、Service A を呼び出すためのエンドポイント (`/invoke-serviceA`) を実装します。ここでは、サンプルを簡単にするため `RestTemplate` でサービス A を呼び出し、サービス B から呼び出された事がわかるように、文字列 (`"INVOKE SERVICE A FROM SERVICE B: "`) を追加して返しています。

また、検証のため `/list-all` のエンドポイントも追加しています。これは、サービスが正しく Service Registry と通信を行えているかを確認するために実装しています。このエンドポイントを呼び出すと Service Registry に登録されているアプリケーションの一覧を取得できます。

```java
package foo.bar.Sample.Service.B;
import java.util.List;
import java.util.stream.Collectors;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ServiceBEndpoint {
    @Autowired
    private EurekaClient discoveryClient;
    
    @GetMapping(value = "/invoke-serviceA")
    public String invokeServiceA() 
    {   
        RestTemplate  restTemplate = new RestTemplate();
        String response = restTemplate.getForObject("http://servicea/serviceA",String.class);   
        return "INVOKE SERVICE A FROM SERVICE B: " + response;
    }

    @GetMapping(value = "/list-all")    
    public List<String> listsAllServices() {
        Applications applications = discoveryClient.getApplications();
        List<Application> registeredApplications = applications.getRegisteredApplications();
        List<String> appNames = registeredApplications.stream().map(app -> app.getName()).collect(Collectors.toList());
        return appNames;
    }
}
```

#### 3.4 Service B のソースコードのビルド

下記のコマンドを実行し、Maven プロジェクトをビルドしてください。

```bash
mvn clean package
```

### 4. Service B を　Azure Spring Cloud にデプロイし、Service Registry に登録

サービス B をデプロイするために、Azure Spring Cloud にアプリケーションを作成してください。

```azurecli
az spring-cloud app create \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    --name serviceB \
    --instance-count 1 \
    --memory 2Gi \
    --assign-endpoint
```

アプリケーションを作成した後、次に Service Registry にアプリケーションをバインドしてください。

```azurecli
az spring-cloud service-registry bind \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    --app serviceB
```

バインドが完了したのち、サービスをデプロイしてください。

```azurecli
az spring-cloud app deploy \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    --name serviceB \
    --artifact-path ./target/Sample-Service-B-0.0.1-SNAPSHOT.jar \   --jvm-options="-Xms1024m -Xmx1024m"
```

デプロイが完了した後、アプリケーションの状況を確認してください。

```azurecli
 az spring-cloud app list \
    -g $RESOURCE-GROUP \
    -s $AZURE_SPRING_CLOUD_NAME \
    -o table
```

サービス A, サービス B が正しくデプロイされている場合、下記のような画面が表示されます。

```text
Name      Location       ResourceGroup           Public Url                                                    Production Deployment    Provisioning State    CPU    Memory    Running Instance    Registered Instance    Persistent Storage    Bind Service Registry    Bind Application Configuration Service
--------  -------------  ----------------------  ------------------------------------------------------------  -----------------------  --------------------  -----  --------  ------------------  ---------------------  --------------------  -----------------------  ----------------------------------------
servicea  southeastasia  SpringCloud-Enterprise  https://$AZURE_SPRING_CLOUD_NAME-servicea.azuremicroservices.io  default                  Succeeded             1      2Gi       1/1                 1/1                    -                     default                  -
serviceb  southeastasia  SpringCloud-Enterprise  https://$AZURE_SPRING_CLOUD_NAME-serviceb.azuremicroservices.io  default                  Succeeded             1      2Gi       1/1                 1/1                    -                     default                  -
```


### 5. Service B から Service A を呼び出す

上記のコマンドの実行結果から、サービス B の URL に対してアクセスします。この時、エンドポイント (`/invoke-serviceA`) を追加して実行してください。すると ’INVOKE SERVICE A FROM SERVICE B: This is a result of Service A’ の文字列が返されている事を確認できます。  
この実行結果より、サービス B からサービス A を呼び出し、その結果が正しく返ってきている事が確認できました。

```bash
curl https://$AZURE_SPRING_CLOUD_NAME-serviceb.azuremicroservices.io/invoke-serviceA

INVOKE SERVICE A FROM SERVICE B: This is a result of Service A
```

ここで改めて、サービス B の実装ソースコードを確認してみます。

```java
String response = restTemplate.getForObject("http://servicea/serviceA",String.class);   
```

このように、プログラム上 Azure Spring Cloud のアプリケーションを作成する際に指定した名前で、アクセスしています。(例：`az spring-cloud app create --name ServiceA`)  
アプリケーション名と Service Registry に登録するサービス名が一致しているため、サービス名の管理もやりやすくなります。

#### 5.1 サービス から Service Registry の情報を参照

最後に、`/list-all` のエンドポイントにアクセスし、サービスから Service Registry の情報を取得します。ここでは、Service Registry に登録されているサービスの一覧を取得しています。

```bash
 curl https://$AZURE_SPRING_CLOUD_NAME-serviceb.azuremicroservices.io/list-all       
["SERVICEA","EUREKA-SERVER","SERVICEB"]
```

このように、必要に応じてプログラム上からも細かな情報を取得する事ができるようになっています。

## Next steps

- [Create Roles and Permissions](https://docs.microsoft.com/azure/spring-cloud/how-to-permissions?tabs=Azure-portal)

---
※ この記事の内容は、将来的に英語にして下記のコンテンツに置き換えたいと考えています。
https://docs.microsoft.com/en-us/azure/spring-cloud/how-to-enterprise-service-registry
---