Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

L'objectiu de l'article és introduir al lector als conceptes bàsics de la xarxa i la gestió de polítiques de xarxa a Kubernetes, així com el complement Calico de tercers que amplia les capacitats estàndard. Al llarg del camí, es demostrarà la facilitat de configuració i algunes característiques mitjançant exemples reals de la nostra experiència operativa.

Una introducció ràpida a l'aparell de xarxa Kubernetes

No es pot imaginar un clúster de Kubernetes sense una xarxa. Ja hem publicat materials sobre els seus fonaments bàsics: "Una guia il·lustrada per a la creació de xarxes a Kubernetes"I"Una introducció a les polítiques de xarxa de Kubernetes per a professionals de la seguretat».

En el context d'aquest article, és important assenyalar que el propi K8s no és responsable de la connectivitat de xarxa entre contenidors i nodes: per a això, diversos Connectors CNI (Interfície de xarxa de contenidors). Més informació sobre aquest concepte també em van dir.

Per exemple, el més comú d'aquests connectors és Flanel — proporciona connectivitat de xarxa completa entre tots els nodes del clúster aixecant ponts a cada node, assignant-li una subxarxa. Tanmateix, l'accessibilitat completa i no regulada no sempre és beneficiosa. Per proporcionar algun tipus d'aïllament mínim al clúster, cal intervenir en la configuració del tallafoc. En el cas general, es col·loca sota el control del mateix CNI, motiu pel qual qualsevol intervenció de tercers en iptables es pot interpretar incorrectament o ignorar-se del tot.

I es proporciona "fora de la caixa" per organitzar la gestió de polítiques de xarxa en un clúster de Kubernetes API NetworkPolicy. Aquest recurs, distribuït en espais de noms seleccionats, pot contenir regles per diferenciar l'accés d'una aplicació a una altra. També us permet configurar l'accessibilitat entre pods, entorns (espais de noms) o blocs d'adreces IP específics:

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

Aquest no és l'exemple més primitiu documentació oficial pot desanimar d'una vegada per sempre el desig d'entendre la lògica de com funcionen les polítiques de xarxa. Tanmateix, encara intentarem entendre els principis bàsics i els mètodes de processament dels fluxos de trànsit mitjançant polítiques de xarxa...

És lògic que hi hagi 2 tipus de trànsit: entrant al pod (Ingress) i sortint d'aquest (Egress).

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

De fet, la política es divideix en aquestes 2 categories en funció de la direcció del moviment.

El següent atribut obligatori és un selector; aquell a qui s'aplica la regla. Pot ser un pod (o un grup de pods) o un entorn (és a dir, un espai de noms). Un detall important: tots dos tipus d'objectes han de contenir una etiqueta (etiqueta en terminologia de Kubernetes) - aquests són amb els quals operen els polítics.

A més d'un nombre finit de selectors units per algun tipus d'etiqueta, és possible escriure regles com "Permetre/denegar tot/tothom" en diferents variacions. Per a això, s'utilitzen construccions de la forma:

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

— en aquest exemple, tots els pods de l'entorn estan bloquejats del trànsit entrant. El comportament contrari es pot aconseguir amb la construcció següent:

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

De la mateixa manera per a sortints:

  podSelector: {}
  policyTypes:
  - Egress

- per apagar-lo. I aquí teniu què incloure:

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

Tornant a l'elecció d'un connector CNI per a un clúster, val la pena assenyalar-ho no tots els connectors de xarxa admeten NetworkPolicy. Per exemple, el ja esmentat Flannel no sap com configurar les polítiques de xarxa, que es diu directament al repositori oficial. També s'esmenta una alternativa: un projecte de codi obert Calic, que amplia significativament el conjunt estàndard d'API de Kubernetes en termes de polítiques de xarxa.

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Conèixer Calico: teoria

El connector Calico es pot utilitzar en integració amb Flannel (subprojecte Canal) o de manera independent, abastant tant la connectivitat de xarxa com les capacitats de gestió de la disponibilitat.

Quines oportunitats ofereix l'ús de la solució "en caixa" K8s i el conjunt d'API de Calico?

Això és el que està integrat a NetworkPolicy:

  • els polítics estan limitats pel medi ambient;
  • les polítiques s'apliquen als pods marcats amb etiquetes;
  • les regles es poden aplicar a pods, entorns o subxarxes;
  • Les regles poden contenir protocols, especificacions de ports amb nom o simbòliques.

Així és com Calico amplia aquestes funcions:

  • les polítiques es poden aplicar a qualsevol objecte: pod, contenidor, màquina virtual o interfície;
  • les regles poden contenir una acció específica (prohibició, permís, registre);
  • l'objectiu o la font de les regles pot ser un port, una sèrie de ports, protocols, atributs HTTP o ICMP, IP o subxarxa (4a o 6a generació), qualsevol selector (nodes, hosts, entorns);
  • A més, podeu regular el pas del trànsit mitjançant la configuració de DNAT i les polítiques de reenviament de trànsit.

Els primers compromisos a GitHub al repositori de Calico es remunten al juliol de 2016, i un any més tard, el projecte va ocupar una posició de lideratge en l'organització de la connectivitat de xarxa de Kubernetes; així ho demostren, per exemple, els resultats de l'enquesta. dirigit per The New Stack:

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Moltes solucions gestionades grans amb K8, com ara Amazon EKS, Azure AKS, Google GKE i altres van començar a recomanar-lo per al seu ús.

Pel que fa al rendiment, aquí tot és genial. En provar el seu producte, l'equip de desenvolupament de Calico va demostrar un rendiment astronòmic, executant més de 50000 contenidors en 500 nodes físics amb una taxa de creació de 20 contenidors per segon. No s'han identificat problemes amb l'escala. Aquests resultats van ser anunciats ja a l'anunci de la primera versió. Estudis independents centrats en el rendiment i el consum de recursos també confirmen que el rendiment de Calico és gairebé tan bo com el de Flannel. Per exemple:

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

El projecte es desenvolupa molt ràpidament, admet el treball en solucions populars gestionades K8s, OpenShift, OpenStack, és possible utilitzar Calico quan es desplega un clúster mitjançant kops, hi ha referències a la construcció de xarxes Service Mesh (heus aquí un exemple utilitzat conjuntament amb Istio).

Practica amb Calico

En el cas general d'utilitzar Vanilla Kubernetes, instal·lar CNI es redueix a utilitzar el fitxer calico.yaml, descarregat del lloc web oficial, mitjançant l'ús de kubectl apply -f.

Per regla general, la versió actual del connector és compatible amb les 2-3 versions més recents de Kubernetes: el funcionament en versions anteriors no es prova i no està garantit. Segons els desenvolupadors, Calico s'executa amb nuclis Linux per sobre de 3.10 amb CentOS 7, Ubuntu 16 o Debian 8, a més d'iptables o IPVS.

Aïllament dins l'entorn

Per a una comprensió general, mirem un cas senzill per entendre com les polítiques de xarxa en la notació Calico difereixen de les estàndards i com l'enfocament per crear regles simplifica la seva llegibilitat i flexibilitat de configuració:

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Hi ha 2 aplicacions web desplegades al clúster: a Node.js i PHP, una de les quals utilitza Redis. Per bloquejar l'accés a Redis des de PHP, mantenint la connectivitat amb Node.js, només cal que apliqueu la política següent:

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

Bàsicament, vam permetre el trànsit entrant al port Redis des de Node.js. I és clar que no prohibien res més. Tan bon punt apareix NetworkPolicy, tots els selectors esmentats comencen a aïllar-se, tret que s'especifiqui el contrari. Tanmateix, les regles d'aïllament no s'apliquen a altres objectes no coberts pel selector.

L'exemple utilitza apiVersion Kubernetes fora de la caixa, però res no us impedeix utilitzar-lo recurs del mateix nom del lliurament Calico. La sintaxi allà és més detallada, de manera que haureu de reescriure la regla per al cas anterior en la forma següent:

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

Les construccions esmentades anteriorment per permetre o denegar tot el trànsit a través de l'API de NetworkPolicy normal contenen construccions amb parèntesis que són difícils d'entendre i recordar. En el cas de Calico, per canviar la lògica d'una regla de tallafocs a la contrària, només cal canviar action: Allow en action: Deny.

Aïllament per entorn

Ara imagineu una situació en què una aplicació generi mètriques empresarials per a la recollida a Prometheus i una anàlisi posterior amb Grafana. La càrrega pot contenir dades sensibles, que de nou es poden veure públicament de manera predeterminada. Ocultem aquestes dades de mirades indiscretes:

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Prometheus, per regla general, es col·loca en un entorn de servei independent; a l'exemple serà un espai de noms com aquest:

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

Camp metadata.labels això no va resultar ser cap casualitat. Com s'ha esmentat anteriorment, namespaceSelector (així com podSelector) funciona amb etiquetes. Per tant, per permetre que es prenguin mètriques de tots els pods d'un port específic, haureu d'afegir algun tipus d'etiqueta (o agafar-ne de les existents) i després aplicar una configuració com:

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

I si utilitzeu polítiques de Calico, la sintaxi serà així:

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

En general, afegint aquest tipus de polítiques per a necessitats específiques, podeu protegir contra interferències malicioses o accidentals en el funcionament de les aplicacions del clúster.

La millor pràctica, segons els creadors de Calico, és l'enfocament "Bloqueja-ho tot i obre explícitament el que necessites", documentat a documentació oficial (altres segueixen un enfocament similar, en particular, en article ja esmentat).

Ús d'objectes Calico addicionals

Permeteu-me que us recordi que mitjançant el conjunt estès d'API de Calico podeu regular la disponibilitat de nodes, sense limitar-vos als pods. En l'exemple següent utilitzant GlobalNetworkPolicy la capacitat de passar sol·licituds ICMP al clúster està tancada (per exemple, pings d'un pod a un node, entre pods o d'un node a un pod IP):

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

En el cas anterior, encara és possible que els nodes del clúster "es posin en contacte" entre ells mitjançant ICMP. I aquest problema es resol per mitjans GlobalNetworkPolicy, aplicat a una entitat 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"]

El cas VPN

Finalment, donaré un exemple molt real de l'ús de funcions de Calico per al cas d'interacció propera al clúster, quan no n'hi ha prou amb un conjunt estàndard de polítiques. Per accedir a l'aplicació web, els clients utilitzen un túnel VPN, i aquest accés està estretament controlat i limitat a una llista específica de serveis permesos per al seu ús:

Calico per al networking a Kubernetes: una introducció i una mica d'experiència

Els clients es connecten a la VPN mitjançant el port UDP estàndard 1194 i, quan estan connectats, reben rutes a les subxarxes del clúster de pods i serveis. S'envien subxarxes senceres per no perdre serveis durant els reinicis i els canvis d'adreça.

El port de la configuració és estàndard, la qual cosa imposa alguns matisos al procés de configuració de l'aplicació i transferència al clúster Kubernetes. Per exemple, al mateix AWS LoadBalancer per a UDP va aparèixer literalment a finals de l'any passat en una llista limitada de regions, i NodePort no es pot utilitzar a causa del seu reenviament a tots els nodes del clúster i és impossible escalar el nombre d'instàncies de servidor per a finalitats de tolerància a errors. A més, hauràs de canviar l'interval predeterminat de ports...

Com a resultat de la recerca de possibles solucions, s'han escollit les següents:

  1. Els pods amb VPN es programen per node hostNetwork, és a dir, a la IP real.
  2. El servei es publica fora a través ClusterIP. Al node s'instal·la físicament un port, que és accessible des de l'exterior amb reserves menors (presència condicional d'una adreça IP real).
  3. Determinar el node en què es va aixecar la beina està fora de l'abast de la nostra història. Només diré que podeu "clavar" el servei a un node o escriure un petit servei de sidecar que supervisarà l'adreça IP actual del servei VPN i editarà els registres DNS registrats amb els clients, qui tingui prou imaginació.

Des d'una perspectiva d'encaminament, podem identificar de manera única un client VPN per la seva adreça IP emesa pel servidor VPN. A continuació es mostra un exemple primitiu de restringir l'accés d'aquest client als serveis, il·lustrat al Redis esmentat anteriorment:

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]

Aquí, connectar-se al port 6379 està estrictament prohibit, però al mateix temps es conserva el funcionament del servei DNS, el funcionament del qual sovint pateix quan s'elaboren regles. Perquè, com s'ha esmentat anteriorment, quan apareix un selector, se li aplica la política de denegació predeterminada tret que s'especifiqui el contrari.

Resultats de

Així, utilitzant l'API avançada de Calico, podeu configurar i canviar de manera dinàmica l'encaminament dins i al voltant del clúster. En general, el seu ús pot semblar disparar pardals amb un canó, i la implementació d'una xarxa L3 amb túnels BGP i IP-IP sembla monstruosa en una senzilla instal·lació de Kubernetes en una xarxa plana... Tanmateix, en cas contrari, l'eina sembla força viable i útil. .

Aïllar un clúster per complir els requisits de seguretat pot no ser sempre factible, i aquí és on Calico (o una solució similar) ve al rescat. Els exemples que es donen en aquest article (amb petites modificacions) s'utilitzen en diverses instal·lacions dels nostres clients a AWS.

PS

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari