APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Azure Private Link Service でネットワークアドレスの重複を許容する

はじめに

こんにちは。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 LinkPrivate EndpointPrivate 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 される

learn.microsoft.com

いくつか制限事項があります。特に大きなポイントは以下です。

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 以外の選択も可能です。

learn.microsoft.com

デザインパターン

Private Link Service の利用時のデザインパターンをいくつか紹介します。

参考として ExpressRoute で記載していますが、もちろんただピアリングされた VNet でも構いません。何らかの理由で直接接続したくないネットワーク群から、アプリケーションへの接続が必要な場合に利用できます。

1 対 N パターン

まずは 1 対 N パターン です。構成イメージは以下の通りです。

オンプレミスの既存ネットワークに殆ど影響しない形で Azure と接続することができる構成です。

On-Premises NetworkHub Network はアドレス重複が許容されませんが、それ以外はどのようなアドレスを用いても問題ありません。極端な例を挙げれば Hub Network192.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 #nHub Network #n 間のみです。On-Premises Network #n は重複していてもよく、さらに On-Premises #n 間は行き来ができないことが特徴です。

N 対 N パターン

最後は N 対 N パターン です。構成イメージは以下の通りです。

これまで紹介したパターンの複合系です。
管理ポリシーが異なる複数のネットワークに対して、複数のアプリケーションを公開できます。

実現できることや制約は基本的に N 対 1 パターンと同じです。

サンプル構成

下記のドキュメントでクイックスタートが紹介されています。

learn.microsoft.com

それだけでは味気ないので 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 で実施します。

learn.microsoft.com

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 を作成します。

# 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 からブラウザでアクセスしています。

pe-pls-hub-001 の IP アドレス (192.168.10.4) にアクセスした結果

pe-pls-hub-002 の IP アドレス (192.168.10.5) にアクセスした結果

アドレス帯が重複したネットワークのアプリケーションにも問題なくアクセスできました。

おわりに

Private Link Service を使ったネットワークアドレス重複の許容を紹介しました。

頻出はしませんがとても便利なサービスです。複雑なネットワーク構成を考える際は、手段のひとつとして抱えておくと柔軟性が増します。積極的に使っていきましょう!といった性質のものではないですが、覚えておけばいざという時に役に立ちます。

次の機会では Application Gateway の Private Link を試してみようと思います。

learn.microsoft.com

私達ACS事業部はAzure・AKSを活用した内製化のご支援をしております。
ご相談等ありましたらぜひご連絡ください。

www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です!
切磋琢磨しながらスキルを向上できる、エンジニアには良い環境だと思います。
ご興味を持っていただけたら嬉しく思います。

www.ap-com.co.jp

本記事の投稿者: 中島 勇人
AKSをメインにインフラ系のご支援を担当しています。ネットワーク系の技術が好きです。