EKSでロードバランサーを構築する
皆さん、こんにちは。技術開発グループのn-ozawanです。
西日本側で梅雨入りしましたね。関東は週末ごろに梅雨入りするそうです。
本題です。
EKSで構築したコンテナ環境を外部に公開するにはロードバランサーを使います。ロードバランサーは外部からのトラフィックを複数のサーバに分散する仕組みで、サーバへの負荷軽減、サーバ障害時のフェースセーフなどに活躍します。今回はTerraformでKubernetesクラスタからALB (Application Load Balancer) を作成する仕組みを構築します。
目次
AWS Load Balancer Controller のインストール
KubernetesクラスタからALBを利用するには、KubernetesクラスタにAWS Load Balancer Controller (以降、LBCと略称)というアドオンをインストールします。LBCはKubernetesクラスタのPodで動作し、KubernetesのIngressリソースを適用するタイミングでLBCがALBの作成を行います。
ゴール
LBCはKubernetesクラスタ内で動作します。Kubernetesクラスタの中からAWSリソースへアクセスするための権限付与が必要となります。ロードバランサーの構築に必要なステップは大きく2つです。1つ目はサービスアカウントの作成、2つ目はLBCアドオンのインストールです。今回はEKSのユーザーガイドを参考に、Terraformのコードを紹介します。

サービスアカウントの作成
Kubernetesのサービスアカウントは、PODと紐づけて動作するアカウントです。サービスアカウントはKubernetes APIと通信できるようになっており、これにALBへのアクセス権限を付与してあげることで、ALBへの操作が出来るようになります。
まず初めにKubernetesクラスタ用のIAM OIDCプロバイダを作成します。このOIDCプロバイダはサービスアカウントへ権限を一時的に付与してあげる働きがあります。
data "tls_certificate" "main" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "main" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = data.tls_certificate.main.certificates[*].sha1_fingerprint
url = data.tls_certificate.main.url
}
詳細な説明は省きますが、aws_eks_cluster
にて環境を構築した後、クラスタ情報から認証用の発行者URLを取得して作成します。詳細はAWSのユーザーガイドもしくはTerraformのドキュメントを参照してください。
権限を発行してくれるOIDCプロバイダは作成できましたので、次はOIDCプロバイダがサービスアカウントに付与したい権限(=ロール)を作成します。まずはポリシーを作成します。
locals {
service_account_name = "aws-load-balancer-controller"
}
resource "aws_iam_policy" "main" {
name = "${local.service_account_name}-policy"
policy = file("${path.module}/iam_policy.json")
}
iam_policy.json
はサービスアカウントに付与したい権限が定義されているjsonファイルです。AWSが公開してくれていますので、それを使います。jsonファイルはローカルにダウンロードして使った方がよいでしょう。ダウンロードせずにURL指定でも出来ると思いますが、構築時に外部との接続を考慮しなくてはいけないのと、仮に公開先のjsonに変更が入った場合、再現性がなくなるので避けた方が良いかと思います。
次はロールを作成して、先ほど作成したポリシーをアタッチします。
data "aws_caller_identity" "current" {}
locals {
oidc = replace(var.eks.identity[0].oidc[0].issuer, "https://", "")
service_account_name = "aws-load-balancer-controller"
namespace = "kube-system"
}
resource "aws_iam_role" "main" {
name = "${local.service_account_name}-role"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${local.oidc}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${local.oidc}:aud": "sts.amazonaws.com",
"${local.oidc}:sub": "system:serviceaccount:${local.namespace}:${local.service_account_name}"
}
}
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "eks_cluster_AmazonEKSClusterPolicy" {
policy_arn = aws_iam_policy.main.arn
role = aws_iam_role.main.name
}
assume_role_policy
には、先ほど作成したOIDCプロバイダから権限を付与するようなことが書かれています。最後にaws_iam_role_policy_attachment
で、ロールに対して、先ほど作成したポリシーをアタッチしています。
最後にサービスアカウントを作成します。
locals {
service_account_name = "aws-load-balancer-controller"
namespace = "kube-system"
}
resource "kubernetes_service_account" "lbc" {
metadata {
name = local.service_account_name
namespace = local.namespace
labels = {
"app.kubernetes.io/name" = local.service_account_name
}
annotations = {
"eks.amazonaws.com/role-arn" = aws_iam_role.main.arn
}
}
}
metadata.name
とnamespace
はLBCアドオンのインストール時にも利用します。annotations."eks.amazonaws.com/role-arn"
に先ほど作成したロールのARNを指定しています。
LBCのインストール
LBCをインストールするにはHELMを利用します。HELMはKubernetes用のパッケージ管理ツールで、よく使う構成やアドオンなどをインストールすることが出来る優れものです。ユーザーガイドにはhelmコマンドでインストールする手順が記載されていますが、これをTerraformでコード化すると以下のようになります。
resource "helm_release" "lbc" {
name = "aws-load-balancer-controller"
chart = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
namespace = "kube-system"
version = "2.5.2"
dynamic "set" {
for_each = {
"serviceAccount.create" = false
"serviceAccount.name" = "aws-load-balancer-controller"
clusterName = var.eks.name
region = "ap-northeast-1"
vpcId = var.vpcid
"image.repository" = "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon/aws-load-balancer-controller"
}
content {
name = set.key
value = set.value
}
}
}
Helmでインストールするにはhelm_releaseを利用します。name
はリリース名で、chart
はインストールするチャートを指定します。チャートというのはhelmが管理しているパッケージだと思ってください。repository
はLBCのチャートが管理されているリポジトリです。namespace
はPODが動作する名前空間を指定します。特に理由がなければkube-system
で良いと思います。
dynamic "set"
ブロックには、LBCをKubernetesクラスタにインストールする際に必要となるパラメータを与えています。ここで与えるパラメータはLBCの動作に影響するものになります。for_eachでkey-valueを繰り返し、以下のようにsetブロックを生成しています。
set {
name = "serviceAccount.create"
value = false
}
set {
name = "serviceAccount.name"
value = "aws-load-balancer-controller"
}
# 以下略
パラメータの説明は割愛しますが、今回のお話で重要なのはserviceAccount.name
で、先ほど作成したサービスアカウントの名前を指定します。ここで指定することにより、LBCは先ほど作成したサービスアカウントで動作するようになり、ALBに対して操作する権限が得られるようになります。それ以外の詳細はこちらを参照してください。
おわりに
権限周りの仕組みは難しいですね。ここまででLBCのインストールと動作に必要なサービスアカウントの作成は出来ました。あとは、実際にKubernetesでIngressに必要なアノテーションを指定してあげれば、ALBによって外部に公開されることでしょう。以下に例を記載します。Ingressに指定するアノテーションの詳細はこちらを参照してください。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: http-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: http-service
port:
number: 80
ではまた。