Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Come assegnare IP pubblici statici ai worker node EKS nelle Local Zones con KubeIP v2

By Wiriyang (Pup) PipatsakulrojDec 10, 20247 min read

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

In questo articolo vediamo come assegnare un Elastic IP ai nodi EKS in una Local Zone tramite KubeIP v2.

Introduzione

I clienti AWS adottano Amazon EKS nelle AWS Local Zones per ottenere accesso a bassa latenza e rispettare i requisiti di localizzazione dei dati. Alcuni scenari richiedono indirizzi IP pubblici statici per i workloads che comunicano con partner soggetti a vincoli normativi. Le risorse Kubernetes (k8s) come i worker node sono però effimere: l'indirizzo IP cambia in occasione di eventi quali gli aggiornamenti di versione. KubeIP risolve il problema assegnando indirizzi IP statici tramite le funzionalità del cloud provider, garantendo un indirizzamento coerente anche al variare del ciclo di vita dei nodi.

Panoramica della soluzione

L'articolo illustra come configurare un cluster Amazon EKS con un managed-node group in una region e un self-managed node group nella local zone. L'obiettivo è mostrare come assegnare un Elastic IP a un nodo EKS nella local zone con KubeIP v2.

Il diagramma di architettura qui sotto mostra un edge service in esecuzione nella local zone e due servizi backend nella region.

Prerequisiti

Procedura

  1. L'artefatto di esempio contiene 4 cartelle: 01-vpc, 02-eks, 03-kubeip e 04-app. Le applicheremo nell'ordine, da 01-vpc a 04-app.

Cloni l'artefatto di esempio da github nella directory di lavoro.

git clone https://github.com/duoh/kubeip-eks-localzone
cd kubeip-eks-localzone
  1. Per prima cosa effettuiamo il provisioning di una VPC e delle subnet nella region e nella local zone. Nel file main.tf utilizziamo il modulo vpc per creare le subnet pubbliche e private nella region; le subnet della local zone sono invece definite nelle risorse 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" {
  ...
}

...

Definisca le variabili di input. Nell'esempio: il nome della VPC è `kubeip-lz-eks-vpc`, la region è `ap-southeast-1` e la Local Zone è `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"

Faccia il deploy di VPC, subnet e route table con terraform.

terraform init
terraform apply -auto-approve

Prenda nota dell'output.

Outputs:
private_subnets = [\
  "subnet-09139a5ea9dcd335d",\
  "subnet-0d7cc25e5066687be",\
  "subnet-04bb1e787c9b6e28f",\
]
public_subnets_local_zone = "subnet-0edd1b731c48fb7d7"
vpc_id = "vpc-061f434818a666d8b"
  1. A questo punto creiamo un cluster EKS con un managed node group nella region, oltre a un self-managed node group nella local zone. Per quest'ultimo, ai nodi vengono associate le label 'eks.amazonaws.com/nodegroup=public-lz-ng' e 'kubeip=use'. Utilizziamo inoltre il modulo kubeip_role per creare un IRSA (IAM Role for Service Accounts) destinato al DaemonSet di 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
  }
  ...
}

...

Definisca le variabili di input. Per vpc_id, private_subnets e public_subnets_local_zone, riprenda i valori dall'output precedente.

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"

Faccia il deploy delle risorse con terraform.

terraform init
terraform apply -auto-approve

Prenda nota degli output.

Outputs:
eks_cluster_name = "kubeip-eks-lz-cluster"
kubeip_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/kubeip-agent-role"
  1. Successivamente provvediamo al provisioning di un Elastic IP e delle risorse k8s, quali un DaemonSet e un service account. L'Elastic IP definito nella risorsa aws_eip viene allocato all'interno della local zone tramite l'argomento network_border_group. Il DaemonSet di KubeIP viene assegnato al nodo nella local zone usando il campo node_selector. Una caratteristica utile di KubeIP è la capacità di selezionare gli EIP filtrandoli tramite tag AWS. È fondamentale che al service account di KubeIP siano associati l'IRSA e un ruolo 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" {
  ...
}

Definisca le variabili di input. Per kubeip_role_arn, riprenda i valori dall'output precedente.

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"

Faccia il deploy di un Elastic IP (EIP) e delle risorse Kubernetes (k8s) con Terraform.

terraform init
terraform apply -auto-approve

Prenda nota dell'indirizzo IP in output, servirà per i test successivi.

elastic_ips = [\
  "15.220.243.225",\
]
  1. Per concludere, illustriamo uno scenario in cui due applicazioni girano su un nodo regionale e una terza su un nodo della local zone. L'applicazione nella local zone svolge il ruolo di edge service: gestisce il traffico web degli utenti finali e lo instrada verso le applicazioni nella region.

I file app_a.yaml e app_b.yaml definiscono le app di back-end in esecuzione nella region.

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

Il file edge_svc.yaml definisce un'app edge nella 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
  ...

Si sposti nella directory 04-app ed esegua la AWS CLI indicando la propria region per aggiornare il file kubeconfig usato per autenticarsi al cluster EKS.

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

Esegua kubectl per verificare che l'accesso vada a buon fine.

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

Faccia il deploy delle applicazioni e verifichi che siano in esecuzione.

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

Verifichi che l'applicazione funzioni correttamente utilizzando l'indirizzo IP pubblico ottenuto al passaggio precedente.

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

In alternativa, può eseguire il test direttamente dal browser.

Conclusioni

Nelle Local Zones di AWS è possibile usare solo gli Application Load Balancer (ALB) del servizio Elastic Load Balancing (ELB) e gli IP pubblici associati agli ALB possono cambiare nel tempo. Per mantenere indirizzi IP pubblici statici sui worker node, i cluster EKS in esecuzione nelle Local Zones richiedono quindi una soluzione di automazione personalizzata: in questi casi KubeIP è la risposta ideale.

DoiT mette a disposizione tecnologia intelligente ed expertise multi-cloud per aiutare le aziende a comprendere e sfruttare al meglio i cloud pubblici come Amazon Web Services (AWS), Google Cloud (GCP) e Microsoft Azure, accelerando la crescita del business. Per scoprire le soluzioni DoiT, visiti doit.com.