はじめに
こんにちは。ACS 事業部の中島です。
クラウドのネットワーク管理、結構大変ですよね。
特に ExpressRoute や S2S VPN のようなオンプレミスとの接続が入ると、アドレッシングやルーティング整理が重たくのしかかります。既存のネットワークアドレスに重複しないアドレスの払い出し、広報すべき経路情報の整理、もし重複してしまった場合の対処法検討など、考慮事項が一気に膨れ上がります。
特に重複してしまった場合の対応は大変です。もちろんアドレスを変更してしまえればそれで終わりですが、背景的に何らかの制約でそうは行かないことが殆どです。個人的に経験したケースでもかなり頭を悩ませました。
今回はそんな場合の手札のひとつとして持っておきたい Azure Private Link Service を紹介します。
Private Endpoint と Private Link Service
本題に入る前に、Azure における Private Link について少し説明します。
Azure での Private Link という単語は、以下両方のサービスの総称を指します。
- Azure のサービスをプライベートアドレスから利用可能にする Private Endpoint
- 直接接続されない VNet 間を通信できるようにする Private Link Service
少しややこしい点としては、Private Link Service は仕組み上 Private Endpoint も利用します。会話の中でどの話をしているか認識齟齬が発生することがあるので、Private Link、Private Endpoint、Private Link Service はそれぞれの単語をきっちり使い分けましょう。
Private Link Service とは
Private Link Service は、簡単に言えば Private Endpoint を介して Azure Load Balancer にアクセスできるサービスです。以下の特徴があるので、VNet 間やオンプレミスとのアドレス重複を許容できます。
- Private Endpoint と Azure Load Balancer は同一 VNet に存在しなくてもよい
- それぞれが存在する VNet はピアリング接続されていなくてもよい
- Azure Load Balancer に到着した通信はサブネットの IP アドレスに SNAT される
いくつか制限事項があります。特に大きなポイントは以下です。
VM/VMSS を使用するときに NIC によってバックエンド プールが構成されている Standard Load Balancer でのみサポートされる。
この制限から Private Link Service で公開可能なアプリケーションは VM/VMSS にホストされるものに限定されます。
ただし AKS でホストしたアプリケーションは公開は可能です。AKS のノードプールは VMSS と Azure Load Balancer で公開されるので、上記の制限事項をクリアできます。また、AKS で公開する場合は Azure Load Balancer と NGINX Ingress Controller を関連付けることで、Private Link Service 経由でも Ingress を利用できます。
現在はパブリックプレビューですが、Application Gateway を用いた Private Link Service も公開されています。こちらは AGIC でも利用できるので、将来的には Ingress として NGINX Ingress Controller 以外の選択も可能です。
デザインパターン
Private Link Service の利用時のデザインパターンをいくつか紹介します。
参考として ExpressRoute で記載していますが、もちろんただピアリングされた VNet でも構いません。何らかの理由で直接接続したくないネットワーク群から、アプリケーションへの接続が必要な場合に利用できます。
1 対 N パターン
まずは 1 対 N パターン です。構成イメージは以下の通りです。
オンプレミスの既存ネットワークに殆ど影響しない形で Azure と接続することができる構成です。
On-Premises Network
と Hub Network
はアドレス重複が許容されませんが、それ以外はどのようなアドレスを用いても問題ありません。極端な例を挙げれば Hub Network
が 192.168.10.0/24
、それ以外のネットワークが全て 192.168.1.0/24
でも問題なく通信できます。
Private Link Service と Azure Load Balancer は 1 対 1 の関係です。公開したいアプリケーションの数だけそれぞれのセットが必要になる点を留意しておきましょう。ただし、ひとつの Private Link Service に複数の Private Endpoint を紐づけることは可能です。次のパターンで実際の利用例を紹介します。
N 対 1 パターン
次は N 対 1 パターン です。構成イメージは以下の通りです。
管理ポリシーが異なる複数のネットワークに対して、単一のアプリケーションを公開できます。
アドレス重複が許容されないのは On-Premises Network #n
と Hub Network #n
間のみです。On-Premises Network #n
は重複していてもよく、さらに On-Premises #n
間は行き来ができないことが特徴です。
N 対 N パターン
最後は N 対 N パターン です。構成イメージは以下の通りです。
これまで紹介したパターンの複合系です。
管理ポリシーが異なる複数のネットワークに対して、複数のアプリケーションを公開できます。
実現できることや制約は基本的に N 対 1 パターンと同じです。
サンプル構成
下記のドキュメントでクイックスタートが紹介されています。
それだけでは味気ないので 1 対 N パターン を例に AKS で Pod を公開してみます。
構成は以下の通りです。
すべての VNet のネットワークアドレスを 192.168.10.0/24
として重複させています。
では実際にリソースを作成していきましょう。Azure CLI で作成します。
変数定義
COMMON_RG_NAME="pls-test" AKS_RG_NAME="rg-pls-aks" LOCATION="japaneast" HUB_VNET_NAME="vnet-pls-hub-001" HUB_PE_SUBNET_NAME="snet-pls-hub-pe" HUB_VM_SUBNET_NAME="snet-pls-hub-vm" SPOKE_VNET_NAME="vnet-pls-spoke" SPOKE_AKS_SUBNET_NAME="snet-pls-spoke-aks" AKS_NAME="aks-pls-spoke" PLS_NAME="pls-pls-aks" PE_NAME="pe-pls-hub"
VNet & サブネット
今回利用する VNet とサブネットをまとめて作成します。
# Hub VNet と Private Endpoint 用のサブネット az network vnet create \ --name ${HUB_VNET_NAME} \ --resource-group ${COMMON_RG_NAME} \ --location ${LOCATION} \ --address-prefixes 192.168.10.0/24 \ --subnet-name ${HUB_PE_SUBNET_NAME} \ --subnet-prefixes 192.168.10.0/25 # VM 用のサブネット az network vnet subnet create \ --name ${HUB_VM_SUBNET_NAME} \ --resource-group ${COMMON_RG_NAME} \ --vnet-name ${HUB_VNET_NAME} \ --address-prefixes 192.168.10.128/25 # Spoke VNet と AKS 用のサブネット az network vnet create \ --name ${SPOKE_VNET_NAME}-001 \ --resource-group ${COMMON_RG_NAME} \ --location ${LOCATION} \ --address-prefixes 192.168.10.0/24 \ --subnet-name ${SPOKE_AKS_SUBNET_NAME} \ --subnet-prefixes 192.168.10.0/24
AKS
AKS を作成します。
# AKS 用サブネットの ID 取得 AKS_SUBNET_ID=$( az network vnet subnet show \ --resource-group ${COMMON_RG_NAME} \ --vnet-name ${SPOKE_VNET_NAME}-001 \ --name ${SPOKE_AKS_SUBNET_NAME} \ --query "id" \ --output tsv ) # AKS az aks create \ --name ${AKS_NAME}-001 \ --resource-group ${COMMON_RG_NAME} \ --location japaneast \ --node-resource-group ${AKS_RG_NAME}-001 \ --network-plugin azure \ --vnet-subnet-id ${AKS_SUBNET_ID} \ --node-vm-size Standard_B2ms \ --node-count 1 \ --generate-ssh-keys
NGINX Ingress Controller
AKS の作成が完了したら NGINX Ingress Controller をデプロイします。
下記 URL を参考に Helm で実施します。
Internal Load Balancer を構成するために annotations
には service.beta.kubernetes.io/azure-load-balancer-internal: "true"
を指定しましょう。
# annotations を渡して Internal Load Balancer を構成 helm install ingress-nginx ingress-nginx/ingress-nginx \ --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true
Private Link Service & Private Endpoint
最後に Private Link Service と Private Endpoint を作成します。
# privateLinkServiceNetworkPolicies を無効化 az network vnet subnet update \ --name ${SPOKE_AKS_SUBNET_NAME} \ --resource-group ${COMMON_RG_NAME} \ --vnet-name ${SPOKE_VNET_NAME}-001 \ --disable-private-link-service-network-policies true # Internal Load Balancer の フロントエンド IP 構成を取得 AKS_FRONT_IP_CONFIG=$( az network lb frontend-ip list \ --resource-group ${AKS_RG_NAME}-001 \ --lb-name kubernetes-internal \ --query "[0].id" \ --output tsv ) # Private Link Service az network private-link-service create \ --name ${PLS_NAME}-001 \ --resource-group ${COMMON_RG_NAME} \ --location ${LOCATION} \ --vnet-name ${SPOKE_VNET_NAME}-001 \ --subnet ${SPOKE_AKS_SUBNET_NAME} \ --lb-name kubernetes-internal \ --lb-frontend-ip-configs ${AKS_FRONT_IP_CONFIG} # Private Link Service の ID を取得 PLS_ID=$( az network private-link-service show \ --name ${PLS_NAME}-001 \ --resource-group ${COMMON_RG_NAME} \ --query "id" \ --output tsv ) # Private Link Service に紐づく Private Endpoint az network private-endpoint create \ --name ${PE_NAME}-001 \ --resource-group ${COMMON_RG_NAME} \ --connection-name PLS \ --vnet-name ${HUB_VNET_NAME} \ --subnet ${HUB_PE_SUBNET_NAME} \ --private-connection-resource-id ${PLS_ID} \ --manual-request false
Spoke Network #2 用手順
# Spoke VNet と AKS 用のサブネット az network vnet create \ --name ${SPOKE_VNET_NAME}-002 \ --resource-group ${COMMON_RG_NAME} \ --location ${LOCATION} \ --address-prefixes 192.168.10.0/24 \ --subnet-name ${SPOKE_AKS_SUBNET_NAME} \ --subnet-prefixes 192.168.10.0/24 # AKS 用サブネットの ID 取得 AKS_SUBNET_ID=$( az network vnet subnet show \ --resource-group ${COMMON_RG_NAME} \ --vnet-name ${SPOKE_VNET_NAME}-002 \ --name ${SPOKE_AKS_SUBNET_NAME} \ --query "id" \ --output tsv ) # AKS az aks create \ --name ${AKS_NAME}-002 \ --resource-group ${COMMON_RG_NAME} \ --location japaneast \ --node-resource-group ${AKS_RG_NAME}-002 \ --network-plugin azure \ --vnet-subnet-id ${AKS_SUBNET_ID} \ --node-vm-size Standard_B2ms \ --node-count 1 \ --generate-ssh-keys # annotations を渡して Internal Load Balancer を構成 helm install ingress-nginx ingress-nginx/ingress-nginx \ --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true # privateLinkServiceNetworkPolicies を無効化 az network vnet subnet update \ --name ${SPOKE_AKS_SUBNET_NAME} \ --resource-group ${COMMON_RG_NAME} \ --vnet-name ${SPOKE_VNET_NAME}-002 \ --disable-private-link-service-network-policies true # Internal Load Balancer の フロントエンド IP 構成を取得 AKS_FRONT_IP_CONFIG=$( az network lb frontend-ip list \ --resource-group ${AKS_RG_NAME}-002 \ --lb-name kubernetes-internal \ --query "[0].id" \ --output tsv ) # Private Link Service az network private-link-service create \ --name ${PLS_NAME}-002 \ --resource-group ${COMMON_RG_NAME} \ --location ${LOCATION} \ --vnet-name ${SPOKE_VNET_NAME}-002 \ --subnet ${SPOKE_AKS_SUBNET_NAME} \ --lb-name kubernetes-internal \ --lb-frontend-ip-configs ${AKS_FRONT_IP_CONFIG} # Private Link Service の ID を取得 PLS_ID=$( az network private-link-service show \ --name ${PLS_NAME}-002 \ --resource-group ${COMMON_RG_NAME} \ --query "id" \ --output tsv ) # Private Link Service に紐づく Private Endpoint az network private-endpoint create \ --name ${PE_NAME}-002 \ --resource-group ${COMMON_RG_NAME} \ --connection-name PLS \ --vnet-name ${HUB_VNET_NAME} \ --subnet ${HUB_PE_SUBNET_NAME} \ --private-connection-resource-id ${PLS_ID} \ --manual-request false
動作確認
では AKS 上にいくつかリソースをデプロイして動作確認をしてみましょう。
Spoke Network #1 には httpd
、Spoke Network #2 には nginx
をデプロイします。
# httpd.yml apiVersion: apps/v1 kind: Deployment metadata: name: httpd spec: replicas: 1 selector: matchLabels: app: httpd template: metadata: labels: app: httpd spec: containers: - name: httpd image: httpd:latest --- apiVersion: v1 kind: Service metadata: name: httpd-ip spec: type: ClusterIP selector: app: httpd ports: - port: 8000 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: httpd-ingress spec: ingressClassName: nginx rules: - http: paths: - pathType: Prefix path: "/" backend: service: name: httpd-ip port: number: 8000
# nginx.yml apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest --- apiVersion: v1 kind: Service metadata: name: nginx-ip spec: type: ClusterIP selector: app: nginx ports: - port: 8000 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-ingress spec: ingressClassName: nginx rules: - http: paths: - pathType: Prefix path: "/" backend: service: name: nginx-ip port: number: 8000
Ingress が公開する IP アドレスは以下の通りです。
重複したアドレス帯を利用していることがわかります。
# aks-pls-spoke-001 kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE httpd-ingress nginx * 192.168.10.33 80 79m
# aks-pls-spoke-002 kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE nginx-ingress nginx * 192.168.10.33 80 68m
適当な VM を snet-pls-hub-vm
に作成してそれぞれに紐づいた Private Endpoint にアクセスします。
例ではわかりやすいように Windows VM からブラウザでアクセスしています。
アドレス帯が重複したネットワークのアプリケーションにも問題なくアクセスできました。
おわりに
Private Link Service を使ったネットワークアドレス重複の許容を紹介しました。
頻出はしませんがとても便利なサービスです。複雑なネットワーク構成を考える際は、手段のひとつとして抱えておくと柔軟性が増します。積極的に使っていきましょう!といった性質のものではないですが、覚えておけばいざという時に役に立ちます。
次の機会では Application Gateway の Private Link を試してみようと思います。
私達ACS事業部はAzure・AKSを活用した内製化のご支援をしております。
ご相談等ありましたらぜひご連絡ください。
また、一緒に働いていただける仲間も募集中です!
切磋琢磨しながらスキルを向上できる、エンジニアには良い環境だと思います。
ご興味を持っていただけたら嬉しく思います。