Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Statische öffentliche IPs für Amazon EKS Worker in Local Zones mit KubeIP v2

By Wiriyang (Pup) PipatsakulrojDec 10, 20247 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

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

Schritt-für-Schritt-Anleitung

  1. 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
  1. 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"
  1. 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"
  1. 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",\
]
  1. 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.