Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Syftet med artikeln är att introducera läsaren till grunderna för nätverkande och hantering av nätverkspolicyer i Kubernetes, såväl som tredjeparts plugin Calico som utökar standardmöjligheterna. Längs vägen kommer den enkla konfigurationen och vissa funktioner att demonstreras med hjälp av verkliga exempel från vår driftserfarenhet.

En snabb introduktion till Kubernetes nätverksverktyg

Ett Kubernetes-kluster kan inte föreställas utan ett nätverk. Vi har redan publicerat material om deras grunder: "En illustrerad guide till nätverkande i Kubernetes"Och"En introduktion till Kubernetes nätverkspolicyer för säkerhetspersonal".

I samband med denna artikel är det viktigt att notera att K8s själv inte ansvarar för nätverksanslutning mellan behållare och noder: för detta, olika CNI-plugins (Container Networking Interface). Mer om detta koncept vi de berättade också för mig.

Till exempel är den vanligaste av dessa plugins Flanell — ger full nätverksanslutning mellan alla klusternoder genom att höja bryggor på varje nod, tilldela ett subnät till den. Men fullständig och oreglerad tillgänglighet är inte alltid fördelaktigt. För att tillhandahålla någon form av minimal isolering i klustret är det nödvändigt att ingripa i konfigurationen av brandväggen. I det allmänna fallet är det placerat under kontroll av samma CNI, vilket är anledningen till att tredje parts ingripanden i iptables kan tolkas felaktigt eller ignoreras helt.

Och "out of the box" för att organisera nätverkspolicyhantering i ett Kubernetes-kluster tillhandahålls NetworkPolicy API. Denna resurs, fördelad över utvalda namnområden, kan innehålla regler för att skilja åtkomst från ett program till ett annat. Det låter dig också konfigurera tillgänglighet mellan specifika poddar, miljöer (namnutrymmen) eller block av IP-adresser:

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

Detta är inte det mest primitiva exemplet på officiell dokumentation kan en gång för alla motverka önskan att förstå logiken i hur nätverkspolicyer fungerar. Men vi kommer fortfarande att försöka förstå de grundläggande principerna och metoderna för att behandla trafikflöden med hjälp av nätverkspolicyer...

Det är logiskt att det finns två typer av trafik: inträde i poden (Ingress) och utgående från den (Egress).

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Egentligen är politiken indelad i dessa 2 kategorier baserat på rörelseriktningen.

Nästa obligatoriska attribut är en väljare; den som regeln gäller. Detta kan vara en pod (eller en grupp av pods) eller en miljö (dvs. ett namnområde). En viktig detalj: båda typerna av dessa objekt måste innehålla en etikett (etikett i Kubernetes terminologi) - det är de som politiker arbetar med.

Förutom ett ändligt antal väljare förenade av någon form av etikett, är det möjligt att skriva regler som "Tillåt/neka allt/alla" i olika varianter. För detta ändamål används konstruktioner av formuläret:

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

— i det här exemplet är alla poddar i miljön blockerade från inkommande trafik. Det motsatta beteendet kan uppnås med följande konstruktion:

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

Likaså för utgående:

  podSelector: {}
  policyTypes:
  - Egress

- för att stänga av den. Och här är vad som ska inkluderas:

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

För att återgå till valet av ett CNI-plugin för ett kluster är det värt att notera det inte alla nätverksplugin stöder NetworkPolicy. Till exempel, den redan nämnda Flannel vet inte hur man konfigurerar nätverkspolicyer, vilket det sägs direkt i det officiella arkivet. Där nämns också ett alternativ – ett Open Source-projekt Kalikå, vilket avsevärt utökar standarduppsättningen av Kubernetes API:er när det gäller nätverkspolicyer.

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Lär känna Calico: teori

Calico plugin kan användas i integration med Flannel (delprojekt Canal) eller oberoende, som täcker både nätverksanslutning och tillgänglighetshanteringsfunktioner.

Vilka möjligheter ger användningen av K8s "boxade" lösning och API-setet från Calico?

Här är vad som är inbyggt i NetworkPolicy:

  • politiker begränsas av miljön;
  • policyer tillämpas på kapslar märkta med etiketter;
  • regler kan tillämpas på pods, miljöer eller subnät;
  • regler kan innehålla protokoll, namngivna eller symboliska portspecifikationer.

Så här utökar Calico dessa funktioner:

  • policyer kan tillämpas på alla objekt: pod, container, virtuell maskin eller gränssnitt;
  • regler kan innehålla en specifik åtgärd (förbud, tillstånd, loggning);
  • målet eller källan till regler kan vara en port, en rad portar, protokoll, HTTP- eller ICMP-attribut, IP eller subnät (4:e eller 6:e ​​generationen), valfri väljare (noder, värdar, miljöer);
  • Dessutom kan du reglera passagen av trafik med hjälp av DNAT-inställningar och policyer för vidarebefordran av trafik.

De första commits på GitHub i Calico-förvaret går tillbaka till juli 2016, och ett år senare tog projektet en ledande position när det gäller att organisera Kubernetes nätverksanslutning - detta bevisas till exempel av undersökningsresultaten, genomförd av The New Stack:

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Många stora hanterade lösningar med K8:or, som t.ex Amazon EX, Azure AKS, Googla GKE och andra började rekommendera den för användning.

När det gäller prestanda är allt bra här. När de testade sin produkt visade Calicos utvecklingsteam astronomiska prestanda, och körde mer än 50000 500 behållare på 20 fysiska noder med en skapelsehastighet på XNUMX behållare per sekund. Inga problem identifierades med skalning. Sådana resultat tillkännagavs redan vid tillkännagivandet av den första versionen. Oberoende studier som fokuserar på genomströmning och resursförbrukning bekräftar också att Calicos prestanda är nästan lika bra som Flannels. Till exempel:

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Projektet utvecklas mycket snabbt, det stöder arbete i populära lösningar som hanteras K8s, OpenShift, OpenStack, det är möjligt att använda Calico när du distribuerar ett kluster med sparka, det finns referenser till konstruktionen av Service Mesh-nätverk (här är ett exempel används tillsammans med Istio).

Träna med Calico

I det allmänna fallet med att använda vanilla Kubernetes, kommer installationen av CNI till att använda filen calico.yaml, laddas ner från den officiella webbplatsen, genom att använda kubectl apply -f.

Som regel är den nuvarande versionen av plugin kompatibel med de senaste 2-3 versionerna av Kubernetes: drift i äldre versioner testas inte och garanteras inte. Enligt utvecklarna kör Calico på Linux-kärnor över 3.10 som kör CentOS 7, Ubuntu 16 eller Debian 8, ovanpå iptables eller IPVS.

Isolering inom miljön

För en allmän förståelse, låt oss titta på ett enkelt fall för att förstå hur nätverkspolicyer i Calico-notationen skiljer sig från standard och hur tillvägagångssättet för att skapa regler förenklar deras läsbarhet och konfigurationsflexibilitet:

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Det finns två webbapplikationer distribuerade i klustret: i Node.js och PHP, varav en använder Redis. För att blockera åtkomst till Redis från PHP, samtidigt som du upprätthåller anslutning till Node.js, tillämpa bara följande policy:

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

Vi tillät i huvudsak inkommande trafik till Redis-porten från Node.js. Och de förbjöd uppenbarligen inget annat. Så snart NetworkPolicy visas börjar alla väljare som nämns i den att isoleras, om inte annat anges. Isoleringsreglerna gäller dock inte andra objekt som inte omfattas av väljaren.

Exemplet använder apiVersion Kubernetes ur lådan, men ingenting hindrar dig från att använda den resurs med samma namn från Calico-leveransen. Syntaxen där är mer detaljerad, så du måste skriva om regeln för ovanstående fall i följande form:

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

De ovan nämnda konstruktionerna för att tillåta eller neka all trafik genom det vanliga NetworkPolicy API innehåller konstruktioner med parenteser som är svåra att förstå och komma ihåg. I fallet med Calico, för att ändra logiken i en brandväggsregel till det motsatta, ändra bara action: Allowaction: Deny.

Isolering av miljön

Föreställ dig nu en situation där en applikation genererar affärsmått för insamling i Prometheus och vidare analys med Grafana. Uppladdningen kan innehålla känslig data, som återigen är offentligt synlig som standard. Låt oss dölja denna information från nyfikna ögon:

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Prometheus placeras som regel i en separat tjänstemiljö - i exemplet kommer det att vara ett namnutrymme så här:

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

Fält metadata.labels detta visade sig inte vara någon slump. Som nämnts ovan, namespaceSelector (såväl som podSelector) fungerar med etiketter. Därför, för att tillåta att mätvärden tas från alla pods på en specifik port, måste du lägga till någon form av etikett (eller ta från befintliga) och sedan tillämpa en konfiguration som:

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

Och om du använder Calico-policyer blir syntaxen så här:

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

I allmänhet, genom att lägga till den här typen av policyer för specifika behov, kan du skydda mot skadlig eller oavsiktlig störning i driften av applikationer i klustret.

Den bästa praxisen, enligt skaparna av Calico, är metoden "Blockera allt och öppna explicit vad du behöver", dokumenterad i officiell dokumentation (andra följer ett liknande tillvägagångssätt - i synnerhet i redan nämnda artikel).

Använda ytterligare Calico-objekt

Låt mig påminna dig om att genom den utökade uppsättningen av Calico API:er kan du reglera tillgängligheten av noder, inte begränsat till pods. I följande exempel använder du GlobalNetworkPolicy möjligheten att skicka ICMP-förfrågningar i klustret är stängd (till exempel pingar från en pod till en nod, mellan pods eller från en nod till en 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

I ovanstående fall är det fortfarande möjligt för klusternoder att "nå ut" till varandra via ICMP. Och denna fråga löses med hjälp av medel GlobalNetworkPolicy, tillämpas på en enhet 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"]

VPN-fallet

Slutligen kommer jag att ge ett mycket verkligt exempel på att använda Calico-funktioner för fallet med nära-klusterinteraktion, när en standarduppsättning policyer inte räcker. För att komma åt webbapplikationen använder klienter en VPN-tunnel, och denna åtkomst är hårt kontrollerad och begränsad till en specifik lista över tjänster som är tillåtna för användning:

Calico för nätverk i Kubernetes: introduktion och lite erfarenhet

Klienter ansluter till VPN via standard UDP-port 1194 och, när de är anslutna, tar de emot rutter till klusterundernäten av pods och tjänster. Hela subnät pushas för att inte förlora tjänster under omstarter och adressändringar.

Porten i konfigurationen är standard, vilket lägger vissa nyanser på processen att konfigurera applikationen och överföra den till Kubernetes-klustret. Till exempel, i samma AWS dök LoadBalancer för UDP upp bokstavligen i slutet av förra året i en begränsad lista över regioner, och NodePort kan inte användas på grund av dess vidarebefordran på alla klusternoder och det är omöjligt att skala antalet serverinstanser för feltoleranta syften. Dessutom måste du ändra standardintervallet för portar...

Som ett resultat av att söka igenom möjliga lösningar valdes följande:

  1. Pods med VPN är schemalagda per nod in hostNetwork, det vill säga till den faktiska IP-adressen.
  2. Tjänsten postas utanför genom ClusterIP. En port är fysiskt installerad på noden, som är tillgänglig från utsidan med mindre reservationer (villkorad närvaro av en riktig IP-adress).
  3. Att bestämma noden på vilken podrosen ligger utanför ramen för vår berättelse. Jag säger bara att du kan "spika" tjänsten ordentligt till en nod eller skriva en liten sidovagnstjänst som kommer att övervaka den aktuella IP-adressen för VPN-tjänsten och redigera DNS-posterna registrerade hos klienter - vem som helst har tillräckligt med fantasi.

Ur ett routingperspektiv kan vi unikt identifiera en VPN-klient genom dess IP-adress utfärdad av VPN-servern. Nedan är ett primitivt exempel på att begränsa en sådan klients tillgång till tjänster, illustrerad på ovan nämnda 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]

Här är det strängt förbjudet att ansluta till port 6379, men samtidigt bevaras driften av DNS-tjänsten, vars funktion ganska ofta lider när man utarbetar regler. Eftersom, som tidigare nämnts, när en väljare visas, tillämpas den förinställda neka-policyn på den om inte annat anges.

Resultat av

Med hjälp av Calicos avancerade API kan du alltså flexibelt konfigurera och dynamiskt ändra routing i och runt klustret. I allmänhet kan dess användning se ut som att skjuta sparvar med en kanon, och att implementera ett L3-nätverk med BGP- och IP-IP-tunnlar ser monstruöst ut i en enkel Kubernetes-installation på ett platt nätverk... Men annars ser verktyget ganska lönsamt och användbart ut. .

Det är kanske inte alltid möjligt att isolera ett kluster för att uppfylla säkerhetskraven, och det är här Calico (eller en liknande lösning) kommer till undsättning. Exemplen som ges i denna artikel (med mindre ändringar) används i flera installationer av våra kunder i AWS.

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar