EKSのマネージドワーカーはとても便利ですがデプロイされるEC2につくセキュリティグループはEKSが作成したものです。自分で作成した任意のセキュリティグループを付与するにはEC2の起動テンプレートを作成し、その中でセキュリティグループを指定します。
これをTerraformで設定する方法を残しておきます。
マネージドノードグループが作成する起動テンプレートの確認
Terraformで作成する起動テンプレートの参考にするためまずは普通にマネージドワーカーをデプロイして設定を確認します。
マネージメントコンソールなどからマネージドノードグループが作成する起動テンプレートのIDを確認します。たとえばマネージメントコンソールからだとEC2->インタンス->テンプレートの起動に起動テンプレートはあります。またはEC2->Auto ScalingグループでEKSマネージドノードグループのオートスケーリンググループから確認しても良いです。
マネージメントコンソールからでも起動テンプレートの設定を確認できるが念の為コマンドで設定を確認します。
$ aws ec2 describe-launch-template-versions --launch-template-id lt-084a093919e4b2c36
出力例
{
"LaunchTemplateVersions": [
{
"LaunchTemplateId": "lt-084a093919e4b2c36",
"LaunchTemplateName": "eks-08bd9e75-1ab5-77ea-3e5e-96f926cdda77",
"VersionNumber": 1,
"CreateTime": "2021-08-13T00:30:03+00:00",
"CreatedBy": "arn:aws:sts::461548955069:assumed-role/AWSServiceRoleForAmazonEKSNodegroup/EKS",
"DefaultVersion": true,
"LaunchTemplateData": {
"IamInstanceProfile": {
"Name": "eks-08bd9e75-1ab5-77ea-3e5e-96f926cdda77"
},
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"DeleteOnTermination": true,
"VolumeSize": 30,
"VolumeType": "gp2"
}
}
],
"NetworkInterfaces": [
{
"DeviceIndex": 0,
"Groups": [
"sg-0bd04e1bd3c43d395",
"sg-0195e42ca26f31d49"
]
}
],
"ImageId": "ami-02a49655b336f1b47",
"InstanceType": "t3.medium",
"KeyName": "hoge",
"UserData": "TUlNRS1WZXJzaW...,
"MetadataOptions": {
"HttpPutResponseHopLimit": 2
}
}
}
]
}
userdataをbase64デコードすると以下の内容
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"
--//
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
set -ex
B64_CLUSTER_CA=LS0tLS1CRUdJTiBDRVJUSUZ...
API_SERVER_URL=https://B575989085247853299602E94FEED13C.gr7.ap-northeast-1.eks.amazonaws.com
K8S_CLUSTER_DNS_IP=172.20.0.10
/etc/eks/bootstrap.sh o2-dev-cluster --kubelet-extra-args '--node-labels=eks.amazonaws.com/nodegroup-image=ami-02a49655b336f1b47,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=o2-dev-node-group' --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL --dns-cluster-ip $K8S_CLUSTER_DNS_IP
--//--
上記結果を参考にTerraformを作成します。
Terraform
Terraformは以下5種類とvariablesを作成します。
- iam.tf
- managed-node-group.tf
- launch-config.tf
- sg.tf
- eks.tf
- variables.tf
iam.tf
EKSおよびマネージドノードグループに必要なIAMを作成します。元ネタはAWSドキュメントにあるAmazon EKS クラスター の IAM ロール、Amazon EKS ノードの IAM ロールを参考にしています。
# eks cluster
resource "aws_iam_role" "eks_cluster" {
name = "${var.base_name}-eks-cluster-role"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
tags = {
"Name" = "${var.base_name}-eks-cluster-role"
}
}
resource "aws_iam_role_policy_attachment" "eks_cluster" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster.name
}
resource "aws_iam_role_policy_attachment" "eks_vpc_resource_controller" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
role = aws_iam_role.eks_cluster.name
}
# managed node group
resource "aws_iam_role" "eks_node_group" {
name = "${var.base_name}-eks-node-group"
assume_role_policy = jsonencode({
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
Version = "2012-10-17"
})
tags = {
"Name" = "${var.base_name}-eks-node-group"
}
}
resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.eks_node_group.name
}
resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.eks_node_group.name
}
resource "aws_iam_role_policy_attachment" "ec2_ecr_ro" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_node_group.name
}
managed-node-group.tf
マネージドノードグループの設定をします。ここで任意のセキュリティグループを付けたいため、launch_template
を指定します。また、eks-node-group.tf
とlaunch-config.tf
にはいつくか両方ともで設定できるパラメータがあります。そのようなパラメータを両方で設定するとエラーになるため一方でのみ設定します。どちらで設定すべきかはAWSのドキュメント起動テンプレートのサポートに案内があります。
resource "aws_eks_node_group" "worker" {
cluster_name = var.eks_name
node_group_name = "${var.base_name}-node-group"
node_role_arn = aws_iam_role.eks_node_group.arn
subnet_ids = var.public_subnet_ids
launch_template {
id = aws_launch_template.eks_node_group.id
version = aws_launch_template.eks_node_group.latest_version
}
scaling_config {
desired_size = var.eks-node_desired_capacity
max_size = var.eks-node_max_size
min_size = var.eks-node_min_size
}
tags = {
"Name" = "${var.base_name}-node-group"
}
lifecycle {
ignore_changes = [scaling_config[0].desired_size]
}
depends_on = [
aws_eks_cluster.this
]
}
launch-config.tf
起動テンプレートの設定をします。基本的には[マネージドノードグループが作成する起動テンプレートの確認](#マネージドノードグループが作成する起動テンプレートの確認で確認した内容をそのままTerraformに落とし込んでいます。
network_interfaces
に目的である任意のセキュリティグループを指定します。また、EKSマスターと通信するためのセキュリティグループも必要です。(元ネタ:カスタムセキュリティグループを使用する)
USERDATAの中でK8S_CLUSTER_DNS_IP
を指定しています。これはCoreDNSのServiceのIPアドレスですが、ServiceネットワークのCIDRによってIPアドレスが異なります。デフォルトのServiceネットワークのCIDRはTerraformのaws_eks_cluster
リソースのドキュメントkubernetes_network_configにある通り、EKSをデプロイするVPCのCIDRとは異なるCIDR(10.100.0.0/16 or 172.20.0.0/16)を使用します。今回はEKSの設定でservice_ipv4_cidr
を指定し、そのvaluesを加工してCoreDNSのServiceのIPアドレスを指定しています。
locals {
eks-nodes_init_script = <<USERDATA
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"
--//
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
set -ex
B64_CLUSTER_CA=${aws_eks_cluster.this.certificate_authority.0.data}
API_SERVER_URL=${aws_eks_cluster.this.endpoint}
K8S_CLUSTER_DNS_IP=${replace(var.eks_service_ipv4_cidr, "0/16", "10")}
/etc/eks/bootstrap.sh ${var.eks_name} --kubelet-extra-args '--node-labels=eks.amazonaws.com/nodegroup-image=${data.aws_ami.eks-node.id},eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=${var.base_name}-node-group' --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL --dns-cluster-ip $K8S_CLUSTER_DNS_IP
--//--
USERDATA
}
data "aws_ami" "eks-node" {
most_recent = true
owners = ["602401143452"]
filter {
name = "name"
values = ["amazon-eks-node-${var.eks_version}-*"]
}
}
resource "aws_launch_template" "eks_node_group" {
name = "${var.base_name}-eks_node_group"
block_device_mappings {
device_name = "/dev/xvda"
ebs {
delete_on_termination = true
volume_size = var.eks-node_volume_size
volume_type = "gp2"
}
}
network_interfaces {
device_index = 0
security_groups = [aws_security_group.eks-node.id, aws_security_group.eks.id]
}
image_id = data.aws_ami.eks-node.id
instance_type = var.eks-node_instance_type
key_name = var.eks-node_key_name
user_data = base64encode(local.eks-nodes_init_script)
depends_on = [
aws_eks_cluster.this
]
tags = {
"Name" = "${var.base_name}-eks_node_group"
}
}
sg.tf
セキュリティグループを設定します。カスタムセキュリティグループを使用するにある通り、マネージドノードに付けたいセキュリティグループとEKSマスターと通信するためのセキュリティグループを作ります。
セキュリティグループのルールはAmazon EKS セキュリティグループの考慮事項を参考にします。
# eks cluster
resource "aws_security_group" "eks" {
name = "${var.base_name}-eks-sg"
description = "for eks sg"
vpc_id = var.vpc_id
tags = {
"Name" = "${var.base_name}-eks-sg",
}
}
resource "aws_security_group_rule" "eks_in_443_self" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
self = true
security_group_id = aws_security_group.eks.id
}
resource "aws_security_group_rule" "eks_in_1025-65535_self" {
type = "ingress"
from_port = 1025
to_port = 65535
protocol = "tcp"
self = true
security_group_id = aws_security_group.eks.id
}
resource "aws_security_group_rule" "eks_out_all" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_group_id = aws_security_group.eks.id
}
# managed node group
resource "aws_security_group" "eks-node" {
name = "${var.base_name}-eks-node-sg"
description = "for eks-node server sg"
vpc_id = var.vpc_id
tags = {
"Name" = "${var.base_name}-eks-node-sg",
"kubernetes.io/cluster/${var.eks_name}" = "owned"
}
}
resource "aws_security_group_rule" "eks-node_in_30000-65535" {
type = "ingress"
from_port = 30000
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.eks-node.id
}
resource "aws_security_group_rule" "eks-node_in_self" {
type = "ingress"
from_port = 0
to_port = 0
protocol = -1
self = true
security_group_id = aws_security_group.eks-node.id
}
resource "aws_security_group_rule" "eks-node_out_all" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_group_id = aws_security_group.eks-node.id
}
eks.tf
EKSの設定です。マネージドワーカーと通信するため、security_group_ids
に作成したEKSクラスタ用のセキュリティグループを指定します。また、launch-configtfのUSERDATAでCoreDNSのIPアドレスを指定したいため、kubernetes_network_config
も設定します。
resource "aws_eks_cluster" "this" {
name = var.eks_name
role_arn = aws_iam_role.eks_cluster.arn
version = var.eks_version
vpc_config {
endpoint_private_access = var.eks_endpoint_private_access
endpoint_public_access = var.eks_endpoint_public_access
public_access_cidrs = var.eks_public_access_cidrs
security_group_ids = [aws_security_group.eks.id]
subnet_ids = flatten([var.private_subnet_ids, var.public_subnet_ids])
}
enabled_cluster_log_types = var.eks_enabled_cluster_log_types
kubernetes_network_config {
service_ipv4_cidr = var.eks_service_ipv4_cidr
}
tags = {
"Name" = var.eks_name
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster,
aws_iam_role_policy_attachment.eks_vpc_resource_controller,
]
}
variables.tf
variable "base_name" {
description = "作成するリソースに付与する接頭語"
type = string
}
## EKS
variable "eks_version" {
description = "EKSのバージョン"
type = string
}
variable "eks_name" {
description = "EKSクラスタの名前"
type = string
}
variable "eks_endpoint_private_access" {
description = "VPC内からのアクセスを許可するか"
type = bool
}
variable "eks_endpoint_public_access" {
description = "VPC外からのアクセスを許可するか"
type = bool
}
variable "eks_public_access_cidrs" {
description = "VPC外からのアクセスを許可するCIDR"
type = list(string)
}
variable "eks_enabled_cluster_log_types" {
description = "保存するマスターコンポーネントのログ"
type = list(string)
}
variable "eks_service_ipv4_cidr" {
description = "serviceのCIDR"
type = string
}
## EKS node group
variable "eks-node_desired_capacity" {
description = "EKSノードグループの希望数"
type = number
}
variable "eks-node_max_size" {
description = "EKSノードグループの最大数"
type = number
}
variable "eks-node_min_size" {
description = "EKSノードグループの最小数"
type = number
}
variable "eks-node_volume_size" {
description = "EKSノードのディスク容量"
type = number
}
variable "eks-node_instance_type" {
description = "EKSノードのインスタンスタイプ"
type = string
}
variable "eks-node_key_name" {
description = "EKSノードのキーペア名"
type = string
}
tfvarsの例
# common parameter
base_name = "test-eks"
# network parameter
private_subnet_ids = [
"subnet-04e80b9b64g8ab944",
"subnet-05dec3759d2ff895a",
]
public_subnet_ids = [
"subnet-0fd24b7b1d9f11078",
"subnet-0e12b21b7fc7ccf77",
]
vpc_cidr = "10.1.0.0/16"
vpc_id = "vpc-0b02175fcb37cb133"
# eks cluster
eks_version = "1.21"
eks_name = "test-eks-cluster"
eks_endpoint_private_access = true
eks_endpoint_public_access = true
eks_public_access_cidrs = ["126.72.69.141/32"]
eks_enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
eks_service_ipv4_cidr = "172.20.0.0/16"
# eks node group
eks-node_desired_capacity = 4
eks-node_max_size = 4
eks-node_min_size = 4
eks-node_volume_size = 30
eks-node_instance_type = "m5.large"
eks-node_key_name = "hoge"
あとがき
オートスケーリンググループが使用する起動テンプレートは上記自分で作成した起動テンプレートではなく、自分で作成したテンプレートをコピーして新たに作られる起動テンプレートが使用されるようです。マネージドノードグループの設定でテンプレートのバージョンをlatest
で指定していれば自分で作成したテンプレートを更新するとコピーの起動テンプレートも更新されました。
ただ、ここまでいろいろやるならセルフマネージドのオートスケーリンググループ作るのとあんまり変わらない気もした。