Azure 仮想ネットワークサービスエンドポイントと Azure Private Link の併用方法

# はじめに
Azure の VNet 内の VM などから Azure PaaS リソース (Azure Storage、SQL Database など) に対して、Azure のバックボーンネットワーク経由で直接接続する方法として、**Azure 仮想ネットワークサービスエンドポイント** と **Azure Private Link** の 2 つがあります。

本記事では、**サービスエンドポイントと Private Link の両方を 1 つのリソースに設定する方法**について、ストレージアカウントを例にしてまとめたいと思います。

なお、サービスエンドポイントと Private Link の構成や挙動の違いについて、本記事でも簡単にまとめますが、以下の記事も合わせてご覧頂くと理解の助けになると思います。

 [Azure 仮想ネットワークサービスエンドポイントと Azure Private Link の挙動確認 - Qiita](https://qiita.com/nakazax/items/d1229ad3b3ca4bec95f3)

# 構成図
本記事で作成する構成図は以下の通りです。

|![qiita-service-endpoint-private-link-together.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/ec9433e9-35ab-7048-8ff8-e5abf80b2fa6.png)|
|:-:|

構成の概要は以下の通りです。

- 東日本リージョンのストレージアカウント `stnakazaxblobsepl1` を作成します
- VNet を 3 つ作成し、東日本リージョンの 1 つめの VNet のサブネット (`vnet-nakazax-private-jpeast-01`) の `VmSubnet` をサービスエンドポイントの接続元として指定します
- 東日本リージョンの 2 つめの VNet (`vnet-nakazax-private-jpeast-02`) に Private Link を設定し、プライベート IP 経由で接続できるようにします
- 東日本リージョンの 2 つめの VNet と東南アジアリージョンの VNet (`vnet-nakazax-privateseasia-01`) を VNet ピアリングで接続します。これにより、東南アジアリージョンの VNet の VM から東日本リージョンの 2 つめの VNet のプライベートエンドポイントを経由してストレージアカウントに接続します
- 東南アジアリージョンの VNet 内の VM はオンプレミスを模した環境として、名前解決に `/etc/hosts` を利用します

# 両者の違い / 使い分け / 併用のアイディア
本編に入る前に、サービスエンドポイントと Private Link の違い / 使い分け / 併用のアイディアについて簡単にまとめます。

## 両者の違い
サービスエンドポイントと Private Link は大まかに、名前解決の結果 / 接続元 / 料金 / 接続アーキテクチャー (Azure SQL Database & Azure Synapse Analytics - SQL Pool) という点で差異があります。

### 名前解決の結果
サービスエンドポイントはパブリック IP アドレスに、Private Link はプライベート IP アドレスに名前解決されます。いずれも通信は Azure のバックボーンネットワークを通ります。

### 接続元
サービスエンドポイントは指定した VNet のサブネットからのアクセスを許可します。一方、Private Link は指定した VNet のサブネットに仮想ネットワークインターフェイス (NIC) が作成されます。
仮想 NIC はプライベート IP アドレスを持っており、このプライベート IP にアクセス可能なネットワークから Private Link が設定された PaaS リソースにアクセスできます。
ですので、オンプレミス環境や VNet ピアリング先の VNet からも Private Link であればアクセスできるというのが特長です。

### 料金
サービスエンドポイントの使用には追加料金は掛かりません。一方、Private Link を使用する場合、プライベートエンドポイントの時間課金、送受信のデータ処理量に応じた課金が発生します。

- [料金と制限 - Azure 仮想ネットワーク サービス エンドポイント | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-service-endpoints-overview#pricing-and-limits)
- [価格 - Azure Private Link | Microsoft Azure](https://azure.microsoft.com/ja-jp/pricing/details/private-link/)

### 接続アーキテクチャー
Azure SQL Database と Azure Synapse Analytics - SQL Pool では、リダイレクトモード / プロキシモード / 既定値の 3 つの接続ポリシー設定があります。サービスエンドポイントはリダイレクトモード、Private Link はプロキシモードでの接続になります。

- リダイレクトモードの場合、クライアントはデータベースをホストしているノードへの直接接続を確立します。これにより、待機時間が短縮され、スループットが向上します
- プロキシモードの場合、クライアントからのすべての接続がゲートウェイ経由でプロキシ化されるため、待機時間が長くなり、スループットが低下します
- 既定値として、VM など Azure の内部からのすべてのクライアント接続はリダイレクトモード、外部からのすべてのクライアント接続はプロキシモードになります。

[Azure SQL Database 接続アーキテクチャ - Azure SQL Database and Azure Synapse Analytics | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/azure-sql/database/connectivity-architecture#change-azure-sql-database-connection-policy)

## 使い分け / 併用のアイディア
そもそもサービスエンドポイントと Private Link のどちらかしかサポートしていないサービスの場合は自動的にどちらを利用するかが決まってきますが、Azure Storage や SQL Database のように両方をサポートしている場合には、要件に応じて判断することになります。

例えば、特定のサブネットからの通信のみ許可したいというケースであればサービスエンドポイントから始めてみる、オンプレミス環境から直接 PaaS リソースにアクセスしたいというケースであれば Private Link を検討するのが適当でしょう。

サービスエンドポイントと Private Link の設定は後からでも変更することができます。また本記事で紹介するように両者を組み合わせて利用することもできます。まずは素早く構築してみて、要件が出てきたらどちらか、あるいは両方を検討するということでも問題ないと思います。

# 事前作業
前置きが長くなりましたが、ここから本編です。事前作業として以下を実施していきます。

1. 仮想ネットワーク (VNet) & サブネット作成
2. 仮想マシン (VM) 作成
3. VNet ピアリング設定
4. VM 同士の疎通確認
5. VM に Azure CLI をインストール

### VNet & サブネット作成
3 つの VNet を作成します。

(vnet-nakazax-private-jpeast-01)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/25d195b9-89ea-7534-9f98-88c3b273e959.png)

(vnet-nakazax-private-jpeast-02)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/9c582615-a128-0ec3-a8c0-4df02c7604d0.png)

(vnet-nakazax-private-seasia-01)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/d41702c1-39df-857a-2339-61e367e98b10.png)

### VM 作成
各 VNet の `VmSubnet` に VM を 1 台ずつ作成します。

(vm-nakazax-private-jpeast-01)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/50649fbd-ae59-aae1-4393-783a4d1de7b5.png)

(vm-nakazax-private-jpeast-02)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/e5ef2b97-e540-4928-3843-30a9f101e147.png)

(vm-nakazax-private-seasia-01)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/c3eb9749-fff4-fe07-6a78-3abe39ba1758.png)

### VNet ピアリング設定
東日本リージョンの 2 番目の VNet `vnet-nakazax-private-jpeast-02` と東南アジアリージョンの VNet `vnet-nakazax-private-seasia-01` の間を VNet ピアリングで接続します。

東日本リージョン側の VNet でピアリングを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/ddab7ad2-2a2e-7d9b-7019-4915cf5d8fc4.png)

ピアリングの追加画面です。必要な情報を入力して [OK] をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/8989ad9c-7a98-8671-27bc-c520d3670353.png)

少々待つと VNet ピアリングが作成されます。以下は東日本リージョンの VNet からのビューです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/4454844f-29df-dba3-7269-348840c6151e.png)

以下は東南アジアリージョンの VNet からのビューです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/055c7893-6ce2-1aac-830f-4e55847e70b5.png)

### VM 間の疎通確認
これで東日本リージョンの 2 番目の VNet `vnet-nakazax-private-jpeast-02` と東南アジアリージョンの VNet `vnet-nakazax-private-seasia-01`  内の VM 同士で疎通できるようになったはずなので確認してみます。

(`vm-nakazax-private-jpeast-02` -> `vm-nakazax-private-seasia-01` への Ping : 成功)

```
$ ping 192.168.10.4
PING 192.168.10.4 (192.168.10.4) 56(84) bytes of data.
64 bytes from 192.168.10.4: icmp_seq=1 ttl=64 time=79.1 ms
64 bytes from 192.168.10.4: icmp_seq=2 ttl=64 time=79.0 ms
64 bytes from 192.168.10.4: icmp_seq=3 ttl=64 time=78.7 ms
^C
--- 192.168.10.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 78.765/78.969/79.120/0.274 ms
```

(`vm-nakazax-private-seasia-01` -> `vm-nakazax-private-jpeast-02` への Ping : 成功)

```
$ ping 10.2.10.4
PING 10.2.10.4 (10.2.10.4) 56(84) bytes of data.
64 bytes from 10.2.10.4: icmp_seq=1 ttl=64 time=79.3 ms
64 bytes from 10.2.10.4: icmp_seq=2 ttl=64 time=78.8 ms
64 bytes from 10.2.10.4: icmp_seq=3 ttl=64 time=78.9 ms
^C
--- 10.2.10.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 78.888/79.074/79.385/0.318 ms
```

当然ですが、VNet ピアリングを設定していない VNet 間の疎通は失敗します。
(以下は `vm-nakazax-private-jpeast-01` -> `vm-nakazax-private-jpeast-02` への Ping)

```
$ ping 10.2.10.4
PING 10.2.10.4 (10.2.10.4) 56(84) bytes of data.
^C
--- 10.2.10.4 ping statistics ---
68 packets transmitted, 0 received, 100% packet loss, time 68614ms
```

### VM に Azure CLI をインストール
この後の動作確認で使うため 3 台の VM に Azure CLI をインストールします。
https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-apt?view=azure-cli-latest

```
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
```

# ストレージアカウント設定
ここから、ストレージアカウントの設定に入ります。

### ストレージアカウント作成 & サービスエンドポイント設定
まずは、東日本リージョンの 1 番目の VNet `vnet-nakazax-private-jpeast-01` の `VmSubnet` に対するサービスエンドポイントを設定したストレージアカウントを作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/fe007e7d-b354-0d44-5c5c-0af7ce8f2d05.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/02880db2-3f78-704e-2349-765a61bcf953.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/03be4448-01a7-a6a8-761b-9c6dad528cbe.png)

Azure ポータルからテスト用コンテナーを作成しておきます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/cc652761-982d-ef08-28a0-8dc0dcf5be1a.png)|
|:-:|

サービスエンドポイント設定後の挙動は以下のようになります。

VM | 名前解決結果 | Azure CLI でのファイルリスト等
--- | --- | ---
`vm-nakazax-private-jpeast-01` | **パブリック IP** | **成功**
`vm-nakazax-private-jpeast-02` | パブリック IP | 失敗
`vm-nakazax-private-seasia-01` | パブリック IP | 失敗

(`vm-nakazax-private-jpeast-01` からの名前解決 : パブリック IP アドレスに解決される)
※ `vm-nakazax-private-jpeast-02` `vm-nakazax-private-seasia-01` も同じ結果

```
nakazax@vm-nakazax-private-jpeast-01:~$ nslookup stnakazaxblobsepl1.blob.core.windows.net
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
stnakazaxblobsepl1.blob.core.windows.net        canonical name = blob.ty1prdstr02a.store.core.windows.net.
Name:   blob.ty1prdstr02a.store.core.windows.net
Address: 40.115.231.94
```

(`vm-nakazax-private-jpeast-01` からのファイルアップロード : 成功)

```
nakazax@vm-nakazax-private-jpeast-01:~$ echo "This is example1" > example1.txt
nakazax@vm-nakazax-private-jpeast-01:~$ az storage blob upload \
> --file example1.txt \
> --name example1.txt \
> --account-name stnakazaxblobsepl1 \
> --container-name test-container \
> --account-key xxx
Finished[#############################################################]  100.0000%
{
  "etag": "\"0x8D853DE874B42FB\"",
  "lastModified": "2020-09-08T10:04:09+00:00"
}
```

(`vm-nakazax-private-jpeast-01` からのファイルリスト : 成功)

```
nakazax@vm-nakazax-private-jpeast-01:~$ az storage blob list \
> --account-name stnakazaxblobsepl1 \
> --container-name test-container \
> --account-key xxx
[
  {
    "container": "test-container",
    (省略)
    "versionId": null
  }
]
```

(`vm-nakazax-private-jpeast-02` からのファイルリスト : 失敗)
※ `vm-nakazax-private-seasia-01` も同じく失敗

```
nakazax@vm-nakazax-private-jpeast-02:~$ az storage blob list \
> --account-name stnakazaxblobsepl1 \
> --container-name test-container \
> --account-key xxx

The request may be blocked by network rules of storage account. Please check network rule set using 'az storage account show -n accountname --query networkRuleSet'.
If you want to change the default action to apply when no rule matches, please use 'az storage account update'.
```

### Private Link 作成
先ほど作成したストレージアカウントを対象として、東日本リージョンの 2 番目の VNet `vnet-nakazax-private-jpeast-02` の `PrivateLinkSubnet` にプライベートエンドポイントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/40a486db-91ca-f6e7-55ca-828d926117a1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/33adec1a-5940-2457-6bf6-35e781400584.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/489282f3-7314-e188-4b36-ef00e8765c76.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/e3e8a0d1-6e29-269f-bdea-3e1bf80e6467.png)

Private Link 設定後の挙動は以下のようになります。

VM | 名前解決結果 | Azure CLI でのファイルリスト等
--- | --- | ---
`vm-nakazax-private-jpeast-01` | パブリック IP | 成功
`vm-nakazax-private-jpeast-02` | **プライベート IP** | **成功**
`vm-nakazax-private-seasia-01` | パブリック IP | 失敗

(`vm-nakazax-private-jpeast-02` からの名前解決 : プライベート IP アドレスに解決される)

```
nakazax@vm-nakazax-private-jpeast-02:~$ nslookup stnakazaxblobsepl1.blob.core.windows.net
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
stnakazaxblobsepl1.blob.core.windows.net        canonical name = stnakazaxblobsepl1.privatelink.blob.core.windows.net.
Name:   stnakazaxblobsepl1.privatelink.blob.core.windows.net
Address: 10.2.20.4
```

(`vm-nakazax-private-jpeast-02` からのファイルリスト : 成功)

```
nakazax@vm-nakazax-private-jpeast-02:~$ az storage blob list \
> --account-name stnakazaxblobsepl1 \
> --container-name test-container \
> --account-key xxx
[
  {
    "container": "test-container",
    (省略)
    "versionId": null
  }
]
```

### `/etc/hosts` 設定
オンプレミス環境を模した東南アジアリージョンの VM `vm-nakazax-private-seasia-01` の `/etc/hosts` ファイルにストレージアカウントのプライベートエンドポイントの行を追加します。(以下の例では `localhost` の下の行)

```
nakazax@vm-nakazax-private-seasia-01:~$ cat /etc/hosts
127.0.0.1 localhost
10.2.20.4 stnakazaxblobsepl1.blob.core.windows.net

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
```

これにより当該 VM でもプライベートエンドポイント経由でのコマンドが成功するようになります。

VM | 名前解決結果 | Azure CLI でのファイルリスト等
--- | --- | ---
`vm-nakazax-private-jpeast-01` | パブリック IP | 成功
`vm-nakazax-private-jpeast-02` | プライベート IP | 成功
`vm-nakazax-private-seasia-01` | **プライベート IP** | **成功**

(`vm-nakazax-private-seasia-01` からの名前解決 : プライベート IP アドレスに解決される)

```
nakazax@vm-nakazax-private-seasia-01:~$ nslookup stnakazaxblobsepl1.blob.core.windows.net
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
Name:   stnakazaxblobsepl1.blob.core.windows.net
Address: 10.2.20.4
```

(`vm-nakazax-private-seasia-01` からのファイルリスト : 成功)

```
nakazax@vm-nakazax-private-seasia-01:~$ az storage blob list \
> --account-name stnakazaxblobsepl1 \
> --container-name test-container \
> --account-key xxx
[
  {
    "container": "test-container",
    (省略)
    "versionId": null
  }
]
```

### (Option) プライベート DNS ゾーン利用
上記手順では東南アジアリージョンの VNet をオンプレミス環境に模して扱ったため、名前解決に `/etc/hosts` を利用しました。普通に VNet として扱う場合、以下構成図のようにプライベート DNS ゾーンを関連付けて名前解決を行うことも可能です。

|![qiita-service-endpoint-private-link-together-appendix1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/905952e3-a7ab-8c0a-3ec7-2c4995efb5f8.png)|
|:-:|

VNet にプライベート DNS ゾーンを関連付ける流れは以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/217f8a03-d9e8-2a49-c72e-90379ba0e3be.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/719e0b81-7a60-6284-5421-9714eeea9cff.png)

# べからず集
本編は以上です。ここからはべからず集として、やる意味がない / やらない方が良い構成について記載します。

### 単一リソースのサービスエンドポイントと Private Link を単一 VNet に設定
単一のストレージアカウントに対して、サービスエンドポイントと (プライベート DNS ゾーンが統合された) Private Link を単一の VNet に設定した場合、VM からストレージアカウントにアクセスする際にプライベート DNS ゾーンで名前解決が行われます。その結果、プライベート IP に名前解決され Private Link の経路を通ります。サービスエンドポイントの経路は通らないため、設定する意味がありません。

|![qiita-service-endpoint-private-link-together-anti1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/598468c5-e3ea-c644-37fd-09c9b26f7c35.png)|
|:-:|

以上です。