Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Мэта артыкула – пазнаёміць чытача з асновамі сеткавага ўзаемадзеяння і кіраваннем сеткавымі палітыкамі ў Kubernetes, а таксама са іншым плагінам Calico, які пашырае стандартныя магчымасці. Адначасна будуць прадэманстраваны зручнасць канфігурацыі і некаторыя фічы на ​​рэальных прыкладах з вопыту нашай эксплуатацыі.

Хуткае ўвядзенне ў сеткавую прыладу Kubernetes

Кластар Kubernetes немагчыма ўявіць без сеткі. Мы ўжо публікавалі матэрыялы па іх асновах: «Ілюстраваны кіраўніцтва па прыладзе сеткі ў Kubernetes»І«Увядзенне ў сеткавыя палітыкі Kubernetes для спецыялістаў па бяспецы.

У кантэксце гэтага артыкула важна адзначыць, што за сеткавую складнасць паміж кантэйнерамі і вузламі адказвае не сам K8s: для гэтага выкарыстоўваюцца разнастайныя убудовы CNI (Container Networking Interface). Больш падрабязна аб гэтай канцэпцыі мы таксама расказвалі.

Напрыклад, найбольш распаўсюджаны з такіх плагінаў. фланель - забяспечвае поўную сеткавую складнасць паміж усімі вузламі кластара з дапамогай узняцця мастоў на кожным вузле, замацоўваючы за ім падсетку. Аднак поўная і нерэгулюемая даступнасць не заўсёды карысная. Каб забяспечыць нейкую мінімальную ізаляцыю ў кластары, неабходна ўмяшацца ў канфігураванне firewall'а. Увогуле выпадку яно аддадзена ва ўпраўленне таго самага CNI, з-за чаго любыя іншыя ўмяшанні ў iptables могуць быць інтэрпрэтаваныя некарэктна ці ігнаравацца зусім.

А "са скрынкі" для арганізацыі кіравання сеткавымі палітыкамі ў кластары Kubernetes прадастаўляецца NetworkPolicy API. Гэты рэсурс, які распаўсюджваецца на абраныя прасторы імёнаў, можа ўтрымоўваць правілы для размежавання доступу ад адных прыкладанняў да іншых. Ён таксама дазваляе наладжваць даступнасць паміж пэўнымі pod'амі, асяроддзямі (прасторамі імёнаў) або блокамі 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

Гэты не самы прымітыўны прыклад з афіцыйнай дакументацыі можа раз і назаўжды адбіць жаданне разбірацца ў логіцы працы сеткавых палітык. Аднак мы ўсё ж паспрабуем зразумець асноўныя прынцыпы і метады апрацоўкі плыняў трафіку з дапамогай сеткавых палітык…

Лагічна, што ёсць 2 тыпу трафіку: які ўваходзіць у pod (Ingress) і выходны з яго (Egress).

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Уласна, на гэтыя дзве катэгорыі па кірунку руху і падзяляецца палітыка.

Наступны абавязковы атрыбут - селектар; той, да каго прымяняецца правіла. Гэта можа быць pod (ці група pod'аў) ці асяроддзе (г.зн. прастора імёнаў). Важная дэталь: абодва віды гэтых аб'ектаў павінны ўтрымліваць пазнаку (этыкетка у тэрміналогіі Kubernetes) - менавіта імі аперуюць палітыкі.

Апроч канчатковага ліку селектараў, аб'яднаных нейкай пазнакай, існуе магчымасць напісання правіл накшталт "Дазволіць/забараніць усё/усім" у розных варыяцыях. Для гэтага выкарыстоўваюцца канструкцыі выгляду:

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

- у гэтым прыкладзе ўсім pod'ам асяроддзя зачыняецца ўваходны трафік. Процілеглых паводзін можна дамагчыся такой канструкцыяй:

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

Аналагічна для выходнага:

  podSelector: {}
  policyTypes:
  - Egress

- для яго адключэння. І вось што для ўключэння:

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

Вяртаючыся да выбару CNI-плагіна для кластара, варта адзначыць, што не кожны сеткавы плягін падтрымлівае працу з NetworkPolicy. Напрыклад, ужо згаданы Flannel не ўмее канфігураваць сеткавыя палітыкі, аб чым прама сказана у афіцыйным рэпазітары. Там жа згаданая альтэрнатыва – Open Source-праект каленкор, які прыкметна пашырае стандартны набор API Kubernetes у плане сеткавых палітык.

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Знаёмімся з Calico: тэорыя

Убудова Calico можа выкарыстоўвацца ў інтэграцыі з Flannel (падпраект Канал) або самастойна, пакрываючы як функцыі па забеспячэнні сеткавай складнасці, так і магчымасці кіравання даступнасцю.

Якія магчымасці дае выкарыстанне "скрынкавага" рашэнні K8s і набору API з Calico?

Вось што ўбудавана ў NetworkPolicy:

  • палітыкі абмежаваны асяроддзем;
  • палітыкі прымяняюцца да pod'ах, пазначаных лэйбламі;
  • правілы могуць быць ужытыя да pod'аў, асяроддзем або падсеткам;
  • правілы могуць змяшчаць пратаколы, названыя або сімвалальныя ўказанні партоў.

А вось як Calico пашырае гэтыя функцыі:

  • палітыкі могуць прымяняцца да любога аб'екта: pod, кантэйнер, віртуальная машына або інтэрфейс;
  • правілы могуць змяшчаць канкрэтнае дзеянне (забарона, дазвол, лагіраванне);
  • у якасці мэты або крыніцы правілаў можа быць порт, дыяпазон партоў, пратаколы, HTTP- або ICMP-атрыбуты, IP або падсетку (4 ці 6 пакаленні), любыя селектары (вузлоў, хастоў, асяроддзяў);
  • дадаткова можна рэгуляваць праходжанне трафіку з дапамогай налад DNAT і палітык пракіду трафіку.

Першыя коміты на GitHub у рэпазітары Сalico датуюцца ліпенем 2016 года, а ўжо праз год праект заняў лідзіруючыя пазіцыі ў арганізацыі сеткавай складнасці Kubernetes – пра гэта абвяшчаюць, напрыклад, вынікі апытання, праведзенага The New Stack:

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Многія буйныя managed-рашэнні з K8s, такія як Amazon EKS, Azure AKS, Google GKE і іншыя, сталі рэкамендаваць яго да выкарыстання.

Што да прадукцыйнасці, тут усё выдатна. Пры тэставанні свайго прадукта каманда распрацоўкі Calico прадэманстравала астранамічныя паказчыкі, запусціўшы больш за 50000 кантэйнераў на 500 фізічных вузлах з хуткасцю стварэння 20 кантэйнераў у секунду. Праблем пры маштабаванні не выяўлена. Такія вынікі былі агучаныя ужо пры анонсе першай версіі. Незалежныя даследаванні, накіраваныя на прапускную здольнасць і аб'ёмы спажывання рэсурсаў, таксама пацвярджаюць прадукцыйнасць Calico, практычна не якая саступае Flannel. Напрыклад:

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Праект вельмі хутка развіваецца, падтрымліваецца праца ў папулярных рашэннях managed K8s, OpenShift, OpenStack, маецца магчымасць выкарыстоўваць Calico пры разгортванні кластара з дапамогай удар нагамі, сустракаюцца згадкі пабудовы Service Mesh-сетак (вось прыклад выкарыстання сумесна з Istio).

Практыка з Calico

У агульным выпадку выкарыстання ванільнага Kubernetes усталёўка CNI зводзіцца да ўжывання файла calico.yaml, запампаванага з афіцыйнага сайта, з дапамогай kubectl apply -f.

Як правіла, актуальная версія плагіна сумяшчальная з 2-3 апошнімі версіямі Kubernetes: працу ў больш старых версіях не тэсціруюць і не гарантуюць. Па заявах распрацоўнікаў, Calico працуе на ядры Linux вышэй 3.10 пад кіраваннем CentOS 7, Ubuntu 16 ці Debian 8, па-над iptables ці IPVS.

Ізаляцыя ўнутры акружэння

Для агульнага разумення разгледзім просты выпадак, каб зразумець, чым адрозніваюцца сеткавыя палітыкі ў натацыі Calico ад стандартных і як падыход да складання правіл спрашчае іх чытальнасць і гнуткасць канфігуравання:

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

У кластары разгорнутыя 2 вэб-прыкладанні: на Node.js і PHP, - адно з якіх выкарыстоўвае Redis. Каб закрыць доступ да Redis з PHP, пакінуўшы пры гэтым складнасць з Node.js, дастаткова прымяніць наступную палітыку:

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

Па сутнасці мы дазволілі ўваходзіць трафік на порт Redis з Node.js. І відавочна не забаранялі нічога іншага. Як толькі з'яўляецца NetworkPolicy, то ўсе селектары, згаданыя ў ім, пачынаюць ізалявацца, калі не пазначана іншае. Пры гэтым правілы ізаляцыі не распаўсюджваюцца на іншыя аб'екты, якія не пакрываюцца селектарам.

У прыкладзе выкарыстоўваецца apiVersion Kubernetes'а "са скрынкі", але нішто не мяшае выкарыстоўваць аднайменны рэсурс з пастаўкі Calico. Сінтаксіс там больш разгорнуты, таму спатрэбіцца перапісаць правіла для вышэйапісанага выпадку ў наступным выглядзе:

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

Згаданыя вышэй канструкцыі для дазволу ці забароны ўсяго трафіку пасродкам звычайнага NetworkPolicy API утрымоўваюць складаныя для ўспрымання і запамінання канструкцыі са дужкамі. У выпадку з Calico, каб змяніць логіку працы правілы firewall'а на процілеглую, дастаткова змяніць action: Allow на action: Deny.

Ізаляцыя па асяродках

Цяпер прадставім сітуацыю, калі прыкладанне генеруе бізнес-метрыкі для іх збору ў Prometheus і далейшага аналізу з дапамогай Grafana. У выгрузцы могуць змяшчацца адчувальныя дадзеныя, якія па змаўчанні зноў жа даступныя для ўсеагульнага агляду. Закрыем ад старонніх вачэй гэтыя дадзеныя:

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Prometheus, як правіла, вынесены ў асобнае службовае асяроддзе - у прыкладзе гэта будзе namespace наступнага выгляду:

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

Поле metadata.labels тут аказалася не выпадкова. Як вышэй ужо згадвалася, namespaceSelector (як і podSelector) аперуе лэйбламі. Таму, каб дазволіць забіраць метрыкі са ўсіх pod'аў на вызначаным порце, прыйдзецца дадаць якую-небудзь пазнаку (ці ўзяць з існых), а затым ужыць канфігурацыю накшталт:

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

А ў выпадку выкарыстання палітык Calico сінтаксіс будзе такім:

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

У цэлым, дадаючы падобнага роду палітыкі пад канкрэтныя патрэбы, можна ахаваць ад зламыснага або выпадковага ўмяшання ў працу прыкладанняў у кластары.

Лепшай практыкай, па меркаванні стваральнікаў Calico, з'яўляецца падыход "Забарані ўсё і відавочна адчыняй неабходнае", зафіксаваны ў афіцыйнай дакументацыі (аналагічнага падыходу прытрымліваюцца і іншыя - у прыватнасці, у ужо згаданым артыкуле).

Ужыванне дадатковых аб'ектаў Calico

Нагадаю, што пасродкам пашыранага набору API Calico можна рэгуляваць даступнасць вузлоў, не абмяжоўваючыся pod'амі. У наступным прыкладзе з дапамогай GlobalNetworkPolicy зачыняецца магчымасць праходжання ICMP-запытаў у кластары (напрыклад, пінгі з pod'а на вузел, паміж pod'мі ці з вузла на 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

У прыведзеным вышэй кейсе застаецца магчымасць вузлам кластара "дастукацца" паміж сабой па ICMP. І гэтае пытанне вырашаецца сродкамі GlobalNetworkPolicy, ужытай да сутнасці 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

Нарэшце, прывяду суцэль рэальны прыклад выкарыстання функцый Calico для выпадку з околокластерным узаемадзеяннем, калі стандартнага набору палітык бракуе. Для доступу да вэб-дадатку кліентамі выкарыстоўваецца VPN-тунэль, і гэты доступ жорстка кантралюем і абмежаваны канкрэтным спісам дазволеных да выкарыстання сэрвісаў:

Calico для сеткі ў Kubernetes: знаёмства і крыху з вопыту

Кліенты падключаюцца да VPN праз стандартны UDP-порт 1194 і пры падключэнні атрымліваюць маршруты да кластарных падсетак pod'аў і сэрвісаў. Падсеткі push'ацца цалкам, каб не губляць сэрвісы пры перазапусках і змене адрасоў.

Порт у канфігурацыі - стандартны, што накладвае некаторыя нюансы на працэс канфігуравання прыкладання і яго перанос у Kubernetes-кластар. Напрыклад, у тым жа AWS LoadBalancer для UDP з'явіўся літаральна ў канцы мінулага года ў абмежаваным спісе рэгіёнаў, а NodePort нельга выкарыстоўваць з-за яго пракіду на ўсіх вузлах кластара і немагчыма маштабаваць колькасць інстансаў сервера ў мэтах адмоваўстойлівасці. Плюс, прыйдзецца мяняць дыяпазон партоў, які выбіраецца па змаўчанні…

У выніку перабору магчымых рашэнняў было абрана наступнае:

  1. Pod'ы з VPN плануюцца на вузел у рэжыме hostNetwork, гэта значыць на фактычны IP.
  2. Сэрвіс вывешваецца вонкі праз ClusterIP. На вузле фізічна паднімаецца порт, які даступны звонку з невялікімі агаворкамі (умоўнае наяўнасць рэальнага IP-адрасы).
  3. Вызначэнне вузла, на якім падняўся pod, ляжыць за межамі нашага апавядання. Скажу толькі, што можна цвёрда «прыбіць» сэрвіс да вузла ці ж напісаць невялікі sidecar-сэрвіс, які будзе сачыць за бягучым IP-адрасам VPN-сэрвісу і кіраваць DNS-запісы, прапісаныя ў кліентаў – у каго на што хопіць фантазіі.

З пункту гледжання маршрутызацыі мы можам адназначна ідэнтыфікаваць кліента за VPN па ім IP-адрасу, які выдаецца серверам VPN. Ніжэй – прымітыўны прыклад абмежавання доступу такому кліенту да сэрвісаў, ілюстрацыя на вышэйзгаданым 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]

Тут цвёрда забараняецца падлучэнне на порт 6379, але пры гэтым захавана праца службы DNS, функцыянаванне якой даволі часта пакутуе пры складанні правіл. Таму што, як раней згадвалася, пры з'яўленні селектара да яго прымяняецца забаронная палітыка па змаўчанні, калі не пазначана іншае.

Вынікі

Такім чынам, з дапамогай пашыранага API Calico можна гнутка канфігураваць і дынамічна мяняць маршрутызацыю ў кластары і вакол яго. У агульным выпадку яго выкарыстанне можа выглядаць як стральба з гарматы па вераб'ях, а ўкараненне L3-сеткі з BGP- і IP-IP-тунэлямі выглядае монструозна ў простай усталёўцы Kubernetes у плоскай сетцы… Аднак у астатнім прылада выглядае суцэль жыццяздольным і карысным.

Ізаляцыя кластара для забеспячэння патрабаванняў бяспекі не заўсёды можа быць рэалізаваная, і менавіта ў такіх выпадках на дапамогу прыходзіць Calico (ці падобнае рашэнне). Прыведзеныя ў артыкуле прыклады (з невялікай дапрацоўкай) выкарыстоўваюцца ў некалькіх усталёўках нашых кліентаў у AWS.

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар