FinOps in Aktion: Effiziente AWS EKS-Bereitstellung mit Terraform
Ein Leitfaden mit FinOps-Kostenanalyse und Einrichtung von Connection Draining
Einführung
Die Orchestrierung von Containern ist ein zentraler Bestandteil moderner Anwendungsbereitstellungen. Amazon Elastic Kubernetes Service (EKS) bietet eine verwaltete Kubernetes-Umgebung auf AWS, die die Einrichtung und Verwaltung von Kubernetes-Clustern vereinfacht. In Kombination mit Terraform, einem leistungsstarken Infrastructure-as-Code-Tool, können wir die Bereitstellung und Verwaltung von EKS-Clustern automatisieren und versionieren.
In diesem Blogbeitrag zeige ich dir, wie du mit Terraform einen EKS-Cluster auf AWS erstellen kannst. Darüber hinaus betrachten wir im Kontext von FinOps (Financial Operations) die Kostenoptimierung und vergleichen verschiedene Optionen vom Standard-Setup bis hin zu Spot-Instanzen mit Connection Draining. Eine Kostenübersichtstabelle hilft dabei, die finanziellen Auswirkungen der verschiedenen Optionen zu verstehen.
Teil 1: Bereitstellung des EKS Clusters mit Terraform
Schritt 1: Projektstruktur einrichten
Erstelle ein neues Verzeichnis für dein Terraform-Projekt:
mkdir terraform-eks
cd terraform-eks
Schritt 2: Provider konfigurieren
Erstelle eine Datei namens main.tf
und füge den AWS-Provider hinzu:
provider "aws" {
region = "eu-central-1" # Wähle deine gewünschte Region
}
Schritt 3: VPC erstellen
Für unseren EKS-Cluster benötigen wir eine Virtual Private Cloud (VPC). Wir können entweder eine bestehende VPC nutzen oder eine neue erstellen.
VPC-Modul verwenden:
Füge folgendes in vpc.tf
hinzu:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "eks-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "eks-vpc"
}
}
Dieses Modul erstellt eine VPC mit öffentlichen und privaten Subnetzen über drei Availability Zones.
Schritt 4: IAM-Rollen und -Policies definieren
EKS benötigt bestimmte IAM-Rollen und -Policies. Erstelle eine Datei iam.tf
:
data "aws_iam_policy_document" "eks_assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["eks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "eks_cluster_role" {
name = "eks_cluster_role"
assume_role_policy = data.aws_iam_policy_document.eks_assume_role_policy.json
}
resource "aws_iam_role_policy_attachment" "eks_cluster_policy_attachment" {
role = aws_iam_role.eks_cluster_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
}
resource "aws_iam_role_policy_attachment" "eks_service_policy_attachment" {
role = aws_iam_role.eks_cluster_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
}
Schritt 5: EKS-Cluster erstellen
Füge in eks-cluster.tf
folgendes hinzu:
resource "aws_eks_cluster" "eks_cluster" {
name = "my-eks-cluster"
role_arn = aws_iam_role.eks_cluster_role.arn
vpc_config {
subnet_ids = module.vpc.private_subnets
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy_attachment,
aws_iam_role_policy_attachment.eks_service_policy_attachment,
]
}
Dies erstellt den eigentlichen EKS-Cluster unter Verwendung der zuvor erstellten IAM-Rolle und VPC-Subnetze.
Schritt 6: Node Group hinzufügen
Für die Worker Nodes benötigen wir eine Node Group. Füge in node-group.tf
hinzu:
data "aws_iam_policy_document" "eks_worker_assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "eks_worker_role" {
name = "eks_worker_role"
assume_role_policy = data.aws_iam_policy_document.eks_worker_assume_role_policy.json
}
resource "aws_iam_role_policy_attachment" "worker_node_policy_attachment" {
role = aws_iam_role.eks_worker_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}
resource "aws_iam_role_policy_attachment" "cni_policy_attachment" {
role = aws_iam_role.eks_worker_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}
resource "aws_iam_role_policy_attachment" "registry_policy_attachment" {
role = aws_iam_role.eks_worker_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}
resource "aws_eks_node_group" "eks_node_group" {
cluster_name = aws_eks_cluster.eks_cluster.name
node_group_name = "eks-node-group"
node_role_arn = aws_iam_role.eks_worker_role.arn
subnet_ids = module.vpc.private_subnets
scaling_config {
desired_size = 2
max_size = 3
min_size = 1
}
instance_types = ["t3.medium"]
depends_on = [
aws_eks_cluster.eks_cluster,
aws_iam_role_policy_attachment.worker_node_policy_attachment,
aws_iam_role_policy_attachment.cni_policy_attachment,
aws_iam_role_policy_attachment.registry_policy_attachment,
]
}
Schritt 7: Outputs definieren
Erstelle eine Datei outputs.tf
, um wichtige Informationen auszugeben:
output "cluster_endpoint" {
description = "EKS Cluster Endpoint"
value = aws_eks_cluster.eks_cluster.endpoint
}
output "cluster_certificate" {
description = "EKS Cluster Certificate Authority"
value = aws_eks_cluster.eks_cluster.certificate_authority[0].data
}
output "cluster_name" {
description = "EKS Cluster Name"
value = aws_eks_cluster.eks_cluster.name
}
Schritt 8: Terraform ausführen
Initialisieren
terraform init
Plan erstellen
terraform plan -out=eks.plan
Überprüfe den Plan und stelle sicher, dass alles korrekt ist.
Anwenden
terraform apply eks.plan
Dieser Schritt kann einige Minuten dauern, da AWS die Ressourcen bereitstellt.
Schritt 9: Kubernetes-Kontext konfigurieren
Um kubectl
mit dem neuen Cluster zu verbinden, benötigen wir eine Konfigurationsdatei.
Führe folgendes Skript aus (du kannst es in update-kubeconfig.sh
speichern):
#!/bin/bash
aws eks update-kubeconfig --region eu-central-1 --name my-eks-cluster
Dadurch wird die kubeconfig
aktualisiert und du kannst mit dem Cluster interagieren:
kubectl get nodes
Du solltest die Worker Nodes sehen, die im Cluster registriert sind.
Schritt 10: Testen des Clusters
Erstelle eine einfache Anwendung, um den Cluster zu testen. Zum Beispiel ein Nginx-Deployment:
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=LoadBalancer
Überprüfe den Status:
kubectl get services
Nach einigen Minuten sollte ein ELB-Endpunkt verfügbar sein, über den du auf Nginx zugreifen kannst.
Aufräumen
Wenn du den Cluster nicht mehr benötigst, kannst du die Ressourcen entfernen:
terraform destroy
Teil 2: Kostenbetrachtung im Kontext von FinOps
Warum FinOps?
In der heutigen Cloud-Ära ist die Kostenoptimierung ein wesentlicher Aspekt der Infrastrukturverwaltung. FinOps (Financial Operations) kombiniert Finanzmanagement und DevOps-Prinzipien, um die Effizienz und Transparenz der Cloud-Ausgaben zu maximieren. Bei der Bereitstellung eines EKS-Clusters auf AWS gibt es verschiedene Optionen, um Kosten zu optimieren, ohne die Leistung zu beeinträchtigen.
Standard-Setup vs. Spot-Instanzen mit Connection Draining
Standard-Setup (On-Demand-Instanzen)
Im Standard-Setup verwenden wir On-Demand-EC2-Instanzen für unsere Worker Nodes. Diese bieten:
- Vorteile:
- Stabilität: Instanzen werden nicht unerwartet beendet.
- Flexibilität: Skalierung nach Bedarf ohne Unterbrechungen.
- Einfache Verwaltung: Weniger Komplexität bei der Einrichtung.
- Nachteile:
- Höhere Kosten: On-Demand-Preise sind im Vergleich zu anderen Optionen höher.
Spot-Instanzen mit Connection Draining
Spot-Instanzen ermöglichen es, ungenutzte EC2-Kapazitäten zu einem deutlich reduzierten Preis zu nutzen. Allerdings können sie jederzeit von AWS beendet werden, wenn die Kapazität benötigt wird.
- Vorteile:
- Kosteneinsparungen: Bis zu 90 % günstiger als On-Demand-Instanzen.
- Skalierbarkeit: Möglichkeit, große Workloads kostengünstig zu betreiben.
- Nachteile:
- Unterbrechungen: Instanzen können kurzfristig beendet werden.
- Komplexität: Erfordert zusätzliche Mechanismen, um Unterbrechungen zu handhaben.
Connection Draining hilft dabei, laufende Verbindungen sauber zu beenden, bevor eine Instanz entfernt wird. In Kombination mit Spot-Instanzen kann dies dazu beitragen, die Auswirkungen von Unterbrechungen zu minimieren.
Implementierung von Spot-Instanzen in Terraform
Wir können unsere Node Group so konfigurieren, dass sie Spot-Instanzen verwendet:
resource "aws_eks_node_group" "eks_spot_node_group" {
cluster_name = aws_eks_cluster.eks_cluster.name
node_group_name = "eks-spot-node-group"
node_role_arn = aws_iam_role.eks_worker_role.arn
subnet_ids = module.vpc.private_subnets
scaling_config {
desired_size = 4
max_size = 6
min_size = 2
}
instance_types = ["t3.medium"]
capacity_type = "SPOT"
labels = {
lifecycle = "Ec2Spot"
}
taints = [{
key = "spotInstance",
value = "true",
effect = "NoSchedule"
}]
depends_on = [
aws_eks_cluster.eks_cluster,
aws_iam_role_policy_attachment.worker_node_policy_attachment,
aws_iam_role_policy_attachment.cni_policy_attachment,
aws_iam_role_policy_attachment.registry_policy_attachment,
]
}
Teil 3: Einrichtung von Connection Draining
Warum ist Connection Draining wichtig?
Beim Einsatz von Spot-Instanzen besteht die Möglichkeit, dass AWS diese mit einer Vorwarnzeit von nur 2 Minuten beendet. Ohne entsprechende Maßnahmen können laufende Anwendungen abrupt unterbrochen werden, was zu Datenverlust oder Dienstunterbrechungen führen kann.
Connection Draining ermöglicht es, laufende Verbindungen sauber zu beenden und Pods auf anderen verfügbaren Nodes neu zu starten, bevor die Instanz tatsächlich heruntergefahren wird.
Implementierung des AWS Node Termination Handlers
Der AWS Node Termination Handler ist ein DaemonSet, das Benachrichtigungen über bevorstehende Instanzunterbrechungen überwacht und automatisch das Drainieren der betroffenen Nodes initiiert.
Schritt 1: Hinzufügen des Node Termination Handlers
Option 1: Installation mit Helm
Falls du Helm noch nicht installiert hast, kannst du es mit folgendem Befehl installieren:
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
Füge das Helm-Repository hinzu:
helm repo add eks https://aws.github.io/eks-charts
helm repo update
Installiere den Node Termination Handler:
helm install aws-node-termination-handler eks/aws-node-termination-handler \
--namespace kube-system \
--set enableSpotInterruptionDraining=true \
--set enableRebalanceMonitoring=true \
--set enableRebalanceDraining=true \
--set enableScheduledEventDraining=true \
--set enablePrometheusMetrics=true
Option 2: Installation mit kubectl
Du kannst die YAML-Manifeste direkt von GitHub anwenden:
kubectl apply -f https://github.com/aws/aws-node-termination-handler/releases/download/v1.14.1/all-resources.yaml
Hinweis: Überprüfe die neueste Version auf der GitHub-Seite des Projekts.
Schritt 2: Konfiguration des Node Termination Handlers in Terraform
Wenn du den Node Termination Handler mit Terraform verwalten möchtest, kannst du ein Helm-Release direkt in Terraform definieren.
Füge in helm.tf
folgendes hinzu:
provider "helm" {
kubernetes {
host = aws_eks_cluster.eks_cluster.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.eks_cluster.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.eks_auth.token
}
}
data "aws_eks_cluster_auth" "eks_auth" {
name = aws_eks_cluster.eks_cluster.name
}
resource "helm_release" "aws_node_termination_handler" {
name = "aws-node-termination-handler"
repository = "https://aws.github.io/eks-charts"
chart = "aws-node-termination-handler"
namespace = "kube-system"
version = "0.18.0" # Überprüfe die neueste Version
values = [
<<EOF
enableSpotInterruptionDraining: true
enableRebalanceMonitoring: true
enableRebalanceDraining: true
enableScheduledEventDraining: true
enablePrometheusMetrics: true
EOF
]
depends_on = [aws_eks_cluster.eks_cluster]
}
Schritt 3: Überprüfen der Installation
Nach der Installation kannst du überprüfen, ob das DaemonSet läuft:
kubectl get daemonset -n kube-system aws-node-termination-handler
Die Pods sollten auf allen Nodes im Cluster laufen.
Anpassen der Pod-Termination-Grace-Period
Stelle sicher, dass deine Pods eine ausreichende Termination Grace Period haben, um laufende Verbindungen sauber zu beenden.
Beispiel für ein Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
terminationGracePeriodSeconds: 120
containers:
- name: my-app-container
image: my-app-image
# Weitere Container-Konfiguration
Hinweis: Die terminationGracePeriodSeconds
sollte mindestens 120 Sekunden betragen, um die 2-Minuten-Vorwarnzeit von Spot-Instanzunterbrechungen abzudecken.
Testen des Connection Draining
Du kannst das Verhalten simulieren, indem du eine Node manuell drainst:
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
Überprüfe, ob die Pods sauber auf andere Nodes verschoben werden und keine Verbindungen abrupt beendet werden.
Automatische Skalierung mit Cluster Autoscaler
In Kombination mit dem Kubernetes Cluster Autoscaler kannst du sicherstellen, dass bei Wegfall von Nodes automatisch neue Nodes hinzugefügt werden, um die gewünschte Kapazität zu halten.
Installation des Cluster Autoscalers mit Helm:
helm repo add autoscaler https://kubernetes.github.io/autoscaler
helm repo update
helm install cluster-autoscaler autoscaler/cluster-autoscaler-chart \
--namespace kube-system \
--set autoDiscovery.clusterName=my-eks-cluster \
--set awsRegion=eu-central-1 \
--set expander=random \
--set cloudProvider=aws \
--set extraArgs.balance-similar-node-groups=true \
--set extraArgs.skip-nodes-with-system-pods=false \
--set rbac.create=true
Konfiguration in Terraform:
Du kannst auch den Cluster Autoscaler als Helm-Release in Terraform hinzufügen.
Anpassen der Node Group für Auto Scaling
Stelle sicher, dass deine Node Groups das Auto Scaling unterstützen, indem du die richtigen Parameter setzt.
Beispiel:
resource "aws_eks_node_group" "eks_spot_node_group" {
# ... (wie zuvor)
scaling_config {
desired_size = 4
max_size = 10
min_size = 2
}
# ... (restliche Konfiguration)
}
Aktualisierte Kostenübersicht
Mit der zusätzlichen Implementierung von Connection Draining und dem Cluster Autoscaler kannst du die Vorteile von Spot-Instanzen voll ausschöpfen und gleichzeitig die Servicequalität aufrechterhalten.
Option | Kosten pro Stunde | Monatliche Kosten (730 Std.) | Einsparungen |
---|---|---|---|
On-Demand-Instanzen | 0,0416 USD | 30,37 USD | - |
Spot-Instanzen mit Connection Draining | 0,0125 USD | 9,13 USD | ~70% |
Fazit
Die Einrichtung von Connection Draining in Kombination mit Spot-Instanzen und dem Cluster Autoscaler ermöglicht eine kosteneffiziente und robuste Kubernetes-Infrastruktur auf AWS EKS. Durch die Verwendung von Terraform und Helm kannst du diese Konfigurationen automatisieren und versionieren, was die Verwaltung deiner Cloud-Ressourcen erheblich erleichtert.
Weiterführende Ressourcen
- AWS Node Termination Handler GitHub Repository
- AWS Spot Instances Dokumentation
- Kubernetes Cluster Autoscaler
- Helm Charts für EKS
- FinOps Foundation
- Terraform AWS EKS Modul
Hast du weitere Fragen oder benötigst du Unterstützung bei der Implementierung? Kontaktiere uns direkt!