Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Asigna IPs públicas estáticas a workers de Amazon EKS en Local Zones con KubeIP v2

By Wiriyang (Pup) PipatsakulrojDec 10, 20247 min read

Esta página también está disponible en English, Deutsch, Français, Italiano, 日本語 y Português.

Este post muestra cómo asignar una Elastic IP a nodos de EKS en una Local Zone con KubeIP v2.

Introducción

Los clientes de AWS usan Amazon EKS en AWS Local Zones para lograr acceso de baja latencia y cumplir con requisitos de localización de datos. Algunos casos de uso requieren direcciones IP públicas estáticas para workloads que se comunican con socios regulados. Sin embargo, los recursos de Kubernetes (k8s), como los nodos worker, son efímeros, lo que provoca cambios en las direcciones IP durante eventos como las actualizaciones de versión. KubeIP asigna direcciones IP estáticas aprovechando las capacidades del proveedor de nube y garantiza un direccionamiento IP consistente a pesar de los cambios en el ciclo de vida de los nodos.

Visión general de la solución

Este post muestra cómo configurar un clúster de Amazon EKS con un grupo de nodos administrado en una región y un grupo de nodos autoadministrado en la Local Zone. El objetivo es mostrar cómo asignar una Elastic IP a un nodo de EKS en la Local Zone usando KubeIP v2.

El siguiente diagrama de arquitectura muestra un servicio edge ejecutándose en la Local Zone y dos servicios backend ejecutándose en la región.

Requisitos previos

Paso a paso

  1. El artefacto de ejemplo contiene 4 carpetas: 01-vpc, 02-eks, 03-kubeip y 04-app. Se aplican en orden, desde 01-vpc hasta 04-app.

Clona el artefacto de ejemplo desde github en tu directorio de trabajo.

git clone https://github.com/duoh/kubeip-eks-localzone
cd kubeip-eks-localzone
  1. Primero, se aprovisiona una VPC y subredes en la región y en la Local Zone. En el archivo main.tf se usa el módulo vpc para crear subredes públicas y privadas en una región. Las subredes en la Local Zone se definen en los recursos aws_subnet.
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  name            = var.name
  cidr            = var.vpc_cidr
  azs             = local.azs
  public_subnets  = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k)]
  private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 10)]

  ...

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

resource "aws_subnet" "public-subnet-lz" {
  vpc_id                  = module.vpc.vpc_id
  cidr_block              = cidrsubnet(var.vpc_cidr, 8, 5)
  availability_zone       = local.lzs[0]
  map_public_ip_on_launch = true
}

resource "aws_subnet" "private-subnet-lz" {
  ...
}

...

Define las variables de entrada. En el ejemplo: el nombre de la VPC es `kubeip-lz-eks-vpc`, la región es `ap-southeast-1` y la Local Zone es `ap-southeast-1-bkk-1a`.

cd 01-vpc
vi example.auto.tfvars
region          = "ap-southeast-1"
lzs             = ["ap-southeast-1-bkk-1a"]
name            = "kubeip-eks-lz-vpc"
vpc_cidr        = "10.0.0.0/16"
cluster_name    = "kubeip-eks-lz-cluster"

Despliega la VPC, las subredes y las tablas de ruteo con terraform.

terraform init
terraform apply -auto-approve

Toma nota del output.

Outputs:
private_subnets = [\
  "subnet-09139a5ea9dcd335d",\
  "subnet-0d7cc25e5066687be",\
  "subnet-04bb1e787c9b6e28f",\
]
public_subnets_local_zone = "subnet-0edd1b731c48fb7d7"
vpc_id = "vpc-061f434818a666d8b"
  1. A continuación, se crea un clúster de EKS con un grupo de nodos administrado en la región. También se crea un grupo de nodos autoadministrado en la Local Zone. En el grupo de nodos autoadministrado, los nodos llevan definidas las etiquetas 'eks.amazonaws.com/nodegroup=public-lz-ng' y 'kubeip=use'. Además, se utiliza el módulo kubeip_role para crear un IRSA (IAM Role for Service Accounts) destinado al daemonSet de KubeIP.
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  cluster_name    = var.cluster_name
  cluster_version = "1.30"

  vpc_id                         = var.vpc_id
  subnet_ids                     = var.private_subnets
  cluster_endpoint_public_access = true
  enable_irsa = true
  ...

  eks_managed_node_groups = {
    region-ng = {
      ...
      subnet_ids    = var.private_subnets
      labels = {
        region  = "true"
      }
      ...
    }
  }

  self_managed_node_groups = {
    local-ng = {
      ...
      subnet_ids    = [var.public_subnets_local_zone]
      bootstrap_extra_args = "--kubelet-extra-args '--node-labels=eks.amazonaws.com/nodegroup=public-lz-ng,kubeip=use'"
      ...
    }
  }
  enable_cluster_creator_admin_permissions = true
}

resource "aws_iam_policy" "kubeip-policy" {
  name        = "kubeip-policy"
  description = "KubeIP required permissions"
  policy = jsonencode({
    ...
  })
}

module "kubeip_role" {
  source    = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  role_name = var.kubeip_role_name
  role_policy_arns = {
    "kubeip-policy" = aws_iam_policy.kubeip-policy.arn
  }
  ...
}

...

Define las variables de entrada. Para vpc_id, private_subnets y public_subnets_local_zone, copia los valores del output anterior.

cd ../02-eks
vi example.auto.tfvars
region                     = "ap-southeast-1"
vpc_id                     = "vpc-061f434818a666d8b"
private_subnets            = [\
                              "subnet-09139a5ea9dcd335d",\
                              "subnet-0d7cc25e5066687be",\
                              "subnet-04bb1e787c9b6e28f",\
                             ]
public_subnets_local_zone  = "subnet-0edd1b731c48fb7d7"
cluster_name               = "kubeip-eks-lz-cluster"
kubeip_role_name           = "kubeip-agent-role"
kubeip_sa_name             = "kubeip-agent-sa"

Despliega los recursos con terraform.

terraform init
terraform apply -auto-approve

Toma nota de los outputs.

Outputs:
eks_cluster_name = "kubeip-eks-lz-cluster"
kubeip_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/kubeip-agent-role"
  1. Luego, se aprovisiona una Elastic IP y recursos de k8s como un DaemonSet y una service account. La Elastic IP definida en el recurso aws_eip se aprovisiona dentro de la Local Zone mediante el argumento network_border_group. También se asigna el DaemonSet de KubeIP al nodo de la Local Zone usando el campo node_selector. Una característica útil de KubeIP es que permite seleccionar EIPs filtradas mediante etiquetas de AWS. Es importante destacar que la service account de KubeIP debe tener asignados el IRSA y un rol RBAC.
resource "aws_eip" "kubeip" {
  count = 1
  tags = {
    Name        = "kubeip-${count.index}"
    environment = "demo"
    kubeip      = "reserved"
  }
  network_border_group = var.network_border_group
}

resource "kubernetes_daemonset" "kubeip_daemonset" {
  metadata {
    name      = "kubeip-agent"
    ...
  }
  spec {
    ...

    template {
      ...
      spec {
        service_account_name             = var.kubeip_sa_name
        ...
        container {
          name  = "kubeip-agent"
          image = "doitintl/kubeip-agent"
          env {
            name  = "FILTER"
            value = "Name=tag:kubeip,Values=reserved;Name=tag:environment,Values=demo"
          }
          ...
        }
        node_selector = {
          "eks.amazonaws.com/nodegroup" = "public-lz-ng"
          kubeip = "use"
        }
      }
    }
  }
  depends_on = [kubernetes_service_account.kubeip_service_account]
}

resource "kubernetes_service_account" "kubeip_service_account" {
  metadata {
    name        = var.kubeip_sa_name
    namespace   = "kube-system"
    annotations = {
      "eks.amazonaws.com/role-arn" = var.kubeip_role_arn
    }
  }
}

resource "kubernetes_cluster_role" "kubeip_cluster_role" {
  ...
}

resource "kubernetes_cluster_role_binding" "kubeip_cluster_role_binding" {
  ...
}

Define las variables de entrada. Para kubeip_role_arn, copia los valores del output anterior.

cd ../03-kubeip
vi example.auto.tfvars
region                  = "ap-southeast-1"
network_border_group    = "ap-southeast-1-bkk-1"
cluster_name            = "kubeip-eks-lz-cluster"
kubeip_role_arn         = "arn:aws:iam::xxxxxxxxxxxx:role/kubeip-agent-role"
kubeip_sa_name          = "kubeip-agent-sa"

Despliega una Elastic IP (EIP) y los recursos de Kubernetes (k8s) con Terraform.

terraform init
terraform apply -auto-approve

Toma nota de la dirección IP del output para probarla más adelante.

elastic_ips = [\
  "15.220.243.225",\
]
  1. Por último, mostramos un escenario en el que dos aplicaciones se ejecutan en un nodo regional y una aplicación se ejecuta en un nodo de la Local Zone. La aplicación de la Local Zone funciona como un servicio edge que atiende el tráfico web de los usuarios finales y lo enruta hacia las aplicaciones en la región.

Los archivos app_a.yaml y app_b.yaml definen las apps de back-end que se ejecutan en la región.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-a-deployment
  ...
spec:
  ...
    spec:
      containers:
      - name: app-a
        image: hashicorp/http-echo
        ports:
        - containerPort: 5678
        args: ["-text=<h1>I'm APP <em>A</em></h1>"]
      nodeSelector:
        region: "true"
---
apiVersion: v1
kind: Service
metadata:
  name: app-a-service
spec:
  type: ClusterIP
  ...

El archivo edge_svc.yaml define una app edge en la Local Zone.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: edge-deployment
  ...
spec:
  ...
    spec:
      containers:
      - name: edge-svc
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: indexfile
          mountPath: /usr/share/nginx/html/
          readOnly: true
        - name: nginx-conf
          mountPath: /etc/nginx/conf.d/
      nodeSelector:
        kubeip: use
      volumes:
      ...

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-indexfile-configmap
data:
  index.html: |
    <h1>I am EDGE SERVICE</h1>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-conf-configmap
data:
  default.conf: |
    server {
      resolver kube-dns.kube-system.svc.cluster.local valid=1s
      ...
    }
---
apiVersion: v1
kind: Service
metadata:
  name: edge-service
spec:
  type: NodePort
  ...

Sitúate en el directorio 04-app y ejecuta el aws cli según tu región para actualizar el archivo kubeconfig y autenticarte con el clúster de EKS.

cd ../04-app
aws eks update-kubeconfig --name kubeip-lz-cluster --region ap-southeast-1

Ejecuta kubectl para confirmar que el acceso funciona.

kubectl get no
NAME                                             STATUS   ROLES    AGE     VERSION
ip-10-0-10-213.ap-southeast-1.compute.internal   Ready    <none>   4m      v1.30.0-eks-036c24b
ip-10-0-5-76.ap-southeast-1.compute.internal     Ready    <none>   3m11s   v1.30.0-eks-036c24b

Despliega las aplicaciones y verifica que estén en ejecución.

kubectl apply -f edge_svc.yaml
kubectl apply -f app_a.yaml
kubectl apply -f app_b.yaml

kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
app-a-deployment-587b484997-k6f8q   1/1     Running   0          12s
app-b-deployment-78bc6675db-2mk2s   1/1     Running   0          12s
edge-deployment-f6b9f4d5f-5j6f7     1/1     Running   0          13s

Comprueba que la aplicación funciona usando la dirección IP pública del paso anterior.

Prueba con curl.

% curl 15.220.243.225:30000
<h1>I am EDGE SERVICE</h1>

% curl 15.220.243.225:30000/app-a
<h1>I'm APP <em>A</em></h1>

% curl 15.220.243.225:30000/app-b
<h1>I'm APP <em>B</em></h1>

O bien, pruébalo desde un navegador.

Conclusión

Las Local Zones de AWS solo admiten Application Load Balancers (ALBs) del servicio Elastic Load Balancing (ELB). Además, las direcciones IP públicas asignadas a los ALBs pueden cambiar con el tiempo. Los clústeres de EKS que se ejecutan en Local Zones requieren una solución de automatización personalizada para mantener direcciones IP públicas estáticas en los nodos worker. En esos casos, KubeIP es una buena opción para resolver este requerimiento.

DoiT ofrece tecnología inteligente y experiencia multi-cloud para ayudar a las organizaciones a entender y aprovechar nubes públicas como Amazon Web Services (AWS), Google Cloud (GCP) y Microsoft Azure, e impulsar así el crecimiento del negocio. Conoce la oferta de DoiT en doit.com.