Dieser Beitrag zeigt, wie Sie EKS Nodes in einer Local Zone mit KubeIP v2 eine Elastic IP zuweisen.
Einleitung
AWS-Kunden setzen Amazon EKS in AWS Local Zones ein, um niedrige Latenzen zu erreichen und Vorgaben zur Datenlokalisierung einzuhalten. In manchen Anwendungsfällen sind statische öffentliche IP-Adressen nötig, etwa wenn workloads mit regulierten Partnern kommunizieren. Kubernetes-Ressourcen (k8s) wie Worker Nodes sind jedoch flüchtig – bei Versionsupgrades und ähnlichen Ereignissen ändern sich die IP-Adressen. KubeIP weist mithilfe der Cloud-Provider-APIs statische IP-Adressen zu und sorgt so für eine konsistente IP-Adressierung, unabhängig vom Lebenszyklus der Nodes.
Lösungsüberblick
In diesem Beitrag richten wir einen Amazon EKS-Cluster mit einer Managed Node Group in einer Region und einer self-managed Node Group in der Local Zone ein. Ziel ist es zu zeigen, wie sich einem EKS Node in der Local Zone mit KubeIP v2 eine Elastic IP-Adresse zuweisen lässt.
Das folgende Architekturdiagramm zeigt einen Edge-Service in der Local Zone sowie zwei Backend-Services in der Region.

Voraussetzungen
- Opt-in für die Local Zone, in der die workloads laufen
- Ein AWS-Konto mit den erforderlichen Berechtigungen, das für die AWS CLI verwendet wird
- Installation der CLI-Tools, mit denen in diesem Beispiel Ressourcen bereitgestellt werden: AWS CLI, Terraform und kubectl
Schritt-für-Schritt-Anleitung
- Das Beispielartefakt enthält 4 Ordner: 01-vpc, 02-eks, 03-kubeip und 04-app. Wir wenden sie der Reihe nach an, von 01-vpc bis 04-app.
Klonen Sie das Beispielartefakt von GitHub in Ihr Arbeitsverzeichnis.
git clone https://github.com/duoh/kubeip-eks-localzone
cd kubeip-eks-localzone
- Zunächst legen wir eine VPC samt Subnetzen in der Region und in der Local Zone an. In der Datei main.tf erzeugen wir mit dem vpc-Modul öffentliche und private Subnetze in der Region. Subnetze in einer Local Zone werden über aws_subnet-Ressourcen definiert.
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" {
...
}
...
Definieren Sie die Eingabevariablen. Im Beispiel lautet der VPC-Name `kubeip-lz-eks-vpc`, die Region ist `ap-southeast-1` und die 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"
Stellen Sie VPC, Subnetze und Routing-Tabellen mit Terraform bereit.
terraform init
terraform apply -auto-approve
Notieren Sie sich die Ausgabe.
Outputs:
private_subnets = [\
"subnet-09139a5ea9dcd335d",\
"subnet-0d7cc25e5066687be",\
"subnet-04bb1e787c9b6e28f",\
]
public_subnets_local_zone = "subnet-0edd1b731c48fb7d7"
vpc_id = "vpc-061f434818a666d8b"
- Anschließend erstellen wir einen EKS-Cluster mit einer Managed Node Group in der Region sowie eine self-managed Node Group in der Local Zone. Die Nodes der self-managed Node Group erhalten die Labels "eks.amazonaws.com/nodegroup=public-lz-ng" und "kubeip=use". Zusätzlich legen wir mit dem kubeip_role-Modul eine IRSA (IAM Role for Service Accounts) für das KubeIP DaemonSet an.
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
}
...
}
...
Definieren Sie die Eingabevariablen. Übernehmen Sie für vpc_id, private_subnets und public_subnets_local_zone die Werte aus der vorherigen Ausgabe.
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"
Stellen Sie die Ressourcen mit Terraform bereit.
terraform init
terraform apply -auto-approve
Notieren Sie sich die Ausgaben.
Outputs:
eks_cluster_name = "kubeip-eks-lz-cluster"
kubeip_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/kubeip-agent-role"
- Im nächsten Schritt stellen wir eine Elastic IP-Adresse sowie k8s-Ressourcen wie ein DaemonSet und einen Service Account bereit. Die in der aws_eip-Ressource definierte Elastic IP-Adresse wird über das Argument network_border_group innerhalb der Local Zone bereitgestellt. Über das Feld node_selector heften wir das KubeIP DaemonSet an den Node in der Local Zone. Praktisch an KubeIP: Es kann gezielt EIPs anhand von AWS-Tags filtern. Wichtig dabei: Dem Service Account von KubeIP müssen sowohl die IRSA als auch eine RBAC-Rolle zugewiesen werden.
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" {
...
}
Definieren Sie die Eingabevariablen. Übernehmen Sie für kubeip_role_arn den Wert aus der vorherigen Ausgabe.
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"
Stellen Sie eine Elastic IP (EIP) und Kubernetes-Ressourcen (k8s) mit Terraform bereit.
terraform init
terraform apply -auto-approve
Notieren Sie sich die ausgegebene IP-Adresse für den späteren Test.
elastic_ips = [\
"15.220.243.225",\
]
- Zum Abschluss zeigen wir ein Szenario, in dem zwei Anwendungen auf einem regionalen Node und eine Anwendung auf einem Node in der Local Zone laufen. Die Anwendung in der Local Zone fungiert als Edge-Service: Sie nimmt Web-Traffic von Endnutzern an und leitet ihn an die Anwendungen in der Region weiter.
Die Dateien app_a.yaml und app_b.yaml beschreiben die Backend-Apps, die in der Region laufen.
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
...
Die Datei edge_svc.yaml beschreibt eine Edge-App in der 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
...
Wechseln Sie ins Verzeichnis 04-app und führen Sie die AWS CLI passend zu Ihrer Region aus, um die kubeconfig-Datei für die Authentifizierung am EKS-Cluster zu aktualisieren.
cd ../04-app
aws eks update-kubeconfig --name kubeip-lz-cluster --region ap-southeast-1
Prüfen Sie mit kubectl, ob der Zugriff funktioniert.
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
Deployen Sie die Anwendungen und prüfen Sie, ob sie laufen.
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
Prüfen Sie über die öffentliche IP-Adresse aus dem vorherigen Schritt, ob die Anwendung wie erwartet funktioniert.
Test per 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>
Alternativ können Sie den Test im Browser durchführen.



Fazit
In AWS Local Zones lassen sich aus dem Elastic Load Balancing (ELB)-Service ausschließlich Application Load Balancer (ALBs) nutzen. Hinzu kommt: Die öffentlichen IP-Adressen, die einem ALB zugewiesen werden, können sich im Laufe der Zeit ändern. EKS-Cluster in Local Zones brauchen daher eine eigene Automatisierungslösung, um statische öffentliche IP-Adressen für Worker Nodes sicherzustellen. Genau hier setzt KubeIP an.
DoiT verbindet intelligente Technologie mit Multi-Cloud-Expertise und unterstützt Unternehmen dabei, Public Clouds wie Amazon Web Services (AWS), Google Cloud (GCP) und Microsoft Azure souverän zu nutzen und für ihr Wachstum einzusetzen. Das Angebot von DoiT finden Sie unter doit.com.