Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Attribuer des IP publiques statiques aux workers Amazon EKS en Local Zones avec KubeIP v2

By Wiriyang (Pup) PipatsakulrojDec 10, 20247 min read

Cette page est également disponible en English, Deutsch, Español, Italiano, 日本語 et Português.

Cet article explique comment attribuer une Elastic IP à des nœuds EKS dans une Local Zone à l'aide de KubeIP v2.

Introduction

Les clients AWS s'appuient sur Amazon EKS dans les AWS Local Zones pour bénéficier d'un accès à faible latence et respecter les exigences de localisation des données. Certains cas d'usage exigent des adresses IP publiques statiques pour les workloads qui communiquent avec des partenaires soumis à régulation. Or, les ressources Kubernetes (k8s), comme les nœuds worker, sont éphémères : leurs adresses IP changent lors d'événements tels qu'une montée de version. KubeIP attribue des adresses IP statiques en exploitant les fonctionnalités du fournisseur cloud, ce qui garantit un adressage IP cohérent malgré les changements liés au cycle de vie des nœuds.

Vue d'ensemble de la solution

Cet article montre comment configurer un cluster Amazon EKS avec un managed-node group dans une région et un self-managed node group dans la Local Zone. L'objectif est de présenter une approche pour attribuer une Elastic IP à un nœud EKS situé dans la Local Zone à l'aide de KubeIP v2.

Le diagramme d'architecture ci-dessous illustre un service edge exécuté dans la Local Zone et deux services backend exécutés dans la région.

Prérequis

Marche à suivre

  1. L'artefact d'exemple contient 4 dossiers : 01-vpc, 02-eks, 03-kubeip et 04-app. Nous les appliquons dans l'ordre, de 01-vpc à 04-app.

Clonez l'artefact d'exemple disponible sur github dans votre répertoire de travail.

git clone https://github.com/duoh/kubeip-eks-localzone
cd kubeip-eks-localzone
  1. Nous commençons par provisionner un VPC et des sous-réseaux dans la région et la Local Zone. Dans le fichier main.tf, le module vpc sert à créer les sous-réseaux publics et privés de la région. Les sous-réseaux de la Local Zone, eux, sont définis dans des ressources 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" {
  ...
}

...

Définissez les variables d'entrée. Dans cet exemple : le VPC s'appelle `kubeip-lz-eks-vpc`, la région est `ap-southeast-1` et la Local Zone est `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"

Déployez le VPC, les sous-réseaux et les tables de routage avec Terraform.

terraform init
terraform apply -auto-approve

Notez la sortie obtenue.

Outputs:
private_subnets = [\
  "subnet-09139a5ea9dcd335d",\
  "subnet-0d7cc25e5066687be",\
  "subnet-04bb1e787c9b6e28f",\
]
public_subnets_local_zone = "subnet-0edd1b731c48fb7d7"
vpc_id = "vpc-061f434818a666d8b"
  1. Nous créons ensuite un cluster EKS avec un managed node group dans la région, ainsi qu'un self-managed node group dans la Local Zone. Pour ce dernier, les nœuds portent les labels eks.amazonaws.com/nodegroup=public-lz-ng et kubeip=use. Le module kubeip_role nous sert par ailleurs à créer une IRSA (IAM Role for Service Accounts) destinée au DaemonSet 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
  }
  ...
}

...

Définissez les variables d'entrée. Pour vpc_id, private_subnets et public_subnets_local_zone, reprenez les valeurs de la sortie précédente.

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"

Déployez les ressources avec Terraform.

terraform init
terraform apply -auto-approve

Notez les sorties obtenues.

Outputs:
eks_cluster_name = "kubeip-eks-lz-cluster"
kubeip_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/kubeip-agent-role"
  1. Nous provisionnons ensuite une adresse Elastic IP ainsi que des ressources k8s, à savoir un DaemonSet et un service account. L'adresse Elastic IP définie dans la ressource aws_eip est provisionnée dans la Local Zone via l'argument network_border_group. Le DaemonSet KubeIP est rattaché au nœud de la Local Zone grâce au champ node_selector. L'un des atouts de KubeIP est sa capacité à filtrer les EIP à l'aide de tags AWS. Point essentiel : le service account de KubeIP doit être associé à l'IRSA et à un rôle 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" {
  ...
}

Définissez les variables d'entrée. Pour kubeip_role_arn, reprenez la valeur de la sortie précédente.

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"

Déployez une Elastic IP (EIP) et les ressources Kubernetes (k8s) avec Terraform.

terraform init
terraform apply -auto-approve

Notez l'adresse IP renvoyée : elle servira aux tests par la suite.

elastic_ips = [\
  "15.220.243.225",\
]
  1. Pour finir, nous illustrons un scénario où deux applications s'exécutent sur un nœud régional et une troisième sur un nœud de la Local Zone. L'application déployée dans la Local Zone joue le rôle de service edge : elle traite le trafic web des utilisateurs finaux et le redirige vers les applications hébergées dans la région.

Les fichiers app_a.yaml et app_b.yaml définissent les applications backend exécutées dans la région.

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

Le fichier edge_svc.yaml définit, lui, l'application edge dans 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
  ...

Placez-vous dans le répertoire 04-app et lancez l'AWS CLI selon votre région pour mettre à jour le fichier kubeconfig et vous authentifier auprès du cluster EKS.

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

Exécutez kubectl pour vérifier que l'accès fonctionne.

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

Déployez les applications et vérifiez qu'elles s'exécutent correctement.

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

Vérifiez le bon fonctionnement de l'application en utilisant l'adresse IP publique obtenue à l'étape précédente.

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

Ou via un navigateur.

Conclusion

Dans les AWS Local Zones, seuls les Application Load Balancers (ALB) du service Elastic Load Balancing (ELB) sont disponibles. De plus, les adresses IP publiques attribuées aux ALB peuvent évoluer au fil du temps. Les clusters EKS exécutés dans les Local Zones nécessitent donc une solution d'automatisation sur mesure pour conserver des adresses IP publiques statiques sur les nœuds worker. KubeIP répond précisément à ce besoin.

DoiT met à votre disposition une technologie intelligente et une expertise multi-cloud pour aider les organisations à comprendre et à tirer parti des clouds publics tels qu'Amazon Web Services (AWS), Google Cloud (GCP) et Microsoft Azure afin de stimuler leur croissance. Découvrez l'offre DoiT sur doit.com.