Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Der Zweck des Artikels besteht darin, den Leser in die Grundlagen des Netzwerks und der Verwaltung von Netzwerkrichtlinien in Kubernetes sowie in das Calico-Plugin eines Drittanbieters einzuführen, das die Standardfunktionen erweitert. Nebenbei werden die einfache Konfiguration und einige Features anhand realer Beispiele aus unserer Betriebserfahrung demonstriert.

Eine kurze Einführung in die Kubernetes-Netzwerk-Appliance

Ein Kubernetes-Cluster ist ohne Netzwerk nicht vorstellbar. Wir haben bereits Materialien zu ihren Grundlagen veröffentlicht: „Eine illustrierte Anleitung zum Netzwerken in Kubernetes"Und"Eine Einführung in Kubernetes-Netzwerkrichtlinien für Sicherheitsexperten".

Im Zusammenhang mit diesem Artikel ist es wichtig zu beachten, dass K8s selbst nicht für die Netzwerkkonnektivität zwischen Containern und Knoten verantwortlich ist: Dafür gibt es verschiedene CNI-Plugins (Container-Netzwerkschnittstelle). Mehr zu diesem Konzept erfahren Sie bei uns sie haben es mir auch gesagt.

Das häufigste dieser Plugins ist beispielsweise Flanell – Bietet vollständige Netzwerkkonnektivität zwischen allen Cluster-Knoten, indem auf jedem Knoten Brücken errichtet und ihm ein Subnetz zugewiesen wird. Allerdings ist eine vollständige und ungeregelte Zugänglichkeit nicht immer von Vorteil. Um eine minimale Isolation im Cluster zu gewährleisten, ist ein Eingriff in die Konfiguration der Firewall erforderlich. Im Regelfall steht es unter der Kontrolle desselben CNI, weshalb etwaige Eingriffe Dritter in iptables falsch interpretiert oder ganz ignoriert werden können.

Und es wird „out of the box“ für die Organisation der Netzwerkrichtlinienverwaltung in einem Kubernetes-Cluster bereitgestellt NetworkPolicy-API. Diese über ausgewählte Namespaces verteilte Ressource kann Regeln enthalten, um den Zugriff von einer Anwendung auf eine andere zu unterscheiden. Außerdem können Sie die Zugänglichkeit zwischen bestimmten Pods, Umgebungen (Namespaces) oder Blöcken von IP-Adressen konfigurieren:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Dies ist nicht das primitivste Beispiel dafür amtliche Dokumentation kann ein für alle Mal den Wunsch entmutigen, die Logik der Funktionsweise von Netzwerkrichtlinien zu verstehen. Wir werden jedoch weiterhin versuchen, die Grundprinzipien und Methoden der Verarbeitung von Verkehrsströmen mithilfe von Netzwerkrichtlinien zu verstehen ...

Es ist logisch, dass es zwei Arten von Datenverkehr gibt: den Eingang zum Pod (Ingress) und den ausgehenden Datenverkehr (Egress).

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Tatsächlich wird die Politik je nach Bewegungsrichtung in diese beiden Kategorien unterteilt.

Das nächste erforderliche Attribut ist ein Selektor. derjenige, für den die Regel gilt. Dies kann ein Pod (oder eine Gruppe von Pods) oder eine Umgebung (d. h. ein Namespace) sein. Ein wichtiges Detail: Beide Arten dieser Objekte müssen eine Beschriftung enthalten (Etikette in der Kubernetes-Terminologie) – das sind diejenigen, mit denen Politiker operieren.

Zusätzlich zu einer endlichen Anzahl von Selektoren, die durch eine Art Label vereint sind, ist es möglich, Regeln wie „Alles/jeden zulassen/verbieten“ in verschiedenen Variationen zu schreiben. Zu diesem Zweck werden Konstruktionen der Form verwendet:

  podSelector: {}
  ingress: []
  policyTypes:
  - Ingress

– In diesem Beispiel werden alle Pods in der Umgebung für eingehenden Datenverkehr blockiert. Das gegenteilige Verhalten kann mit folgender Konstruktion erreicht werden:

  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

Ebenso für ausgehende:

  podSelector: {}
  policyTypes:
  - Egress

- um es auszuschalten. Und hier ist, was Sie einschließen sollten:

  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Zurück zur Wahl eines CNI-Plugins für einen Cluster: Es ist erwähnenswert Nicht jedes Netzwerk-Plugin unterstützt NetworkPolicy. Beispielsweise weiß der bereits erwähnte Flannel nicht, wie man Netzwerkrichtlinien konfiguriert es wird direkt gesagt im offiziellen Repository. Dort wird auch eine Alternative erwähnt – ein Open-Source-Projekt Kattun, was den Standardsatz der Kubernetes-APIs in Bezug auf Netzwerkrichtlinien erheblich erweitert.

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Calico kennenlernen: Theorie

Das Calico-Plugin kann in Integration mit Flannel (Teilprojekt) verwendet werden Kanal) oder unabhängig und deckt sowohl Netzwerkkonnektivitäts- als auch Verfügbarkeitsmanagementfunktionen ab.

Welche Möglichkeiten bietet die Verwendung der „Boxed“-Lösung von K8s und des API-Sets von Calico?

Folgendes ist in NetworkPolicy integriert:

  • Politiker werden durch die Umwelt eingeschränkt;
  • Richtlinien werden auf Pods angewendet, die mit Labels gekennzeichnet sind.
  • Regeln können auf Pods, Umgebungen oder Subnetze angewendet werden;
  • Regeln können Protokolle, benannte oder symbolische Portspezifikationen enthalten.

So erweitert Calico diese Funktionen:

  • Richtlinien können auf jedes Objekt angewendet werden: Pod, Container, virtuelle Maschine oder Schnittstelle;
  • Regeln können eine bestimmte Aktion enthalten (Verbot, Erlaubnis, Protokollierung);
  • Das Ziel oder die Quelle von Regeln kann ein Port, eine Reihe von Ports, Protokollen, HTTP- oder ICMP-Attributen, eine IP oder ein Subnetz (4. oder 6. Generation) oder beliebige Selektoren (Knoten, Hosts, Umgebungen) sein.
  • Darüber hinaus können Sie den Datenverkehr mithilfe von DNAT-Einstellungen und Richtlinien zur Datenweiterleitung regulieren.

Die ersten Commits auf GitHub im Calico-Repository stammen aus dem Juli 2016, und ein Jahr später nahm das Projekt eine führende Position bei der Organisation der Kubernetes-Netzwerkkonnektivität ein – dies belegen beispielsweise die Umfrageergebnisse: durchgeführt von The New Stack:

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Viele große verwaltete Lösungen mit K8s, wie z Amazon EX, Azure AKS, Google GKE und andere begannen, es zur Verwendung zu empfehlen.

Was die Leistung angeht, ist hier alles super. Beim Testen seines Produkts stellte das Calico-Entwicklungsteam eine astronomische Leistung unter Beweis und betrieb mehr als 50000 Container auf 500 physischen Knoten mit einer Erstellungsrate von 20 Containern pro Sekunde. Bei der Skalierung wurden keine Probleme festgestellt. Solche Ergebnisse wurden angekündigt bereits bei der Ankündigung der ersten Version. Unabhängige Studien, die sich auf Durchsatz und Ressourcenverbrauch konzentrieren, bestätigen auch, dass die Leistung von Calico fast so gut ist wie die von Flannel. Beispielsweise:

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Das Projekt entwickelt sich sehr schnell, es unterstützt die Arbeit in gängigen verwalteten Lösungen wie K8s, OpenShift, OpenStack, es ist möglich, Calico bei der Bereitstellung eines Clusters zu verwenden Kops, gibt es Hinweise auf den Aufbau von Service Mesh-Netzwerken (Hier ist ein Beispiel wird in Verbindung mit Istio verwendet).

Übe mit Calico

Im allgemeinen Fall der Verwendung von Vanilla Kubernetes läuft die Installation von CNI auf die Verwendung der Datei hinaus calico.yaml, von der offiziellen Website heruntergeladen, mit der Hilfe kubectl apply -f.

In der Regel ist die aktuelle Version des Plugins mit den neuesten 2-3 Versionen von Kubernetes kompatibel: Der Betrieb in älteren Versionen ist nicht getestet und kann nicht garantiert werden. Nach Angaben der Entwickler läuft Calico auf Linux-Kerneln über 3.10 mit CentOS 7, Ubuntu 16 oder Debian 8, zusätzlich zu iptables oder IPVS.

Isolation innerhalb der Umgebung

Für ein allgemeines Verständnis schauen wir uns einen einfachen Fall an, um zu verstehen, wie sich Netzwerkrichtlinien in der Calico-Notation von Standardrichtlinien unterscheiden und wie der Ansatz zur Erstellung von Regeln ihre Lesbarkeit und Konfigurationsflexibilität vereinfacht:

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Im Cluster werden zwei Webanwendungen bereitgestellt: in Node.js und PHP, von denen eine Redis verwendet. Um den Zugriff auf Redis von PHP aus zu blockieren und gleichzeitig die Konnektivität mit Node.js aufrechtzuerhalten, wenden Sie einfach die folgende Richtlinie an:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-redis-nodejs
spec:
  podSelector:
    matchLabels:
      service: redis
  ingress:
  - from:
    - podSelector:
        matchLabels:
          service: nodejs
    ports:
    - protocol: TCP
      port: 6379

Im Wesentlichen haben wir eingehenden Datenverkehr von Node.js zum Redis-Port zugelassen. Und sie haben offensichtlich nichts anderes verboten. Sobald NetworkPolicy angezeigt wird, werden alle darin genannten Selektoren isoliert, sofern nicht anders angegeben. Die Isolationsregeln gelten jedoch nicht für andere Objekte, die nicht vom Selektor abgedeckt werden.

Das Beispiel verwendet apiVersion Kubernetes ist sofort einsatzbereit, aber nichts hindert Sie daran, es zu verwenden gleichnamige Ressource aus der Calico-Lieferung. Die dortige Syntax ist detaillierter, daher müssen Sie die Regel für den oben genannten Fall in der folgenden Form umschreiben:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-redis-nodejs
spec:
  selector: service == 'redis'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: service == 'nodejs'
    destination:
      ports:
      - 6379

Die oben genannten Konstrukte zum Zulassen oder Ablehnen des gesamten Datenverkehrs über die reguläre NetworkPolicy-API enthalten Konstrukte mit Klammern, die schwer zu verstehen und zu merken sind. Um im Fall von Calico die Logik einer Firewall-Regel ins Gegenteil zu ändern, reicht es aus, sie zu ändern action: Allow auf action: Deny.

Isolation durch die Umgebung

Stellen Sie sich nun eine Situation vor, in der eine Anwendung Geschäftsmetriken zur Erfassung in Prometheus und zur weiteren Analyse mithilfe von Grafana generiert. Der Upload kann vertrauliche Daten enthalten, die wiederum standardmäßig öffentlich einsehbar sind. Lassen Sie uns diese Daten vor neugierigen Blicken verbergen:

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Prometheus wird in der Regel in einer separaten Serviceumgebung platziert – im Beispiel handelt es sich um einen Namensraum wie diesen:

apiVersion: v1
kind: Namespace
metadata:
  labels:
    module: prometheus
  name: kube-prometheus

Feld metadata.labels Es stellte sich heraus, dass dies kein Zufall war. Wie oben erwähnt, namespaceSelector (sowie podSelector) arbeitet mit Etiketten. Damit Metriken von allen Pods an einem bestimmten Port übernommen werden können, müssen Sie daher eine Art Label hinzufügen (oder von vorhandenen Labels übernehmen) und dann eine Konfiguration wie die folgende anwenden:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          module: prometheus
    ports:
    - protocol: TCP
      port: 9100

Und wenn Sie Calico-Richtlinien verwenden, sieht die Syntax wie folgt aus:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  ingress:
  - action: Allow
    protocol: TCP
    source:
      namespaceSelector: module == 'prometheus'
    destination:
      ports:
      - 9100

Im Allgemeinen können Sie sich durch das Hinzufügen dieser Art von Richtlinien für bestimmte Anforderungen vor böswilligen oder versehentlichen Eingriffen in den Betrieb von Anwendungen im Cluster schützen.

Die beste Vorgehensweise ist laut den Machern von Calico der Ansatz „Alles blockieren und explizit öffnen, was Sie brauchen“, dokumentiert in amtliche Dokumentation (Andere verfolgen einen ähnlichen Ansatz – insbesondere in bereits erwähnter Artikel).

Verwendung zusätzlicher Calico-Objekte

Ich möchte Sie daran erinnern, dass Sie mit dem erweiterten Satz von Calico-APIs die Verfügbarkeit von Knoten regulieren können, nicht nur von Pods. Im folgenden Beispiel wird verwendet GlobalNetworkPolicy Die Möglichkeit, ICMP-Anfragen im Cluster weiterzuleiten, ist geschlossen (z. B. Pings von einem Pod zu einem Knoten, zwischen Pods oder von einem Knoten zu einem IP-Pod):

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: block-icmp
spec:
  order: 200
  selector: all()
  types:
  - Ingress
  - Egress
  ingress:
  - action: Deny
    protocol: ICMP
  egress:
  - action: Deny
    protocol: ICMP

Im oben genannten Fall ist es weiterhin möglich, dass Cluster-Knoten über ICMP untereinander „Kontakt“ haben. Und dieses Problem wird mit Mitteln gelöst GlobalNetworkPolicy, auf eine Entität angewendet HostEndpoint:

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: deny-icmp-kube-02
spec:
  selector: "role == 'k8s-node'"
  order: 0
  ingress:
  - action: Allow
    protocol: ICMP
  egress:
  - action: Allow
    protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: kube-02-eth0
  labels:
    role: k8s-node
spec:
  interfaceName: eth0
  node: kube-02
  expectedIPs: ["192.168.2.2"]

Der VPN-Fall

Abschließend werde ich ein sehr reales Beispiel für die Verwendung von Calico-Funktionen für den Fall einer Cluster-nahen Interaktion geben, wenn ein Standardsatz von Richtlinien nicht ausreicht. Um auf die Webanwendung zuzugreifen, verwenden Clients einen VPN-Tunnel. Dieser Zugriff wird streng kontrolliert und auf eine bestimmte Liste von Diensten beschränkt, die zur Nutzung zugelassen sind:

Calico für Networking in Kubernetes: Einführung und ein wenig Erfahrung

Clients stellen über den Standard-UDP-Port 1194 eine Verbindung zum VPN her und empfangen bei bestehender Verbindung Routen zu den Cluster-Subnetzen von Pods und Diensten. Ganze Subnetze werden gepusht, um bei Neustarts und Adressänderungen keine Dienste zu verlieren.

Der Port in der Konfiguration ist Standard, was einige Nuancen beim Konfigurationsprozess der Anwendung und deren Übertragung an den Kubernetes-Cluster mit sich bringt. Beispielsweise erschien AWS LoadBalancer für UDP buchstäblich Ende letzten Jahres in einer begrenzten Liste von Regionen, und NodePort kann aufgrund seiner Weiterleitung auf alle Clusterknoten nicht verwendet werden und es ist unmöglich, die Anzahl der Serverinstanzen zu skalieren Fehlertoleranzzwecke. Außerdem müssen Sie den Standard-Portbereich ändern ...

Als Ergebnis der Suche nach möglichen Lösungen wurde Folgendes ausgewählt:

  1. Pods mit VPN werden pro Knoten geplant hostNetwork, also auf die eigentliche IP.
  2. Der Gottesdienst wird draußen durch veröffentlicht ClusterIP. Auf dem Knoten ist physikalisch ein Port installiert, der mit geringen Einschränkungen (vorausgesetztes Vorhandensein einer echten IP-Adresse) von außen zugänglich ist.
  3. Die Bestimmung des Knotens, an dem die Schote wuchs, würde den Rahmen unserer Geschichte sprengen. Ich sage nur, dass Sie den Dienst fest an einen Knoten „nageln“ oder einen kleinen Sidecar-Dienst schreiben können, der die aktuelle IP-Adresse des VPN-Dienstes überwacht und die bei Clients registrierten DNS-Einträge bearbeitet – wer auch immer genug Fantasie hat.

Aus Routing-Sicht können wir einen VPN-Client anhand seiner vom VPN-Server vergebenen IP-Adresse eindeutig identifizieren. Nachfolgend finden Sie ein einfaches Beispiel für die Einschränkung des Zugriffs eines solchen Clients auf Dienste, dargestellt auf dem oben genannten Redis:

apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: vpnclient-eth0
  labels:
    role: vpnclient
    environment: production
spec:
  interfaceName: "*"
  node: kube-02
  expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: vpn-rules
spec:
  selector: "role == 'vpnclient'"
  order: 0
  applyOnForward: true
  preDNAT: true
  ingress:
  - action: Deny
    protocol: TCP
    destination:
      ports: [6379]
  - action: Allow
    protocol: UDP
    destination:
      ports: [53, 67]

Hier ist die Verbindung zum Port 6379 strengstens untersagt, gleichzeitig bleibt jedoch der Betrieb des DNS-Dienstes erhalten, dessen Funktionsfähigkeit bei der Regelerstellung häufig beeinträchtigt wird. Denn wie bereits erwähnt, wird beim Erscheinen eines Selektors die standardmäßige Ablehnungsrichtlinie auf ihn angewendet, sofern nicht anders angegeben.

Ergebnisse

Mithilfe der erweiterten API von Calico können Sie das Routing im und um den Cluster flexibel konfigurieren und dynamisch ändern. Im Allgemeinen kann seine Verwendung wie das Abschießen von Spatzen mit einer Kanone aussehen, und die Implementierung eines L3-Netzwerks mit BGP- und IP-IP-Tunneln sieht in einer einfachen Kubernetes-Installation in einem flachen Netzwerk monströs aus ... Ansonsten sieht das Tool jedoch recht brauchbar und nützlich aus .

Die Isolierung eines Clusters zur Erfüllung von Sicherheitsanforderungen ist möglicherweise nicht immer machbar, und hier kommt Calico (oder eine ähnliche Lösung) zur Rettung. Die in diesem Artikel angegebenen Beispiele (mit geringfügigen Änderungen) werden in mehreren Installationen unserer Kunden in AWS verwendet.

PS

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kommentar hinzufügen