はじめに
こんにちは。今年ももうすぐ終わりですね。
今年は色々ありましたが、技術的には K8s を学ぶことが多かったです。
実際に本番環境で運用しているわけではないので、来年以降運用などに関われたらなと思っています。
今回は最近の EKS を調査、使ってみた話を書いてみたいと思います。
私自身お家 K8s をゆるくやっているのですが、EKS に関しては去年少し触ったぐらいで、そこまでわかっていなかったですし、最近の EKS の Update も追えていませんでした。
そこで、最近の EKS の機能を調査し、使ってみた感想などを書いていきます。
ちなみに今回利用したサンプルコードはこちらです。
環境独自の値を多少埋めてもらう必要はありますが、GitHub Actions などでデプロイ可能ですので、よろしければご利用ください!
EKS Auto Mode について
まずは最近追加されたアツイ機能である EKS Auto Mode についてです。
EKS Auto Mode は 2024 年 12 月 1 日に発表された出来立てほやほやの機能です。
AWS の紹介1では以下の機能が有効になるとされています。
コンピューティングの自動スケーリングと管理
アプリケーションの負荷分散管理
ポッドとサービスのネットワーキングとネットワークポリシー
クラスター DNS と GPU のサポート
ブロックストレージボリュームのサポート
簡単にいうと、K8s の運用に必要な機能は AWS が管理、提供してくれるということです笑
もし EKS Auto Mode を使わない場合は、AWS Load Balancer Controller や AWS VPC CNI(もしくは別の CNI 準拠のプラグイン) などなどクラスターの作成とは別に多くの設定が必要です。
また、EKS の設定によっては負荷に合わせたワーカーノードの追加/削除やパッチ当てなども自分で管理する必要があります。
EKS Fargate を利用するとワーカーノードの管理は楽になりますが、Fargate は K8s のコア機能である DaemonSet が使えないことや、Pod につき一つの Fargate インスタンスが割り当てられるため、Pod の収容率をあげてコスト貢献するのが難しいです。
EKS Auto Mode を利用すると上述の通り、K8s の運用に必要な機能がすでに提供されているため、設定がほとんど不要です。
また、EKS Auto Mode は EC2 を利用しているため、Fargate のような制約がなく、その上 EC2 の管理は AWS が行ってくれるため、OS の更新やセキュリティパッチの適用なども自動でやってくれるとのことです
これだけ見ると新規で EKS を始める場合は EKS Auto Mode ほぼ一択なのではないかと個人的には思いました。
作成も簡単で、Terraform の場合以下のようなコードで EKS Auto Mode を作成することができます。
resource "aws_eks_cluster" "main" {
name = var.cluster_name
access_config {
authentication_mode = "API"
}
// EKS Auto Modeの場合はfalseに設定すること(Terrafomのdefaultがtrueなので)
bootstrap_self_managed_addons = false
role_arn = aws_iam_role.cluster.arn
version = var.eks_version
compute_config {
// このenabledと
enabled = true
node_pools = ["general-purpose"]
node_role_arn = aws_iam_role.node.arn
}
kubernetes_network_config {
elastic_load_balancing {
// このenabledと
enabled = true
}
}
storage_config {
block_storage {
// このenabledがtrueであればEKS Auto Mode
enabled = true
}
}
vpc_config {
endpoint_private_access = true
endpoint_public_access = true
subnet_ids = var.subnet_ids
}
depends_on = [
aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSComputePolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSBlockStoragePolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSLoadBalancingPolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSNetworkingPolicy,
]
}
VPC や Subnet といったネットワークリソースは自分で作成する必要がありますが、それ以外はほぼほぼ AWS が管理してくれるというありがたい機能です。
次は EKS Auto Mode のコンピューティングの自動スケーリングと管理
で利用されている Karpenter についてまとめています。
Karpenter について
Karpenter は AWS 主体となって開発している OSS です。こちらは 2021 年ごろに発表2されているため、最近の機能ではないかもですが、紹介させてください。。。
Karpenter の主な責務は前の項目でも述べたとおり、コンピューティングの自動スケーリングと管理です。
同じような機能として ClusterAutoScaler がありますが、ClusterAutoScaler は柔軟なワーカーノードの割り当てが難しいという問題があり、 Karpenter がその問題を解決しているとのことです。2
ちなみに AWS 以外のクラウドプロバイダーでも利用できるとのことです。
Karpenter は NodePool と NodeClass という K8s のリソースを使ってワーカーノードの管理を行います。
NodePool にはネットワーク、ストレージ、タグ付などのインフラ寄りの設定を定義することができ、
NodeClass には インスタンスタイプやアーキテクチャ(amd64,arm64 など),CPU,メモリなどのリソースの設定を定義することができます。
Pod の設定で Affinity や NodeSelector などを使ってノード指定をすると、Karpenter は NodePool,NodeClass に設定された条件を元にいい具合にノードを作成してくれます。
また、Node に Pod が一定時間存在しなくなると、Node は自動的に削除されコスト効率も良くなるとのことです。
以下は Private tag がついている Subnet に配置し、for-eks-worker-nodes という名前のセキュリティグループを適用する NodeClass の例です。
apiVersion: eks.amazonaws.com/v1
kind: NodeClass
metadata:
name: schedule-private
spec:
role: eks-auto-node-example
subnetSelectorTerms:
- tags:
Private: "1"
securityGroupSelectorTerms:
- tags:
Name: "for-eks-worker-nodes"
...
以下は上記 NodeClass を利用し、独自ラベルであるdeployNodeClass
が default
であることを Pod が要求してきた時に起動する NodePool の例です。
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: schedule-private
spec:
template:
metadata:
labels:
provisioner: private
spec:
nodeClassRef:
group: eks.amazonaws.com
kind: NodeClass
name: schedule-private
requirements:
- key: "deployNodeClass"
operator: In
values: ["default"]
...
ちなみに Karpenter は helm などを使い自分のクラスターにデプロイする必要がありますが、EKS Auto Mode の場合はデフォルトで有効化されており、デプロイおよび管理を AWS が行ってくれるようです。
そのため利用者は NodePool と NodeClass をマニフェストで定義して apply するだけで Karpenter の機能を利用することができます。
helm によるデプロイの場合は、helm に加え IAM Role などの設定が必要になり、ドキュメントを見た感じだいぶ複雑そうでした。
そのため、EKS Auto Mode を利用することで、Karpenter の機能を簡単に利用することができると感じました。
次の項目からは IAM 関連の機能についてまとめています。
AWS IAM を用いた K8s へのアクセスについて
EKS では AWS の IAM プリンシパル(IAM User や IAM Role) を用いて EKS 上の K8s リソースへアクセスすることができます。
kubectl を使ったアクセスや、EKS のコンソール上から K8s のリソースを閲覧、操作する時に特に利用するのかなと思います。
旧来の EKS では aws-auth という ConfigMap に IAM プリンシパルと K8s の 権限を紐づけることでアクセス権限を付与していました。
aws-auth だと、IAM プリンシパルを追加したい場合都度 aws-auth を更新する必要があったり、aws-auth を誤って削除してしまうとクラスターにアクセスできなくなるなどの問題があったようです。
そこで去年の 12 月頃から AccessEntry と AccessPolicy という機能が EKS で提供されました。
こちらは EKS の API を利用して、IAM プリンシパルを K8s の権限に紐づけることができる機能です。
AccessEntry というリソースを EKS の API を利用して作成し、このリソースに IAM プリンシパルを紐づけます。
加えて AccessPolicy というリソースを作成し、このリソースにも IAM プリンシパルを紐づけます。
AccessPolicy は AWS が既に用意している arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy
などのポリシーを選択し、付与することができます。
また、AccessEntry には K8s の Group や User を指定することができます。
この Group や User は K8s の RoleBinding を使って権限を紐づけることができるので、AccessPolicy なしでも、RoleBinding を行えば K8s へのアクセス権限を付与することができます。
この方法の良いところは aws-auth で起きうるような事故が起きにくいことです。
aws-auth の情報を消してしまうとクラスターにアクセスできなくなりますが、AccessEntry の場合は誤って削除してしまっても、EKS の API にアクセスできる IAM プリンシパルがあれば、再度 AccessEntry を作成し、アクセスすることができるようになります。
また、AccessEntry は EKS の API を利用して作成するため、EKS クラスターを作成する時と同じ IaC で管理することが容易です。(Terraform や CloudFormation など)
そもため個人的には EKS の関心毎と K8s の関心毎を分離して考えやすくなったのかなと思います。(IAM の設定は K8s ではなく EKS 側の設定に近いと考えています)
以下は AWS リソースに対しては最小権限だが K8s に関しては Cluster Admin な IAM Role
を作成する例です。
// AccessEntryの作成
resource "aws_eks_access_entry" "cluster_admin" {
cluster_name = aws_eks_cluster.main.name
principal_arn = aws_iam_role.cluster_admin.arn
type = "STANDARD"
}
// AccessPolicyの作成
// ここではAWSが提供しているAmazonEKSClusterAdminPolicyをAccessEntryに付与
resource "aws_eks_access_policy_association" "cluster_admin" {
cluster_name = aws_eks_cluster.main.name
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
principal_arn = aws_iam_role.cluster_admin.arn
access_scope {
type = "cluster"
}
}
// IAM Roleの作成
data "aws_caller_identity" "current" {}
resource "aws_iam_role" "cluster_admin" {
name = "eks-cluster-admin"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = ["sts:AssumeRole"]
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
},
]
})
}
resource "aws_iam_policy" "cluster_admin" {
name = "eks-cluster-admin"
description = "Full access to EKS cluster"
policy = data.aws_iam_policy_document.cluster_admin.json
}
// K8sへのアクセスができれば良いのでAWSへの権限は最小限に
data "aws_iam_policy_document" "cluster_admin" {
statement {
effect = "Allow"
actions = ["sts:GetCallerIdentity"]
resources = ["*"]
}
}
現在 aws-auth を利用した設定は非推奨となっており、AccessEntry と AccessPolicy へ移行することが推奨されています。
K8s から AWS のリソースにアクセス
最後に K8s から AWS のリソースにアクセスする方法についてまとめています。
このケースはよくある話だと思います。例えば、S3 など優秀な AWS サービスを Pod から利用したい場合などです。
K8s から AWS のリソースにアクセスするためにはいくつか方法があります。
前まで一般的だったのが IRSA (IAM Role for ServiceAccount) です。
これは EKS における ServiceAccount が AWS の IAM Role を一時的に引き受ける(AssumeRoleWithWebIdentity)仕組みです。
この方法では、EKS の OIDC プロバイダーを利用して ServiceAccount を認証し、EKS が OIDC トークンを発行します。このトークンを用いて AWS にリクエストを送信することで、ServiceAccount と IAM Role の紐付けを行うものです。
そして去年の 11 月ごろから EKS PodIdentity という方法も提供されています。
こちらは OIDC 認証を必要とせず、Pod に IAM Role を紐づけることができる方法です。
OIDC 認証を利用せずに済むと以下のような IRSA を利用するための設定が不要になります。設定が不要になることで運用面に加え、サービスクォータに引っかかる可能性も減ります。
- EKS の OIDC プロバイダー 有効化
- AssumeRoleWithWebIdentity を可能にするために IAM の Identity provider 設定を EKS クラスターごとに設定
- AssumeRole 可能な IAM Role の信頼関係には OIDC のプロバイダを追加する必要があるため、同じ権限でもクラスター毎に設定が必要
また、Terraform では以下のようなコードで EKS PodIdentity を利用することができ、K8s 側では ServiceAccount の作成と Pod への紐付けだけで済みます。
resource "aws_eks_pod_identity_association" "test_pod_identity" {
cluster_name = var.cluster_name
// このnamespaceで
namespace = "default"
// このServiceAccountを利用するPodに
service_account = "test-pod-identity"
// このIAM Roleを紐づける
role_arn = aws_iam_role.test_pod_identity.arn
}
resource "aws_iam_role" "test_pod_identity" {
name = "test-pod-identity"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
// 利用主体がpods.eks.amazonaws.comであることが特徴
Service = "pods.eks.amazonaws.com"
},
Action = ["sts:AssumeRole", "sts:TagSession"]
}
]
})
}
// Podに必要な権限を付与
resource "aws_iam_role_policy_attachment" "test_pod_identity" {
policy_arn = aws_iam_policy.test_pod_identity.arn
role = aws_iam_role.test_pod_identity.name
}
ただ、IRSA は様々な環境(EKS 以外でも)で利用可能ですが、EKS PodIdentity は EKS 環境にのみ利用できるため、IRSA にしか対応していない場合は IRSA を利用する必要があります。
ひとまずここで、EKS の最近の機能についての説明は以上にします。
次からは所感などを書いていきます。
様々な機能を振り返ってみて
めちゃ便利になっている!
去年の終わり頃から最近の EKS Auto Mode にかけて、EKS はめちゃめちゃ便利になっているなと感じました。
特に EKS Auto Mode は K8s のややこしい設定の多くを AWS 側が管理してくれていることと、Fargate ではなく AWS マネジドな EC2 を利用可能になったことが大きいと思います。
また、IAM 周りも作成から管理まで簡単にできるようになったのはとても良いと思います。
それでもやはり難しいところ
EKS は上で述べているように様々な機能があり、やりたいことの多くが提供されていますが、やはり難しい部分もあると感じました。
特に難しいと思ったのが、どこでエラーが発生したかわかりにくいという点です。
これは一部の K8s リソースを AWS のリソースで実現しているためかと個人的には思います。
私がハマったのは、
-
Karpenter で Node がなかなか作成されなかった
- IAM の権限不足でエラーが発生していた
- spot インスタンスの ServiceLinkedRole がなかった
- Karpenter に対して AccessEntry が設定されていなかった
-
うまく動作する Pod と動作しない Pod があった
- Node に紐づいている SecurityGroup が通信を許可していなかった
-
Ingress,LoadBalancer が動作しているが、アクセスできなかった
- LoadBalancer が Private Subnet に配置されていた
- Public Subnet に配置させる方法を学ぶ必要があった
ネットワークに関するエラーは EKS に関係なく生じることかも知れませんが、つまづきがあったため書きました。
Karpenter のエラーに関しては最初は全く解決できなかったです。
なぜなら Karpenter を kubectl describe コマンドで確認しても、なぜエラーが出ているのかのエラーメッセージが私にとって十分ではなかったからです。
最終的に CloudTrail のイベントを見ることで、IAM の権限不足でエラーが発生していることがわかりました。
今も何かあれば、K8s の 情報とともに CloudTrail を見ることでエラーを解消しようとしていますが、もっと良い方法もありそうな気もします。
また、ハマったことではないのですが、K8s と AWS の権限が混在しているため、どちらの権限を足せば良いのかはまだ混乱することもあります。
何を操作するかを考えればわかるのですが、簡単ではないと感じています。
ただ、お家 K8s に比べると多くのことが提供されており、K8s のコア機能に集中できると感じています。
本番で利用するのであればやはり自前で K8s を構築するよりも EKS を利用した方が良いと今回の調査を通して感じました。
特に Auto Mode は個人的にほぼ一択だと思います。
おわりに
EKS Auto Mode が出たことをきっかけに EKS をざっと学んでみましたが、アップデートが多くあり調査できてとてもよかったです。
まだ、オブザーバビリティ周りに関しては調査できていないこともあるので、今度はこの辺りも調査してみたいと思います。
また、Karpenter についてはハマったことも多かったので、コードレベルで挙動を追っていければと思います。
ここまで読んでいただきありがとうございました!
おまけ
ちなみに今回 Terraform を利用してドキュメント通りに EKS Auto Mode を作成したところ、エラーが起きました。
そこで EKS Auto Mode のドキュメントに対して PR を送り、マージされました!
初の OSS への貢献で、とても嬉しく興奮しました!
今後もこのような OSS への貢献に挑戦していきたいなと思います。