Translate

вівторок, 20 червня 2023 р.

Kubernetes. Part VIII: EKS Cluster With Terraform, AWS LB Controller

Цього разу подивимось на установку 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.27.143 Python/3.11.2 Linux/6.2.0-20-generic botocore/1.29.143

Додамо 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.4.6.

Для створення інфраструктури будемо користуватись готовим модулем 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.0.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
  public_subnet_tags = {
    "kubernetes.io/role/elb" = 1
  }

  private_subnet_tags = {
    "kubernetes.io/role/internal-elb" = 1
  }
}


Теги необхідні для AWS LB controller-а, мова про який піде далі. Конфігураційний файл зі змінними має наступний вигляд:

$ cat variables.tf

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/28", "10.0.1.16/28", "10.0.1.32/28"]
}

variable "worker_subnets" {
  description = "Subnets for EKS worker nodes"
  type        = list(any)
  default     = ["10.0.4.0/22", "10.0.8.0/22", "10.0.12.0/22"]
}

variable "public_subnets" {
  description = "Public subnets for LBs, NAT GWs and so on"
  type        = list(any)
  default     = ["10.0.100.0/24", "10.0.101.0/24", "10.0.102.0/24"]
}

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
}

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 рекомендує їх тримати окремо від підмереж воркерів. /28 підмережа (16 адрес) обрана тому, що вузлів для майстрів зазвичай багато не потрібно, але можливо буде розумно обрати більшу. Фізично вузли 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.27, а код, як я вже згадував, знаходиться тут:

$ cd ../eks

$ cat main.tf

data "aws_availability_zones" "available" {}

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
}

locals {
  cluster_name = "${var.eks_name_prefix}-${random_string.suffix.result}"
}


module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "19.15.2"

  cluster_name    = local.cluster_name
  cluster_version = var.cluster_version

  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 = true

  eks_managed_node_group_defaults = var.eks_managed_node_group_defaults
  eks_managed_node_groups         = var.eks_managed_node_groups
}

Варто буде попередньо поправити назву бакета (навів жирним), де лежить стейт мережі. Параметри, що я використовую:

$ 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        = number
  default     = "1.27"
}

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
    }
  }
}


Деякі пояснення до коду:

  • вичитуємо terraform стейт vpc із s3 задля отримання параметрів мережі, в яких буде встановлено EKS кластер
  • вказуємо, що плануємо використовувати eks_managed_node_group. Є ще self_managed_node_groups, де потрібно самостійно менеджити воркера. Їх створення також підтримує цей модуль
  • control plane кластеру буде мати зв'язок із зовнішнім світом завдяки параметру cluster_endpoint_public_access. Це зручно для роботи через kubectl на локальній системі, хоч він і зовсім не обов'язковий

Всі існуючі параметри добре описані в документації до модуля.

Ініціюємо додаткові модулі та стартуємо створення кластеру 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:1234567890:cluster/my-eks-NbP3tleo to /home/ipeacocks/.kube/config

Нагадаю, що ім'я кластеру було згенеровано із рандомною частиною під час створення VPC задля простановки тегів на публічних мережах. Його можна взяти із terraform output.

$ kubectl get pods -A
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-hn95c             1/1     Running   0          12m
kube-system   aws-node-r9cxx             1/1     Running   0          12m
kube-system   coredns-79df7fff65-4b5r6   1/1     Running   0          18m
kube-system   coredns-79df7fff65-72nbv   1/1     Running   0          18m
kube-system   kube-proxy-4kjwc           1/1     Running   0          12m
kube-system   kube-proxy-82x6p           1/1     Running   0          12m


Із коробки можна користуватись 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


У якості балансувальника AWS створив Classic LB у публічній підмережі, що доволі дорого на великих об'ємах додатків. Наразі AWS рекомендує використовувати Load Balancer Controller, що дозволяє використовувати ALB/NLB балансувальники.


3. INSTALLATION OF AWS LOAD BALANCER CONTROLLER

Наразі AWS рекомендує використовувати свій контролер для Kubernetes сервісів та Ingress. На відміну від дефолтного підходу із Classic LB для Kubernetes LoadBalancer сервісу, про який я згадав вище, AWS Load Balancer Controller пропонує NLB балансувальник, а для Ingress об'єктів - ALB (цього разу різні target-групи дозволять обслуговувати групу різних доменів).

Як і раніше скористаємось Terraform-ом та офіційним helm-чартом AWS Load Balancer Controller для його установки:

$ cd ../lb-controller
$ cat main.tf


data "aws_availability_zones" "available" {}

data "aws_caller_identity" "current" {}

data "terraform_remote_state" "eks" {
  backend = "s3"

  config = {
    bucket = "my-tf-state-2023-06-01"
    key    = "my-eks.tfstate"

    region = "us-east-1"
  }
}

resource "aws_iam_policy" "AWSLoadBalancerControllerIAMPolicy" {
  name        = "AWSLoadBalancerControllerIAMPolicy"
  path        = "/"
  description = "AWS LB controller IAM policy"
  policy      = file(format("%s/policies/AWSLoadBalancerControllerIAMPolicy.json", path.module))
}

data "template_file" "value_generated" {
  template = file(format("%s/policies/AmazonEKSLoadBalancerControllerPolicy.json_tmpl", path.module))

  vars = {
    oidc_provider     = data.terraform_remote_state.eks.outputs.oidc_provider
    oidc_provider_arn = data.terraform_remote_state.eks.outputs.oidc_provider_arn
  }
}

resource "aws_iam_role" "role" {
  name               = "AmazonEKSLoadBalancerControllerRole"
  assume_role_policy = data.template_file.value_generated.rendered
}

resource "aws_iam_role_policy_attachment" "attach" {
  role       = aws_iam_role.role.name
  policy_arn = aws_iam_policy.AWSLoadBalancerControllerIAMPolicy.arn
}

resource "null_resource" "update-local-kube-config" {
  provisioner "local-exec" {
    command = "aws eks update-kubeconfig --region ${var.region} --name ${data.terraform_remote_state.eks.outputs.cluster_name}"
  }
}

provider "kubernetes" {
  config_path = "~/.kube/config"
}

resource "kubectl_manifest" "ServiceAccount" {
  yaml_body = <<YAML
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: aws-load-balancer-controller
  name: aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/AmazonEKSLoadBalancerControllerRole
YAML
}

provider "helm" {
  kubernetes {
    config_path = "~/.kube/config"
  }
}

resource "helm_release" "aws-load-balancer-controller" {
  name       = "aws-load-balancer-controller"
  repository = "https://aws.github.io/eks-charts"
  chart      = "aws-load-balancer-controller"
  version    = var.aws-load-balancer-controller_version
  namespace  = "kube-system"

  set {
    name  = "clusterName"
    value = data.terraform_remote_state.eks.outputs.cluster_name
  }

  set {
    name  = "serviceAccount.create"
    value = "false"
  }

  set {
    name  = "serviceAccount.name"
    value = "aws-load-balancer-controller"
  }
}


Це офіційні рекомендації по установці контролера, проте описані Terraform-ом. Якщо коротко, то цього разу на стороні AWS IAM ми створюємо:

  • policy AWSLoadBalancerControllerIAMPolicy, тобто всі дозволи на об'єкти та дії, із котрими має працювати контролер
  • роль AmazonEKSLoadBalancerControllerRole
  • та AssumeRole, котра надає право користування вищезгаданої policy кластеру EKS, через oidc-провайдер. Вона логічно прив'язується до ролі, згаданої вище

У свою чергу в самому EKS має бути доданий ServiceAccount і через анотації описана AmazonEKSLoadBalancerControllerRole роль. 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 "aws-load-balancer-controller_version" {
  default     = "1.5.3"
  description = "Helm package version of AWS LB Controller"
}


Застосуємо код для установки 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. 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=146.0.84.207
        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" і ці записи виглядатимуть наступним чином:



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.5/deploy/installation/
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.5/guide/service/nlb/
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.5/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

Немає коментарів:

Дописати коментар