Кантэйнеры, мікрасэрвісы і сэрвіс-мешы

У Інтэрнэце куча артыкулаў о сэрвіс-мешах (service mesh), і вось яшчэ адна. Ура! Але навошта? Затым, што я хачу выказаць сваё меркаванне, што лепей бы сэрвіс-меші з'явіліся 10 гадоў таму, да з'яўлення кантэйнерных платформаў, такіх як Docker і Kubernetes. Я не сцвярджаю, што мой пункт гледжання лепш ці горш за іншых, але паколькі сэрвіс-мешы - даволі складаныя жывёлы, множнасць пунктаў гледжання дапаможа лепш іх зразумець.

Я распавяду аб платформе dotCloud, якая была пабудавана на больш за сотні мікрасэрвісах і падтрымлівала тысячы прыкладанняў у кантэйнерах. Я растлумачу праблемы, з якімі мы сутыкнуліся пры яе распрацоўцы і запуску, і як сэрвіс-меші маглі б дапамагчы (ці не маглі).

Гісторыя dotCloud

Я ўжо пісаў аб гісторыі dotCloud і выбары архітэктуры для гэтай платформы, але мала расказваў аб сеткавым узроўні. Калі не жадаеце апускацца ў чытанне мінулага артыкула аб dotCloud, то сцісла вось іста: гэта платформа-як-сэрвіс PaaS, якая дазваляе кліентам запускаць шырокі спектр прыкладанняў (Java, PHP, Python…), з падтрымкай шырокага спектру службаў дадзеных (MongoDB, MySQL, Redis…) і працоўным працэсам як у Heroku: вы загружаеце свой код на платформу, яна будуе выявы кантэйнераў і разгортвае іх.

Я раскажу, як накіроўваўся трафік на платформу dotCloud. Не таму, што гэта было асабліва выдатна (хоць для свайго часу сістэма працавала нядрэнна!), але першым чынам таму, што з дапамогай сучасных прылад такі дызайн лёгка можа рэалізаваць за кароткі час сціплая каманда, калі ім патрэбен спосаб маршрутызацыі трафіку паміж кучай мікрасэрвісаў ці кучай прыкладанняў. Такім чынам, можна параўнаць варыянты: што атрымліваецца, калі распрацаваць усё самім або выкарыстоўваць існуючы сэрвіс-меш. Стандартны выбар: зрабіць самім ці купіць.

Маршрутызацыя трафіку для hosted-прыкладанняў

Прыкладанні на dotCloud могуць падаваць канчатковыя кропкі HTTP і TCP.

Канчатковыя кропкі HTTP дынамічна дадаюцца ў канфігурацыю кластара балансавальнікаў нагрузкі Hipache. Гэта падобна на тое, што сёння робяць рэсурсы. Уваход у Kubernetes і балансавальнік нагрузкі накшталт Траефік.

Кліенты падключаюцца да канчатковых кропак HTTP праз адпаведныя дамены пры ўмове, што даменнае імя паказвае на балансавальнікі нагрузкі dotCloud. Нічога асаблівага.

Канчатковыя кропкі TCP звязаныя з нумарам порта, які затым перадаецца ўсім кантэйнерам гэтага стэка праз зменныя асяроддзі.

Кліенты могуць падлучацца да канчатковых кропак TCP, выкарыстаючы якое адпавядае імя хаста (нешта накшталт gateway-X.dotcloud.com) і нумар порта.

Гэта імя хаста рэзалюе на кластар сервераў “nats“ (не мае дачынення да НАТС), якія будуць маршрутызаваць ўваходныя TCP-злучэнні ў правільны кантэйнер (ці, у выпадку службаў з балансаваннем нагрузкі, у правільныя кантэйнеры).

Калі вы знаёмыя з Kubernetes, верагодна, гэта нагадае вам службы NodePort.

На платформе dotCloud не было эквівалента службаў ClusterIP: для прастаты доступ да службаў адбываўся аднолькава як знутры, так і звонку платформы.

Усё было арганізавана дастаткова проста: першапачатковыя рэалізацыі сетак маршрутызацыі HTTP і TCP, верагодна, усяго па некалькіх сотняў радкоў Python. Простыя (я б сказаў, наіўныя) алгарытмы, якія дапрацоўваліся з ростам платформы і з'яўленнем дадатковых патрабаванняў.

Шырокі рэфактарынг існуючага кода не патрабаваўся. У прыватнасці, 12-фактарныя прыкладанні могуць наўпрост выкарыстоўваць адрас, атрыманы праз зменныя асяроддзі.

Чым гэта адрозніваецца ад сучаснага сэрвіс-меша?

Абмежаваная обзорность. У нас увогуле не было ніякіх метрык для сеткі маршрутызацыі TCP. Што да маршрутызацыі HTTP, то ў пазнейшых версіях з'явіліся падрабязныя HTTP-метрыкі з кодамі памылак і часам водгуку, але сучасныя сэрвіс-меші ідуць яшчэ далей, забяспечваючы інтэграцыю з сістэмамі збору метрык, як Prometheus, напрыклад.

Обзорность важная не толькі з аператыўнага пункта гледжання (каб дапамагаць ва ўхіленні праблем), але і пры выпуску новых функцый. Гаворка пра бяспечнае сіне-зялёным дэплое и дэплое канарэек.

Эфектыўнасць маршрутызацыі таксама абмежавана. У сетцы маршрутызацыі dotCloud увесь трафік павінен быў праходзіць праз кластар выдзеленых вузлоў маршрутызацыі. Гэта азначала патэнцыйнае скрыжаванне некалькіх меж AZ (зоны даступнасці) і значнае павелічэнне затрымкі. Памятаю, як ухіляў праблемы з кодам, які рабіў больш за сотню SQL-запытаў на старонку і для кожнага запыту адкрываў новае злучэнне з SQL-серверам. Пры лакальным запуску старонка загружаецца імгненна, але ў dotCloud загрузка займае некалькі секунд, таму што для кожнага TCP-злучэнні (і наступнага SQL-запыту) патрабуецца дзясяткі мілісекунд. У гэтым канкрэтным выпадку праблему вырашылі пастаянныя злучэнні.

Сучасныя сэрвіс-мешы лепш спраўляюцца з такімі праблемамі. Перш за ўсё, яны правяраюць, што злучэнні маршрутызуюцца у крыніцы. Лагічны струмень той жа: клиент → меш → сервис, але цяпер меш працуе лакальна, а не на выдаленых вузлах, таму злучэнне клиент → меш з'яўляецца лакальным і вельмі хуткім (мікрасекунды замест мілісекунд).

Сучасныя сэрвіс-мешы таксама рэалізуюць разумнейшыя алгарытмы балансавання нагрузкі. Кантралюючы працаздольнасць бэкенд, яны могуць адпраўляць больш трафіку на больш хуткія бэкэнд, што прыводзіць да павышэння агульнай прадукцыйнасці.

бяспеку таксама лепш. Сетка маршрутызацыі dotCloud працавала цалкам на EC2 Classic і не шыфравала трафік (зыходзячы са здагадкі, што калі камусьці атрымалася паставіць сниффер на сеткавы трафік EC2, у вас ужо вялікія праблемы). Сучасныя сэрвіс-мешы празрыста абараняюць увесь наш трафік, напрыклад, з узаемнай TLS-аўтэнтыфікацыяй і наступным шыфраваннем.

Маршрутызацыя трафіку для службаў платформы

Добра, мы абмеркавалі трафік паміж праграмамі, але як наконт самой платформы dotCloud?

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

Многія службы высокага ўзроўню могуць выкарыстоўваць сетку маршрутызацыі, апісаную вышэй. Насамрэч, шматлікія з больш за сотні мікрасэрвісаў dotCloud былі разгорнутыя як звычайныя прыкладанні на самой платформе dotCloud. Але невялікая колькасць нізкаўзроўневых сэрвісаў (у прыватнасці, якія рэалізуюць гэтую сетку маршрутызацыі) мелі патрэбу ў чымсьці прасцейшым, з меншымі залежнасцямі (паколькі для працы яны не маглі залежаць ад саміх сябе – старая добрая праблема курыцы і яйкі).

Гэтыя нізкаўзроўневыя, важныя службы былі разгорнуты шляхам запуску кантэйнераў непасрэдна на некалькіх ключавых вузлах. Пры гэтым не задзейнічаліся стандартныя службы платформы: кампаноўнік, планавальнік і runner. Калі хочаце параўнаць з сучаснымі кантэйнернымі платформамі, гэта падобна на запуск плоскасці кіравання з docker run непасрэдна на вузлах, замест дэлегавання задачы Kubernetes. Гэта даволі падобна на канцэпцыю статычных модуляў (подаў), якія выкарыстоўвае кубеадм або bootkube пры загрузцы аўтаномнага кластара.

Гэтыя службы экспанаваліся простым і грубіянскім спосабам: у файле YAML былі пералічаныя іх імёны і адрасы; а кожны кліент павінен быў для дэплоя ўзяць копію гэтага YAML-файла.

З аднаго боку, гэта надзвычай надзейна, таму што не патрабуе падтрымкі вонкавага сховішчы ключоў/значэнняў, такога як Zookeeper (не забывайце, у той час яшчэ не існавала etcd ці Consul). З іншага боку, гэта абцяжарвала перасоўванне службаў. Кожны раз пры перамяшчэнні ўсе кліенты павінны былі атрымаць абноўлены файл YAML (і патэнцыйна перазагрузіцца). Не надта зручна!

Пасля мы пачалі ўкараняць новую схему, дзе кожны кліент падключаўся да лакальнага проксі-серверу. Замест адраса і порта яму дастаткова ведаць толькі нумар порта службы, і падключацца праз localhost. Лакальны проксі-сервер апрацоўвае гэтае злучэнне і накіроўвае яго на фактычны сервер. Цяпер пры перасоўванні бэкенда на іншую машыну або маштабаванні замест абнаўлення ўсіх кліентаў трэба абнавіць толькі ўсе гэтыя лакальныя проксі; і перазагрузка больш не патрабуецца.

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

Гэта вельмі падобна на SmartStack ад Airbnb, але істотная розніца ў тым, што SmartStack рэалізаваны і разгорнуты ў прадакшн, у той час як унутраную сістэму маршрутызацыі dotCloud прыбралі ў скрыню, калі dotCloud ператварыўся ў Docker.

Я асабіста лічу SmartStack адным з папярэднікаў такіх сістэм, як Istio, Linkerd і Consul Connect, таму што ўсе яны ідуць аднаму шаблону:

  • Запуск проксі на кожным вузле.
  • Кліенты падключаюцца да проксі.
  • Плоскасць кіравання абнаўляе канфігурацыю проксі-сервера пры змене бэкэндаў.
  • … Профіт!

Сучасная рэалізацыя сэрвіс-меша

Калі нам трэба рэалізаваць падобную сетку сёння, мы можам выкарыстоўваць аналагічныя прынцыпы. Напрыклад, наладзіць унутраную зону DNS, супастаўляючы імёны службаў адрасам у прасторы 127.0.0.0/8. Затым запусціць HAProxy на кожным вузле кластара, прымаючы злучэнні па кожным адрасе службы (у гэтай падсетцы 127.0.0.0/8) і перанакіроўваючы/балансуючы нагрузку на адпаведныя бэкэнды. Канфігурацыя HAProxy можа кіравацца confd, дазваляючы захоўваць інфармацыю аб бэкендзе ў etcd або Consul і аўтаматычна пушыць абноўленую канфігурацыю на HAProxy, калі гэта неабходна.

Прыкладна так працуе Istio! Але з некаторымі адрозненнямі:

  • Выкарыстоўвае Envoy Proxy замест HAProxy.
  • Захоўвае канфігурацыю бэкенда праз Kubernetes API замест etcd або Consul.
  • Службам вылучаюцца адрасы ва ўнутранай падсетцы (адрасы Kubernetes ClusterIP) замест 127.0.0.0/8.
  • Мае дадатковы кампанент (Citadel) для дадання ўзаемнай праверкі сапраўднасці TLS паміж кліентам і серверамі.
  • Падтрымлівае новыя функцыі, такіх як разрыў ланцуга (circuit breaking), размеркаваная трасіроўка, дэплой канарэек і інш.

Давайце сцісла разгледзім некаторыя адрозненні.

Envoy Proxy

Envoy Proxy напісала кампанія Lyft [канкурэнт Uber на рынку таксі – заўв. зав.]. Ён шмат у чым падобны на іншыя проксі (напрыклад, HAProxy, Nginx, Traefik…), але Lyft напісала свой, таму што ім былі патрэбныя функцыі, якія адсутнічаюць у іншых проксі, і разумней падалося зрабіць новы, чым пашыраць існы.

Envoy можна выкарыстоўваць сам па сабе. Калі ў мяне ёсць пэўная служба, якая павінна падлучацца да іншых службаў, я магу наладзіць яе на падлучэнні да Envoy, а затым дынамічна наладжваць і пераналаджваць Envoy з размяшчэннем іншых службаў, атрымліваючы пры гэтым шмат выдатных дадатковых функцый, напрыклад, па обзорности. Замест кастамнай кліенцкай бібліятэкі або ўкаранення ў код трасіроўкі выклікаў мы накіроўваем трафік у Envoy, а ён збірае для нас метрыкі.

Але Envoy таксама здольны працаваць як плоскасць дадзеных (data plane) для сэрвіс-меша. Гэта азначае, што зараз для дадзенага сэрвіс-меша Envoy наладжваецца плоскасцю кіравання (control plane).

Плоскасць кіравання

У плоскасці кіравання Istio належыць на Kubernetes API. Гэта не вельмі адрозніваецца ад выкарыстання confd, Які належыць на etcd або Consul для прагляду набору ключоў у сховішча дадзеных. Istio праз Kubernetes API праглядае набор рэсурсаў Kubernetes.

Паміж справай: асабіста мне падалося карысным гэта апісанне Kubernetes API, якое абвяшчае:

Сервер Kubernetes API - гэта "дурны сервер", які прапануе захоўванне, кіраванне версіямі, праверку, абнаўленне і семантыку рэсурсаў API.

Istio распрацаваны для працы з Kubernetes; і калі вы хочаце выкарыстоўваць яго за межамі Kubernetes, то вам трэба запусціць асобнік сервера Kubernetes API (і дапаможнай службы etcd).

Адрасы службаў

Istio належыць на адрасы ClusterIP, якія вылучае Kubernetes, таму службы Istio атрымліваюць унутраны адрас (не ў дыяпазоне 127.0.0.0/8).

Трафік на адрас ClusterIP для канкрэтнай службы ў кластары Kubernetes без Istio перахапляецца kube-proxy і адпраўляецца на серверную частку гэтага проксі. Калі вас цікавяць тэхнічныя дэталі, то kube-proxy усталёўвае правілы iptables (або балансавальнікі нагрузкі IPVS, у залежнасці ад таго, як яго наладзілі), каб перапісаць IP-адрасы прызначэння злучэнняў, якія ідуць па адрасе ClusterIP.

Пасля ўсталёўкі Istio у кластары Kubernetes нічога не змяняецца, пакуль ён не будзе відавочна ўключаны для дадзенага спажыўца ці нават усёй прасторы імёнаў, шляхам уводзін кантэйнера. sidecar у кастамныя поды. Эцьот кантэйнер запусціць асобнік Envoy і ўсталюе шэраг правіл iptables для перахопу трафіку, які ідзе ў іншыя службы, і перанакіраванні гэтага трафіку на Envoy.

Пры інтэграцыі з Kubernetes DNS гэта азначае, што наш код можа падлучацца па імі службы, і ўсё "проста працуе". Іншымі словамі, наш код выдае запыты тыпу http://api/v1/users/4242, тады api рэзавіт запыт на 10.97.105.48, правілы iptables перахапляюць злучэнні з 10.97.105.48 і перанакіроўваюць іх на лакальны проксі Envoy, а гэты лакальны проксі накіруе запыт на фактычны бэкенд API. Фух!

Дадатковыя рушачкі

Istio таксама забяспечвае скразное шыфраванне і аўтэнтыфікацыю праз mTLS (mutual TLS). За гэта адказвае кампанент пад назвай Citadel.

Таксама ёсць кампанент Змяшальнік, які Envoy можа запытаць для кожнага запыту, каб прыняць спецыяльнае рашэнне аб гэтым запыце ў залежнасці ад розных фактараў, такіх як загалоўкі, загрузка бэкенда і т. д… (не хвалюйцеся: ёсць шмат сродкаў забяспечыць працаздольнасць Mixer, і нават калі ён зляціць, Envoy працягне нармальна працаваць як проксі) .

І, вядома, мы згадалі обзорность: Envoy збірае велізарную колькасць метрык, забяспечваючы пры гэтым размеркаваную трасіроўку. У архітэктуры мікрасэрвісаў, калі адзін запыт API павінен прайсці праз мікрасэрвісы A, B, C і D, то пры ўваходзе ў сістэму размеркаваная трасіроўка дадасць да запыту ўнікальны ідэнтыфікатар і захавае дадзены ідэнтыфікатар праз подзапросы да ўсіх гэтых мікрасэрвісаў, дазваляючы фіксаваць усе злучаныя выклікі, іх затрымкі і т. д.

Распрацоўваць ці купіць

У Istio рэпутацыя складанай сістэмы. Наадварот, пабудова сеткі маршрутызацыі, якую я апісаў у пачатку гэтай пасады, адносна проста з дапамогай існых прылад. Такім чынам, ці ёсць сэнс замест гэтага ствараць уласны сервіс-меш?

Калі ў нас сціплыя запатрабаванні (не патрэбна обзорность, прерыватель ланцуга і іншыя тонкасці), то прыходзяць думкі аб распрацоўцы ўласнай прылады. Але калі мы выкарыстоўваем Kubernetes, ён можа нават не спатрэбіцца, таму што Kubernetes ужо дае базавыя прылады для выяўлення службаў і балансаванні нагрузкі.

Але калі ў нас прасунутыя патрабаванні, то "купля" сэрвіс-меша ўяўляецца нашмат лепшым варыянтам. (Гэта не заўсёды менавіта "купля", паколькі Istio пастаўляецца з адкрытым зыходным кодам, але нам усё роўна трэба інвеставаць інжынерны час, каб разабрацца ў яго працы, задэплоіць і кіраваць ім).

Што абраць: Istio, Linkerd ці Consul Connect?

Пакуль мы казалі толькі пра Istio, але гэта не адзіны сэрвіс-меш. Папулярная альтэрнатыва - Linkerd, а ёсць яшчэ Consul Connect.

Што выбраць?

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

Адзін са шматабяцальных падыходаў - выкарыстоўваць інструмент накшталт SuperGloo. Ён рэалізуе пласт абстракцыі для спрашчэння і ўніфікацыі API, якія прадстаўляюцца сэрвіс-мешамі. Замест таго, каб вывучаць канкрэтныя (і, на мой погляд, адносна складаныя) API розных сэрвіс-мешей, мы можам выкарыстоўваць прасцейшыя канструкцыі SuperGloo — і лёгка перамыкацца з аднаго на іншы, нібы ў нас прамежкавы фармат канфігурацыі, які апісвае HTTP-інтэрфейсы і бэкэнды, здольны генераваць фактычную канфігурацыю для Nginx, HAProxy, Traefik, Apache…

Я крыху папесціўся з Istio і SuperGloo, а ў наступным артыкуле жадаю паказаць, як дадаць Istio або Linkerd у існы кластар з дапамогай SuperGloo, і наколькі апошні зладзіцца са сваёй працай, гэта значыць дазваляе перамыкацца з аднаго сэрвіс-меша на іншы без перазапісу канфігурацый.

Крыніца: habr.com

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