Trong bài blog này, chúng ta sẽ tìm hiểu về cách xây dựng một môi trường Amazon Elastic Kubernetes Service (EKS) sử dụng công cụ Hashicorp Terraform. EKS là một dịch vụ quản lý Kubernetes được Amazon Web Services (AWS) cung cấp, cho phép bạn triển khai và quản lý các ứng dụng container một cách dễ dàng trên AWS.
Để xây dựng môi trường EKS, chúng ta sẽ sử dụng tài liệu hướng dẫn có sẵn từ EKS Workshop. Tài liệu này cung cấp cho chúng ta các bước chi tiết và code mẫu để triển khai EKS bằng Terraform.
Terraform là một công cụ mã hóa cơ sở hạ tầng (infrastructure-as-code) mạnh mẽ, giúp tự động hóa quá trình tạo và quản lý các tài nguyên cloud. Với việc sử dụng Terraform, chúng ta có thể định nghĩa cấu hình hạ tầng của môi trường EKS dưới dạng mã nguồn, giúp việc xây dựng và cấu hình trở nên dễ dàng và nhất quán.
Lợi ích của việc sử dụng Terraform cho EKS:
Tự động hóa: Terraform tự động hóa các tác vụ thủ công, giúp tiết kiệm thời gian và giảm thiểu sai sót.
Tính nhất quán: Terraform đảm bảo môi trường EKS của bạn luôn được cấu hình theo chuẩn mực nhất định.
Dễ dàng quản lý: Việc quản lý cấu hình EKS trở nên dễ dàng hơn với các file Terraform có thể đọc và chỉnh sửa.
Khả năng lặp lại: Terraform cho phép bạn tái tạo môi trường EKS một cách dễ dàng và nhanh chóng
Understanding Terraform config files
File providers.tf
cấu hình các nhà cung cấp Terraform cần thiết để xây dựng cơ sở hạ tầng
provider "aws" {
default_tags {
tags = local.tags
}
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.67.0"
}
}
required_version = ">= 1.4.2"
}
Đoạn code này thiết lập cấu hình cơ bản cho việc sử dụng Terraform để quản lý tài nguyên trên nền tảng Amazon Web Services (AWS).
provider "aws" { ... }
: Khối này khai báo nhà cung cấp (provider) là AWS.default_tags { ... }
: Bên trong khốiaws
, đoạn này thiết lập các thẻ mặc định (tags) sẽ được gắn cho tất cả tài nguyên AWS được tạo thông qua Terraform. Biếnlocal.tags
được tham chiếu, giả sử biến này được định nghĩa ở một nơi khác trong cấu hình Terraform, chứa danh sách các cặp key-value dùng để gắn thẻ cho tài nguyên.Đọc thêm về Tag : https://kiloai.hashnode.dev/complete-terraform-part-3
terraform { ... }
: Khối này khai báo các cấu hình chung cho toàn bộ file Terraform.required_providers { ... }
: Phần này yêu cầu các nhà cung cấp cần thiết.aws = { ... }
: Khối con này khai báo nhà cung cấp AWS.source = "hashicorp/aws"
: Xác định nguồn của plugin Terraform dành cho AWS, được phát triển bởi HashiCorp, công ty cung cấp Terraform.version = ">= 4.67.0"
: Yêu cầu phiên bản của plugin AWS phải lớn hơn hoặc bằng 4.67.0. Terraform sẽ kiểm tra và cài đặt phiên bản phù hợp nếu cần.
required_version = ">= 1.4.2"
: Thiết lập phiên bản Terraform tối thiểu cần thiết để chạy cấu hình này. Phiên bản này phải lớn hơn hoặc bằng 1.4.2.
File main.tf
thiết lập một số nguồn dữ liệu Terraform để chúng ta có thể truy xuất tài khoản AWS hiện tại và AZ đang được sử dụng.
locals {
tags = {
created-by = "eks-workshop-v2"
env = var.cluster_name
}
}
Đoạn code này định nghĩa một biến cục bộ (local) trong Terraform, được sử dụng để tạo một tập hợp các thẻ (tags) sẽ được áp dụng cho các tài nguyên AWS được tạo.
locals { ... }
: Khối này khai báo một biến cục bộ (local) trong Terraform.tags = { ... }
: Biếntags
được định nghĩa và gán cho một tập hợp các cặp key-value, đại diện cho các thẻ sẽ được sử dụng.created-by = "eks-workshop-v2"
: Thẻcreated-by
được gán giá trị"eks-workshop-v2"
, có thể được sử dụng để theo dõi nguồn gốc tạo ra tài nguyên.env = var.cluster_name
: Thẻenv
được gán giá trị của biếnvar.cluster_name
. Biến này được giả định là được định nghĩa ở một nơi khác trong cấu hình Terraform, chứa tên cụm EKS. Việc sử dụng biến này cho phép tạo thẻenv
động, lấy giá trị từ biếncluster_name
File vpc.tf
sẽ cấu hình và đảm bảo rằng cơ sở hạ tầng VPC của chúng ta được tạo.
locals {
private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 3, k + 3)]
public_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 3, k)]
azs = slice(data.aws_availability_zones.available.names, 0, 3)
}
data "aws_availability_zones" "available" {
state = "available"
}
Đoạn code cung cấp định nghĩa ba biến cục bộ trong khối locals
: private_subnets
, public_subnets
và azs
. Hãy cùng phân tích từng biến và chức năng của nó:
data.aws
_availability_zones.available.names
: Phần này truy xuất danh sách tất cả các vùng khả dụng có sẵn từ nhà cung cấp AWS bằng cách sử dụng nguồn dữ liệudata.aws
_availability_zones
..names
: Truy cập thuộc tínhnames
của tài nguyêndata.aws
_availability_zones.available
, chứa danh sách tên vùng khả dụng.slice(..., 0, 3)
: Sử dụng hàmslice
để trích xuất một phần cụ thể của danh sách. Ở đây, nó chọn ba phần tử đầu tiên (chỉ mục 0, 1 và 2) từ danh sách AZ có sẵn và gán chúng cho biếnazs
private_subnets
[for k, v in local.azs : ...]
: Đây là một kiểu danh sách trong Terraform, tương tự như Python. Nó lặp qua từng phần tử (k
,v
) trong danh sáchlocal.azs
.k
: Biểu thị chỉ mục (vị trí) của AZ hiện tại trong danh sách.v
: Biểu thị tên của AZ hiện tại.
cidrsubnet(var.vpc_cidr, 3, k + 3)
: Lời gọi hàm này tính toán khối CIDR cho subnet private trong VPC. Nó có ba đối số:var.vpc_cidr
: Khối CIDR của toàn bộ VPC, được truyền dưới dạng biến.3
: Số octec cần phân bổ cho subnet, tạo ra subnet /24 với 254 địa chỉ IP có thể sử dụng.k + 3
: Tạo khối CIDR duy nhất cho mỗi subnet private một cách Dynamic. Việc thêm 3 vào chỉ mục đảm bảo rằng subnet private có phạm vi khác với subnet public (được giải thích sau).
Toàn bộ cách hiểu danh sách tạo ra danh sách các khối CIDR subnet riêng tư, một cho mỗi vùng khả dụng trong danh sách
local.azs
public_subnets
Tương tự như
private_subnets
, nó sử dụng hiểu danh sách để lặp qualocal.azs
. Tuy nhiên, lời gọi hàmcidrsubnet
chỉ sử dụng hai đối số:var.vpc_cidr
: Khối CIDR VPC .3
: Bit subnet cho subnet /24.Ở đây không có bù trừ (
k + 3
) được thêm vào. Điều này đảm bảo rằng subnet công khai có phạm vi riêng biệt với subnet riêng tư, thường được đặt ở đầu khối CIDR VPC.
Hãy cùng phân tích cú pháp và chức năng của:
for k, v in local.azs
:- Phần này trong đoạn code lặp qua biến
local.azs
. Cấu trúc vòng lặpfor
trong Terraform tương tự như Python hoặc các ngôn ngữ lập trình khác. Nó lặp qua một tập hợp dữ liệu, gán mỗi phần tử cho hai biến:k
vàv
.
- Phần này trong đoạn code lặp qua biến
k
: Biểu thị chỉ mục hoặc khóa của phần tử hiện tại trong tập hợplocal.azs
.v
: Biểu thị giá trị của phần tử hiện tại trong tập hợplocal.azs
. Trong trường hợp này, vìlocal.azs
là một tập hợp các AZ,v
sẽ là tên của vùng khả dụng.cidrsubnet(var.vpc_cidr, 3, k + 3)
:
Phần này trong đoạn code áp dụng hàm
cidrsubnet
cho mỗi phần tử (vùng AZ) trong tập hợplocal.azs
. Hàmcidrsubnet
có ba đối số:
var.vpc_cidr
: Đối số này chỉ định khối CIDR của VPC trong đó subnet sẽ được tạo. Nó được truyền từ biếnvar.vpc_cidr
.3
: Đối số này cho biết số bit cần phân bổ cho mặt nạ subnet. Giá trị 3 tạo ra subnet /24 địa chỉ IP có thể sử dụng.k + 3
: Đối số này tính toán bù trừ động cho khối CIDR của mỗi subnet. Biếnk
biểu thị chỉ mục của vùng khả dụng hiện tại và việc thêm 3 đảm bảo rằng subnet riêng tư có dải CIDR khác với subnet công khai.
Nhìn chung, dòng code này tạo ra danh sách các block CIDR subnet, một cho mỗi AZs khả dụng trong tập hợp local.azs
. Các khối CIDR được tính toán động, đảm bảo rằng subnet riêng tư có dải CIDR khác với subnet công khai.
Dưới đây là minh họa đơn giản về cách thức hoạt động của cách hiểu danh sách:
local.azs = {
"us-east-1a": "us-east-1a",
"us-east-1b": "us-east-1b",
"us-east-1c": "us-east-1c"
}
# Lặp qua các vùng AZs
for k, v in local.azs:
# Tính toán subnet CIDR cho mỗi vùng AZ
subnet_cidr = cidrsubnet(var.vpc_cidr, 3, k + 3)
# Thêm subnet CIDR vào danh sách
subnet_cidrs.append(subnet_cidr)
# Danh sách khối CIDR subnet kết quả
subnet_cidrs = [
"10.0.0.0/24", # us-east-1a private subnet
"10.0.4.0/24", # us-east-1b private subnet
"10.0.8.0/24", # us-east-1c private subnet
"10.0.12.0/24", # us-east-1a public subnet
"10.0.16.0/24", # us-east-1b public subnet
"10.0.20.0/24", # us-east-1c public subnet
]
Ví dụ này minh họa cách cách hiểu danh sách tạo ra các khối CIDR subnet động dựa trên các vùng khả dụng local.azs
và cấu hình khối CIDR VPC và mặt nạ subnet được cung cấp.
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.1" name = var.cluster_name cidr = var.vpc_cidr azs = local.azs public_subnets = local.public_subnets private_subnets = local.private_subnets public_subnet_suffix = "SubnetPublic" private_subnet_suffix = "SubnetPrivate" enable_nat_gateway = true create_igw = true enable_dns_hostnames = true single_nat_gateway = true # Manage so we can name manage_default_network_acl = true default_network_acl_tags = { Name = "${var.cluster_name}-default" } manage_default_route_table = true default_route_table_tags = { Name = "${var.cluster_name}-default" } manage_default_security_group = true default_security_group_tags = { Name = "${var.cluster_name}-default" } public_subnet_tags = merge(local.tags, { "kubernetes.io/role/elb" = "1" }) private_subnet_tags = merge(local.tags, { "karpenter.sh/discovery" = var.cluster_name }) tags = local.tags }
Đoạn code này thiết lập cấu hình cho VPC (Virtual Private Cloud) và subnet trong EKS cluster trên nền tảng AWS.
source
: Xác định nguồn của module, trong trường hợp này là modulevpc
từ registryterraform-aws-modules/vpc/aws
với phiên bản khoảng~> 5.1
.Các biến đầu vào (
name
,cidr
,azs
, ...) truyền thông tin để cấu hình module VPC.azs
: Sử dụng biếnlocal.azs
để truyền danh sách AZ.public_subnets
vàprivate_subnets
: Sử dụng các biến cùng tên để truyền danh sách subnet.public_subnet_suffix
vàprivate_subnet_suffix
: Thiết lập hậu tố - suffix cho tên "SubnetPublic" và "SubnetPrivate".
Các biến cấu hình khác thiết lập các tính năng cho VPC:
enable_nat_gateway
: Bật NAT Gateway .create_igw
: Tạo Internet Gateway.enable_dns_hostnames
: Bật tính năng sử dụng hostname DNS cho các tài nguyên.single_nat_gateway
: Chỉ sử dụng 1 NAT Gateway (nếu có nhiều AZ). Chú ý chỗ này nên đổi thành false để mỗi AZ sẽ có 1 NAT Gateway riêng
File eks.tf
quy định cấu hình cụm EKS của chúng ta, bao gồm Managed Node Group.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.16"
cluster_name = var.cluster_name
cluster_version = var.cluster_version
cluster_endpoint_public_access = true
cluster_addons = {
vpc-cni = {
before_compute = true
most_recent = true
configuration_values = jsonencode({
env = {
ENABLE_POD_ENI = "true"
ENABLE_PREFIX_DELEGATION = "true"
POD_SECURITY_GROUP_ENFORCING_MODE = "standard"
}
nodeAgent = {
enablePolicyEventLogs = "true"
}
enableNetworkPolicy = "true"
})
}
}
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
create_cluster_security_group = false
create_node_security_group = false
eks_managed_node_groups = {
default = {
instance_types = ["m5.large"]
force_update_version = true
release_version = var.ami_release_version
min_size = 3
max_size = 6
desired_size = 3
update_config = {
max_unavailable_percentage = 50
}
labels = {
workshop-default = "yes"
}
}
}
tags = merge(local.tags, {
"karpenter.sh/discovery" = var.cluster_name
})
}
Đoạn code này định nghĩa một module Terraform tên là eks
để cấu hình một cụm Amazon Elastic Kubernetes Service (EKS)
source
: Chỉ định vị trí của module Terraform. Nó trỏ đến moduleeks
chính thức từ kho lưu trữterraform-aws-modules
.version
: Xác định phạm vi phiên bản tương thích cho modulecluster_name
: Tham chiếu đến biến có têncluster_name
được định nghĩa ở nơi khác trong cấu hình Terraform của bạn. Nó đặt tên cho cụm EKS của bạn.cluster_version
: Tương tự nhưcluster_name
, nó tham chiếu đến biếncluster_version
để xác định phiên bản Kubernetes mong muốn cho cụm.cluster_endpoint_public_access
: Đặt quyền truy cập công khai vào endpoint của cụm EKS . Hãy cân nhắc các tác động bảo mật trước khi bật tính năng này trong môi trường production.cluster_addons
: Định nghĩa các chức năng bổ sung cho cụm của bạn. Ở đây, nó cấu hình add-on VPC CNI (Container Network Interface). Có thể cấu hình các add-on bổ sung tương tự trong khối này. (Sẽ có bài viết cụ thể cho các add-ons này)
Với cấu hình đã cho, Terraform sẽ tạo môi trường Workshop với các thành phần sau:
Tạo một VPC trải rộng trên các AZs (availability zone).
Tạo một cụm EKS.
Tạo một kết nối IAM OIDC.
Thêm một NodeGroup được quản lý tên "default".
Cấu hình VPC CNI để sử dụng (prefix delegation).