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

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

Formålet med artiklen er at introducere læseren til det grundlæggende i netværk og administration af netværkspolitikker i Kubernetes, samt tredjeparts Calico-plugin, der udvider standardmulighederne. Undervejs vil den nemme konfiguration og nogle funktioner blive demonstreret ved hjælp af rigtige eksempler fra vores driftserfaring.

En hurtig introduktion til Kubernetes netværksapparat

En Kubernetes-klynge kan ikke forestilles uden et netværk. Vi har allerede udgivet materialer om deres grundlæggende: "En illustreret guide til netværk i Kubernetes"Og"En introduktion til Kubernetes netværkspolitikker for sikkerhedsprofessionelle'.

I forbindelse med denne artikel er det vigtigt at bemærke, at K8s ikke selv er ansvarlig for netværksforbindelse mellem containere og noder: til dette kan div. CNI plugins (Container netværksgrænseflade). Mere om dette koncept vi de fortalte mig også.

For eksempel er den mest almindelige af disse plugins flannel — giver fuld netværksforbindelse mellem alle klynge noder ved at hæve broer på hver node, tildele et undernet til det. Fuldstændig og ureguleret tilgængelighed er dog ikke altid gavnlig. For at give en form for minimal isolation i klyngen er det nødvendigt at gribe ind i firewallens konfiguration. I det generelle tilfælde er det placeret under kontrol af den samme CNI, hvorfor enhver tredjeparts indgreb i iptables kan fortolkes forkert eller ignoreres helt.

Og "ud af boksen" til organisering af netværkspolitikstyring i en Kubernetes-klynge leveres NetworkPolicy API. Denne ressource, fordelt over udvalgte navneområder, kan indeholde regler for at differentiere adgang fra en applikation til en anden. Det giver dig også mulighed for at konfigurere tilgængelighed mellem specifikke pods, miljøer (navneområder) eller blokke 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 på officiel dokumentation kan én gang for alle afskrække ønsket om at forstå logikken i, hvordan netværkspolitikker fungerer. Vi vil dog stadig 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: ind i poden (Ingress) og udgående fra den (Egress).

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

Faktisk er politik opdelt i disse to kategorier baseret på bevægelsesretningen.

Den næste påkrævede attribut er en vælger; den, reglen gælder for. Dette kan være en pod (eller en gruppe af pods) eller et miljø (dvs. et navneområde). En vigtig detalje: begge typer af disse objekter skal indeholde en etiket (label i Kubernetes terminologi) - det er dem, politikerne opererer med.

Ud over et begrænset antal vælgere forenet af en slags etiket, er det muligt at skrive regler som "Tillad/nægt alt/alle" i forskellige variationer. Til dette formål bruges konstruktioner af formen:

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

— i dette eksempel er alle pods i miljøet blokeret fra indgående trafik. Den modsatte adfærd kan opnås med følgende konstruktion:

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

Tilsvarende for udgående:

  podSelector: {}
  policyTypes:
  - Egress

- for at slukke den. Og her er hvad du skal inkludere:

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

Vender vi tilbage til valget af et CNI-plugin til en klynge, er det værd at bemærke det ikke alle netværksplugins understøtter NetworkPolicy. For eksempel ved den allerede nævnte Flannel ikke, hvordan man konfigurerer netværkspolitikker, hvilket det siges direkte i det officielle depot. Der er også nævnt et alternativ - et Open Source-projekt Calico, som markant udvider standardsættet af Kubernetes API'er med hensyn til netværkspolitikker.

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

Lær Calico at kende: teori

Calico-plugin'et kan bruges i integration med Flannel (delprojekt Canal) eller uafhængigt, der dækker både netværksforbindelse og tilgængelighedsstyringsfunktioner.

Hvilke muligheder giver det at bruge K8s "boxed"-løsning og API-sættet fra Calico?

Her er, hvad der er indbygget i NetworkPolicy:

  • politikere er begrænset af miljøet;
  • politikker anvendes på pods markeret med etiketter;
  • regler kan anvendes på pods, miljøer eller undernet;
  • regler kan indeholde protokoller, navngivne eller symbolske portspecifikationer.

Her er hvordan Calico udvider disse funktioner:

  • politikker kan anvendes på ethvert objekt: pod, container, virtuel maskine eller grænseflade;
  • regler kan indeholde en specifik handling (forbud, tilladelse, logning);
  • målet eller kilden til regler kan være en port, en række porte, protokoller, HTTP- eller ICMP-attributter, IP eller subnet (4. eller 6. generation), alle vælgere (noder, værter, miljøer);
  • Derudover kan du regulere passage af trafik ved hjælp af DNAT-indstillinger og trafikvideresendelsespolitikker.

De første commits på GitHub i Calico-depotet går tilbage til juli 2016, og et år senere indtog projektet en førende position i organiseringen af ​​Kubernetes-netværksforbindelsen - det fremgår for eksempel af undersøgelsesresultaterne, dirigeret af The New Stack:

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

Mange store administrerede løsninger med K8'ere, som f.eks Amazon EX, Azure AKS, Google GKE og andre begyndte at anbefale det til brug.

Hvad angår ydeevne, er alt fantastisk her. Ved at teste deres produkt demonstrerede Calicos udviklingsteam astronomisk ydeevne, idet de kørte mere end 50000 containere på 500 fysiske noder med en oprettelseshastighed på 20 containere i sekundet. Der blev ikke identificeret problemer med skalering. Sådanne resultater blev annonceret allerede ved offentliggørelsen af ​​den første version. Uafhængige undersøgelser med fokus på gennemløb og ressourceforbrug bekræfter også, at Calicos ydeevne er næsten lige så god som Flannels. For eksempel:

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

Projektet udvikler sig meget hurtigt, det understøtter arbejde i populære løsninger administreret K8s, OpenShift, OpenStack, det er muligt at bruge Calico, når du implementerer en klynge vha. sparke, der er referencer til opbygningen af ​​Service Mesh-netværk (her er et eksempel bruges sammen med Istio).

Øv med Calico

I det generelle tilfælde med at bruge vanilla Kubernetes, kommer installation af CNI ned til at bruge filen calico.yaml, downloadet fra den officielle hjemmeside, ved hjælp af kubectl apply -f.

Som regel er den aktuelle version af plugin'et kompatibel med de seneste 2-3 versioner af Kubernetes: drift i ældre versioner er ikke testet og er ikke garanteret. Ifølge udviklerne kører Calico på Linux-kerner over 3.10, der kører CentOS 7, Ubuntu 16 eller Debian 8, oven på iptables eller IPVS.

Isolation i miljøet

For en generel forståelse, lad os se på en simpel case for at forstå, hvordan netværkspolitikker i Calico-notationen adskiller sig fra standard, og hvordan tilgangen til at skabe regler forenkler deres læsbarhed og konfigurationsfleksibilitet:

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

Der er 2 webapplikationer installeret i klyngen: i Node.js og PHP, hvoraf den ene bruger Redis. For at blokere adgangen til Redis fra PHP, mens du opretholder forbindelsen til Node.js, skal du blot anvende følgende politik:

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

Grundlæggende tillod vi indgående trafik til Redis-porten fra Node.js. Og de forbød tydeligvis ikke andet. Så snart NetworkPolicy vises, begynder alle vælgere nævnt i den at blive isoleret, medmindre andet er angivet. Isolationsreglerne gælder dog ikke for andre objekter, der ikke er omfattet af vælgeren.

Eksemplet bruger apiVersion Kubernetes ud af æsken, men intet forhindrer dig i at bruge det ressource af samme navn fra Calico-leveringen. Syntaksen der er mere detaljeret, så du bliver nødt til at omskrive reglen for ovenstående tilfælde i følgende 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 ovennævnte konstruktioner til at tillade eller nægte al trafik gennem den almindelige NetworkPolicy API indeholder konstruktioner med parenteser, som er svære at forstå og huske. I tilfælde af Calico, for at ændre logikken i en firewall-regel til det modsatte, skal du bare ændre action: Allowaction: Deny.

Isolation af miljø

Forestil dig nu en situation, hvor en applikation genererer forretningsmålinger til indsamling i Prometheus og yderligere analyse ved hjælp af Grafana. Uploaden kan indeholde følsomme data, som igen er offentligt synlige som standard. Lad os skjule disse data fra nysgerrige øjne:

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

Prometheus er som regel placeret i et separat servicemiljø - i eksemplet vil det være et navneområde som dette:

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

Field metadata.labels dette viste sig ikke at være nogen tilfældighed. Som nævnt ovenfor, namespaceSelector (såvel som podSelector) fungerer med etiketter. Derfor, for at tillade, at metrics kan tages fra alle pods på en specifik port, bliver du nødt til at tilføje en slags etiket (eller tage fra 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 hvis du bruger Calico-politikker, vil syntaksen være sådan:

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 du ved at tilføje denne slags politikker til specifikke behov beskytte mod ondsindet eller utilsigtet interferens i driften af ​​programmer i klyngen.

Den bedste praksis, ifølge skaberne af Calico, er tilgangen "Bloker alt, og åbn eksplicit, hvad du har brug for", dokumenteret i officiel dokumentation (andre følger en lignende tilgang - især i allerede nævnte artikel).

Brug af yderligere Calico-objekter

Lad mig minde dig om, at du gennem det udvidede sæt af Calico API'er kan regulere tilgængeligheden af ​​noder, ikke begrænset til pods. I det følgende eksempel ved hjælp af GlobalNetworkPolicy muligheden for at videregive ICMP-anmodninger i klyngen er lukket (f.eks. pings fra en pod til en node, mellem pods eller fra en node til 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 ovenstående tilfælde er det stadig muligt for klynge noder at "nå ud" til hinanden via ICMP. Og dette problem er løst ved hjælp af midler GlobalNetworkPolicy, anvendt på en enhed 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-sagen

Til sidst vil jeg give et meget reelt eksempel på brug af Calico-funktioner i tilfælde af nær-klynge-interaktion, når et standardsæt af politikker ikke er nok. For at få adgang til webapplikationen bruger klienter en VPN-tunnel, og denne adgang er stramt kontrolleret og begrænset til en specifik liste over tjenester, der er tilladt til brug:

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

Klienter opretter forbindelse til VPN via standard UDP-port 1194, og når de er tilsluttet, modtager de ruter til klyngeundernet af pods og tjenester. Hele undernet skubbes for ikke at miste tjenester under genstart og adresseændringer.

Porten i konfigurationen er standard, hvilket pålægger nogle nuancer i processen med at konfigurere applikationen og overføre den til Kubernetes-klyngen. For eksempel, i den samme AWS optrådte LoadBalancer for UDP bogstaveligt i slutningen af ​​sidste år i en begrænset liste over regioner, og NodePort kan ikke bruges på grund af dens videresendelse på alle klynge noder, og det er umuligt at skalere antallet af serverforekomster for fejltolerance formål. Plus, du bliver nødt til at ændre standardområdet for porte...

Som et resultat af at søge gennem mulige løsninger, blev følgende valgt:

  1. Pods med VPN er planlagt pr. node ind hostNetwork, altså til den faktiske IP.
  2. Tjenesten er postet udenfor igennem ClusterIP. En port er fysisk installeret på noden, som er tilgængelig udefra med mindre forbehold (betinget tilstedeværelse af en rigtig IP-adresse).
  3. At bestemme knudepunktet, hvorpå bælgrosen er uden for vores histories omfang. Jeg vil bare sige, at du stramt kan "nagle" tjenesten til en node eller skrive en lille sidevognstjeneste, der overvåger den aktuelle IP-adresse på VPN-tjenesten og redigerer DNS-registreringerne, der er registreret hos klienter - hvem der end har fantasi nok.

Fra et routingperspektiv kan vi entydigt identificere en VPN-klient ved dens IP-adresse udstedt af VPN-serveren. Nedenfor er et primitivt eksempel på begrænsning af en sådan klients adgang til tjenester, illustreret på ovennæ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 forbindelse til port 6379 strengt forbudt, men samtidig bevares driften af ​​DNS-tjenesten, hvis funktion ret ofte lider under udarbejdelse af regler. Fordi, som tidligere nævnt, når en vælger vises, anvendes standardafvisningspolitikken på den, medmindre andet er angivet.

Resultaterne af

Ved hjælp af Calicos avancerede API kan du således fleksibelt konfigurere og dynamisk ændre routing i og omkring klyngen. Generelt kan brugen af ​​det ligne at skyde gråspurve med en kanon, og implementering af et L3-netværk med BGP og IP-IP-tunneler ser monstrøs ud i en simpel Kubernetes-installation på et fladt netværk... Men ellers ser værktøjet ganske levedygtigt og brugbart ud. .

Det er ikke altid muligt at isolere en klynge for at opfylde sikkerhedskravene, og det er her, Calico (eller en lignende løsning) kommer til undsætning. Eksemplerne givet i denne artikel (med mindre ændringer) bruges i flere installationer af vores kunder i AWS.

PS

Læs også på vores blog:

Kilde: www.habr.com

Tilføj en kommentar