Kubernetes und AWS

May 14, 2018

Während allerorts fieberhaft auf Preview-Zugänge zu Amazon EKS gewartet wird, haben wir an dieser Stelle eine Liste nützlicher Werkzeuge zusammengestellt, mit denen man sich direkt in das Abenteuer Kubernetes stürzen kann.

kops

Es gibt unterschiedliche Ansätze Kubernetes-Cluster bei AWS aufzusetzen. Einer davon ist kops. kops steht für Kubernetes-Operations und wird aktiv durch die Kubernetes-Community gepflegt.

kops erstellt alle benötigten AWS-Ressourcen, wie z.B. VPC, EC2-Instanzen, ELBs etc. Die Anzahl der Master und Knoten lässt sich per Kommandozeile festlegen. Der folgende Befehl erstellt einen Kubernetes-Cluster in der AWS-Region eu-central mit sechs Knoten und drei Mastern.

$ > export CLUSTER_NAME=my-first-cluster
$ > kops create cluster \
	--zones=eu-central-1a,eu-central-1b,eu-central-1c \
	--master-zones=eu-central-1a,eu-central-1b,eu-central-1c \
	--node-count=6 \
	--master-count=3 \
	--node-size=m4.2xlarge \
	--master-size=m4.large \
	--topology=private \
	--name=$(CLUSTER_NAME)

kops kann auch den Lebenszyklus eines Clusters verwalten und z.B. Ausskalieren oder Kubernetes durch rollende Upgrades auf neue Versionen bringen. Eine detaillierte Anleitung findet sich hier.

kube2iam

Wenn Pods, die auf Kubernetes laufen, AWS-APIs konsumieren, ist der Einsatz von kube2iam angezeigt. kube2iam erlaubt Pods IAM-Rollen anzunehmen, so wie sonst EC2-Instanzen über Instanzprofile Rollen annehmen können. kube2iam simuliert die Instance-Metadata API (https://169.254.169.254/latest/meta-data/ ) und isoliert Pods voneinander, so dass diese nur ihre Ihnen zugedachten Berechtigungen erhalten. kube2iam wird als DaemonSet auf jeder Node installiert. Per Annotation am Pod oder Deployment wird den Containern die jeweilige Rolle zugeordnet.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  annotations:
    iam.amazonaws.com/role: role-arn
spec:
  containers:
  - image: nginx 
    name: nginx

kube2iam liest die Annotation iam.amazonaws.com/role aus und ordnet dem Pod die Rolle role-arn zu. Damit das funktioniert, müssen die ec2-Instanzen selber eine IAM-Rolle haben, die ihnen erlaubt weiter Rollen anzunehmen. Details sind der Dokumentation zu entnehmen.

ExternalDNS

Um auf Services, die auf Kubernetes laufen, durch das öffentliche Internet zugreifen zu können, ist es üblich diese mit DNS-Einträgen zu versehen. ExternalDNS erlaubt eben dies. Per Annotation oder Konvention werden Services mit DNS-Einträgen versehen. ExternalDNS delegiert diese dann an AWS Route53 oder andere DNS-Services. Die Rechte, die ExternalDNS benötigt, um DNS-Einträge mit Route53 zu machen, lassen sich auch per kube2iam vergeben. ExternalDNS kann sowohl Ingress- als auch Service-Ressourcen DNS-Einträge zuordnen. Damit ExternalDNS funktioniert, braucht es Verwaltungsrechte für die öffentlich gehostete Zone. Im folgenden Beispiel wird der Ingress-Ressource der Name cool-website. in der Zone example.com. zugeordnet. Bei Ingress-Ressourcen wird das host-Keyword aus den Regeln ausgelesen und ein passender DNS-Eintrag erstellt.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - host: cool-website.example.com
    http:
      paths:
      - backend:
          serviceName: nginx
          servicePort: 80	

Eine Anleitung wie ExternalDNS im Zusammenhang mit AWS Route53 zu konfigurieren ist, findet sich hier.

Log-Aggregation via Fluentd

Um Container-Logs so wie die Logs der einzelnen Kubernetes-Services zu aggregieren, empfiehlt sich Fluentd und die Kinesis Firehose.

Fluentd wird als DaemonSet konfiguriert und schickt alle Logs an Kinesis Firehose. Von dort lassen sich die Logs weiterverarbeiten. Zum Beispiel können sie in S3 zur Archivierung abgelegt werden oder an ElasticSearch weitergeleitet werden, um Diagnostik zu betreiben. Dazu muss der offizielle fluentd-Container mit dem fluent-plugin-kinesis erweitert werden.

FROM fluent/fluentd-kubernetes-daemonset:stable-cloudwatch

RUN apk add --no-cache --virtual .build-deps \
        build-base \
        ruby-dev \
        libffi-dev \
	&& gem install fluentd -v 0.12.43 \
	&& gem install fluent-plugin-kinesis -v 2.1.1 \
	&& gem install fluent-plugin-route -v 0.2.0 \
	&& apk del .build-deps

Jetzt wird die ursprüngliche Konfiguration fluent.conf dahingehend verändert, dass die Logs an Kinesis geleitet werden.

<match **>
  type kinesis_firehose
  delivery_stream_name my-kinesis-stream 
</match>

Nun sollten die Logs in Kinesis auftauchen.

Authentifizierung mit guard

Authentifizierung läuft bei Kubernetes entweder per Client-Zertifikat, Basic Auth oder unterschiedlichsten Bearer Tokens. Auch lassen sich OIDC-Provider anschließen, wie z.B. Google G Suite oder MS Active Directory. Ein weiterer Mechanismus ist die Webhook-Authentifizierung. Hierzu wird der Kubernetes API Server derart konfiguriert, dass eingehende Tokens per Webhook von einer dritten Partei überprüft werden. Der API-Server schickt eine als JSON serialisiertes authentication.k8s.io/v1beta1/TokenReview-Objekt, welches den Token enthält. In der Antwort wird der Nutzer entweder bestätigt und Gruppenzugehörigkeiten aufgelöst oder der Zugriff verweigert.

guard ist ein solcher Server. So lassen sich Nutzer beispielsweise per GitHub authentifizieren. Nutzer erstellen einen GitHub-API Token und hinterlegen diesen in ihrer kubeconfig. kubectl schickt den Token an den Kubernetes API Server. Dieser schickt den Token per Webhook an guard. guard wiederum validiert den Token mit Hilfe der GitHub-API.

Hier gibt es eine detaillierte Anleitung, wie guard für einen mit kops aufgesetzten Kubernetes-Cluster konfiguriert wird.

Fazit

Mit Hilfe der obigen Tools sollte der Betrieb eines Kubernetes-Clusters auf AWS leichter fallen. Mit Sicherheit gibt es noch mehr hilfreiche Tools aus dem Kubernetes-Umfeld, die einem dass Leben mit AWS einfacher gestalten. Kennt ihr welche? Schreibt sie gerne in die Kommentare!

photo of Jan

Jan is Co-founder and Cloud Consultant at superluminar and AWS Certified Solutions Architect Professional. He writes here about AWS-specific topics. He can be found under the name @bracki on Twitter.