はじめに
コンテナオーケストレーションツールと言えば、パブリッククラウドのプロバイダが提供するマネージドサービスを除けば Kubernetes がほぼデファクトスタンダードになってきたと言っても過言ではないだろう。
そこで、そろそろ真面目に Kubernetes を勉強しようにも最初の一歩で何をしたら良いか躓いている人も多いと思う。
本記事では、そんな感じで Kubernetes やってみようぜ!な初学者向けに書いてみる。
これでいいのか?という気はするが、Kubernetes のコントロールプレーンのインストール等の手間を省くことを考慮して、EKS を使ってみる。
一方で、コンテナやパブリッククラウドに関しての完全な初学者向けに書くのはなかなか難しいため、以下の知識があることを前提にする。
- ECS でのコンテナ管理を少しでもしたことがある
- Terraform をそこそこ書いたことがある
Terraform は別になくても良いのだけど、せっかくコンテナを使うからには冪等に作るべきなので、本記事では、terraform apply
の一撃で、EKS on Fargate な Nginx のサービスを起動して ALB 経由でインターネットからアクセスできるようにすることを目標にする。
また、今回はひとまず動かすところまでを優先しているため、セキュリティグループの設定はデフォルトのまま(EKS 作成時に勝手に作られるものと、ALB 作成時に勝手につくられるものから変更していない)としている。必要に応じて修正をしていただきたい。
最初に結論
先に結論を書いておく。
ECS はすごく良くできてると改めて感じた。
少なくとも、今 ECS を使っている人が、パブリッククラウドのプロバイダを変更することを前提にすること以外に、ECS → EKS に乗り換えるメリットというのはあまり無いように感じる(それとて、どうせ乗り換えるならあえて EKS にするくらいであれば、さっさと乗り換えてしまった方が良いだろう)。
これからコンテナをゼロから学ぶというのであれば、Kubernetes は選択肢になり得ると思う。ただし、ECS のように AWS に完全統合された環境ではないので、覚えることは ECS よりも多くなるだろう。
あと、素で EKS を触ると大変すぎて死ねるので、eksctl (コマンドラインからEKSを制御するコマンド)は必須だろう。ちゃんとインストールしておこう。というか、仕組みを理解したら、その後は eksctl を使わない理由はあまり無いと思う。
2021/8/11追記
上記で eksctl を使わない理由が無いと言いつつ、eksctl は中で CloudFormation を動かしてしまうため、やはりリソース管理の上ではつらみがある。Terraform と組み合わせると、terraform, eksctl, cloudformation, kubectl が作るリソースを頑張って整合するように考えなければいけなくなる。本記事では、後半に追記分として、eksctl に頼らずになるべく terraform でリソース作成をして、どうしても無理な部分だけ kubectl にやらせるという方法も紹介する。
全体構成
今回の Terraform では以下のリソースを作成する。
- VPC、サブネット
- EKS クラスタ、Fargate プロファイル
- OIDCプロバイダ
- ALB Ingress Controller(Kubernetesのリソース)
VPCはありものを使っても良いが、EKS がいろいろバックエンドでゴニョゴニョやるための識別子をタグに埋め込む必要がある。既存の環境を汚したくない人は、新規でVPCを作っておいた方が良いだろう。
また、途中で使うコマンドの都合上、terraform destroy
で消せないリソースが登場するので注意。
そういったリソースには本記事中でも注記をすることにする。
ネットワークを作る
ここはそんなに難しいことはない。EKS の制約上、最低でも2つ以上のプライベートネットワークが必要になるので、それぞれ作って Nat Gateway をアタッチしておこう。
また、VPC とサブネットのリソースには "kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
のタグを付与しておこう。これを忘れるとうまく動いてくれない。パブリックサブネットには "kubernetes.io/role/elb" = "1"
、プライベートサブネットには "kubernetes.io/role/internal-elb" = "1"
を付与しておく。
################################################################################
# VPC #
################################################################################
resource "aws_vpc" "for_eks_fargate" {
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = local.vpc_name
"kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
}
}
################################################################################
# Public Subnet #
################################################################################
resource "aws_subnet" "public1" {
vpc_id = aws_vpc.for_eks_fargate.id
cidr_block = "192.168.0.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1a"
tags = {
"Name" = local.public_subnet_name1
"kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_subnet" "public2" {
vpc_id = aws_vpc.for_eks_fargate.id
cidr_block = "192.168.1.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1c"
tags = {
"Name" = local.public_subnet_name2
"kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
################################################################################
# Private Subnet #
################################################################################
resource "aws_subnet" "private1" {
vpc_id = aws_vpc.for_eks_fargate.id
cidr_block = "192.168.2.0/24"
map_public_ip_on_launch = false
availability_zone = "ap-northeast-1a"
tags = {
"Name" = local.private_subnet_name1
"kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
resource "aws_subnet" "private2" {
vpc_id = aws_vpc.for_eks_fargate.id
cidr_block = "192.168.3.0/24"
map_public_ip_on_launch = false
availability_zone = "ap-northeast-1c"
tags = {
"Name" = local.private_subnet_name2
"kubernetes.io/cluster/${local.eks_cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
################################################################################
# Internet Gateway #
################################################################################
resource "aws_internet_gateway" "for_eks_fargate" {
vpc_id = aws_vpc.for_eks_fargate.id
tags = {
"Name" = local.igw_name
}
}
################################################################################
# EIP #
################################################################################
resource "aws_eip" "for_nat_gateway1" {
vpc = true
tags = {
Name = local.eip_name1
}
}
resource "aws_eip" "for_nat_gateway2" {
vpc = true
tags = {
Name = local.eip_name2
}
}
################################################################################
# Nat Gateway #
################################################################################
resource "aws_nat_gateway" "for_eks_fargate1" {
depends_on = [aws_internet_gateway.for_eks_fargate]
subnet_id = aws_subnet.public1.id
allocation_id = aws_eip.for_nat_gateway1.id
tags = {
Name = local.ngw_name1
}
}
resource "aws_nat_gateway" "for_eks_fargate2" {
depends_on = [aws_internet_gateway.for_eks_fargate]
subnet_id = aws_subnet.public2.id
allocation_id = aws_eip.for_nat_gateway2.id
tags = {
Name = local.ngw_name2
}
}
################################################################################
# Route Table #
################################################################################
resource "aws_route_table" "public1" {
vpc_id = aws_vpc.for_eks_fargate.id
}
resource "aws_route" "public1" {
route_table_id = aws_route_table.public1.id
gateway_id = aws_internet_gateway.for_eks_fargate.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "public1" {
subnet_id = aws_subnet.public1.id
route_table_id = aws_route_table.public1.id
}
resource "aws_route_table" "public2" {
vpc_id = aws_vpc.for_eks_fargate.id
}
resource "aws_route" "public2" {
route_table_id = aws_route_table.public2.id
gateway_id = aws_internet_gateway.for_eks_fargate.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "public2" {
subnet_id = aws_subnet.public2.id
route_table_id = aws_route_table.public2.id
}
resource "aws_route_table" "private1" {
vpc_id = aws_vpc.for_eks_fargate.id
}
resource "aws_route" "private1" {
route_table_id = aws_route_table.private1.id
nat_gateway_id = aws_nat_gateway.for_eks_fargate1.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "private1" {
subnet_id = aws_subnet.private1.id
route_table_id = aws_route_table.private1.id
}
resource "aws_route_table" "private2" {
vpc_id = aws_vpc.for_eks_fargate.id
}
resource "aws_route" "private2" {
route_table_id = aws_route_table.private2.id
nat_gateway_id = aws_nat_gateway.for_eks_fargate2.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "private2" {
subnet_id = aws_subnet.private2.id
route_table_id = aws_route_table.private2.id
}
IAM の準備
EKS クラスタと Pod 実行用のサービスロールが必要になるので作成しておく。
それぞれ AWS マネージドな IAM ポリシーがあるので、それをアタッチすれば良いだろう。
################################################################################
# IAM Role for EKS Cluster #
################################################################################
resource "aws_iam_role" "ekscluster" {
name = local.ekscluster_role_name
assume_role_policy = data.aws_iam_policy_document.ekscluster_assume.json
}
data "aws_iam_policy_document" "ekscluster_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"eks.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy_attachment" "ekscluster1" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.ekscluster.name
}
resource "aws_iam_role_policy_attachment" "ekscluster2" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
role = aws_iam_role.ekscluster.name
}
################################################################################
# IAM Role for EKS Pod Execution #
################################################################################
resource "aws_iam_role" "ekspodexecution" {
name = local.ekspodexecution_role_name
assume_role_policy = data.aws_iam_policy_document.ekspodexecution_assume.json
}
data "aws_iam_policy_document" "ekspodexecution_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"eks-fargate-pods.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy_attachment" "ekspodexecution1" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy"
role = aws_iam_role.ekspodexecution.name
}
EKS クラスタの作成
いよいよ EKS クラスタの作成だ。
ここで必要になるのは、aws_eks_cluster
と aws_eks_fargate_profile
だ。
aws_cloudwatch_log_group
についてはログ採取が必要な場合に設定する。何も指定しなくても設定されるが、保存期間が無制限になってしまうので、予め EKS 指定の名前で作っておくことで、Terraform の制御下に置くことができる。
aws_eks_cluster
については、明示的にポリシーのアタッチとの順番を制御する必要があるため、depends_on
でポリシーのアタッチが先に完了するようにしておく。vpc_config
については、パブリック、プライベート問わず作成したすべてのサブネットを設定する。
aws_eks_fargate_profile
の subnet_ids
については、バックエンドのノードを作るサブネットなので、プライベートのみで良い。
################################################################################
# EKS #
################################################################################
resource "aws_eks_cluster" "example" {
depends_on = [
aws_iam_role_policy_attachment.ekscluster1,
aws_iam_role_policy_attachment.ekscluster2,
aws_cloudwatch_log_group.eks_cluster,
]
name = local.eks_cluster_name
role_arn = aws_iam_role.ekscluster.arn
version = "1.19"
vpc_config {
subnet_ids = [
aws_subnet.public1.id,
aws_subnet.public2.id,
aws_subnet.private1.id,
aws_subnet.private2.id,
]
}
enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
}
resource "aws_eks_fargate_profile" "kubesystem" {
cluster_name = aws_eks_cluster.example.name
fargate_profile_name = local.eks_fargate_kubesystem_profile_name
pod_execution_role_arn = aws_iam_role.ekspodexecution.arn
subnet_ids = [aws_subnet.private1.id, aws_subnet.private2.id]
selector {
namespace = "default"
}
selector {
namespace = "kube-system"
}
}
resource "aws_cloudwatch_log_group" "eks_cluster" {
name = "/aws/eks/${local.eks_cluster_name}/cluster"
retention_in_days = 3
}
Kubernetes が必要とするYAMLを作成する
さて、ここまでは大したことない(実際、EKS on EC2 であればだいたいこれで終わり)のだが、ここからが大変な部分だ。
Kubernetes を制御するための YAML を作成していく。
必要なものは以下。
- Kubernetes の Config ファイル
- ALB Ingress Controller の Manifest ファイル
- Kubernetes に設定するロールの Manifest ファイル
- Nginx コンテナを起動するための Manifest ファイル
それぞれ、AWS リソースを埋め込む必要があるため、以下のような感じで template_file
を使って自動で作成する。
ちなみに、この節の元ネタはAWSのブログなのだが、コピペ元のYAMLファイルが壊れていたり、既にこのブログが書かれたときから時間がったっていてすでに使えない apiVersion
があったりして、かなり大変だった……。1年ちょっと前の記事にもかかわらず既に deprecated だったり使えなくなったりする構文があるというライフサイクルの速さは、EKS がしんどいと言われる所以でもある……。
################################################################################
# Local File for Kubernetes Config #
################################################################################
resource "local_file" "kubeconfig" {
filename = "./output_files/kubeconfig.yaml"
content = data.template_file.kubeconfig.rendered
}
data "template_file" "kubeconfig" {
template = file("${path.module}/kubernetes_template/01_kubeconfig_template.yaml")
vars = {
eks_certificate_authority_data = aws_eks_cluster.example.certificate_authority.0.data
eks_cluster_endpoint = aws_eks_cluster.example.endpoint
eks_cluster_arn = aws_eks_cluster.example.arn
eks_cluster_region = data.aws_region.current.name
eks_cluster_name = local.eks_cluster_name
}
}
################################################################################
# Local File for ALB Ingress Controller #
################################################################################
resource "local_file" "alb_ingress_controller" {
filename = "./output_files/alb-ingress-controller.yaml"
content = data.template_file.alb_ingress_controller.rendered
}
data "template_file" "alb_ingress_controller" {
template = file("${path.module}/kubernetes_template/11_alb-ingress-controller.yaml")
vars = {
eks_cluster_name = aws_eks_cluster.example.name
vpc_id = aws_vpc.for_eks_fargate.id
region_name = data.aws_region.current.name
}
}
################################################################################
# Local File for RBAC Role #
################################################################################
resource "local_file" "rbac_role" {
filename = "./output_files/rbac-role.yaml"
content = data.template_file.rbac_role.rendered
}
data "template_file" "rbac_role" {
template = file("${path.module}/kubernetes_template/12_rbac-role.yaml")
}
################################################################################
# Local File for Nginx Deployment #
################################################################################
resource "local_file" "nginx_deployment" {
filename = "./output_files/nginx-deployment.yaml"
content = data.template_file.nginx_deployment.rendered
}
data "template_file" "nginx_deployment" {
template = file("${path.module}/kubernetes_template/13_nginx-deployment.yaml")
vars = {
eks_fargate_profile_name = aws_eks_fargate_profile.kubesystem.fargate_profile_name
}
}
################################################################################
# Local File for Nginx Service #
################################################################################
resource "local_file" "nginx_service" {
filename = "./output_files/nginx-service.yaml"
content = data.template_file.nginx_service.rendered
}
data "template_file" "nginx_service" {
template = file("${path.module}/kubernetes_template/14_nginx-service.yaml")
}
################################################################################
# Local File for Nginx Ingress #
################################################################################
resource "local_file" "nginx_ingress" {
filename = "./output_files/nginx-ingress.yaml"
content = data.template_file.nginx_ingress.rendered
}
data "template_file" "nginx_ingress" {
template = file("${path.module}/kubernetes_template/15_nginx-ingress.yaml")
}
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ${eks_certificate_authority_data}
server: ${eks_cluster_endpoint}
name: ${eks_cluster_arn}
contexts:
- context:
cluster: ${eks_cluster_arn}
user: ${eks_cluster_arn}
name: ${eks_cluster_arn}
current-context: ${eks_cluster_arn}
kind: Config
preferences: {}
users:
- name: ${eks_cluster_arn}
user:
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
args:
- --region
- ${eks_cluster_region}
- eks
- get-token
- --cluster-name
- ${eks_cluster_name}
command: aws
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: kube-system
name: alb-ingress-controller
labels:
app.kubernetes.io/name: alb-ingress-controller
spec:
selector:
matchLabels:
app.kubernetes.io/name: alb-ingress-controller
template:
metadata:
labels:
app.kubernetes.io/name: alb-ingress-controller
spec:
containers:
- name: alb-ingress-controller
args:
- --ingress-class=alb
- --cluster-name=${eks_cluster_name}
- --aws-vpc-id=${vpc_id}
- --aws-region=${region_name}
image: docker.io/amazon/aws-alb-ingress-controller:v1.1.4
serviceAccountName: alb-ingress-controller
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: alb-ingress-controller
name: alb-ingress-controller
rules:
- apiGroups:
- ""
- extensions
resources:
- configmaps
- endpoints
- events
- ingresses
- ingresses/status
- services
verbs:
- create
- get
- list
- update
- watch
- patch
- apiGroups:
- ""
- extensions
resources:
- nodes
- pods
- secrets
- services
- namespaces
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: alb-ingress-controller
name: alb-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: alb-ingress-controller
subjects:
- kind: ServiceAccount
name: alb-ingress-controller
namespace: kube-system
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
name: nginx-deployment
labels:
eks.amazonaws.com/fargate-profile: ${eks_fargate_profile_name}
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.20
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
apiVersion: v1
kind: Service
metadata:
namespace: "default"
name: "nginx-service"
annotations:
alb.ingress.kubernetes.io/target-type: ip
spec:
selector:
app: "nginx"
ports:
- port: 80
targetPort: 80
protocol: TCP
type: NodePort
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: nginx-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
labels:
app: nginx-ingress
spec:
rules:
- http:
paths:
- path: /*
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
CoreDNS を Fargate 向けに書き換える
さて、素で起動してきた EKS Cluster は、DNS をEC2 で起動しようとして進まなくなる。
これを Fargate に向けてあげる必要がある。
コマンド自体は AWS 公式のユーザーガイド参照。
今回は、冪等性を高めるために null_resource
を使って自動でパッチをして再起動する。
resource "null_resource" "coredns_patch" {
depends_on = [
aws_eks_fargate_profile.kubesystem,
local_file.kubeconfig,
local_file.alb_ingress_controller,
local_file.rbac_role,
local_file.nginx_deployment,
local_file.nginx_ingress,
local_file.nginx_service,
]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl patch deployment coredns -n kube-system --type json -p='[{\"op\": \"remove\", \"path\": \"/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type\"}]'"
on_failure = fail
}
}
resource "null_resource" "coredns_restart" {
depends_on = [null_resource.coredns_patch]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl rollout restart -n kube-system deployment coredns"
on_failure = fail
}
}
ALB を構築する
本記事を書いた2021年8月時点では、ALB Ingress Controllerを使うのがプラクティスとされていたが、現在では非推奨とされている。2023年11月時点では、後述のAWS Load Balancer Controllerを使用するのがセオリーとされている。
これでサービス公開の準備はほぼ整った。あとは、実際に ALB を構築してコンテナをデプロイしていく。
ここも AWS 公式のブログが元ネタで、null_resource
で自動化をしていく。
まずは、ALB の IAM 権限を制御できるように以下のIDプロバイダと IAM ポリシーを作成する。
data "tls_certificate" "for_eks_fargate_pod" {
url = aws_eks_cluster.example.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "for_eks_fargate_pod" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.for_eks_fargate_pod.certificates[0].sha1_fingerprint]
url = aws_eks_cluster.example.identity[0].oidc[0].issuer
}
resource "aws_iam_policy" "alb_ingress_controller" {
name = local.eksalbingresscontroller_policy_name
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"acm:DescribeCertificate",
"acm:ListCertificates",
"acm:GetCertificate"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateSecurityGroup",
"ec2:CreateTags",
"ec2:DeleteTags",
"ec2:DeleteSecurityGroup",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcs",
"ec2:ModifyInstanceAttribute",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:AddTags",
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeleteRule",
"elasticloadbalancing:DeleteTargetGroup",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:DescribeListenerCertificates",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeSSLPolicies",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyRule",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:RemoveListenerCertificates",
"elasticloadbalancing:RemoveTags",
"elasticloadbalancing:SetIpAddressType",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:SetWebAcl"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole",
"iam:GetServerCertificate",
"iam:ListServerCertificates"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cognito-idp:DescribeUserPoolClient"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"waf-regional:GetWebACLForResource",
"waf-regional:GetWebACL",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"tag:GetResources",
"tag:TagResources"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"waf:GetWebACL"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"wafv2:GetWebACL",
"wafv2:GetWebACLForResource",
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"shield:DescribeProtection",
"shield:GetSubscriptionState",
"shield:DeleteProtection",
"shield:CreateProtection",
"shield:DescribeSubscription",
"shield:ListProtections"
],
"Resource": "*"
}
]
}
EOF
}
そのうえで、以下のように Kubernetes のロールの設定と、IAM の紐づけを行う。
resource "null_resource" "create_rbac_role" {
depends_on = [null_resource.coredns_restart]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/rbac-role.yaml"
on_failure = fail
}
}
resource "null_resource" "create_iamserviceaccount" {
depends_on = [null_resource.create_rbac_role]
provisioner "local-exec" {
command = "eksctl create iamserviceaccount --name alb-ingress-controller --namespace kube-system --cluster ${aws_eks_cluster.example.name} --attach-policy-arn ${aws_iam_policy.alb_ingress_controller.arn} --approve"
on_failure = fail
}
}
ここで CloudFormation のスタックが作成され、そこから IAM ロールが作成&ポリシーへのアタッチが行われる。
スタックを削除しないと、IAM の削除ができずに terraform destroy
が失敗するので注意が必要だ。
ALB を作成する準備ができたら、あとは↑で作った Manifest ファイルを、kubectl apply
で流していけば良い。
resource "null_resource" "create_alb_ingress_controller" {
depends_on = [null_resource.create_iamserviceaccount]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/alb-ingress-controller.yaml"
on_failure = fail
}
}
resource "null_resource" "nginx_service" {
depends_on = [null_resource.create_alb_ingress_controller]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/nginx-service.yaml"
on_failure = fail
}
}
resource "null_resource" "nginx_deployment" {
depends_on = [null_resource.nginx_service]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/nginx-deployment.yaml"
on_failure = fail
}
}
resource "null_resource" "nginx_ingress" {
depends_on = [null_resource.nginx_deployment]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/nginx-ingress.yaml"
on_failure = fail
}
}
うまく作れると、ALB が作成され、そのURLへのアクセスで Nginx の画面が開けるはずだ。
ちなみに、この ALB を作る過程で、AWSリソースとしての ALB、ターゲットグループ、セキュリティグループが自動で作成される。
これらを削除しないと、terraform destroy
で VPC が削除できずエラーになるので注意が必要だ。
この ALB はマネージメントコンソールから確認できるものの、ちゃんと負荷状況に応じてスケールしてくれるかが分からない。
ALB がボトルネックになってしまったら元も子もないので、このあたりは以降でしっかり検証していきたい。
2021/8/11 追記 eksctl に頼らずALBを構築する
本記事を書いた2021年8月時点では、ALB Ingress Controllerを使うのがプラクティスとされていたが、現在では非推奨とされている。2023年11月時点では、後述のAWS Load Balancer Controllerを使用するのがセオリーとされている。
さて、これまで書いてきた通り、リソースの作成過程で Terraform 管理外の様々なリソースを作ってきているが、これを、ALB とターゲットグループのみに減らすことができる。
これにより、オペミスでリソースを消せなくなるリスクを極力下げることができる。
※そもそもリソースを消すということ自体があまり無いので、困らないかもしれないが。リソースのアップデートに対しても同様の事が言えるので、管理を寄せるのは悪いことではないだろう。
IAM の準備(追加分)
ALB を作る前段で、OIDC に関連したポリシーとプロバイダを作っているが、これをもとにIAMロールを eks 経由の CloudFormation で作成している。これを自力で作っておこう。
resource "aws_iam_role" "ekscluster_oidc" {
name = local.ekscluster_oidc_role_name
assume_role_policy = data.aws_iam_policy_document.ekscluster_oidc_assume_policy.json
tags = {
"alpha.eksctl.io/cluster-name" = aws_eks_cluster.example.name
"eksctl.cluster.k8s.io/v1alpha1/cluster-name" = aws_eks_cluster.example.name
"alpha.eksctl.io/iamserviceaccount-name" = "kube-system/alb-ingress-controller"
"alpha.eksctl.io/eksctl-version" = "0.47.0"
}
}
data "aws_iam_policy_document" "ekscluster_oidc_assume_policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.for_eks_fargate_pod.url, "https://", "")}:sub"
values = ["system:serviceaccount:kube-system:alb-ingress-controller"]
}
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.for_eks_fargate_pod.url, "https://", "")}:aud"
values = ["sts.amazonaws.com"]
}
principals {
identifiers = [aws_iam_openid_connect_provider.for_eks_fargate_pod.arn]
type = "Federated"
}
}
}
resource "aws_iam_role_policy_attachment" "ekscluster_oidc" {
role = aws_iam_role.ekscluster_oidc.name
policy_arn = aws_iam_policy.alb_ingress_controller.arn
}
eksctl している部分については、以下のようにマニフェストファイルを作って、コマンドを置き換えよう。
apiVersion: v1
kind: ServiceAccount
metadata:
name: alb-ingress-controller
namespace: kube-system
annotations:
eks.amazonaws.com/role-arn: ${sa_role_arn}
上記の呼び出しについては、以下のようにしてロールの情報を Kubernetes に渡す。
resource "local_file" "serviceaccount" {
filename = "./output_files/serviceaccount.yaml"
content = data.template_file.serviceaccount.rendered
}
data "template_file" "serviceaccount" {
template = file("${path.module}/kubernetes_template/serviceaccount.yaml")
vars = {
sa_role_arn = aws_iam_role.ekscluster_oidc.arn
}
}
これを、以下のように呼び出そう。
resource "null_resource" "create_iamserviceaccount" {
depends_on = [null_resource.create_rbac_role]
provisioner "local-exec" {
environment = {
KUBECONFIG = local_file.kubeconfig.filename
}
command = "kubectl apply -f ./output_files/serviceaccount.yaml"
on_failure = fail
}
}
セキュリティグループの準備
15_nginx-ingress.yaml を kubectl apply
する際にセキュリティグループを自動で準備してくれるが、これがなかなかうまく消えてくれないことがあり、冪等性が下がる原因となる。
このため、セキュリティグループは自前で作って、マニフェストに値を渡してあげることにしよう。
まずはセキュリティグループの定義である。
resource "aws_security_group" "for_eks_ingress" {
name = local.eks_ingress_sg_name
description = "managed LoadBalancer securityGroup by ALB Ingress Controller"
vpc_id = aws_vpc.for_eks_fargate.id
ingress {
from_port = 80
to_port = 80
protocol = "TCP"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow ingress on port 80 from 0.0.0.0/0"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = local.eks_ingress_sg_name
}
}
また、上記セキュリティグループを、EKS クラスタのセキュリティグループに紐づける必要があるため、以下の定義も入れておく。
aws_eks_cluster.example.vpc_config[0].cluster_security_group_id
は自分では作ることができず、EKS に任せるしかないため、EKS クラスタの定義から値を引っ張ってくるのが必要だ。
resource "aws_security_group_rule" "for_eks_cluster_allow_eks_ingress" {
security_group_id = aws_eks_cluster.example.vpc_config[0].cluster_security_group_id
description = "for_eks_cluster_allow_eks_ingress"
type = "ingress"
from_port = 0
to_port = 65535
protocol = "TCP"
source_security_group_id = aws_security_group.for_eks_ingress.id
}
また、上記で作成したセキュリティグループを ALB に設定するために、15_nginx-ingress.yaml に、以下を追記する。
これで、nginx-ingress の kubectl apply
時にデフォルトのセキュリティグループ作成をせずに、自前で設定したセキュリティグループを使える。
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/security-groups: ${eks_ingress_sg_id} # ★これを追記
nginx-ingress 作成用の YAML を生成する null_resource には、以下を追記しておこう。
vars = {
eks_ingress_sg_id = aws_security_group.for_eks_ingress.id
}
さらに terraform destroy を冪等にするために
さて、ここまでやればリソースは Terraform に大部分寄せることができたが、terraform destroy
時にゴミが残る課題は残る。
これについては、terraform destroy
時だけ操作する null_resource を使用する。
resource "null_resource" "kubectl_delete" {
depends_on = [
aws_eks_cluster.example,
aws_eks_fargate_profile.kubesystem,
local_file.kubeconfig,
local_file.create_namespace_awsobservablity,
local_file.awslogging_cloudwatch_configmap,
local_file.serviceaccount,
local_file.alb_ingress_controller,
local_file.rbac_role,
local_file.nginx_service,
local_file.nginx_deployment,
local_file.nginx_ingress,
]
triggers = {
kubeconfig = local_file.kubeconfig.filename
}
provisioner "local-exec" {
when = destroy
on_failure = continue
environment = {
KUBECONFIG = self.triggers.kubeconfig
}
command = <<-EOF
kubectl delete -f ./output_files/nginx-ingress.yaml --grace-period=0 --force &&
sleep 30 &&
kubectl delete -f ./output_files/nginx-deployment.yaml --grace-period=0 --force &&
kubectl delete -f ./output_files/nginx-service.yaml --grace-period=0 --force &&
kubectl delete -f ./output_files/alb-ingress-controller.yaml --grace-period=0 --force &&
kubectl delete -f ./output_files/serviceaccount.yaml --grace-period=0 --force &&
kubectl delete -f ./output_files/rbac-role.yaml --grace-period=0 --force &&
EOF
}
}
とりあえず、一通り作ったリソースは全部壊してから Terraform のリソース削除をしようという考えだ、
local_file
に依存関係を作っているのは、内部コマンドでファイルを使っている手前、local_file が destroyされる(=kubectlできなくなる)のを防ぐためだ。
また、destroy 時の local-exec では、値の参照ができないという制約がある。
このため、無理矢理 triggers に値を入れて参照可能にしている。
あとは、kubectl delete --grace-period=0 --force
で作ったリソースを順次消していく(強制削除しないとリソースが消えないことがある)。
nginx-ingress.yaml の後のみ sleep が入っているのは、リソース削除途中に次の kubectl リクエストを受けると、何か不整合になってしまうのか、ターゲットグループが消しきれないケースがあるためだ。
sleep 30
は不格好だが、他に方法を見つけられなかった……。
なお、ここまでやってもリソースが消えないケースがある。残念ながらそういう場合は、ALB ⇒ターゲットグループの順序で手動削除しよう(先に ALB を消さないとターゲットグループを消せない)。
なお、今回追記分は、eksctl を使わない前提で書いてきたが、eksctl を使う場合も、
kubectl delete -f ./output_files/serviceaccount.yaml --grace-period=0 --force &&
の部分を、
$ eksctl delete iamserviceaccount --name alb-ingress-controller --namespace kube-system --cluster eks-fargate-example-cluster
な感じで削除することも可能だ。
これで、基本的に自由に作成削除の試行錯誤ができるようになったはずだ!
2023/11/26追記 AWS Load Balancer Controllerを使ってLB組み込みを制御する
AWS Load Balancer Controllerの概要についてはAWS公式のブログを見るのが良い。
ALB Ingress Controllerとの違いとして、ALB Ingress ControllerはこのリソースでALBを作成するが、AWS Load Balancer Controllerでは、ロードバランサーをKubernetesの制御外にして、通常のAWSリソースとして定義したものと連動をする。負荷分散機能とサービス提供機能の責任分担をより分かりやすくしたと考えられるだろう。
AWSリソースとしてのALBの定義
ALBはTerraformで以下のように通常通り作成すれば良い。
セキュリティグループには、通常のALBのフォワードに必要な設定を入れておこう。
################################################################################
# ALB #
################################################################################0
resource "aws_lb" "example" {
name = local.alb_name
load_balancer_type = "application"
subnets = [
aws_subnet.public1.id,
aws_subnet.public2.id,
]
security_groups = [
aws_security_group.for_eks_ingress.id,
]
tags = {
"elbv2.k8s.aws/cluster" = local.eks_cluster_name
"ingress.k8s.aws/resource" = "LoadBalancer"
"ingress.k8s.aws/stack" = "default/nginx-ingress"
}
}
resource "aws_lb_listener" "example" {
load_balancer_arn = aws_lb.example.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.example.arn
}
}
resource "aws_lb_target_group" "example" {
name = local.alb_tg_name
vpc_id = aws_vpc.for_eks_fargate.id
port = 80
protocol = "HTTP"
target_type = "ip"
tags = {
"elbv2.k8s.aws/cluster" = local.eks_cluster_name
"ingress.k8s.aws/resource" = "default/nginx-ingress-nginx-service:80"
"ingress.k8s.aws/stack" = "default/nginx-ingress"
}
}
IAMロールの設定
IAMロールは以下のように設定する。
Kubernetesのサービスアカウントと連動させるためのOpenIDConnectの設定が必要だ。
それぞれの設定は、公式のユーザーガイドのIAM設定を持ってきている。
################################################################################
# IAM Policy for AWS Load Balancer Controller #
################################################################################
resource "aws_iam_role" "aws_loadbalancer_controller" {
name = local.eksawsloadbalancercontroller_role_name
assume_role_policy = data.aws_iam_policy_document.aws_loadbalancer_controller_assume_policy.json
tags = {
"alpha.eksctl.io/cluster-name" = aws_eks_cluster.example.name
"eksctl.cluster.k8s.io/v1alpha1/cluster-name" = aws_eks_cluster.example.name
"alpha.eksctl.io/iamserviceaccount-name" = "kube-system/aws-load-balancer-controller"
"alpha.eksctl.io/eksctl-version" = "0.47.0"
}
}
data "aws_iam_policy_document" "aws_loadbalancer_controller_assume_policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.for_eks_fargate_pod.url, "https://", "")}:sub"
values = ["system:serviceaccount:kube-system:aws-load-balancer-controller"]
}
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.for_eks_fargate_pod.url, "https://", "")}:aud"
values = ["sts.amazonaws.com"]
}
principals {
identifiers = [aws_iam_openid_connect_provider.for_eks_fargate_pod.arn]
type = "Federated"
}
}
}
resource "aws_iam_role_policy" "aws_loadbalancer_controller" {
name = local.eksawsloadbalancercontroller_policy_name
role = aws_iam_role.aws_loadbalancer_controller.name
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeVpcs",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeTags",
"ec2:GetCoipPoolUsage",
"ec2:DescribeCoipPools",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeListenerCertificates",
"elasticloadbalancing:DescribeSSLPolicies",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTags"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cognito-idp:DescribeUserPoolClient",
"acm:ListCertificates",
"acm:DescribeCertificate",
"iam:ListServerCertificates",
"iam:GetServerCertificate",
"waf-regional:GetWebACL",
"waf-regional:GetWebACLForResource",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL",
"wafv2:GetWebACL",
"wafv2:GetWebACLForResource",
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL",
"shield:GetSubscriptionState",
"shield:DescribeProtection",
"shield:CreateProtection",
"shield:DeleteProtection"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateSecurityGroup"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateTags"
],
"Resource": "arn:aws:ec2:*:*:security-group/*",
"Condition": {
"StringEquals": {
"ec2:CreateAction": "CreateSecurityGroup"
},
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateTags",
"ec2:DeleteTags"
],
"Resource": "arn:aws:ec2:*:*:security-group/*",
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "true",
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress",
"ec2:DeleteSecurityGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:DeleteRule"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:RemoveTags"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
],
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "true",
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:RemoveTags"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
]
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
],
"Condition": {
"StringEquals": {
"elasticloadbalancing:CreateAction": [
"CreateTargetGroup",
"CreateLoadBalancer"
]
},
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:SetIpAddressType",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:DeleteTargetGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeregisterTargets"
],
"Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:SetWebAcl",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:RemoveListenerCertificates",
"elasticloadbalancing:ModifyRule"
],
"Resource": "*"
}
]
}
EOF
}
KubernetesのServiceAccountの設定
前節で作ったIAMロールをKubernetesのServiceAccountと連動させる。
eks.amazonaws.com/role-arn
のannotationで紐づけを行う。
ALB Load Balancer Controllerを作成するNamespaceはサービス提供するNamespaceではなくkube-system
なので注意だ。
################################################################################
# Service Account #
################################################################################
resource "kubernetes_service_account" "awsloadbalancercontroller" {
metadata {
namespace = "kube-system"
name = "aws-load-balancer-controller"
annotations = {
"eks.amazonaws.com/role-arn" = aws_iam_role.aws_loadbalancer_controller.arn
}
}
}
HelmでAWS Load Balancer Controllerを起動する
AWS Load Balancer ControllerはHelmから作成するのが手っ取り早い。
というか、個別に定義するのはかなり大変(Manifestが500行程度ある)だ。
ServiceAccountは自動で作ることもできるが、今回は仕組みを知るためにもFalseにした。
################################################################################
# Helm(AWS Load Balancer Controller) #
################################################################################
resource "helm_release" "aws_load_balancer_controller" {
depends_on = [kubernetes_service_account.awsloadbalancercontroller]
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
wait_for_jobs = true
set {
name = "clusterName" // EKSのクラスタ名
value = aws_eks_cluster.example.name
}
set {
name = "region" // EKSクラスタを起動しているリージョン
value = data.aws_region.current.name
}
set {
name = "vpcId" // EKSクラスタを起動しているVPCのVPC-ID
value = aws_vpc.for_eks_fargate.id
}
set {
name = "serviceAccount.create" // ServiceAccountを自動で作成するか
value = false
}
set {
name = "serviceAccount.name" // 前節で作成したServiceAccountと合わせる
value = "aws-load-balancer-controller"
}
set {
name = "ingressClassParams.create" // IngressClassを自動で作るか
value = false
}
set {
name = "createIngressClassResource" // IngressClassを自動で作るか
value = false
}
}
ALBのターゲットグループに組み込む
ALBのターゲットグループに組み込むには、カスタムリソースであるTargetGroupBinding
を作成する必要がある。これは、以下のCRDS(
Custom Resource Definitions)のManifestから作成することが可能だ。
$ kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller/crds?ref=master"
さて、これで準備が整った。
今回は、NginxのDeployment, Serviceのリソース群とあわせて以下のManifestを作成してkubectl apply
する。なお、ALB Load Balancer ControllerがIngressの役割を肩代わりをしてくれるので、↑の節で作成していたIngressはこの方法の場合は作成不要だ。
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: 組み込みたいターゲットグループのターゲットグループ名
namespace: NginxのDeployment, Serviceと同じNamespace
spec:
serviceRef:
name: NginxのServiceと同じ名前
port: 80
targetGroupARN: 組み込みたいターゲットグループのARN
targetType: ip
これでPodを起動すると、自動でターゲットグループに組み込んでくれるようになる。