Calico til netværk i Kubernetes: introduktion og lidt erfaring

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Målet med artiklen er at introducere læseren til grundlæggende netværksinteraktion og styring af netværkspolitikker i Kubernetes, samt til det tredjepartsplugin Calico, der udvider de standardmæssige funktioner. Undervejs vil bekvemmeligheden ved konfiguration og nogle funktioner blive demonstreret med reelle eksempler fra vores erfaring.

Hurtig introduktion til Kubernetes-netværksenheder

Det er umuligt at forestille sig et Kubernetes-klynge uden netværk. Vi har allerede offentliggjort materialer om deres grundlæggende: "Illustreret guide til netværkets opbygning i Kubernetes" og "Introduktion til Kubernetes-netværkspolitikker for sikkerhedsspecialister».

I konteksten af denne artikel er det vigtigt at nævne, at netværksforbindelsen mellem containere og noder ikke håndteres af K8s selv: til dette anvendes forskellige CNI-plugins (Container Networking Interface). Vi har også beskrevet denne koncept tidligere.

For eksempel er den mest udbredte af sådanne plugins — Flannel — ansvarlig for at sikre fuld netværksforbindelse mellem alle noder i klyngen ved at oprette broer på hver node og tildele et subnet. Men fuld og ureguleret tilgængelighed er ikke altid nyttig. For at sikre en slags minimal isolation i klyngen er det nødvendigt at intervenere i konfigurationen af firewall. Generelt er det overladt til den nævnte CNI, hvilket kan betyde, at enhver ekstern indgriben i iptables kan blive fejltolket eller helt ignoreret.

Og "ud af boksen" til etablering af styringen af netværkspolitikker i Kubernetes-klyngen tilvejebringes NetworkPolicy API. Denne ressource, der gælder for valgte navnerum, kan indeholde regler for at begrænse adgangen mellem applikationer. Den tillader også at konfigurere tilgængelighed mellem specifikke pods, miljøer (navnerum) eller blokkeringer af 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

Dette er ikke det mest primitive eksempel den officielle dokumentation Det kan måske for en gangs skyld slå enhver lyst til at forstå logikken bag netværkspolitikker fra. Alligevel vil vi forsøge at forstå de grundlæggende principper og metoder til behandling af trafikstrømme ved hjælp af netværkspolitikker…

Det er logisk, at der er 2 typer trafik: indgående til pod (Ingress) og udgående fra det (Egress).

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Faktisk opdeles politikken i disse 2 kategorier afhængigt af bevægelsesretningen.

Den næste nødvendige attribut er vælgeren; den, som reglen gælder for. Dette kan være en pod (eller gruppe af pods) eller et miljø (dvs. et navnrum). En vigtig detalje: begge typer af disse objekter skal indeholde en label (label i Kubernetes-terminologi) — det er netop dem, som politikker arbejder med.

Ud over et begrænset antal vælgere, der forbindes med en bestemt label, er det også muligt at skrive regler som "Tillad/forbud alt/alle" i forskellige varianter. Dette gøres med konstruktioner som:

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

— i dette eksempel lukkes indgående trafik for alle pods i miljøet. Den modsatte adfærd kan opnås med en sådan konstruktion:

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

Det samme gælder for udgående:

  podSelector: {}
  policyTypes:
  - Egress

— for at deaktivere det. Og her er hvad der skal til for at aktivere det:

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

Når vi vender tilbage til valget af CNI-plugin til klyngen, er det vigtigt at bemærke, at ikke alle netværksplugins understøtter arbejde med NetworkPolicy. For eksempel kan den allerede nævnte Flannel ikke konfigurere netværkspolitikker, hvilket bliver sagt direkte i det officielle register. Her nævnes også en alternativ Open Source-projekt Calico, som væsentligt udvider den standard API-sæt i Kubernetes med hensyn til netværkspolitikker.

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Lad os lære Calico at kende: teori

Calico-pluginet kan bruges i integration med Flannel (undersprojektet Canal) eller uafhængigt, idet det dækker både funktionerne til at sikre netværksforbindelse og mulighederne for at styre tilgængeligheden.

Hvilke muligheder giver brugen af en "out-of-the-box" K8s-løsning og API-sættet fra Calico?

Her er hvad der er indbygget i NetworkPolicy:

  • politikker er begrænset til miljøet;
  • politikker anvendes på pods, der er mærket med labels;
  • regler kan anvendes på pods, miljøer eller undernetværk;
  • regler kan indeholde protokoller, navngivne eller symbolske angivelser af porte.

Og sådan udvider Calico disse funktioner:

  • Politikker kan anvendes på enhver enhed: pod, container, virtuel maskine eller interface;
  • Reglerne kan indeholde en specifik handling (forbud, tilladelse, logning);
  • Som mål eller kilde for reglerne kan der være en port, en port-range, protokoller, HTTP- eller ICMP-attributter, IP eller subnet (4. eller 6. generation), og enhver vælger (noder, værter, miljøer);
  • Derudover kan trafikgennemgang reguleres ved hjælp af DNAT-indstillinger og trafikafpresningspolitikker.

De første commits på GitHub i Calico-repositoriet stammer fra juli 2016, og allerede et år senere indtog projektet førende stillinger inden for netværkssammenkobling i Kubernetes, som det blandt andet fremgår af resultaterne af en undersøgelse, foretaget af The New Stack:

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Mange store managed-løsninger med K8s, såsom Amazon EKS, Azure AKS, Google GKE og andre, anbefaler nu det til brug.

Hvad angår ydeevne, så er det strålende. Under test af deres produkt demonstrerede Calicos udviklingsteam astronomiske resultater ved at køre mere end 50000 containere på 500 fysiske noder med en hastighed på 20 containere i sekundet. Der blev ikke registreret problemer ved skalering. Sådanne resultater blev nævnt allerede ved lanceringen af den første version. Uafhængige undersøgelser, der har fokus på båndbredde og ressourceforbrug, bekræfter også Calicos ydeevne, som næsten ikke er ringere end Flannels. For eksempel:

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Projektet udvikler sig meget hurtigt, understøtter arbejde i populære managed K8s-løsninger, OpenShift, OpenStack, med mulighed for at bruge Calico ved implementering af en klynge ved hjælp af kops, der er omtaler om opbygning af Service Mesh-netværk (her er et eksempel på brug sammen med Istio).

Praksis med Calico

I almindelighed ved brug af vanilje Kubernetes, installeres CNI ved at anvende filen calico.yaml, downloadet fra den officielle hjemmeside, ved hjælp af kubectl apply -f.

Som regel er den aktuelle version af pluginet kompatibel med de 2-3 seneste versioner af Kubernetes: der testes ikke for, at det fungerer i ældre versioner, og der gives ingen garantier for dette. Ifølge udviklerne fungerer Calico på en Linux-kerne over 3.10 under CentOS 7, Ubuntu 16 eller Debian 8, oven på iptables eller IPVS.

Isolation inden for miljøet

For a general understanding, let's consider a simple case to grasp how Calico's network policies differ from standard ones and how the approach to rule creation simplifies their readability and configuration flexibility:

Calico til netværk i Kubernetes: introduktion og lidt erfaring

In the cluster, there are 2 web applications deployed: one on Node.js and the other on PHP, with one using Redis. To restrict access to Redis from PHP while maintaining connectivity with Node.js, it is sufficient to apply the following 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

Essentially, we have allowed incoming traffic to the Redis port from Node.js, without explicitly denying anything else. Once a NetworkPolicy is introduced, all selectors mentioned within it begin to be isolated unless specified otherwise. Moreover, the isolation rules do not extend to other objects not covered by the selector.

The example uses apiVersion Kubernetes out of the box, but nothing prevents using the similarly named resource from Calico delivery. The syntax there is more elaborate, so we need to rewrite the rule for the scenario described above in the following 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

The aforementioned constructs for allowing or denying all traffic through a standard NetworkPolicy API contain complex structures with brackets that are difficult to perceive and memorize. In the case of Calico, to invert the firewall rule logic, it is sufficient to change action: Allow og få en fungerende kommando. action: Deny.

Isolation by environments

Now let's imagine a situation where an application generates business metrics for collection in Prometheus and subsequent analysis through Grafana. The export may contain sensitive data that, by default, is again available for public viewing. Let's close off this data from prying eyes:

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Prometheus is generally placed in a separate service environment — in this example, it will be a namespace of the following kind:

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

Felt metadata.labels was not a coincidence here. As previously mentioned, namespaceSelector (as well as podSelector) håndterer etiketter. Derfor, for at tillade at hente metrics fra alle pods på en bestemt port, skal der tilføjes en etiket (eller tages fra de eksisterende), og derefter anvende 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

Og i tilfælde af brug af Calico-politikker vil syntaksen være som følger:

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

Generelt kan man ved at tilføje sådanne policies til specifikke behov beskytte mod ondsindet eller utilsigtet indgriben i applikationernes drift i klyngen.

Den bedste praksis, ifølge skaberne af Calico, er tilgangen "Forbyd alt og åbne specifikt det nødvendige", som er dokumenteret i den officielle dokumentation (den tilsvarende tilgang følges også af andre - specifikt i denne nævnte artikel).

Anvendelsen af yderligere Calico-objekter

Jeg vil minde om, at man gennem det udvidede API-sæt i Calico kan regulere tilgængeligheden af noder, uden at begrænse sig til pods. I følgende eksempel ved brug af GlobalNetworkPolicy blokeres ICMP-anmodninger i klyngen (for eksempel pinger fra en pod til en node, mellem pods eller fra en node til en pod's 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

I det ovenstående tilfælde forbliver muligheden for klyngens noder at "kommunikere" med hinanden via ICMP. Dette problem løses med GlobalNetworkPolicy, anvendt på enheden 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"]

Sagen med VPN

Endelig vil jeg give et helt reelt eksempel på brugen af Calicos funktioner i forbindelse med interaction omkring klyngen, hvor den standardpolitikker ikke er tilstrækkelige. For at give kunderne adgang til webapplikationen anvendes en VPN-tunnel, og denne adgang er strengt kontrolleret og begrænset til en specifik liste over tilladte brugte services:

Calico til netværk i Kubernetes: introduktion og lidt erfaring

Kunder opretter forbindelse til VPN via standard UDP-port 1194 og modtager ruter til klyngens subnetter af pods og tjenester ved tilslutning. Subnetterne pushes hele vejen, så tjenester ikke går tabt ved genstart og ændring af adresser.

Porten i konfigurationen er standard, hvilket medfører nogle nuancer ved konfigurationen af applikationen og dens flytning til Kubernetes-klyngen. For eksempel blev UDP-porten i AWS LoadBalancer bogstaveligt talt tilføjet i slutningen af sidste år i en begrænset liste over regioner, og NodePort kan ikke anvendes på grund af den måde, den bliver sendt videre til alle noder i klyngen, hvilket gør det umuligt at skalere antallet af serverinstanser for at opnå høj tilgængelighed. Derudover skal det valgte portinterval ændres, da det som standard vælges...

Som resultat af gennemgangen af mulige løsninger blev det følgende valgt:

  1. Pods med VPN planlægges på noden i tilstand hostNetwork, hvilket betyder på den faktiske IP.
  2. Tjenesten offentliggøres udadtil gennem ClusterIP. På noden aktiveres en port fysisk, som er tilgængelig udefra med nogle få forbehold (betydning af en reel IP-adresse).
  3. Definitionen af den node, hvor pod'en blev aktiveret, ligger uden for vores fortælling. Jeg vil kun sige, at man kan 'fastlåse' tjenesten til en node eller skrive en lille sidecar-tjeneste, der overvåger den aktuelle IP-adresse for VPN-tjenesten og opdaterer DNS-posterne, der er registreret hos kunderne — alt efter hvad man kan finde på.

Set fra et routing-perspektiv kan vi entydigt identificere klienten over VPN ud fra dens IP-adresse, som udleveres af VPN-serveren. Nedenfor er et primitivt eksempel på at begrænse adgangen for en sådan klient til tjenester, illustreret med den førnævnte 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]

Her er det strengt forbudt at oprette forbindelse til port 6379, men samtidig er tjenesten DNS bevaret, hvis funktionalitet ofte lider under udarbejdelse af reglerne. For som tidligere nævnt, når en vælger anvendes, gælder der som standard en forbudspolitik, medmindre andet er angivet.

Resumé

På denne måde kan Calico's udvidede API fleksibelt konfigurere og dynamisk ændre routing i clustret og omkring det. Generelt kan brugen af det virke som at bruge en kanon til at ramme duer, og implementeringen af et L3-netværk med BGP- og IP-IP-tunneler virker monstrøs i en simpel Kubernetes-installation i et fladt netværk… Men ellers virker værktøjet ganske levedygtigt og nyttigt.

Isoleringen af clustret for at opfylde sikkerhedskrav kan ikke altid implementeres, og netop i sådanne tilfælde kommer Calico (eller en lignende løsning) til hjælp. Eksemplerne i artiklen (med lidt tilpasning) anvendes i flere af vores kunders installationer i AWS.

P.S.

Læs også på vores blog:

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster