☸️ نحوه استقرار یک خوشه مقرون به صرفه AWS/EKS Kubernetes با استفاده از Terraform در سال 2023
تصویر جلد تولید شده به صورت محلی توسط DiffusionBee با مدل ToonYou. سریع بود purple excavator, smoke, best quality, masterpiece, large clouds
Terraform یک ابزار زیرساخت به عنوان کد است که به شما امکان می دهد منابع ابری و اولیه را با خیال راحت و کارآمد بسازید، تغییر دهید و نسخه کنید.
نمونه نقطه ای AWS نمونه ای است که از ظرفیت اضافی EC2 استفاده می کند که با قیمت کمتر از قیمت درخواستی در دسترس است. از آنجایی که Spot Instance ها به شما امکان می دهند نمونه های EC2 استفاده نشده را با تخفیف های زیاد درخواست کنید، می توانید هزینه های آمازون EC2 خود را به میزان قابل توجهی کاهش دهید. و Kubernetes یک کاندید عالی برای استفاده از ماشین های مجازی ناپایدار است.
بهطور شگفتانگیزی، یافتن نمونههای کامل Terraform با استفاده از انواع مختلف نمونههای نقطهای در اینترنت دشوار است، زیرا پارامترهای EKS Module نسخه 18 دوباره کار میکنند. این پست وبلاگ برای پر کردن این حفره وجود دارد.
در اینجا نحوه استقرار یک خوشه EKS با ویژگی های زیر را با استفاده از Terraform توضیح می دهیم:
- یک شبکه VPC با زیرشبکه های خصوصی و عمومی با استفاده از یک دروازه
- یک خوشه Kubernetes با استفاده از نمونه های نقطه ای از نوع مختلط
- ثبت های داکر
برای اینکه بلوک های فایل زیر کار کنند، باید اصول Terraform را بدانید و aws-cli را روی پروفایلی به نام خوشه پیکربندی کنید. اما اگر ترجیح می دهید به نمایه پیش فرض aws بچسبید، حذف کنید --profile
تکه های کد زیر و همه چیز درست خواهد شد.
در اینجا متغیرهایی هستند که توسط اکثر منابع مورد استفاده قرار می گیرند که در زیر توضیح داده شده است. همه مقادیر ساده هستند به جز var.aws_auth_users
variable "region" {
description = "Cluster region"
default = "eu-west-x"
}
variable "cluster_name" {
description = "Name of the EKS cluster"
default = "my-project"
}
variable "kubernetes_version" {
description = "Cluster Kubernetes version"
default = "1.24"
}
# Being in this list is required to see Kubernetes resources in AWS console
variable "aws_auth_users" {
description = "Developers with access to the dev K8S cluster and the container registries"
default = [
{
userarn = "arn:aws:iam::xxx:user/user.name1"
username = "user.name1"
groups = ["system:masters"]
},
{
userarn = "arn:aws:iam::xxx:user/user.name2"
username = "user.name2"
groups = ["system:masters"]
}
]
}
بیایید ارائه دهندگان و برچسب های رایج را تنظیم کنیم.
provider "aws" {
region = var.region
# prerequisite locally : aws configure --profile <cluster-name>
profile = var.cluster_name
}
provider "kubernetes" {
host = module.eks.cluster_endpoint
cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
# This requires the awscli to be installed locally where Terraform is executed
args = ["--profile", var.cluster_name, "eks", "get-token", "--cluster-name", var.cluster_name]
}
}
locals {
tags = {
Environment = "NON-PROD"
creation-date = "01/02/2023" # a variable would update the value on each tf apply
}
}
data "aws_caller_identity" "current" {}
در اینجا یک بلوک Terraform نمونه برای شبکه VPC است که در آن خوشه Kubernetes ایجاد خواهد شد.
چند نکته:
- یک شبکه ایمن از یک زیرشبکه خصوصی و عمومی تشکیل شده است که یکی در هر منطقه در دسترس بودن منطقه انتخاب شده است.
- مراقب باشید که IP کافی در محدوده های مورد نیاز شما در دسترس باشد. در اینجا می تواند 8192 IP را جا دهد
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.0"
name = var.cluster_name
cidr = "10.0.0.0/16" # Last IP : 10.0.255.255
azs = ["${var.region}a", "${var.region}b", "${var.region}c"]
# use https://www.ipaddressguide.com/cidr
# /19 : 8,192 IPs
private_subnets = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19"] # No hole in IP ranges
public_subnets = ["10.0.96.0/19", "10.0.128.0/19", "10.0.160.0/19"] # No hole in IP ranges
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
public_subnet_tags = {
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
private_subnet_tags = {
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
tags = local.tags
}
اکنون غذای اصلی را تعریف می کنیم: خوشه Kubernetes.
چند نکته:
- افزونههای خوشهای مورد استفاده برای کارکرد صحیح خوشه اجباری هستند
- افزونه Coredns ممکن است در اولین اعمال شکست بخورد، فقط دوباره اعمال کنید (تایم اوت سفارشی شده است تا از انتظار طولانی مدت جلوگیری شود)
- کاربران AWS با حق دسترسی به خوشه در تعریف شده اند
var.aws_auth_users
، یک مثال در ادامه این مقاله نشان داده شده است - گروه های امنیتی ساده شده اند و ممکن است برای نیازهای امنیتی شما تنظیم شوند:
- عدم دسترسی از اینترنت (به جز استفاده از کنترل کننده ورودی)
- دسترسی کامل از گره به گره
- دسترسی کامل از گره ها به اینترنت
- EC2 مورد استفاده برای کارگران نمونه های نقطه ای t3 و t3a هستند. وقتی AWS از یک نوع تمام میشود، انواع نمونههای مختلط تضمین میکنند که گرهها گرسنگی ندارند
- یک نظر داد
fulltime-az-a
در صورت عدم اظهار نظر و انطباق با نیازهای شما، امکان ایجاد موارد درخواستی نیز وجود خواهد داشت - گره ها فقط در یک منطقه در دسترس ایجاد می شوند. در یک محیط تولید، حداقل از 2 منطقه در دسترس استفاده کنید، با ایجاد یک
spot-az-b
شبیه بهspot-az-a
. شبکه منطقه به منطقه آزاد نیست و در این مثال، خرابی بالقوه محیط توسعه قابل قبول است
module "eks" {
source = "terraform-aws-modules/eks/aws"
cluster_name = var.cluster_name
cluster_version = var.kubernetes_version
cluster_endpoint_private_access = true
cluster_endpoint_public_access = true
cluster_addons = {
coredns = {
most_recent = true
timeouts = {
create = "2m" # default 20m. Times out on first launch while being effectively created
}
}
kube-proxy = {
most_recent = true
}
vpc-cni = {
most_recent = true
}
aws-ebs-csi-driver = {
most_recent = true
}
}
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
# Self managed node groups will not automatically create the aws-auth configmap so we need to
create_aws_auth_configmap = true
manage_aws_auth_configmap = true
aws_auth_users = var.aws_auth_users
enable_irsa = true
node_security_group_additional_rules = {
ingress_self_all = {
description = "Node to node all ports/protocols"
protocol = "-1"
from_port = 0
to_port = 0
type = "ingress"
self = true
}
egress_all = { # by default, only https urls can be reached from inside the cluster
description = "Node all egress"
protocol = "-1"
from_port = 0
to_port = 0
type = "egress"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}
self_managed_node_group_defaults = {
# enable discovery of autoscaling groups by cluster-autoscaler
autoscaling_group_tags = {
"k8s.io/cluster-autoscaler/enabled" : true,
"k8s.io/cluster-autoscaler/${var.cluster_name}" : "owned",
}
# from https://github.com/terraform-aws-modules/terraform-aws-eks/issues/2207#issuecomment-1220679414
# to avoid "waiting for a volume to be created, either by external provisioner "ebs.csi.aws.com" or manually created by system administrator"
iam_role_additional_policies = {
AmazonEBSCSIDriverPolicy = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
}
}
# possible values : https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/node_groups.tf
self_managed_node_groups = {
default_node_group = {
create = false
}
# fulltime-az-a = {
# name = "fulltime-az-a"
# subnets = [module.vpc.private_subnets[0]]
# instance_type = "t3.medium"
# desired_size = 1
# bootstrap_extra_args = "--kubelet-extra-args '--node-labels=node.kubernetes.io/lifecycle=normal'"
# }
spot-az-a = {
name = "spot-az-a"
subnet_ids = [module.vpc.private_subnets[0]] # only one subnet to simplify PV usage
# availability_zones = ["${var.region}a"] # conflict with previous option. TODO try subnet_ids=null at creation (because at modification it fails)
desired_size = 2
min_size = 1
max_size = 10
bootstrap_extra_args = "--kubelet-extra-args '--node-labels=node.kubernetes.io/lifecycle=spot'"
use_mixed_instances_policy = true
mixed_instances_policy = {
instances_distribution = {
on_demand_base_capacity = 0
on_demand_percentage_above_base_capacity = 0
spot_allocation_strategy = "lowest-price" # "capacity-optimized" described here : https://aws.amazon.com/blogs/compute/introducing-the-capacity-optimized-allocation-strategy-for-amazon-ec2-spot-instances/
}
override = [
{
instance_type = "t3.xlarge"
weighted_capacity = "1"
},
{
instance_type = "t3a.xlarge"
weighted_capacity = "1"
},
]
}
}
}
tags = local.tags
}
بسیاری از ما خوشههای Kubernetes را برای برنامههای سفارشی مستقر میکنیم، بنابراین در اینجا بلوکهای ثبت داکر AWS وجود دارد.
با استفاده از رجیستری های AWS، Kubernetes و Docker بی عیب و نقص ادغام می شوند، بدون نیاز به پیکربندی دیگر.
resource "aws_ecr_repository" "module-a" {
name = "my-app/module-a"
}
resource "aws_ecr_repository" "module-b" {
name = "my-app/module-b"
}
resource "aws_ecr_repository" "module-c" {
name = "my-app/module-c"
}
در اغلب موارد، پروژه ها برای ذخیره فایل ها به یک سطل S3 نیاز دارند، بنابراین در اینجا کد یک سطل S3 ایمن با دسترسی از backend
حساب سرویس kubernetes
resource "aws_s3_bucket" "bucket" {
bucket = "${var.cluster_name}-bucket"
tags = local.tags
}
resource "aws_s3_bucket_acl" "bucket_acl" {
bucket = aws_s3_bucket.bucket.id
acl = "private"
}
data "aws_iam_policy_document" "role_policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
condition {
test = "StringLike"
variable = "${replace(module.eks.cluster_oidc_issuer_url, "https://", "")}:sub"
values = ["system:serviceaccount:*:backend"] # system:serviceaccount:<K8S_NAMESPACE>:<K8S_SERVICE_ACCOUNT>
}
principals {
identifiers = ["arn:aws:iam::${var.aws_account_id}:oidc-provider/${replace(module.eks.cluster_oidc_issuer_url, "https://", "")}"]
type = "Federated"
}
}
}
data "aws_iam_policy_document" "s3_policy" {
statement {
actions = [
"s3:ListAllMyBuckets",
]
resources = [
"*",
]
}
statement {
actions = [
"s3:*",
]
resources = [
aws_s3_bucket.bucket.arn,
"${aws_s3_bucket.bucket.arn}/*"
]
}
}
resource "aws_iam_role" "role" {
assume_role_policy = data.aws_iam_policy_document.role_policy.json
name = "${var.cluster_name}-backend-role"
}
resource "aws_iam_policy" "policy" {
name = "${var.cluster_name}-backend-policy"
path = "https://dev.to/"
policy = data.aws_iam_policy_document.s3_policy.json
}
resource "aws_iam_role_policy_attachment" "attach" {
policy_arn = aws_iam_policy.policy.arn
role = aws_iam_role.role.name
}
شما همچنین باید:
- ایجاد یا استفاده از یک حساب سرویس kubernetes (
backend
در این مثال) توسط غلاف های شما با استفاده از سطل استفاده می شود- در استقرار خود، تنظیم کنید
serviceAccount: backend
- در استقرار خود، تنظیم کنید
- حساب سرویس kubernetes را با موارد زیر حاشیه نویسی کنید:
eks.amazonaws.com/role-arn: arn:aws:iam::<AWS_ACCOUNT_ID>:role/<CLUSTER_NAME>-backend-role
به ویژه نیازی به خروجی خاصی نیست kube.config
، از آنجا که aws-cli
می تواند دسترسی به خوشه را پیکربندی کند. اما برای راحتی، دستور aws-cli به عنوان یک خروجی چاپ می شود.
output "update_local_context_command" {
description = "Command to update local kube context"
value = "aws --profile ${var.cluster_name} eks update-kubeconfig --name=${var.cluster_name} --alias=${var.cluster_name} --region=${var.region}"
}
با جمعآوری تمام این کدهای Terraform، باید بتوانید یک کلاستر را در یک فرمان زیر 20 دقیقه مستقر کنید.
اگر فکر می کنید برخی از کدها باید بهبود یابد، لطفاً در نظرات راهنمایی کنید 🤓