昨天我們把 VPC 架起來,等於把基礎網路的地基鋪好。今天要正式進入 EKS 的重點:建立 EKS Control Plane 與 Worker Node Group。在 Kubernetes 的世界裡,Control Plane 就像「大腦」,負責 API 與資源調度;Worker Node 則是「手腳」,負責實際跑 Pod。
如果只建了 Control Plane 沒有 Node,那集群還是空的;反之,如果只有 Node 沒有 Control Plane,它們就只是一群無法被調度的 EC2。今天的目標,就是透過 Terraform 把兩者一次建好,讓我們的第一個完整 EKS Cluster 正式上線。
和建立 VPC 時一樣,要自己建立所有 EKS 相關資源不是不行,但是會花費太多時間盤點需要開的資源。因此今天也會採用社群維護的 module,使用的是 terraform-aws-eks
。接下來我會針對程式碼逐段作說明:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
# This module always follows the latest version.
# https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions-standard.html
cluster_version = "1.32"
cluster_name = var.eks_name
#### KEY POINT 1
cluster_endpoint_private_access = true
cluster_endpoint_public_access = true
cluster_endpoint_public_access_cidrs = [
"0.0.0.0/0"
]
cluster_ip_family = "ipv4"
cluster_service_ipv4_cidr = var.eks_service_cidr
vpc_id = data.aws_vpc.this.id
subnet_ids = data.aws_subnets.private_subnets.ids
enable_irsa = true
# ...(下段續)
現在的 EKS 同時開啟 public 及 private endpoint,主要使用上的差異會如下表所示:
in-cluster request | kubectl request |
|
---|---|---|
only public | 不走 VPC,走 AWS 內網 | 可以從 Internet 存取 |
public + private | 走 VPC (private endpoint) | 可以從 Internet 存取 |
only private | 走 VPC (private endpoint) | 只能從 VPC 內連線 |
這邊主要是想要討論 public / private access endpoint 的選擇,個人認為是兩個都開比較適合,原因是:
# ...(續上一段)
cluster_addons = {
# DaemonSet: aws-node
vpc-cni = {
addon_version = "v1.19.3-eksbuild.1"
service_account_role_arn = module.irsa_vpc_cni.iam_role_arn
}
# Deployment: coredns
coredns = {
addon_version = "v1.11.4-eksbuild.2"
configuration_values = jsonencode({
tolerations = [
{
key = "SystemOnly"
operator = "Exists"
}
]
})
}
# DaemonSet: ebs-csi-node & Deployment: ebs-csi-controller
aws-ebs-csi-driver = {
addon_version = "v1.40.1-eksbuild.1"
service_account_role_arn = module.irsa_ebs_csi.iam_role_arn
}
kube-proxy = {
addon_version = "v1.32.0-eksbuild.2"
}
}
# ...(下段續)
可以在 addon 指定 toleration 和 QoS 之類的設定(像是在 coredns
裡面的 configuration_values
),在這個 eks module 和 AWS console 上都有這些細部的 addon 設定可以做調整。
這段是所有 node group 共享的 default 設定,另外在 block_device_mappings
區塊,因為公司內部有導入資安掃描工具,其中有項規則是 EBS 必須要加密,因此會在 node group default 設定加上加密的設置
# ...(續上一段)
# https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/modules/eks-managed-node-group/variables.tf
# this block is default setting value for other node groups
eks_managed_node_group_defaults = {
subnet_ids = data.aws_subnets.private_subnets.ids
create_launch_template = true
use_custom_launch_template = true
update_config = {
max_unavailable_percentage = 25
}
block_device_mappings = {
xvda = {
device_name = "/dev/xvda"
ebs = {
volume_size = 20
volume_type = "gp2"
encrypted = true
delete_on_termination = true
}
}
}
}
eks_managed_node_groups = {
general = {
name = "general"
min_size = 2
max_size = 2
desired_size = 2
disk_size = 20
capacity_type = "SPOT"
instance_types = ["t4g.medium", "t4g.small"]
ami_type = "AL2_ARM_64"
labels = {
"usage" = "general"
}
}
}
# ...(下段續)
這個 eks module 預設會建立:
而我們後面用 Karpenter 建立新節點時,需要給新結點掛上同樣的 node security group,用以確保 AWS managed node group 和 Karpenter 長出來的節點可以透過相同的 security groups 設定,來打通網路。
此外,在後續使用 Load balancer controller 建立 NLB 時,因為 NLB 預期 node 只會有一個 security group 包含 kubernetes.io/cluster/${cluster_name}
這個 tag;但是 cluster security group 和 node security group 都會包含這個 tag,導致當節點身上同時有 cluster/node security group 時會無法註冊 nginx(也就是 Load Balancer 的 target),因此我們必須指定節點身上都只有 node security group,並且能夠與 cluster security group 互相溝通。
# ...(續上一段)
node_security_group_tags = {
// karpenter find out which sg should be attach according to this tag
"karpenter.sh/discovery" = var.eks_name
}
// without this rule, kubectl proxy might not work properly
node_security_group_additional_rules = {
AllowAllTrafficFromCluster = {
description = "Allow all traffic from cluster"
protocol = "all"
from_port = -1
to_port = -1
type = "ingress"
source_cluster_security_group = true
}
}
# ...(下段續)
最後是從 EKS module v19 升版到 v20 的設置:
# ...(續上一段)
# Upgrade to 20.x
kms_key_enable_default_policy = true
authentication_mode = "API"
access_entries = local.access_entries
}
AWS 表示 aws-auth
ConfigMap 已被 deprecated。此外,在 access entry release 前建立的 cluster,設定 auth 的方式只能從 ConfigMap
改成 ConfigMap & API(access entry)
,而 ConfigMap & API
只能改成 API
,沒辦法退回 ConfigMap
。
至於為什麼 aws-auth ConfigMap 不是一個好的權限管理方式,主要是因為手動編輯容易出錯,而且在新增角色的步驟上非常繁瑣,詳細的設置可以參考我之前寫過的文章:EKS tf module v20 Survey
這裡要注意,Control Plane 建立時間大約 10–15 分鐘,加上 Node Group 之後,可能會接近 20 分鐘。過程中 Terraform 會先建立 Cluster,再建立 Node IAM Role,最後才建立 Node Group 並把它們註冊進 Cluster。
Terraform 建立完成後,我們可以接著更新本地端的 kubeconfig
:
aws eks update-kubeconfig --name eks-in-practice --alias eks-in-practice
接著測試連線:
$ kubectl cluster-info
# 會顯示 API Server 與 CoreDNS 的 endpoint:
Kubernetes control plane is running at https://XXXXXEKSIDXXXX.xxx.ap-northeast-1.eks.amazonaws.com
CoreDNS is running at https://XXXXXEKSIDXXXX.xxx.ap-northeast-1.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
接著預期可以看到 cluster 內的節點們:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-2-xxx.ap-northeast-1.compute.internal Ready <none> 31h v1.32.1-eks-5d632ec
ip-10-0-2-xxx.ap-northeast-1.compute.internal Ready <none> 136d v1.32.1-eks-5d632ec
...
代表 Worker Node 已經成功註冊進 EKS Control Plane 囉!
到這裡,我們已經完成了 EKS Cluster 的建立:不只是 Control Plane,還包括能實際承載 workload 的 Node Group。透過 Terraform module,我們把許多繁瑣的設定(像是 endpoint、addon、security group 與 node group 預設值)統一自動化管理,讓整個建置流程更簡潔、更穩定。
不過,EKS 叢集建起來之後,還有一個很關鍵的問題:Pod 要怎麼安全地存取 AWS 資源? 例如 VPC CNI、EBS CSI Driver、Load Balancer Controller 這些元件,都需要跟 AWS API 打交道。明天我們就會進一步介紹 IAM Role for Service Account (IRSA) 的概念,看看怎麼把 Pod 的 ServiceAccount 和 IAM Role 綁在一起,讓 Pod 能安全地取得所需的最小權限。