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
- Activer la Local Zone dans laquelle le workload s'exécute
- Un compte AWS disposant des permissions requises, configuré pour l'AWS CLI
- L'installation des outils CLI utilisés pour provisionner les ressources de cet exemple : AWS CLI, Terraform et kubectl
Marche à suivre
- 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
- 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"
- 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"
- 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",\
]
- 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.