Local cluster for EKS on AWS Outpostsについて紹介

はじめに

こんにちは、イノベーションセンターの鈴ヶ嶺です。

本記事では、自社データセンター等でAWSサービスを利用可能なAWS OutpostsにおけるElastic Kubernetes Service(EKS)で新たに追加されたLocal clusterの概要や通信切断検証の結果について紹介します。

以前記事にした2022年3月17日時点では、ワーカーノードはAWS Outposts上で実行し、KubernetesコントロールプレーンはAWS Cloud上で実行する構成でした。この構成がLocal clusterによってKubernetesコントロールプレーンもAWS Outposts上で実行可能になりました。これによりAWS CloudとAWS Outposts間の通信切断などによるアプリケーションのダウンタイムのリスクを軽減することが可能となりました。

engineers.ntt.com

今までのEKS on Outpostsの問題点

上図に、今までのAWS Outposts上でEKSを利用する構成を示しています。現在ではExtended clusterと命名されています。 この構成では、KubernetesコントロールプレーンがAWS Cloud上に存在しています。 このことからAWS CloudとAWS Outpostsのファイバー切断などの通信切断やAWS Cloud上で障害が起きた場合にKubernetesの操作が不可能となる問題がありました。 データセンターに設置するAWS Outpostsの性質を考慮するとEKS on Outpostsには、自律的に動作をして外部の障害に対して頑健であることが求められます。

EKS Local cluster on Outposts

上図にAWS Outposts上でKubernetesコントロールプレーンを実行可能となったLocal clusterの構成を示しています。 この構成ではAWS CloudとAWS Outposts間で通信を必要としていません。 これにより先に述べた外部の障害に対してKubernetesの操作のダウンタイムを抑えた頑健なシステムが構築可能となります。

Extended clusterとLocal clusterの違い

ここでは従来のExtended clusterとLocal clusterの主な違いを次の表にまとめました。詳細はこちらのデプロイオプションの比較を参照してください。

Extended cluster Local cluster
Kubernetes version 1.23, 1.22, 1.21, 1.20から選択可能(2022/10/31時点) 1.21のみ
Amazon EKS 最適化 AMI Amazon Linux, Windows, Bottlerocket Amazon Linux
Kubernetes API サーバー認証 IAM, OIDC IAM, X.509証明書
シークレットエンベロープ暗号化 対応 非対応
サービスアカウントの IAM ロール 対応 非対応

Local clusterはKubernetesのversionやAMIが固定化されています。また、通信障害時にも動作可能とするためのX.509証明書によるAPIサーバの認証があります。こちらの認証方法については後述する通信切断の検証で説明します。

構築方法

AWS Consoleでの構築は次のように設定します。Kubernetesコントロールプレーンの場所をAWS Outpostsに選択することでLocal clusterを構築できます。 現時点では、レプリカ数は3に固定されています。

Terraformでは、次のように outpost_config を設定することでLocal clusterを構築できます。

resource "aws_eks_cluster" "outposts_local_cluster" {
  name     = "outposts-local-cluster"
  role_arn = aws_iam_role.local_cluster_role.arn

  enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]

  vpc_config {
    security_group_ids      = [aws_security_group.cluster.id]   # Amazon EKS セキュリティグループの要件および考慮事項を参照 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/sec-group-reqs.html
    subnet_ids              = [aws_subnet.outposts_subnet01.id] # AWS Outposts上のsubnetを指定
    endpoint_public_access  = false
    endpoint_private_access = true
  }

  outpost_config {
    control_plane_instance_type = "m5.large"          # Clusterのインスタンスタイプ
    outpost_arns                = [local.outpost_arn] # AWS OutpostsのARN
  }

  depends_on = [
    aws_iam_role_policy_attachment.eks_local_cluster_policy,
  ]
}

resource "aws_iam_role" "local_cluster_role" {
  name = "outposts-local-cluster"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "eks_local_cluster_policy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy"
  role       = aws_iam_role.local_cluster_role.name
}

実際に構築したLocal clusterのEKSは、次のようになります。クラスタのダッシュボードには、KubernetesコントロールプレーンにAWS OutpostsのARNが表示されています。

次のEC2一覧には、Kubernetesコントロールプレーン用のEC2([EKSの名前]-controle-plane-XXXXXXX)が立てられていることが分かります。このEC2はAWS Outposts上のリソースになります。

また、kubectlでnode情報を見ると以下のようにcontrol-planeとワーカーノードが確認できます。

おそらくcontrol-planeがNot Readyなのは以下のようにTaintsで汚染してワークロードがスケジュールされることを防ぐためだと思われます。

各インスタンスは node-role.eks-local.amazonaws.com/control-plane で汚染されているため、コントロールプレーンインスタンスでワークロードがスケジュールされることはありません。 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/eks-outposts-local-cluster-create.html

❯ kubectl get node
NAME                                            STATUS     ROLES                  AGE     VERSION
ip-10-2-1-174.ap-northeast-1.compute.internal   NotReady   control-plane,master   46h     v1.21.13
ip-10-2-1-185.ap-northeast-1.compute.internal   NotReady   control-plane,master   46h     v1.21.13
ip-10-2-1-61.ap-northeast-1.compute.internal    NotReady   control-plane,master   46h     v1.21.13
ip-10-2-2-15.ap-northeast-1.compute.internal    Ready      <none>                 46h     v1.21.14-eks-ba74326
ip-10-2-2-198.ap-northeast-1.compute.internal   Ready      <none>                 46h     v1.21.14-eks-ba74326
ip-10-2-2-23.ap-northeast-1.compute.internal    Ready      <none>                 46h     v1.21.14-eks-ba74326

通信切断検証

ここでは通信切断時にもダウンタイムなくKubernetesを動作可能かを検証します。まずAWS Outpostsに接続されたルータのBGPを切断して以下のようにサービスリンクの接続ステータスが0となっていることを確認します。

ここから次の3つのケースに分けて検証します。

  1. Extended clusterの場合
  2. Local cluster(AWS IAM認証)の場合
  3. Local cluster(X.509証明書)の場合

1. Extended clusterの場合

従来のKubernetesコントロールプレーンがAWS Cloud上にある構成の場合は次のようにサーバに接続できないというエラーが表示されます。

❯ aws eks --region [Region] update-kubeconfig --name [EKSクラスタの名前]
❯ kubectl get svc
Unable to connect to the server: net/http: TLS handshake timeout

通信切断時にExtended clusterの場合、Kubernetesの操作が不可能となります。

2. Local cluster(AWS IAM認証)の場合

KubernetesコントロールプレーンがAWS Outposts上に存在する場合でも、通信切断時にIAM認証で操作した場合は次のようにUnauthorizedというエラーが表示されます。 この事象はネットワーク切断中にAWS Cloud上のIAMを使用できないことが原因でおきます。

❯ aws eks --region [Region] update-kubeconfig --name [EKSクラスタの名前]
❯ kubectl get svc
error: You must be logged in to the server (Unauthorized)

1. Extended clusterの場合と同様に、通信切断時にLocal cluster(AWS IAM認証)の場合、Kubernetesの操作が不可能となります。

このためKubernetesコントロールプレーンをAWS Outposts上で実行するだけでは、AWS CloudとAWS Outposts間の通信切断によるKubernetesの操作のダウンタイムのリスクを軽減できません。 対策として次に記述したX.509証明書を事前に設定する必要があります。

3. Local cluster(X.509証明書)の場合

AWS CloudとAWS Outposts間の通信障害時に、アクセスするためには次のようにX.509証明書を通信障害以前に事前準備する必要があります。 詳細はこちらのネットワーク切断中のローカルクラスターへの認証を参照してください。 以下にコマンドを記述します。

openssl req -new -newkey rsa:4096 -nodes -days 365 -keyout admin.key -out admin.csr -subj "/CN=admin"

BASE64_CSR=$(cat admin.csr | base64 -w 0)
# macの場合
# BASE64_CSR=$(cat admin.csr | base64)

cat << EOF > admin-csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: admin-csr
spec:
  signerName: kubernetes.io/kube-apiserver-client
  request: ${BASE64_CSR}
  usages:
  - client auth
EOF

kubectl create -f admin-csr.yaml

kubectl certificate approve admin-csr

kubectl get csr admin-csr -o jsonpath='{.status.certificate}' | base64 --decode > admin.crt

kubectl create clusterrolebinding admin --clusterrole=cluster-admin --user=admin --group=system:masters

aws eks describe-cluster --name [EKSクラスタの名前] --query "cluster.certificateAuthority" --output text | base64 --decode > ca.crt
kubectl config --kubeconfig admin.kubeconfig set-cluster [EKSクラスタの名前] --certificate-authority=ca.crt --server [EKSクラスタのAPIエンドポイント] --embed-certs
kubectl config --kubeconfig admin.kubeconfig set-credentials admin --client-certificate=admin.crt --client-key=admin.key --embed-certs
kubectl config --kubeconfig admin.kubeconfig set-context admin@[EKSクラスタの名前] --cluster [EKSクラスタの名前] --user admin
kubectl config --kubeconfig admin.kubeconfig use-context admin@[EKSクラスタの名前] 

kubectl get svc --kubeconfig admin.kubeconfig # 確認

上記により --kubeconfig admin.kubeconfig でX.509証明書によるアクセスが可能となりました。 これにより次のように通信障害時にアクセスした場合も問題なくアクセス可能となります。

❯ kubectl get svc --kubeconfig admin.kubeconfig
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   172.20.0.1   <none>        443/TCP   5h13m

まとめ

本記事では、新たに追加されたAWS OutpostsのEKS Local clusterの概要や従来のExtended clusterとの違い、構築方法について紹介しました。 また、AWS CloudとAWS Outposts間の通信切断検証からダウンタイムなくKubernetesをローカル環境で実行可能であることを確認しました。 外部の障害に対して頑健なシステムを構築する場合には非常に効果的な機能であると思います。

© NTT Communications Corporation 2014