Envoy. 1. Увядзенне

Вітаю! Гэта невялікі артыкул, якая адказвае на пытанні: "што такое envoy?", "навошта ён патрэбен?" і "з чаго пачаць?".

Што гэта

Envoy - гэта L4-L7 балансавальнік напісаны на З ++, арыентаваны на высокую прадукцыйнасць і даступнасць. З аднаго боку, гэта ў некаторым родзе аналог nginx і haproxy, сувымерны з імі па прадукцыйнасці. З іншага, ён больш арыентаваны пад мікрасэрвісную архітэктуру і валодае функцыяналам не горш балансавальнікаў на java і go, такіх як zuul ці traefik.

Табліца параўнання haproxy/nginx/envoy, яна не прэтэндуе на абсалютную ісціну, але дае агульную карціну.

Nginx
гапраксі
пасланы
траефік

зорак на github
11.2k/mirror
1.1k/mirror
12.4k
27.6k

напісаны на
C
C
C + +
go

API
няма
socket only/push
dataplane/pull
цягнуць

active healthcheck
няма
ды
ды
ды

Open tracing
вонкавы плягін
няма
ды
ды

Дж.В.Т.
вонкавы плягін
няма
ды
няма

пашырэнне
Lua/C
Lua/C
Lua/C++
няма

навошта

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

  • Аддача статыкі.

Справа ў тым, што на дадзены момант у пасланы няма падтрымкі кэшавання. Хлопцы з google спрабуюць гэта выправіць. Ідэя аднойчы рэалізаваць у на пасланы усё тонкасці(заапарк хедэраў) адпаведнасці RFC, а для пэўных рэалізацый зрабіць інтэрфейс. Але пакуль гэта нават не альфа, архітэктура ў абмеркаванні, PR адкрыты (пакуль я пісаў артыкул PR умяржылі, але гэты пункт яшчэ актуальны).

А пакуль выкарыстоўвайце для статыкі nginx.

  • Статычная канфігурацыя.

Можна яе выкарыстоўваць, але пасланы быў створаны не для гэтага. Магчымасці ў статычнай канфігурацыі не будуць раскрыты. Момантаў шмат:

Рэдактуючы канфігурацыю ў yaml, Вы будзеце памыляцца, мацерыць распрацоўнікаў за шматслоўнасць і думаць, што канфігі nginx/haproxy, хай меней структураваныя, але лаканічна. У гэтым і сутнасць. Канфігурацыя Nginx і Haproxy стваралася пад рэдагаванне рукамі, а ў пасланы пад генерацыю з кода. Уся канфігурацыя апісана ў protobuf, генеруючы яе па proto файлах памыліцца значна складаней.

Сцэнары canary, b/g дэплою і шмат іншае, нармальна рэалізуюцца толькі ў дынамічнай канфігурацыі. Я не кажу, што гэта нельга зрабіць у статыцы, мы ўсё гэта робім. Але для гэтага трэба абкласціся мыліцамі, у любым з балансараў, у пасланы у тым ліку.

Задачы ў якіх Envoy незаменны:

  • Балансіроўка трафіку ў складаных і дынамічных сістэмах. Сюды пападае service mesh, але гэта не абавязкова толькі ён.
  • Неабходнасць функцыяналу размеркаванай трасіроўкі, складанай аўтарызацыі ці іншага, які ёсць у пасланы са скрынкі ці зручна рэалізоўваецца, а ў nginx/haproxy трэба абкласціся lua і сумнеўнымі ўбудовамі.

І тое, і іншае пры неабходнасці забяспечыць высокую прадукцыйнасць.

Як гэта працуе

Envoy распаўсюджваецца ў бінарніках толькі як docker вобраз. У выяве ўжо ёсць прыклад статычнай канфігурацыі. Але нам ён цікавы толькі для разумення структуры.

envoy.yaml статычная канфігурацыя

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite: www.google.com
                  cluster: service_google
          http_filters:
          - name: envoy.router
  clusters:
  - name: service_google
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_google
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: www.google.com
                port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
        sni: www.google.com

Дынамічная канфігурацыя

Рашэнне якой праблемы мы шукаем? Нельга проста так узяць і перазагрузіць канфігурацыю балансавальніка пад нагрузкай, узнікнуць "невялікія" праблемы:

  • Валідацыя канфігурацыі.

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

  • Доўгажывучыя злучэнні.

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

  • Актыўныя хелсчэкі.

Калі ў нас ёсць актыўныя хелсчэкі, трэба было б іх усё пераправерыць на новым канфігу да таго як паслаць трафік. Калі апстрымаў шмат, гэта патрабуе час. Прывітанне, haproxy.

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

Канфігурацыя пасланы (з файла вышэй) мае наступныя сутнасці:

  • listener - Лісценер які вісіць на вызначаным ip / порце
  • віртуальны хост - віртуальны хост па імені дамена
  • маршрут - правіла балансавання
  • кластар - група апстрымаў з параметрамі балансавання
  • канчатковая кропка - адрас інстансу апстрыму

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

Сэрвісы называюцца адпаведна: LDS, VHDS, RDS, CDS і EDS. Можна камбінаваць статычную і дынамічную канфігурацыю, з абмежаваннем, што дынамічны рэсурс нельга паказаць у статычным.

Для большасці задач дастаткова рэалізаваць апошнія тры сэрвісу, яны называюцца ADS (Aggregated Discovery Service), для ява і go маецца гатовая імплементацыя gRPC dataplane у якой дастаткова толькі запоўніць аб'екты са сваёй крыніцы.

Канфігурацыя набывае наступны выгляд:

envoy.yaml дынамічная канфігурацыя

dynamic_resources:
  ads_config:
    api_type: GRPC
    grpc_services:
      envoy_grpc:
        cluster_name: xds_clr
  cds_config:
    ads: {}
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_http
          rds:
            route_config_name: local_route
            config_source:
              ads: {}
          http_filters:
          - name: envoy.router
  clusters:
  - name: xds_clr
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: xds_clr
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: xds
                port_value: 6565

пры запуску пасланы з гэтым канфігам, ён падлучыцца да control-plane і паспрабуе запытаць канфігурацыю RDS, CDS і EDS. Як адбываецца працэс узаемадзеяння апісана тут.

Калі коратка, пасланы шле запыт, з указаннем тыпу запытанага рэсурсу, версіяй і параметрамі ноды. У адказ атрымлівае рэсурс і версію, калі на control-plane версія не памянялася, ён не адказвае.
Ёсць 4 варыянты ўзаемадзеяння:

  • Адзін gRPC стрымліваецца на ўсе тыпы рэсурсаў, дасылаецца поўнае стан рэсурсу.
  • Паасобныя стрымы, поўны стан.
  • Адзін стрым, інкрыментальны стан.
  • Паасобныя стрымы, інкрыментальны стан.

Incremental xDS дазваляе паменшыць трафік паміж control-plane і пасланы, гэта актуальна для вялікіх канфігурацый. Але ўскладняе ўзаемадзеянне, у запыце перадаецца спіс рэсурсаў для адпіскі і падпіскі.

У нашым прыкладзе выкарыстоўваецца ADS – адзін стрым для RDS, CDS, EDS і не інкрыментальны рэжым. Для ўключэння інкрыментальнага рэжыму, трэба пазначыць api_type: DELTA_GRPC

Так як у запыце ёсць параметры ноды, мы можам на control-plane дасылаць розныя рэсурсы для розных інстансаў. пасланы, гэта зручна для пабудовы service mesh.

Разагрэў

На пасланы пры старце або пры атрыманні новай канфігурацыі ад control-plane запускаецца працэс warmup рэсурсаў. Ён падзелены, на listener warmup і cluster warmup. Першы запускаецца пры зменах у RDS/LDS, другі пры CDS/EDS. Гэта значыць, што калі мяняюцца толькі апстрымы, лістанер не пераствараецца.

У працэсе прагрэву, чакаюцца залежныя рэсурсы ад control-plane на працягу таймаўту. Калі таймаўт выйшаў, ініцыялізацыя не будзе паспяховай, новы лістанер не пачне слухаць порт.
Парадак ініцыялізацыі: EDS, CDS, active health check, RDS, LDS. Пры ўключаных актыўных хелсчэках, трафік пойдзе на апстрым, толькі пасля аднаго паспяховага хелсчэка.

Калі пераствараецца лістар, стары пераходзіць у стан DRAIN, і будзе выдалены пасля зачынення ўсіх злучэнняў або заканчэнні таймаўту --drain-time-s, па змаўчанні 10 хвілін.

Працяг будзе.

Крыніца: habr.com

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