AzureCosmosDBEmulator for DockerにJavaで繋いでみる

# はじめに

AzureCosmosDB Emulatorを使ってみたのですが、割とハマったのでメモを残しておきます。
AzureCosmosDB for DockerはDocker for Windowsからのみ利用可能です。

# AzureCosmosDB Emulatorを起動する

目を疑ったのですが6GBあります・・・。
共用NWでやっている方は夜間とかにやりましょう。

```bash
docker pull microsoft/azure-cosmosdb-emulator
```

ディレクトリを作成する

```bash
md %LOCALAPPDATA%\CosmosDBEmulator\bind-mount
```

起動する

```
docker run --name azure-cosmosdb-emulator --memory 2GB --mount "type=bind,source=%LOCALAPPDATA%\CosmosDBEmulator\bind-mount,destination=C:\CosmosDB.Emulator\bind-mount" --interactive --tty -p 8081:8081 -p 8900:8900 -p 8901:8901 -p 8902:8902 -p 10250:10250 -p 10251:10251 -p 10252:10252 -p 10253:10253 -p 10254:10254 -p 10255:10255 -p 10256:10256 -p 10350:10350 microsoft/azure-cosmosdb-emulator
```

ここでエラーが発生

```
docker: Error response from daemon: failed to create endpoint azure-cosmosdb-emulator on network nat: HNS failed with error : プロセスはファイルにアクセスできません。 別のプロセスが使用中です。.
```

はじめはさっぱりわからなかったのですが、ホスト側のポート（私の場合はマカフィー）と被ってしまったようでして、docker runするときのポートの設定を変えることでうまくいきました。

うまく起動するとpowershellモードになってこんなものが出力されます。
**powershellモードを解除するとdocker stopした状態になりますので注意してください。**

```
Key   : GremlinEndpoint
Value : {http://bd876a98b564:8901/, http://172.30.166.171:8901/}
Name  : GremlinEndpoint


Key   : TableEndpoint
Value : {https://bd876a98b564:8902/, https://172.30.166.171:8902/}
Name  : TableEndpoint


Key   : Key
Value : C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
Name  : Key


Key   : Version
Value : 2.4.5.0
Name  : Version


Key   : IPAddress
Value : 172.30.166.171
Name  : IPAddress


Key   : Emulator
Value : CosmosDB.Emulator
Name  : Emulator


Key   : CassandraEndpoint
Value : {tcp://bd876a98b564:10350/, tcp://172.30.166.171:10350/}
Name  : CassandraEndpoint


Key   : MongoDBEndpoint
Value : {mongodb://bd876a98b564:10255/, mongodb://172.30.166.171:10255/}
Name  : MongoDBEndpoint


Key   : Endpoint
Value : {https://bd876a98b564:8081/, https://172.30.166.171:8081/}
Name  : Endpoint
```

# 証明書をインポート

証明書をホストにインポートします。この手順をやらないとブラウザで管理画面に接続ができません。

```
C:\Users\xxxx\AppData\Local\CosmosDBEmulator\bind-mount>powershell .\importcert.ps1

Importing self-signed certificate generated by the Azure Cosmos DB Emulator runnning in container a9ed9a9e172b to the certificate store on XXXXXX
```

# Explorerにつないでみる

Azure CosmosDBのDataExplorerとそっくりのWEBコンソールを開くことができます。
先ほどのログでEndpointって出てきたアドレス＋_explorer/index.htmlにアクセスしてみます。

```bash
https://172.30.166.177:8081/_explorer/index.html
```

こんな画面がでてくればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57686/118778a3-ca9d-30e6-d678-e4b67993daa1.png)

# JavaからCosmosDBのエミュレータに接続する

JavaでCosmosDBに接続しようとすると以下のようなエラーが発生します。

```
[pool-1-thread-1] ERROR com.microsoft.azure.documentdb.DocumentClient$DocumentDBThreadPoolExecutor  - Runnable execution exception
java.lang.IllegalStateException: com.microsoft.azure.documentdb.DocumentClientException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at com.microsoft.azure.documentdb.internal.routing.ClientCollectionCache.readCollection(ClientCollectionCache.java:35)
	at com.microsoft.azure.documentdb.internal.routing.ClientCollectionCache.getByName(ClientCollectionCache.java:41)
	at com.microsoft.azure.documentdb.internal.routing.CollectionCache$4.call(CollectionCache.java:148)
	at com.microsoft.azure.documentdb.internal.routing.CollectionCache$4.call(CollectionCache.java:145)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.microsoft.azure.documentdb.DocumentClientException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at com.microsoft.azure.documentdb.internal.GatewayProxy.performGetRequest(GatewayProxy.java:252)
	at com.microsoft.azure.documentdb.internal.GatewayProxy.doRead(GatewayProxy.java:93)
	at com.microsoft.azure.documentdb.internal.GatewayProxy.processMessage(GatewayProxy.java:371)
	at com.microsoft.azure.documentdb.DocumentClient$8.apply(DocumentClient.java:3102)
	at com.microsoft.azure.documentdb.internal.RetryUtility.executeDocumentClientRequest(RetryUtility.java:72)
	at com.microsoft.azure.documentdb.DocumentClient.doRead(DocumentClient.java:3108)
	at com.microsoft.azure.documentdb.DocumentClient.readCollection(DocumentClient.java:746)
	at com.microsoft.azure.documentdb.internal.routing.ClientCollectionCache.readCollection(ClientCollectionCache.java:33)
	... 7 more

```

「証明書をインポート」の手順で証明書はホストにはインポートされたのですが、JavaのKeyStoreに証明書が入っていないため、エラーになっているようです。

## 証明書をエクスポートする

以下のURLの手順そのままで問題ありません。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/local-emulator-export-ssl-certificates

## 証明書をインポートする

%JAVA_HOME%/lib/securityディレクトリに移動します。

証明書をキーストアに追加します。

```
keytool -keystore cacerts -importcert -alias documentdbemulatorcert -file documentdbemulatorcert.cer
```

```
キーストアのパスワードを入力してください:changeit
所有者: CN=localhost
発行者: CN=localhost
シリアル番号: 2fe969231bd7868a419cdc4067fb931a
有効期間の開始日: Tue Jul 23 09:27:31 GMT+09:00 2019終了日: Sun Jul 21 09:27:31 GMT+09:00 2024
証明書のフィンガプリント:
         SHA1: 3F:32:E0:2E:31:32:2E:C3:1B:29:11:82:F1:7B:01:21:31:A6:5D:99
         SHA256: 12:2D:B9:6A:38:44:DB:04:00:B9:AB:8E:35:A8:89:76:AD:18:0C:45:AE:01:42:3F:E6:5E:F8:21:80:53:AF:47
署名アルゴリズム名: SHA1withRSA
サブジェクト公開鍵アルゴリズム: 1024ビットRSA鍵
バージョン: 3

拡張:

#1: ObjectId: 2.5.29.17 Criticality=true
SubjectAlternativeName [
  DNSName: a9ed9a9e172b
  DNSName: localhost
  IPAddress: 172.30.166.177
  IPAddress: 127.0.0.1
  DNSName: 172.30.166.177
  DNSName: 127.0.0.1
]

この証明書を信頼しますか。 [いいえ]:  y
証明書がキーストアに追加されました
```

正常に証明書がインポートされたことを確認しておきます

```
keytool -list -keystore cacerts
```

### [参考]証明書を削除する

```
keytool -delete -keystore cacerts -alias documentdbemulatorcert
```

# おわりに

ローカルで2Gのリソースを持っていかれることと、証明書エクスポート/Keystoreへの追加の流れが割とめんどくさいので、このまま使おうか悩んでしまいました・・・。