Цього разу подивимось на установку Elastic Kubernetes Service (EKS), версії Kubernetes, що керується cloud-платформою Amazon. Він з'явився у 2018 році і є кращим способом установки Kubernetes в цьому середовищі. Також поглянемо на AWS Load Balancer Controller, що самостійно імплементує Ingress та Service (type: LoadBalancer) абстракції.
Раніше я вже писав про Kops, 3rd-party спосіб інсталяції Kubernetes, що в деякому сенсі нагадує k0s. Це чудовий варіант установки, що підтримує не лише AWS, а і інші cloud-платформи, проте із появою EKS він дещо втратив свою актуальність.
У цій статті опишемо створення мережі, EKS-кластеру, що буде працювати у цій мережі, AWS LB контролера та протестуємо його роботу. Описувати все будемо в Terraform, адже для нього вже створені всі необхідні модулі.
1. CREATING VPC/SUBNETS FOR EKS
Створимо необхідне дерево директорій, де і буде описаний проект:
$ git clone git@github.com:ipeacocks/terraform-aws-example.git
$ mv terraform-aws-example/eks-infra infrastructure
$ rm -rf terraform-aws-example
Створимо virtualenv для Python в який встановимо aws-cli:
$ cd infrastructure
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install awscli
$ aws --version
aws-cli/1.34.19 Python/3.12.3 Linux/6.8.0-44-generic botocore/1.35.19
Додамо key_id та access_key:
$ aws configure
AWS Access Key ID [None]: MYSECRETKEYID
AWS Secret Access Key [None]: MYSERETKEYVALUE
Default region name [None]: us-east-1
Default output format [None]:
Або ж можна просто імпортувати їх як змінні середовища. Деталі про це можна прочитати наприклад тут.
Після цього створимо бакет для стейтів, його ім'я має бути унікальним, тобто вашим власним:
$ aws s3api create-bucket \
--bucket my-tf-state-2023-06-01 \
--region us-east-1
За цим посиланням можна почитати про те, як створити lock в dynamodb, але це не принципово цього разу. Якщо відсутній Terraform - встановлюємо його зручним для вас способом. Наразі я використовую версію 1.9.5.
Для створення інфраструктури будемо користуватись готовим модулем terraform-aws-modules/vpc/aws. Проте, як на мене, для довгострокових цілей ліпше за все мережі створювати без модулів і з максимально однозначним іменуванням. Так буде простіше орієнтуватись та розширювати/обслуговувати їх. Мережа - базова річ і щось змінювати у ній потім буде складно і часто з неприємними наслідками.
Отже параметри модуля для створення VPC виглядатимуть наступним чином:
$ cd vpc
$ cat main.tf
data "aws_availability_zones" "available" {}
locals {
control_plane_subnets = var.control_plane_subnets
worker_subnets = var.worker_subnets
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.13.0"
name = var.vpc_name
cidr = var.vpc_cidr
azs = slice(data.aws_availability_zones.available.names, 0, 3)
private_subnets = concat(local.control_plane_subnets, local.worker_subnets)
public_subnets = var.public_subnets
enable_nat_gateway = var.enable_nat_gateway
single_nat_gateway = var.single_nat_gateway
one_nat_gateway_per_az = var.one_nat_gateway_per_az
enable_dns_hostnames = var.enable_dns_hostnames
# tags needed for AWS LB Controller
# https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.8/deploy/subnet_discovery/
public_subnet_tags = {
"kubernetes.io/role/elb" = 1
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = 1
}
}
Теги необхідні для AWS LB controller-а, мова про який піде далі. Конфігураційний файл зі змінними має наступний вигляд:
$ cat variables.tf
# Details are here https://github.com/terraform-aws-modules/terraform-aws-vpc#inputs
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "vpc_cidr" {
description = "VPC CIDR"
type = string
default = "10.0.0.0/16"
}
variable "control_plane_subnets" {
description = "Subnets for EKS control plane nodes"
type = list(any)
default = ["10.0.1.0/27", "10.0.1.32/27", "10.0.1.64/27"]
}
variable "worker_subnets" {
description = "Subnets for EKS worker nodes"
type = list(any)
default = ["10.0.8.0/21", "10.0.16.0/21", "10.0.24.0/21"]
}
variable "public_subnets" {
description = "Public subnets for LBs, NAT GWs and so on"
type = list(any)
default = ["10.0.100.0/23", "10.0.102.0/23", "10.0.104.0/23"]
}
variable "credentials" {
default = ["~/.aws/credentials"]
description = "Where your access and secret_key are stored, you create the file when you run the aws config"
type = list(any)
}
variable "vpc_name" {
description = "VPC name"
type = string
default = "my-vpc"
}
variable "enable_nat_gateway" {
description = "Should be true if you want to provision NAT Gateways for each of your private networks"
type = bool
default = true
}
# for prod purposes should be false
variable "single_nat_gateway" {
description = "Should be true if you want to provision a single shared NAT Gateway across all of your private networks"
type = bool
default = false
}
variable "one_nat_gateway_per_az" {
description = "Should be true if you want only one NAT Gateway per availability zone."
type = bool
default = true
}
variable "enable_dns_hostnames" {
description = "Should be true to enable DNS hostnames in the Default VPC"
type = bool
default = true
}
Мережа матиме наступний вигляд:
- все буде розміщено в регіоні us-east-1, в 3-х зонах доступності
- для K8s control plane виділені 3 окремі підмережі (змінна control_plane_subnets), Amazon рекомендує їх тримати окремо від підмереж воркерів. /27 підмережа (96 адрес) обрана тому, що вузлів для майстрів зазвичай багато не потрібно. Фізично вузли control plane лежать у власній AWS VPC, але їхні мережеві інтерфейси прокидуються у цю мережу
- для воркерів також обрано 3 підмережі в різних AZ (змінна worker_subnets). Вони значно більші, адже завдяки Amazon VPC CNI поди будуть використовувати адреси із цього ж діапазону.
- підмережі public_subnets загалом необхідні для NAT gateway-їв в кожній AZ, балансувальників і хостів (наприклад bastion), для яких необхідний доступ із мережі Інтернет
- one_nat_gateway_per_az параметр вказує на те, що в кожній AZ перебуватиме NAT gateway для вищої доступності мережі. Це загальна і правильна практика, проте для здешевлення можна зробити один NAT gateway для всіх приватних підмереж.
Детальніше про всі параметри можна почитати за посиланням до коду модуля.
Всі output змінні необхідні для того, щоб їх далі використовувати при створенні самого кластеру:
$ cat outputs.tf
locals {
control_plane_subnets_length = length(local.control_plane_subnets)
private_subnets_length = length(module.vpc.private_subnets)
}
output "region" {
description = "AWS region"
value = var.region
}
output "vpc_id" {
description = "VPC id"
value = module.vpc.vpc_id
}
output "public_subnets" {
description = "Public subnets"
value = module.vpc.public_subnets
}
output "control_plane_subnet_ids" {
description = "Private subnets for Control plane"
value = slice(module.vpc.private_subnets, 0, local.control_plane_subnets_length)
}
output "worker_subnet_ids" {
description = "Private subnets for Worker nodes"
value = slice(module.vpc.private_subnets, local.control_plane_subnets_length, local.private_subnets_length)
}
Ініціюємо додаткові модулі та стартуємо створення VPC:
$ terraform init
$ terraform plan
$ terraform apply
Після підготовки мереж переходимо до наступної секції.
2. CREATING EKS CLUSTER
Аналогічно скористуємось готовим модулем terraform-aws-modules/eks/aws. Будемо інсталювати останню на даний момент версію Kubernetes 1.30, а код, як я вже згадував, знаходиться тут:
$ cd ../eks
$ cat main.tf
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "my-tf-state-2023-06-01"
key = "my-vpc.tfstate"
region = "us-east-1"
}
}
resource "random_string" "suffix" {
length = 8
special = false
}
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.24.0"
cluster_name = "${var.eks_name_prefix}-${random_string.suffix.result}"
cluster_version = var.cluster_version
cluster_addons = var.cluster_addons
vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id
control_plane_subnet_ids = data.terraform_remote_state.vpc.outputs.control_plane_subnet_ids
subnet_ids = data.terraform_remote_state.vpc.outputs.worker_subnet_ids
cluster_endpoint_public_access = var.cluster_endpoint_public_access
eks_managed_node_group_defaults = var.eks_managed_node_group_defaults
eks_managed_node_groups = var.eks_managed_node_groups
node_security_group_additional_rules = var.node_security_group_additional_rules
kms_key_administrators = var.kms_key_administrators
enable_cluster_creator_admin_permissions = var.enable_cluster_creator_admin_permissions
}
Варто буде попередньо виправити назву бакета (навів жирним), де лежить стейт мережі. Параметри, що я використовую:
$ cat variables.tf
# Details https://github.com/terraform-aws-modules/terraform-aws-eks#inputs
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "credentials" {
default = ["~/.aws/credentials"]
description = "Where your access and secret_key are stored, you create the file when you run the aws config"
}
variable "eks_name_prefix" {
description = "EKS cluster name prefix"
type = string
default = "my-eks"
}
variable "cluster_version" {
description = "EKS version"
type = string
default = "1.30"
}
# can be false if you connect to private network via VPN or something
variable "cluster_endpoint_public_access" {
description = "Indicates whether or not the Amazon EKS public API server endpoint is enabled"
type = bool
default = true
}
variable "eks_managed_node_group_defaults" {
description = "Map of EKS managed node group default configurations"
default = { ami_type = "AL2_x86_64" }
}
variable "eks_managed_node_groups" {
description = "Map of EKS managed node group definitions to create"
default = {
one = {
name = "node-group-1"
instance_types = ["t3.small"]
min_size = 2
max_size = 3
desired_size = 2
}
}
}
variable "node_security_group_additional_rules" {
description = "Node to node traffic access"
default = {
ingress_self_any = {
description = "Node to node All"
protocol = "all"
from_port = 0
to_port = 0
type = "ingress"
self = true
}
egress_self_any = {
description = "Node to node All"
protocol = "all"
from_port = 0
to_port = 0
type = "egress"
self = true
}
}
}
# maybe better to pin versions here
variable "cluster_addons" {
description = "Map of cluster addon configurations to enable for the cluster"
default = {
coredns = {
most_recent = true
}
kube-proxy = {
most_recent = true
}
vpc-cni = {
most_recent = true
}
eks-pod-identity-agent = {
most_recent = true
}
}
}
# here should be your account ID
variable "kms_key_administrators" {
description = "A list of IAM ARNs for key administrators"
default = ["arn:aws:iam::78-your-account-id-27:root"]
}
variable "enable_cluster_creator_admin_permissions" {
description = "Indicates whether or not to add the cluster creator (the identity used by Terraform) as an administrator via access entry"
default = true
}
Деякі пояснення до коду:
- вичитуємо terraform стейт vpc із s3 задля отримання параметрів мережі, в яких буде встановлено EKS кластер
- вказуємо, що плануємо використовувати eks_managed_node_group. Є ще self_managed_node_groups, де потрібно самостійно менеджити воркера. Їх створення також підтримує цей модуль
- control plane кластеру буде мати зв'язок із зовнішнім світом завдяки параметру cluster_endpoint_public_access. Це зручно для роботи через kubectl на локальній системі, хоч він і зовсім не обов'язковий, якщо є зв'язок до кластеру через приватну мережу
- node_security_group_additional_rules дозволяє input/output трафік між воркерами. Це нормально для сервісів, що будуть працювати між собою. Цих правил може бути недостаньо на етапі додавання інших контролерів.
Всі існуючі параметри добре описані в документації до модуля.
Ініціюємо додаткові модулі та стартуємо створення кластеру EKS:
$ terraform init
$ terraform plan
$ terraform apply
Перевіримо на простому додатку чи коректно працює Kubernetes. Але попередньо оновимо kubeconfig:
$ aws eks update-kubeconfig --region us-east-1 --name my-eks-NbP3tleo
Added new context arn:aws:eks:us-east-1:78-your-account-id-27:cluster/my-eks-NbP3tleo to /home/ipeacocks/.kube/config
Нагадаю, що ім'я кластеру було згенеровано із рандомною частиною, тобто воно унікальне. Його можна взяти із terraform output.
$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system aws-node-5dt4w 2/2 Running 0 3h9m
kube-system aws-node-mntwk 2/2 Running 0 3h9m
kube-system coredns-d69f548b6-85stl 1/1 Running 0 3h9m
kube-system coredns-d69f548b6-bxzkl 1/1 Running 0 3h9m
kube-system eks-pod-identity-agent-2q4xp 1/1 Running 0 3h9m
kube-system eks-pod-identity-agent-8thcr 1/1 Running 0 3h9m
kube-system kube-proxy-fwzss 1/1 Running 0 3h9m
kube-system kube-proxy-zvbcf 1/1 Running 0 3h9m
Із коробки можна користуватись LoadBalancer-типом сервісу:
$ kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Pod
metadata:
name: influxdb
labels:
name: influxdb
spec:
containers:
- name: influxdb
image: influxdb
ports:
- containerPort: 8086
---
kind: Service
apiVersion: v1
metadata:
name: influxdb
spec:
type: LoadBalancer
ports:
- port: 8086
selector:
name: influxdb
EOF
$ kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/influxdb 1/1 Running 0 2m48s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/influxdb LoadBalancer 172.20.174.214 a9d00415ab949428b901b9918c87b97d-1421917068.us-east-1.elb.amazonaws.com 8086:30132/TCP 2m47s
$ curl -I a9d00415ab949428b901b9918c87b97d-1421917068.us-east-1.elb.amazonaws.com:8086
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: public, max-age=3600
Content-Length: 534
Content-Type: text/html; charset=utf-8
Etag: "5342613538"
Last-Modified: Wed, 26 Apr 2023 13:05:38 GMT
X-Influxdb-Build: OSS
X-Influxdb-Version: v2.7.1
Date: Sat, 03 Jun 2023 23:03:26 GMT
Проте у такому разі буде створено Classic LB у публічній підмережі, що дорого на великих об'ємах додатків, адже буде необхідний окремий балансувальник для кожного.
3. INSTALLATION OF AWS LOAD BALANCER CONTROLLER (IRSA)
AWS рекомендує використовувати свій контролер для Kubernetes сервісів та Ingress. На відміну від дефолтного підходу із Classic LB для Kubernetes LoadBalancer сервісу, про який я згадав вище, AWS Load Balancer Controller пропонує NLB балансувальник, а для Ingress об'єктів - ALB (цього разу різні target-групи дозволять обслуговувати різні домени).
Як і раніше скористаємось Terraform-ом та офіційним helm-чартом AWS Load Balancer Controller для його установки:
$ cd ../addons/lb-controller
$ cat main.tf
data "terraform_remote_state" "eks" {
backend = "s3"
config = {
bucket = "my-tf-state-2023-06-01"
key = "my-eks.tfstate"
region = "us-east-1"
}
}
module "irsa_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "5.44.0"
role_name = "eks-lb-controller-${data.terraform_remote_state.eks.outputs.cluster_name}"
attach_load_balancer_controller_policy = true
oidc_providers = {
ex = {
provider_arn = data.terraform_remote_state.eks.outputs.oidc_provider_arn
namespace_service_accounts = ["kube-system:aws-load-balancer-controller"]
}
}
}
resource "helm_release" "this" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
version = var.helm_package_version
namespace = "kube-system"
set {
name = "clusterName"
value = data.terraform_remote_state.eks.outputs.cluster_name
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.name"
value = "aws-load-balancer-controller"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = module.irsa_role.iam_role_arn
}
}
Тут можна переглянути офіційні рекомендації по установці контролера, проте ми скористались готовим стороннім модулем . Якщо коротко, то цього разу на стороні AWS IAM ми створюємо:
- окрему policy, тобто всі дозволи на об'єкти та дії, із котрими має працювати контролер
- ресурсну роль із цією policy
- та AssumeRole до ресурсної ролі, котра надає право користування вищезгаданої policy кластеру EKS, через oidc-провайдер. Вона логічно прив'язується до ролі, згаданої вище
У свою чергу в самому EKS має бути доданий ServiceAccount і через анотації описана ця роль (це робить helm-чарт контролера). Service account надає можливість користування роллю всередині Kubernetes подів.
Після всього контролер встановлюється із офіційного helm-чарту. Базові параметри винесені в variables.tf:
$ cat variables.tf
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "credentials" {
default = ["~/.aws/credentials"]
description = "where your access and secret_key are stored, you create the file when you run the aws config"
}
variable "helm_package_version" {
type = string
description = "Version of the helm package."
default = "1.8.2"
}
Застосуємо код для установки AWS Load Balancer Controller-а:
$ terraform init
$ terraform plan
$ terraform apply
Не буде зайвим також перевірити роботу подів контролера:
$ kubectl get svc,deploy -A | grep aws-load-balancer
kube-system service/aws-load-balancer-webhook-service ClusterIP 172.20.20.174 <none> 443/TCP 46s
kube-system deployment.apps/aws-load-balancer-controller 2/2 2 2 46s
4. INSTALLATION OF AWS LOAD BALANCER CONTROLLER (POD IDENTITY)
У випадку використання воркерів на EC2 вузлах можна скористатись pod-identity замість IRSA. Pod-identity addon було активовано на етапі установки EKS і він має доволі багато обмежень, адже далеко не універсальний: він не може працювати із воркерами на Fargate, OS Windows, із контролерами, що надають функціональність різноманітний дискових томів тощо.
Проте pod-identity дещо простіший в контексті конфігурації, адже не потребує вказання ролі в ServiceAccount додатку і його асоціація відбувається в контексті налаштувань EKS кластеру AWS.
Тому розглянемо установку AWS LB контролера із pod identity. Єдина відмінність у змісті основного main.tf:
$ cat main.tf
data "terraform_remote_state" "eks" {
backend = "s3"
config = {
bucket = "my-tf-state-2023-06-01"
key = "my-eks.tfstate"
region = "us-east-1"
}
}
module "aws_lb_controller_pod_identity" {
source = "terraform-aws-modules/eks-pod-identity/aws"
version = "v1.4.1"
name = "aws-lbc"
attach_aws_lb_controller_policy = true
# Pod Identity Associations
association_defaults = {
namespace = "kube-system"
service_account = "aws-load-balancer-controller"
}
associations = {
one = {
cluster_name = data.terraform_remote_state.eks.outputs.cluster_name
}
}
}
resource "helm_release" "this" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
version = var.helm_package_version
namespace = "kube-system"
set {
name = "clusterName"
value = data.terraform_remote_state.eks.outputs.cluster_name
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.name"
value = "aws-load-balancer-controller"
}
}
Деякі пояснення до коду:
- aws_lb_controller_pod_identity модуль створить окрему роль для pod identity, trusted relationship та відповідну асоціацію в EKS кластері:
Асоціація прив'язується до імені сервіс аккаунта в K8s та неймспейсу:
- Далі ми встановлюємо сам хелм чарт, де вказуємо ім'я сервіс аккаунту для того, щоб він чітко співпадав із іменем переданим в попередній модуль
Тож тепер все працюватиме і без указання імені ролі в ServiceAccount:
$ kubectl get sa aws-load-balancer-controller -n kube-system -o yaml
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
annotations:
meta.helm.sh/release-name: aws-load-balancer-controller
meta.helm.sh/release-namespace: kube-system
creationTimestamp: "2024-09-17T23:16:57Z"
labels:
app.kubernetes.io/instance: aws-load-balancer-controller
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: aws-load-balancer-controller
app.kubernetes.io/version: v2.8.2
helm.sh/chart: aws-load-balancer-controller-1.8.2
name: aws-load-balancer-controller
namespace: kube-system
resourceVersion: "10936"
uid: 7a521728-e3c7-48b0-a4bd-dc75379eaf4c
Обидва варіанти приведені в репозиторії, тож можна обрати підходящий за власним смаком.
5. TESTING OF AWS LB CONTROLLER. INGRESS
Нарешті, протестуємо роботу контролера, для чого встановимо тестовий додаток echoserver:
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: echoserver
---
apiVersion: v1
kind: Service
metadata:
name: echoserver
namespace: echoserver
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
app: echoserver
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echoserver
namespace: echoserver
spec:
selector:
matchLabels:
app: echoserver
replicas: 2
template:
metadata:
labels:
app: echoserver
spec:
containers:
- image: k8s.gcr.io/e2e-test-images/echoserver:2.5
imagePullPolicy: Always
name: echoserver
ports:
- containerPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echoserver
namespace: echoserver
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.name: my-group
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /echoserver
pathType: Exact
backend:
service:
name: echoserver
port:
number: 8080
EOF
namespace/echoserver created
service/echoserver created
deployment.apps/echoserver created
ingress.networking.k8s.io/echoserver created
Для роботи Ingress, у випадку "target-type: ip", потреби в NodePort типу сервіса немає, адже адресація буде пряма на IP-адреси кожного поду в деплойменті.
Перевіримо роботу додатку через kubectl:
$ kubectl get all,ing -n echoserver
NAME READY STATUS RESTARTS AGE
pod/echoserver-d46bc6b9-hrnc4 1/1 Running 0 15m
pod/echoserver-d46bc6b9-xjf2f 1/1 Running 0 15m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/echoserver ClusterIP 172.20.132.49 <none> 8080/TCP 18m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/echoserver 2/2 2 2 18m
NAME DESIRED CURRENT READY AGE
replicaset.apps/echoserver-d46bc6b9 2 2 2 15m
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/echoserver alb * k8s-mygroup-de245f3330-926431661.us-east-1.elb.amazonaws.com 80 18m
Виділену адресу Інгреса перевіряємо за допомогою curl:
$ curl k8s-mygroup-de245f3330-926431661.us-east-1.elb.amazonaws.com/echoserver
Hostname: echoserver-d46bc6b9-hrnc4
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.14.2 - lua: 10015
Request Information:
client_address=10.0.102.27
method=GET
real path=/echoserver
query=
request_version=1.1
request_scheme=http
request_uri=http://k8s-mygroup-de245f3330-926431661.us-east-1.elb.amazonaws.com:8080/echoserver
Request Headers:
accept=*/*
host=k8s-mygroup-de245f3330-926431661.us-east-1.elb.amazonaws.com
user-agent=curl/7.88.1
x-amzn-trace-id=Root=1-648e50ab-0fd2ef1438b7570b348ce574
x-forwarded-for=147.10.84.208
x-forwarded-port=80
x-forwarded-proto=http
Request Body:
-no body in request-
Перевіримо адресацію Ingress в AWS-консолі:
У разі проблем можна переглянути роботу контролера наступною командою:
$ kubectl logs -n kube-system --tail -1 -l app.kubernetes.io/name=aws-load-balancer-controller | grep 'echoserver\/echoserver'
Додамо до цього ж ALB ще один сервіс:
$ kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Namespace
metadata:
name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: game-2048
name: deployment-2048
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-2048
replicas: 5
template:
metadata:
labels:
app.kubernetes.io/name: app-2048
spec:
containers:
- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
imagePullPolicy: Always
name: app-2048
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: game-2048
name: service-2048
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: NodePort
selector:
app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: game-2048
name: ingress-2048
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance
alb.ingress.kubernetes.io/group.name: my-group
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
EOF
namespace/game-2048 created
deployment.apps/deployment-2048 created
service/service-2048 created
ingress.networking.k8s.io/ingress-2048 created
$ curl -I k8s-mygroup-de245f3330-926431661.us-east-1.elb.amazonaws.com
HTTP/1.1 200 OK
Date: Sun, 18 Jun 2023 00:52:37 GMT
Content-Type: text/html
Content-Length: 3988
Connection: keep-alive
Server: nginx
Last-Modified: Wed, 06 Oct 2021 17:35:37 GMT
ETag: "615dde69-f94"
Accept-Ranges: byte
Цього разу, зазначивши анотацію "target-type: instance", трафік буде йти на NodePort-и кожного worker-вузла, а не напряму до портів кожного поду:
Це зроблено лише для демонстрації, хоча це єдиний варіант у разі використання інших CNI, відмінних від amazon-vpc-cni.
Загалом обидва додатки будуть додані в один HTTP-listener ALB, завдяки одній групі, що описана в анотаціях "group.name: my-group":
Якщо ж є попередньо створений TLS-сертифікат в ACM, і є бажання шифрувати трафік (а воно має бути), його підключення також можна додати до анотацій:
# TLS
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:account-id:certificate/my-lo-oo-oo-ng-id
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
alb.ingress.kubernetes.io/ssl-redirect: '443'
У 3 рядку описана автоматична переадресація із 80 на 443 порт.
5. TESTING OF AWS LB CONTROLLER. LOAD BALANCER TYPE SERVICE
Жодних додаткових складностей із цим типом сервісу не має бути. Опишемо та застосуємо наступний додаток Kubernetes:
$ kubectl apply -f - <<EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nlb-sample-app
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: public.ecr.aws/nginx/nginx:1.21
ports:
- name: tcp
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nlb-sample-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: LoadBalancer
selector:
app: nginx
EOF
deployment.apps/nlb-sample-app created
service/nlb-sample-service created
Цього разу, завдяки AWS LB контролеру, на допомогу прийде NLB балансувальник. Якщо додатку потрібно буде для роботи декілька портів - то будуть створені додаткові listener-и та target-групи. Проте використати той самий балансер, як у випадку із Ingress, не вийде. Але здається таку логіку можна релізувати із TargetGroupBinding, хоча це дещо псує ідеї Kubernetes як cloud-agnostic системи. Але вже як є.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
nlb-sample-service LoadBalancer 172.20.234.223 k8s-default-nlbsampl-85a45b3c11-ff31fead319d688c.elb.us-east-1.amazonaws.com 80:30714/TCP 14m
$ curl -I k8s-default-nlbsampl-85a45b3c11-ff31fead319d688c.elb.us-east-1.amazonaws.com
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Mon, 19 Jun 2023 11:55:28 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT
Connection: keep-alive
ETag: "61f01158-267"
Accept-Ranges: bytes
Всі опції на стороні AWS у обох випадках налаштовуються через анотації, із ними можна ознайомитись за посиланням.
А увесь інший Terraform-код, згаданий у цій статті, знаходиться у моєму репозиторію.
Посилання:
https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html
https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
https://repost.aws/knowledge-center/eks-subnet-auto-discovery-alb
https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/examples/echo_server.md
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.6/deploy/installation/
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.6/guide/service/nlb/
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.6/deploy/subnet_discovery
https://kubernetes.io/docs/concepts/security/service-accounts
https://github.com/kubernetes-sigs/aws-load-balancer-controller
https://artifacthub.io/packages/helm/aws/aws-load-balancer-controller
Немає коментарів:
Дописати коментар