Triển khai private worker cho EKS
Giới thiệu
Bài này là bài tiếp theo của Tôi bắt đầu học EKS. Bài trước mình đã dựng một cụm EKS với worker node là public nên không được bảo mật cho lắm. Như đã hẹn lần này mình sẽ dựng một cụm EKS private worker bằng việc mình sẽ không expose worker ra ngoài internet với public ip nữa. Nếu các bạn chưa xem bài trước cũng không sao, tuy nhiên bài này sẽ không chi tiết bằng bài trước, và mình khuyên là các bạn nên xem cả bài trước để có cái hiểu hoàn chỉnh về EKS.
Kiến trúc của cụm lần này trông như sau
Các bước dựng cụm lần này cũng tương tự như bài trước, khác mỗi bước dựng network
- Tạo network (*): vpc, subnets, internet gateway, nat gateway.
- Khởi tạo EKS cluster
- Cài đặt addon: vpc-cni, kube-proxy
- Khởi tạo worker node
- Cài đặt addon: coredns
- Test nginx deployment
- Dọn dẹp
GitHub nè aws-eks-example/one
Khởi tạo project
terraform.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.24"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
shared_credentials_files = ["~/.aws/credentials"]
profile = "default"
}
Sau đó khởi tạo provider bằng lệnh
terraform init
Khởi tạo xong provider thì bắt đầu triển
1. Tạo network (*)
Network cho cụm EKS lần này sẽ trông như sau:
- 1 vpc có dải ip là
10.0.0.0/16
có tên làone
- 3 subnet với 3 dải ip khác nhau, mỗi subnet nằm trên một availability zone khác nhau. Trong 3 subnet có
- 1 public subnet:
one_one
sẽ gán ip public cho bất kỳ ec2 instance bằng thuộc tínhmap_public_ip_on_launch = true
- 2 private subnet:
one_two
,one_three
, các ec2 instance trong subnet này sẽ ko được gán ip public và không thể truy cập các ec2 instance này từ internet
- 1 public subnet:
- 1 internet gateway để cho phép truy cập internet từ trong ra ngoài và từ ngoài vào trong.
- 1 nat gateway:
one
để cho phép các ec2 instance trong private subnet truy cập được internet và control plane.- nat gateway cần một ip public (
eip
) để có thể thực hiện NAT
- nat gateway cần một ip public (
- cuối cùng là 2 route table để setup network theo ý định của bài viết
- 1 route table public:
public
cho public subnet publicone_one
- 1 route table nat:
nat
dùng chung cho 2 private subnet - setup route rule cho 2 route table
- 1 route table public:
Toàn bộ source code cho network mình sẽ để ở file network.tf
link GitHub nè https://github.com/tuana9a/aws-eks-example/blob/main/one/network.tf
1.1 Tạo vpc
vpc có dải ip là 10.0.0.0/16
có tên là one
network.tf
resource "aws_vpc" "one" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "one"
}
}
1.2 Tạo subnets
3 subnet với 3 dải ip khác nhau, mỗi subnet nằm trên một availability zone khác nhau.
Trong đó 1 public subnet: one_one
sẽ gán ip public cho bất kỳ ec2 instance bằng thuộc tính map_public_ip_on_launch = true
network.tf
resource "aws_subnet" "one_one" {
vpc_id = aws_vpc.one.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "one_one"
}
}
và 2 private subnet: one_two
, one_three
tức các ec2 instance trong subnet này sẽ ko được gán ip public và không thể truy cập các ec2 instance này từ internet
network.tf
resource "aws_subnet" "one_two" {
vpc_id = aws_vpc.one.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
tags = {
Name = "one_two"
}
}
resource "aws_subnet" "one_three" {
vpc_id = aws_vpc.one.id
cidr_block = "10.0.3.0/24"
availability_zone = "us-east-1c"
tags = {
Name = "one_three"
}
}
1.3 Tạo internet gateway
1 internet gateway để cho phép truy cập internet từ trong ra ngoài và từ ngoài vào trong.
network.tf
resource "aws_internet_gateway" "one" {
vpc_id = aws_vpc.one.id
tags = {
Name = "one"
}
}
1.4 Tạo nat gateway
1 nat gateway: one
dùng chung cho 2 private subnet: one_two
, one_three
để cho phép các ec2 instance trong private subnet truy cập được internet và control plane.
nat gateway one
này sẽ được đặt ở public subnet one_one
.
Lưu ý:
- một nat gateway cần một ip public (
eip
) để có thể đóng vai trò đứng trước public internet và internal subnet của mình - nat gateway cho private subnet nhưng lại nằm ở public subnet. Giải thích: các bạn có thể xem ở đây hoặc các bạn hiểu đơn giản như sau: thằng nat này cần truy cập public internet và traffic từ public internet về cũng phải vào được nat gateway, do vậy nó cần được nằm ở public subnet.
Ok hiểu rùi thì tạo nhé.
network.tf
resource "aws_eip" "one" {
domain = "vpc"
tags = {
Name = "one"
}
}
resource "aws_nat_gateway" "one" {
allocation_id = aws_eip.one.allocation_id
subnet_id = aws_subnet.one_one.id
tags = {
Name = "one"
}
}
1.5 Routing table và route rule
Cuối cùng setup route table để setup routing traffic theo ý muốn của mình
1 route table public: public
cho public subnet one_one
network.tf
resource "aws_route_table" "public" {
vpc_id = aws_vpc.one.id
tags = {
Name = "public"
}
}
Route table public
sẽ có một rule là: sẽ route toàn bộ traffic qua internet gateway one
: (cidr 0.0.0.0/0
sẽ match với mọi destination nếu ko có cidr match hơn: cái này trong môn mạng máy tính)
network.tf
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.one.id
}
Tạo xong route rồi thì gắn route table đó cho public subnet one_one
network.tf
resource "aws_route_table_association" "public_one_one" {
route_table_id = aws_route_table.public.id
subnet_id = aws_subnet.one_one.id
}
1 route table nat: nat
, dùng chung cho 2 private subnet: one_two
, one_three
network.tf
resource "aws_route_table" "nat" {
vpc_id = aws_vpc.one.id
tags = {
Name = "nat"
}
}
Route table nat
này cũng sẽ có một route rule: route toàn bộ traffic qua nat gateway one
(tương tự cái internet gateway)
network.tf
resource "aws_route" "nat" {
route_table_id = aws_route_table.nat.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.one.id
}
Các bạn bắt đầu ghép nối được rồi đúng ko: traffic từ private subnet mặc định sẽ qua nat gateway nằm trên public subnet, và traffic mặc định trên public subnet sẽ qua internet gateway và ra public internet.
Setup xong rule rồi thì gắn route table nat
cho 2 private subnet one_two
và one_three
network.tf
resource "aws_route_table_association" "nat_one_two" {
route_table_id = aws_route_table.nat.id
subnet_id = aws_subnet.one_two.id
}
resource "aws_route_table_association" "nat_one_three" {
route_table_id = aws_route_table.nat.id
subnet_id = aws_subnet.one_three.id
}
cuối cùng cũng xong network Apply thử phát
terraform apply # yes
Yay xong r, đm, tốn não dã man - networking vẫn là một thứ gì đó. Sau khi dựng xong network thì có thể bắt đầu dựng cụm EKS rùi
2. Khởi tạo EKS Cluster
Trước khi tạo control plane thì cần setup quyền cho nó đã, chi tiết giải thích mình có đề cập trong bài trước ở cũng mục 2. Tạo quyền cho control plane
iam.tf
resource "aws_iam_role" "eks_cluster_role" {
name = "EKSClusterRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "eks.amazonaws.com"
}
},
]
})
managed_policy_arns = [
"arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
]
}
Ở đây mình đặt tên là cluster role thì các bạn cũng hiểu là tương đương với control plane role nhé
terraform apply # yes
Đến lúc dựng cụm rồi.
Cụm mình tạo như sau:
- tên cụm là
one
- phiên bản kubernetes là 1.28
- role là role vừa tạo, role có tên là
EKSClusterRole
- subnet là public subnet:
,one_one
mục đích là cho phép mình tiếp cập control plane từ public internet, các worker cũng chọc tới control plane thông qua public internet cho đơn giản- [UPDATE] EKS yêu cầu ít nhất 2 az cho việc tạo cluster
- nên mình đã test tạo cluster rồi cho nó vào 2 private subnet
one_two
,one_three
xem tạo được không? rồi có thể truy cập control plane từ máy mình được không?- Câu trả lời là: có
- [UPDATE] EKS yêu cầu ít nhất 2 az cho việc tạo cluster
main.tf
resource "aws_eks_cluster" "one" {
name = "one"
role_arn = aws_iam_role.eks_cluster_role.arn
version = "1.28"
vpc_config {
subnet_ids = [
aws_subnet.one_two.id,
aws_subnet.one_three.id,
]
}
# Ensure that IAM Role permissions are created before and deleted after EKS Cluster handling.
# Otherwise, EKS will not be able to properly delete EKS managed EC2 infrastructure such as Security Groups.
depends_on = [
aws_iam_role.eks_cluster_role,
]
}
terraform apply # yes
Bình thường sẽ mất khoảng 7-8 phút, lần này mất tận 12 phút (khác mỗi cho vào private subnets). Các thông tin bên lề các bạn có thể xem thêm ở mục 3. Khởi tạo control plane trong bài viết trước. Kết quả khi kiểm tra trên giao diện trông sẽ như sau.
3. Cài đặt addon: vpc-cni, kube-proxy
Trước khi khởi tạo worker chúng ta cần chuẩn bị các addon cần thiết cho cụm: cni, kube-proxy đây là các thành phần bắt buộc phải có trong một cụm k8s, thiếu một trong các thành phần này có thể khiến việc khởi tạo worker node không thành công, chi tiết lỗi các bạn có thể tham khảo ở bài viết trước Tổng hợp lỗi
Lưu ý: cần cài đặt cni trước khi dựng node_group (worker node)
Cách tìm eks addon version các bạn tìm trên mạng hoặc dùng script của mình ở README nhé
addon.tf
# WARN: apply BEFORE node_group
resource "aws_eks_addon" "one_vpccni" {
cluster_name = aws_eks_cluster.one.name
addon_name = "vpc-cni"
addon_version = "v1.14.1-eksbuild.1"
}
# WARN: apply BEFORE node_group
resource "aws_eks_addon" "one_kubeproxy" {
cluster_name = aws_eks_cluster.one.name
addon_name = "kube-proxy"
addon_version = "v1.28.1-eksbuild.1"
}
terraform apply
Check trên aws console, tab Add-on
4. Khởi tạo worker node
Cần tạo quyền cho worker node trước. Chi tiết có thể xem trong bài viết trước 5. Tạo quyền cho worker node
Bổ sung vào iam.tf
như sau
resource "aws_iam_role" "eks_node_role" {
name = "EKSNodeRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ec2.amazonaws.com"
}
},
]
})
managed_policy_arns = [
"arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
"arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
]
}
terraform apply
Giờ có thể dựng worker được rồi, các bạn thêm resource node_group
vào trong main.tf
Lưu ý: cần dựng node_group trước khi cài đặt addon coredns
main.tf
# WARN: apply AFTER vpc-cni, kube-proxy
# WARN: apply BEFORE coredns
resource "aws_eks_node_group" "one" {
cluster_name = aws_eks_cluster.one.name
node_group_name = "one"
node_role_arn = aws_iam_role.eks_node_role.arn
subnet_ids = [
aws_subnet.one_two.id,
aws_subnet.one_three.id,
]
capacity_type = "SPOT"
scaling_config {
desired_size = 1
max_size = 3
min_size = 1
}
update_config {
max_unavailable = 1
}
# Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling.
# Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces.
depends_on = [
aws_iam_role.eks_node_role,
]
}
terraform apply # yes
Check trên aws console
Và khi click vào chi tiết instance thì ta không thấy có ip public được gắn vào worker node
Ok vậy đã dựng xong worker node, các thông tin khác các bạn có thể xem thêm bài trước 6. Khởi tạo worker node
5. Cài đặt addon: coredns
Lưu ý: cài đặt sau khi dựng node_group
Bổ sung vào addon.tf
# WARN: apply AFTER node_group
resource "aws_eks_addon" "one_coredns" {
cluster_name = aws_eks_cluster.one.name
addon_name = "coredns"
addon_version = "v1.10.1-eksbuild.2"
}
terraform apply
Vậy là đã dựng xong cụm EKS, tiếp theo mình sẽ test một vài deployment để xem cụm của mình có hoạt động hay không
6. Test nginx deployment
kéo kubeconfig về bằng lệnh
aws eks update-kubeconfig --name one
sau đó tạo file nginx-deployment.yml
với nội dung như sau
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 10
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "64m"
limits:
memory: "128Mi"
cpu: "128m"
cùng chạy thử và xem kết quả nhé
OK vậy là đã thành công tạo một cụm EKS private worker: an toàn, bảo mật và chuẩn chỉ, theo best practice hơn. Cảm ơn ae đã dành thời gian ra để đọc đến đây.
7. Dọn dẹp
terraform destroy # yes
Kết
Vậy là đã setup xong cụm EKS với worker node nằm ở private subnet, không bị expose ra ngoài internet.
Sau đó các e có thể quay lại bài đầu tiên phần Tôi bắt đầu học EKS#TODO của bài trước để xem các trò tiếp theo mình sẽ làm với EKS.
All rights reserved