APC 技術ブログ

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

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

複数のAKSクラスターをまとめて管理!Azure Kubernetes Fleet Managerの誕生

はじめに

こんにちは、ACS事業部の吉川です。
先日行われたIgniteで Azure Kubernetes Fleet Manager という新サービスが発表されました。

https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-kubernetes-fleet-manager-preview/ba-p/3651516

『Fleet』は「船団」という意味の英単語で、複数のAKSクラスターを束ねる機能のようです。
どんなサービスなのか、さっそく触ってみました。
手順などは以下の公式ドキュメントを参考にしていますが、パラメーターなど一部を変更しています。

learn.microsoft.com

learn.microsoft.com

learn.microsoft.com

検証環境

検証するにあたり以下のような構成を作成します。

まずはAzure Kubernetes Fleet Manager(以下、Fleet Managerと表記します)を作成しましょう。

# サブスクリプションでFleetResourcePreviewを有効化
az feature register --namespace Microsoft.ContainerService --name FleetResourcePreview

# Azure CLIのfleet拡張機能をインストール
az extension add --name fleet

# リソースグループ作成
az group create -l japaneast -n rg-fleetmanager

# Fleet Managerを作成
az fleet create -n kfm-sample -g rg-fleetmanager -l japaneast

なお、Fleet Mnager自身もひとつのAKSクラスターで構成されています。
ポータルから確認すると FL_<リソースグループ名>_<リソース名>_<リージョン名> という名前のリソースグループ内に hub というAKSクラスターが確認できます。

続いてFleetに所属させるAKSクラスターを作成していきます。

# リソースグループ作成
az group create -l japaneast -n rg-japaneast1
az group create -l japaneast -n rg-japaneast2
az group create -l japanwest -n rg-japanwest1

# VNet/サブネットの作成
az network vnet create -n vnet-japaneast -g rg-japaneast1 -l japaneast \
  --address-prefixes 192.168.0.0/16
az network vnet subnet create -n snet-japaneast1 \
  --vnet-name vnet-japaneast -g rg-japaneast1 \
  --address-prefixes 192.168.0.0/24
az network vnet subnet create -n snet-japaneast2 \
  --vnet-name vnet-japaneast -g rg-japaneast1 \
  --address-prefixes 192.168.1.0/24

az network vnet create -n vnet-japanwest -g rg-japanwest1 -l japanwest \
  --address-prefixes 192.168.0.0/16
az network vnet subnet create -n snet-japanwest1 \
  --vnet-name vnet-japanwest -g rg-japanwest1 \
  --address-prefixes 192.168.10.0/24

# AKSクラスターを3つ作成
SUBNET_ID1=$(az network vnet subnet show -n snet-japaneast1 \
  --vnet-name vnet-japaneast -g rg-japaneast1 --query id -o tsv)
az aks create -g rg-japaneast1 -n aks-fleetmember1 -l japaneast \
  --node-count 1 --network-plugin azure --vnet-subnet-id $SUBNET_ID1

SUBNET_ID2=$(az network vnet subnet show -n snet-japaneast2 \
  --vnet-name vnet-japaneast -g rg-japaneast1 --query id -o tsv)
az aks create -g rg-japaneast2 -n aks-fleetmember2 -l japaneast \
  --node-count 1 --network-plugin azure --vnet-subnet-id $SUBNET_ID2

SUBNET_ID3=$(az network vnet subnet show -n snet-japanwest1 \
  --vnet-name vnet-japanwest -g rg-japanwest1 --query id -o tsv)
az aks create -g rg-japanwest1 -n aks-fleetmember3 -l japanwest \
  --node-count 1 --network-plugin azure --vnet-subnet-id $SUBNET_ID3

# 認証情報を取得
az aks get-credentials -g rg-japaneast1 -n aks-fleetmember1
az aks get-credentials -g rg-japaneast2 -n aks-fleetmember2
az aks get-credentials -g rg-japanwest1 -n aks-fleetmember3

作成した3つのAKSクラスターを、Fleet Managerのメンバーとして登録していきます。

AKS_ID1=$(az aks show -g rg-japaneast1 -n aks-fleetmember1 --query id -o tsv)
az fleet member create --fleet-name kfm-sample -g rg-fleetmanager \
  --name aks-fleetmember1 --member-cluster-id $AKS_ID1

AKS_ID2=$(az aks show -g rg-japaneast2 -n aks-fleetmember2 --query id -o tsv)
az fleet member create --fleet-name kfm-sample -g rg-fleetmanager \
  --name aks-fleetmember2 --member-cluster-id $AKS_ID2

AKS_ID3=$(az aks show -g rg-japanwest1 -n aks-fleetmember3 --query id -o tsv)
az fleet member create --fleet-name kfm-sample -g rg-fleetmanager \
  --name aks-fleetmember3 --member-cluster-id $AKS_ID3

最後に作成したFleet ManagerにIAMロールを設定します。
ここでは私自身のユーザーを Azure Kubernetes Fleet Manager RBAC Cluster Admin ロールとして割り当てています。

これで構成の準備は完了です。
以下のようにしてFleetに所属させたメンバーの状態を確認してみましょう。 上記で触れたように、Fleet Manager自身もAKSクラスターで構成されています。そのため、Fleet Managerに対する操作も kubectlで行う というところがポイントですね。

# Fleet Managerの認証情報を取得
az fleet get-credentials -n kfm-sample -g rg-fleetmanager

# メンバーの情報を表示
kubectl get memberclusters
NAME                             JOINED   AGE
aks-fleetmember1-rg-japaneast1   True     22h
aks-fleetmember2-rg-japaneast2   True     22h
aks-fleetmember3-rg-japanwest1   True     22h

さて、Fleet Managerの主な機能は以下の2つです。

  • リソースの伝達
  • L4負荷分散

作成した環境をもとに、それぞれの機能を確認していきましょう。

機能1. リソースの伝達

1つ目の機能 リソースの伝達 では、Fleet ManagerからメンバーのAKSクラスターに対しリソースのデプロイ情報を伝搬することができます。
さっそく試してみましょう。

まずはFleet Managerに対し、以下のマニフェストをデプロイします。

apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kuard
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: kuard
  template:
    metadata:
      labels:
        app: kuard
    spec:
      containers:
        - name: kuard
          image: gcr.io/kuar-demo/kuard-amd64:blue
---
apiVersion: v1
kind: Service
metadata:
  name: kuard
  namespace: demo
  labels:
    app: kuard
spec:
  selector:
    app: kuard
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
# Fleet Managerのクラスターに対しデプロイ
kubectl config use-context hub
kubectl apply -f propagate-sample.yaml

Fleet Managerクラスターの状態を見ると、Load BalancerのIPはpendingのまま、Podは起動していません。
Fleet Manager自身はアプリケーションを稼働させるための環境ではないので、これが正常な状態です。

kubectl get all -n demo
NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kuard   LoadBalancer   10.0.130.141   <pending>     80:31202/TCP   35s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kuard   0/2     0            0           37s

Fleet Managerからメンバークラスターにリソースを伝達させるために、ClusterResourcePlacement というカスタムリソースを作成します。
以下のマニフェストはFleet Managerのdemo 名前空間のリソースをメンバーへ伝達するためのものです。

apiVersion: fleet.azure.com/v1alpha1
kind: ClusterResourcePlacement
metadata:
  name: demo-crp
spec:
  resourceSelectors:
  - group: ""
    version: v1
    kind: Namespace
    name: demo

これをFleet Managerクラスターにデプロイします。

kubectl apply -f crp-1.yaml

以下のコマンドで、SCHEDULEDとAPPLIEDの項目が True となっていることを確認します。

kubectl get clusterresourceplacement demo-crp
NAME       GEN   SCHEDULED   SCHEDULEDGEN   APPLIED   APPLIEDGEN   AGE
demo-crp   1     True        1              True      1            105s

これでメンバークラスターに対しリソースが伝達されています。
状態を確認してみましょう。

【aks-fleetmember1】

kubectl config use-context aks-fleetmember1
kubectl get all -n demo
NAME                         READY   STATUS    RESTARTS   AGE
pod/kuard-6fcbf7b5b5-rrvnl   1/1     Running   0          4m24s
pod/kuard-6fcbf7b5b5-xsbnk   1/1     Running   0          4m24s

NAME            TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
service/kuard   LoadBalancer   10.0.57.103   @@@.@@@.@@@.@@@   80:31729/TCP   4m24s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kuard   2/2     2            2           4m24s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/kuard-6fcbf7b5b5   2         2         2       4m24s

【aks-fleetmember2】

kubectl config use-context aks-fleetmember2
kubectl get all -n demo
NAME                         READY   STATUS    RESTARTS   AGE
pod/kuard-6fcbf7b5b5-dw47x   1/1     Running   0          6m
pod/kuard-6fcbf7b5b5-lr797   1/1     Running   0          6m

NAME            TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
service/kuard   LoadBalancer   10.0.70.168   @@@.@@@.@@@.@@@   80:30781/TCP   6m1s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kuard   2/2     2            2           6m1s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/kuard-6fcbf7b5b5   2         2         2       6m1s

【aks-fleetmember3】

kubectl config use-context aks-fleetmember3
kubectl get all -n demo
NAME                         READY   STATUS    RESTARTS   AGE
pod/kuard-6fcbf7b5b5-n8qjw   1/1     Running   0          6m35s
pod/kuard-6fcbf7b5b5-vh6dm   1/1     Running   0          6m35s

NAME            TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
service/kuard   LoadBalancer   10.0.233.14   @@@.@@@.@@@.@@@   80:32022/TCP   6m35s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kuard   2/2     2            2           6m35s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/kuard-6fcbf7b5b5   2         2         2       6m36s

上記のように、Fleet Managerのデプロイ情報が配下のメンバーに対し伝達されていることがわかると思います。

Fleet Manager側に変更を加えるとメンバー側にも変更が伝達されます。

# Fleet Manager側でレプリカ数を2→1へ変更
kubectl config use-context hub
kubectl scale deployment --replicas=1 kuard -n demo

# aks-fleetmember1でレプリカ数を確認
kubectl config use-context aks-fleetmember1
kubectl get deployment kuard -n demo
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
kuard   1/1     1            1           18m

また、ClusterResourcePlacementにpolicyの記述を加えることで、リソースの伝達範囲を制限することができます。
以下のように変更することで、東日本リージョンのクラスターのみに限定することができます。
(既に伝達済みの西日本リージョンのリソースは削除されます)

apiVersion: fleet.azure.com/v1alpha1
kind: ClusterResourcePlacement
metadata:
  name: demo-crp
spec:
  resourceSelectors:
  - group: ""
    version: v1
    kind: Namespace
    name: demo
  policy:
    affinity:
      clusterAffinity:
        clusterSelectorTerms:
          - labelSelector:
              matchLabels:
                fleet.azure.com/location: japaneast
# 東日本のみに伝達範囲を制限
kubectl config use-context hub
kubectl apply -f crp-2.yaml

# 西日本のaks-fleetmember3で状態確認
kubectl config use-context aks-fleetmember3
kubectl get namespaces

伝達されたdemo名前空間がなくなっていることが確認できます。

NAME              STATUS   AGE
default           Active   23h
fleet-system      Active   23h
kube-node-lease   Active   23h
kube-public       Active   23h
kube-system       Active   23h

機能2. L4負荷分散

2つ目の機能 L4負荷分散 ではロードバランサーで複数のクラスターにまたがった負荷分散を実現します。
この機能を利用する条件として以下がありますのでご注意ください。

  • Azure CNIを利用すること
  • 負荷分散対象のクラスターが同一のVNet or ピアリングされたVNetに配置されていること

今回は同一VNetに配置したaks-fleetmember1, aks-fleetmember2の2つのクラスター間での負荷分散を確認します。

負荷分散を行うためには、まず ServiceExport という名前のカスタムリソースを作成する必要があります。

apiVersion: networking.fleet.azure.com/v1alpha1
kind: ServiceExport
metadata:
  name: kuard
  namespace: demo

これをFleet Managerにデプロイします。
前節の伝達設定により、東日本リージョンのメンバーであるaks-fleetmember1, aks-fleetmember2に対しリソースが伝達されます。

kubectl config use-context hub
kubectl apply -f service-export.yaml

続いて MultiClusterService というカスタムリソースのマニフェストを準備します。

apiVersion: networking.fleet.azure.com/v1alpha1
kind: MultiClusterService
metadata:
  name: kuard
  namespace: demo
spec:
  serviceImport:
    name: kuard

これを 負荷分散対象のメンバークラスターのどれか1つ にデプロイします。
今回はaks-fleetmember1を利用します。

kubectl config use-context aks-fleetmember1
kubectl apply -f multi-cluster-service.yaml

以下のコマンドで負荷分散用のIPアドレスを確認します。

kubectl get multiclusterservice -n demo
NAME    SERVICE-IMPORT   EXTERNAL-IP      IS-VALID   AGE
kuard   kuard            @@@.@@@.@@@.@@@  True       77s

このIPアドレスに対してリクエストを投げてみましょう。
サンプルでデプロイしたコンテナは自身のホスト名・IPアドレスを返してくれるため、レスポンスからどちらのクラスターで動いているかが判別可能です。

curl @@@.@@@.@@@.@@@ -s | grep addrs

今回の環境では 192.168.0.0/24 であれば aks-fleetmember1、

var pageContext = {"urlBase":"","hostname":"kuard-6fcbf7b5b5-rrvnl","addrs":["192.168.0.13"],~以下略~

192.168.1.0/24 であれば aks-fleetmember2 であると判別できます。

var pageContext = {"urlBase":"","hostname":"kuard-6fcbf7b5b5-lr797","addrs":["192.168.1.29"],~以下略~

これで2つのクラスター上のPodに対し負荷分散できていることが確認できました。

おわりに

複数のAKSクラスターを管理するのに便利に使えそうな Azure Kubernetes Fleet Manager の紹介でした。
特にリソースの伝達機能は、例えばDR環境で複数リージョンに配置したクラスターに同一アプリケーションをデプロイするなどの用途に向いていると思います。
L4負荷分散機能は、Application Gatewayなどの外部のLBサービスを経由しなくても複数クラスタ間での負荷分散ができるのは便利そうですね。
まだプレビューの機能ではありますが、今後のロードマップについてGitHub上で確認することができます。

github.com

今後の機能追加が楽しみですね。引き続きウォッチしていきたいと思います。

本記事の投稿者: 吉川 俊甫
AKS/ACAをメインにインフラ系のご支援を担当しています。
Shunsuke Yoshikawa - Credly