Calico pentru networking în Kubernetes: introducere și puțină experiență

Calico pentru networking în Kubernetes: introducere și puțină experiență

Scopul articolului este de a prezenta cititorului elementele de bază ale rețelei și gestionării politicilor de rețea în Kubernetes, precum și pluginul Calico terță parte care extinde capabilitățile standard. Pe parcurs, ușurința de configurare și unele caracteristici vor fi demonstrate folosind exemple reale din experiența noastră de operare.

O introducere rapidă în dispozitivul de rețea Kubernetes

Un cluster Kubernetes nu poate fi imaginat fără o rețea. Am publicat deja materiale despre bazele lor: „Un ghid ilustrat pentru crearea de rețele în Kubernetes"Și"O introducere în politicile de rețea Kubernetes pentru profesioniștii în securitate".

În contextul acestui articol, este important să rețineți că K8s în sine nu este responsabil pentru conexiunea la rețea între containere și noduri: pentru aceasta, diverse Pluginuri CNI (Interfață de rețea container). Mai multe despre acest concept noi mi-au mai spus.

De exemplu, cel mai comun dintre aceste plugin-uri este Flanel — oferă conectivitate completă la rețea între toate nodurile de cluster prin ridicarea punților pe fiecare nod, atribuindu-i o subrețea. Cu toate acestea, accesibilitatea completă și nereglementată nu este întotdeauna benefică. Pentru a asigura un fel de izolare minimă în cluster, este necesar să se intervină în configurarea firewall-ului. În cazul general, acesta este plasat sub controlul aceluiași CNI, motiv pentru care orice intervenții ale terților în iptables pot fi interpretate incorect sau ignorate cu totul.

Și este oferit „din cutie” pentru organizarea managementului politicii de rețea într-un cluster Kubernetes NetworkPolicy API. Această resursă, distribuită pe spații de nume selectate, poate conține reguli pentru a diferenția accesul de la o aplicație la alta. De asemenea, vă permite să configurați accesibilitatea între anumite poduri, medii (spații de nume) sau blocuri de adrese IP:

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

Acesta nu este cel mai primitiv exemplu de documentație oficială poate descuraja odată pentru totdeauna dorința de a înțelege logica modului în care funcționează politicile de rețea. Cu toate acestea, vom încerca în continuare să înțelegem principiile și metodele de bază de procesare a fluxurilor de trafic folosind politicile de rețea...

Este logic că există 2 tipuri de trafic: intrarea în pod (Ingress) și ieșirea din acesta (Egress).

Calico pentru networking în Kubernetes: introducere și puțină experiență

De fapt, politica este împărțită în aceste 2 categorii în funcție de direcția de mișcare.

Următorul atribut necesar este un selector; cel căruia i se aplică regula. Acesta poate fi un pod (sau un grup de pod-uri) sau un mediu (adică un spațiu de nume). Un detaliu important: ambele tipuri de aceste obiecte trebuie să conțină o etichetă (etichetă în terminologia Kubernetes) - acestea sunt cele cu care operează politicienii.

Pe lângă un număr finit de selectori, uniți printr-un fel de etichetă, este posibil să scrieți reguli precum „Permite/interzice totul/toată lumea” în diferite variante. În acest scop, se folosesc construcții de formă:

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

— în acest exemplu, toate podurile din mediu sunt blocate de la traficul de intrare. Comportamentul opus poate fi obținut cu următoarea construcție:

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

La fel pentru ieșiri:

  podSelector: {}
  policyTypes:
  - Egress

- pentru a-l opri. Și iată ce să includă:

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

Revenind la alegerea unui plugin CNI pentru un cluster, merită remarcat faptul că nu orice plugin de rețea acceptă NetworkPolicy. De exemplu, Flannelul deja menționat nu știe cum să configureze politicile de rețea, care se spune direct în depozitul oficial. Acolo este menționată și o alternativă - un proiect Open Source Stambă, care extinde semnificativ setul standard de API-uri Kubernetes în ceea ce privește politicile de rețea.

Calico pentru networking în Kubernetes: introducere și puțină experiență

Cunoașterea lui Calico: teorie

Pluginul Calico poate fi utilizat în integrare cu Flannel (subproiect Canal) sau independent, acoperind atât conectivitatea la rețea, cât și capabilitățile de gestionare a disponibilității.

Ce oportunități oferă utilizarea soluției „în cutie” K8s și a setului API de la Calico?

Iată ce este încorporat în NetworkPolicy:

  • politicienii sunt limitați de mediu;
  • politicile sunt aplicate podurilor marcate cu etichete;
  • regulile pot fi aplicate podurilor, mediilor sau subrețelelor;
  • regulile pot conține protocoale, specificații de port denumite sau simbolice.

Iată cum Calico extinde aceste funcții:

  • politicile pot fi aplicate oricărui obiect: pod, container, mașină virtuală sau interfață;
  • regulile pot conține o acțiune specifică (interzicere, permisiune, înregistrare);
  • ținta sau sursa regulilor poate fi un port, o serie de porturi, protocoale, atribute HTTP sau ICMP, IP sau subrețea (generația a 4-a sau a 6-a), orice selectoare (noduri, gazde, medii);
  • În plus, puteți regla trecerea traficului folosind setările DNAT și politicile de redirecționare a traficului.

Primele angajări pe GitHub din depozitul Calico datează din iulie 2016, iar un an mai târziu, proiectul a luat o poziție de lider în organizarea conectivității rețelei Kubernetes - acest lucru este evidențiat, de exemplu, de rezultatele sondajului, condus de The New Stack:

Calico pentru networking în Kubernetes: introducere și puțină experiență

Multe soluții mari gestionate cu K8, cum ar fi Amazon EKS, Azure AKS, Google GKE iar alții au început să-l recomande pentru utilizare.

În ceea ce privește performanța, totul este grozav aici. În testarea produsului lor, echipa de dezvoltare Calico a demonstrat performanță astronomică, rulând peste 50000 de containere pe 500 de noduri fizice, cu o rată de creare de 20 de containere pe secundă. Nu au fost identificate probleme cu scalarea. Asemenea rezultate au fost anunțate deja la anunțul primei versiuni. Studiile independente care se concentrează pe randamentul și consumul de resurse confirmă, de asemenea, performanța Calico este aproape la fel de bună ca cea a lui Flannel. De exemplu:

Calico pentru networking în Kubernetes: introducere și puțină experiență

Proiectul se dezvoltă foarte repede, acceptă lucrul în soluții populare gestionate K8s, OpenShift, OpenStack, este posibil să utilizați Calico atunci când implementați un cluster folosind lovitură, există referiri la construcția rețelelor Service Mesh (Iată un exemplu folosit împreună cu Istio).

Practică cu Calico

În cazul general al utilizării Vanilla Kubernetes, instalarea CNI se reduce la utilizarea fișierului calico.yaml, descărcat de pe site-ul oficial, prin utilizarea kubectl apply -f.

De regulă, versiunea actuală a pluginului este compatibilă cu cele mai recente 2-3 versiuni de Kubernetes: funcționarea în versiunile mai vechi nu este testată și nu este garantată. Potrivit dezvoltatorilor, Calico rulează pe kernel-uri Linux peste 3.10 care rulează CentOS 7, Ubuntu 16 sau Debian 8, pe lângă iptables sau IPVS.

Izolarea în mediul înconjurător

Pentru o înțelegere generală, să ne uităm la un caz simplu pentru a înțelege modul în care politicile de rețea din notația Calico diferă de cele standard și cum abordarea creării de reguli simplifică lizibilitatea și flexibilitatea de configurare a acestora:

Calico pentru networking în Kubernetes: introducere și puțină experiență

Există 2 aplicații web implementate în cluster: în Node.js și PHP, dintre care una utilizează Redis. Pentru a bloca accesul la Redis din PHP, menținând în același timp conectivitatea cu Node.js, trebuie doar să aplicați următoarea politică:

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

În esență, am permis traficul de intrare către portul Redis de la Node.js. Și în mod clar nu au interzis nimic altceva. De îndată ce apare NetworkPolicy, toți selectorii menționați în ea încep să fie izolați, dacă nu se specifică altfel. Cu toate acestea, regulile de izolare nu se aplică altor obiecte care nu sunt acoperite de selector.

Exemplul folosește apiVersion Kubernetes din cutie, dar nimic nu te împiedică să-l folosești resursă cu același nume din livrarea Calico. Sintaxa de acolo este mai detaliată, așa că va trebui să rescrieți regula pentru cazul de mai sus în următoarea 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

Construcțiile menționate mai sus pentru a permite sau a refuza tot traficul prin API-ul NetworkPolicy obișnuit conțin constructe cu paranteze care sunt greu de înțeles și de reținut. În cazul lui Calico, pentru a schimba logica unei reguli de firewall la opus, trebuie doar să schimbați action: Allow pe action: Deny.

Izolarea prin mediu

Acum imaginați-vă o situație în care o aplicație generează valori de afaceri pentru colectare în Prometheus și analize ulterioare folosind Grafana. Încărcarea poate conține date sensibile, care sunt din nou vizibile public în mod prestabilit. Să ascundem aceste date de privirile indiscrete:

Calico pentru networking în Kubernetes: introducere și puțină experiență

Prometheus, de regulă, este plasat într-un mediu de serviciu separat - în exemplu va fi un spațiu de nume ca acesta:

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

Câmp metadata.labels acest lucru s-a dovedit a fi un accident. Așa cum sa menționat mai sus, namespaceSelector (precum și podSelector) operează cu etichete. Prin urmare, pentru a permite ca valorile să fie preluate din toate podurile de pe un anumit port, va trebui să adăugați un fel de etichetă (sau să luați de la cele existente), apoi să aplicați o configurație precum:

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 dacă utilizați politicile Calico, sintaxa va fi astfel:

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

În general, prin adăugarea acestor tipuri de politici pentru nevoi specifice, vă puteți proteja împotriva interferențelor rău intenționate sau accidentale în funcționarea aplicațiilor din cluster.

Cea mai bună practică, conform creatorilor Calico, este abordarea „Blocați totul și deschideți în mod explicit ceea ce aveți nevoie”, documentată în documentație oficială (alții urmează o abordare similară - în special, în articolul deja mentionat).

Utilizarea obiectelor Calico suplimentare

Permiteți-mi să vă reamintesc că prin setul extins de API-uri Calico puteți regla disponibilitatea nodurilor, fără a se limita la pod-uri. În exemplul următor folosind GlobalNetworkPolicy capacitatea de a transmite solicitări ICMP în cluster este închisă (de exemplu, ping-uri de la un pod la un nod, între poduri sau de la un nod la 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

În cazul de mai sus, este încă posibil ca nodurile de cluster să se „atingă” între ele prin ICMP. Și această problemă este rezolvată prin mijloace GlobalNetworkPolicy, aplicat unei entități 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"]

Cazul VPN

În cele din urmă, voi da un exemplu foarte real de utilizare a funcțiilor Calico pentru cazul interacțiunii aproape de cluster, când un set standard de politici nu este suficient. Pentru a accesa aplicația web, clienții folosesc un tunel VPN, iar acest acces este strict controlat și limitat la o listă specifică de servicii permise pentru utilizare:

Calico pentru networking în Kubernetes: introducere și puțină experiență

Clienții se conectează la VPN prin portul UDP standard 1194 și, atunci când sunt conectați, primesc rute către subrețelele de cluster de pod-uri și servicii. Subrețele întregi sunt împinse pentru a nu pierde serviciile în timpul repornirilor și schimbărilor de adrese.

Portul din configurație este standard, ceea ce impune unele nuanțe procesului de configurare a aplicației și transferarea acesteia în cluster-ul Kubernetes. De exemplu, în același AWS LoadBalancer pentru UDP a apărut literalmente la sfârșitul anului trecut într-o listă limitată de regiuni, iar NodePort nu poate fi utilizat din cauza redirecționării sale pe toate nodurile de cluster și este imposibil să scalați numărul de instanțe de server pentru scopuri de toleranță la erori. În plus, va trebui să modificați intervalul implicit de porturi...

În urma căutării prin posibile soluții s-au ales următoarele:

  1. Pod-urile cu VPN sunt programate pentru fiecare nod în hostNetwork, adică la IP-ul propriu-zis.
  2. Serviciul este postat afară prin ClusterIP. Un port este instalat fizic pe nod, care este accesibil din exterior cu rezerve minore (prezența condiționată a unei adrese IP reale).
  3. Determinarea nodului pe care a crescut păstaia este dincolo de scopul poveștii noastre. Voi spune doar că puteți „strânge” serviciul într-un nod sau puteți scrie un mic serviciu sidecar care va monitoriza adresa IP actuală a serviciului VPN și va edita înregistrările DNS înregistrate la clienți - oricine are suficientă imaginație.

Din perspectiva rutare, putem identifica unic un client VPN prin adresa sa IP emisă de serverul VPN. Mai jos este un exemplu primitiv de restricționare a accesului unui astfel de client la servicii, ilustrat în Redis-ul menționat mai sus:

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]

Aici, conectarea la portul 6379 este strict interzisă, dar în același timp se păstrează funcționarea serviciului DNS, a cărui funcționare suferă destul de des la elaborarea regulilor. Deoarece, după cum sa menționat anterior, atunci când apare un selector, i se aplică politica implicită de refuzare, dacă nu se specifică altfel.

Rezultatele

Astfel, folosind API-ul avansat Calico, puteți configura și schimba dinamic rutarea în și în jurul clusterului. În general, utilizarea sa poate arăta ca împușcarea vrăbiilor cu un tun, iar implementarea unei rețele L3 cu tuneluri BGP și IP-IP arată monstruoasă într-o instalare simplă Kubernetes pe o rețea plată... Cu toate acestea, altfel instrumentul pare destul de viabil și util .

Izolarea unui cluster pentru a îndeplini cerințele de securitate poate să nu fie întotdeauna fezabilă și aici este locul în care Calico (sau o soluție similară) vine în ajutor. Exemplele date în acest articol (cu modificări minore) sunt folosite în mai multe instalări ale clienților noștri în AWS.

PS

Citește și pe blogul nostru:

Sursa: www.habr.com

Adauga un comentariu