Стратегія розгортання застосунків Canary. Як підтримати належний рівень обслуговування

Усі статті, обговорення, новини про DevOps — в одному місці. Підписуйтеся на DOU | DevOps!

Привіт! Вас вітають Констянтин та Гліб, представляємо DevOps-команду компанії Uklon. Ми постійно працюємо над завданнями з впровадження нових версій програмного забезпечення та підтримки належного рівня обслуговування користувачів. За час роботи наша команда знайшла ряд інструментів, які допомагають виконувати завдання ефективно та швидко. Про деякі з цих інструментів хочемо вам розповісти.

Перш за все, маємо розповісти про інфраструктуру. Усі сервіси компанії Uklon запускаються у Kubernetes cluster, також ми використовуємо Istio для додавання різноманітного функціонала до нашої інфраструктури. Нижче розповімо про функціонал Istio, який ми використали в цій статі.

Istio та Service Mesh

Перш ніж перейти до розповіді про Istio, розглянемо поняття Service Mesh. Сучасні програми складаються з мікросервісів, кожен з яких виконує певну бізнес-функцію. Кожен мікросервіс інкапсулюється у Pod та запускається у кластері Kubernetes. Service Mesh — це виділений рівень інфраструктури, який можна додати до вашої інфраструктури без змінення коду застосунку та отримати додаткові можливості та функціонал. Він додається до кожного Pod-у і є додатковим рівнем між Kubernetes кластером та нашим застосунком.

Service Mesh дозволяє прозоро додавати такі можливості як спостереження, керування трафіком та безпекою, не змінюючи власний код або змінюючи його мінімально.

Він надає можливості для впровадження такого функціонала як A/B-тестування, розгортання Canary, обмеження швидкості, контроль доступу, шифрування трафіку та аутентифікації тощо.

Service Mesh може забезпечити безпечний міжсервісний зв’язок у кластері за допомогою шифрування TLS, та аутентифікації.

Є багато реалізацій Service Mesh.

Найбільш відомі реалізації Service Mesh: Istio та Linkerd. Istio є де-факто стандартом.

Istio вже інтегрований з багатьма системами й це було ключовим для нас при виборі реалізації Service Mesh. Далі розповімо, яку інтеграцію з Istio ми використали.

Istio може забезпечити:

  • Безпечний міжсервісний зв’язок у кластері за допомогою шифрування TLS, надійну аутентифікацію та авторизацію.
  • Автоматичне балансування навантаження для трафіку.
  • Детальний контроль поведінки трафіку з розширеними правилами маршрутизації, повторні спроби, перемиканням після відмови.
  • Istio також забезпечує автоматичні показники, журнали та трасування для всього трафіку в межах кластера, включаючи вхідний і вихідний трафік кластера.

Istio має багато функцій, але у цій статті розповімо про частину функцій, які будуть використані у нашому прикладі.

Ви можете встановити Istio самостійно, або кілька постачальників мають продукти, які інтегрують Istio та керують ним за вас.

Давайте розглянемо як звичайні запити від користувачів проходять через Kubernetes. Спочатку запит потрапляє на елемент Kubernetes Ingress, а потім надсилається на Kubernetes Service. Service використовує алгоритм Round Robin, щоб по черзі спрямовувати запити до різних Pod-ів. Проте Service не здійснює прив’язку запиту до конкретного Pod-у, що іноді потрібно розробникам. Наприклад, запити від одного користувача повинні бути спрямовані на один і той самий Pod. Це можна реалізувати завдяки функціоналу Istio.

Коли сервіс А потребує звернення до сервісу В, він отримує ім’я сервісу В з конфігураційного файлу та відправляє запит до DNS-імені сервісу В. Kubernetes спрямовує запит до сервісу Kubernetes, який спрямовує запит до Pod сервісу B.

Після додавання Istio до нашої інфраструктури з’являються додаткові елементи конфігурації, про які розповімо далі в цій статті.

Якщо розглянути потік даних між нашими застосунками, зокрема через дата-плейн, то можна побачити, що дані проходять через Envoy Proxy.

Розглянемо елементи Istio.

  • Control Plane (Istiod) — компонент, який відповідає за конвертування високорівневих правил маршрутизації для керування трафіком у зрозумілу для Envoy конфігурацію, виявлення сервісів та керування безпекою з можливістю аутентифікації. Крім того, IstioD виконує функцію центру сертифікації (CA) та генерує сертифікати для забезпечення безпечного зв’язку mTLS у площині даних.
  • Istio Ingress Gateway є вхідною точкою в Istio Service Mesh, яка використовує Envoy proxy для маршрутизації трафіку до сервісів у Service Mesh.
  • Envoy proxy — це високопродуктивний проксі-сервер, який Istio використовує для посередництва вхідного та вихідного трафіку для всіх сервісі у Service Mesh.
  • Istio Custom Resource Definition — набір віртуальних сутностей, за рахунок яких виконується конфігурація функціонала Istio для конкретного сервісу, серед основних, які розглядаються в наших прикладах, слід виділити:
    • Virtual service — конфігураційний елемент Istio, який відповідає за маршрутизацію запитів.
    • Virtual gateway — відповідає за конфігурацію Istio, яка визначає, які запити на який домен та порт повинен очікувати Istio та як їх опрацьовувати.
    • Destination Rule — набір правил, які застосовуються до сервісу після маршрутизації.

Функціонал Istio:

  • External traffic routing. Нижче наведено приклад налаштування Istio, де вказано, що Istio Ingress Gateway очікує HTTP-запити до «demoapp.demoonair.com» на порту 80.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: demo-app-gateway
spec:
 selector:
   istio: ingressgateway
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "demoapp.demoonair.com"
  • Traffic Shifting. Ми можемо виконати Traffic Shifting за допомогою Virtual Service. Ця функція Istio дозволяє спрямувати частину трафіку до іншого сервісу або до нової версії сервісу.

Приклад наведений нижче. У цьому прикладі є параметр weight, в якому можна вказати відсоток, який спрямовується до сервісу. Згідно з прикладом, до сервісу demo-app-stable-svc спрямовується 100% трафіку, а до сервісу demo-app-canary-svc спрямовується 0% трафіку.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: demo-app
spec:
 hosts:
 - "demoapp.demoonair.com"
 gateways:
 - demo-app-gateway
 http:
 - name: primary
    route:
   - destination:
       host: demo-app-stable-svc
       port:
         number: 8080
     weight: 100
   - destination:
       host: demo-app-canary-svc
       port:
         number: 8080
     weight: 0
  • Timeouts. Request Timeouts — це ще одна функція VirtualService. Вона працює наступним чином: якщо підключений pod не відповідає протягом встановленого порогового часу, то запит автоматично перенаправляється на інший доступний pod.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: demo-app
spec:
 hosts:
 - "demoapp.demoonair.com"
 gateways:
 - demo-app-gateway
 http:
 - name: primary
   timeout: 500ms

де timeout - порогове значення очікування відповіді на HTTP запит

  • Retries. Функція Istio retries аналізує відповідь від Pod, і якщо Pod повертає HTTP code 503, то Istio спрямовує цей запит до іншої Pod. Без цієї функції кінцевий користувач буде отримувати помилку, а завдяки цьому функціоналу користувач отримає відповідь від іншої працюючої Pod-и. Istio retries можуть підвищити доступність сервісу.

Якщо проксі Envoy використає усі спроби та не отримає вдалої відповіді, то користувач отримає помилку.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: demo-app
spec:
 hosts:
 - "demoapp.demoonair.com"
 gateways:
 - demo-app-gateway
 http:
 - name: primary
   timeout: 0.5s
   retries:
     attempts: 3
     perTryTimeout: 100ms
     retryOn: connect-failure,refused-stream,503

де

  • attempts — параметр який визначає який визначає максимальну кількість спроб проксі Envoy підключення до сервісу;
  • perTryTimeout — час очікування для кожної спроби;
  • retryOn — визначає умови, за яких відбувається повторна спроба. У нашому прикладі повторна спроба буде здійснена, якщо станеться connect-failure, rejected_stream або якщо Pod-а відповість Service Unavailable (503).
  • Circuit Breaking. Якщо система знає, що якийсь Pod повертає помилки на запити, то можна сконфігурувати систему, щоб вона не відправляла запити на цей Pod деякий час, а потім знов його перевірити, та якщо Pod буде повертати коректні відповіді, то посилати на нього запити знов. Ця функція зветься Circuit Breaking. Приклад конфігурації Circuit Breaking наведено нижче.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
 name: demo-app
 labels:
   app: demo-app
spec:
 host: demo-app-stable-svc
 trafficPolicy:
   outlierDetection:
     consecutive5xxErrors: 3
     interval: 1s
     baseEjectionTime: 1m
     maxEjectionPercent: 100

де

  • consecutive5xxErrors — визначає Кількість помилок 5xx перед видаленням Pod-и з пулу підключень;
  • interval — інтервал перевірки Pod-ів сервісу;
  • baseEjectionTime — визначає час на який Podа буде видалена з пулу підключень.
  • Traffic Management. Istio має багато режимів балансування.

Locality-weighted load balancing — балансування, з урахуванням значень яких ми можемо задати. Також можна зробити так, щоб запити від одного користувача приходили на один і той самий Pod. Як параметр прив’язки сесії до Pod-и можуть бути використані tcp port, service version, HTTP cookies, httpHeaderName, user source Ip address тощо.

Argo Rollout та Canary Deployment Strategy

Перейдемо до наступної частини нашої статі. Для встановлення застосунків в нашій інфраструктурі ми використовуємо GitOps-підхід. DevOps-команда або розробники розміщують у Git репозиторії маніфест Argo Application. Argo CD періодично перевіряє зміни Git-репозиторію та інсталює ці зміни до Kubernetes cluster.

Нижче наведений приклад розгортання простого застосунку, який використовує Kubernetes Deployment та мінімальний набір Istio компонентів (Istio Gateway та Istio Virtual Service).

Kubernetes Deployment підтримує наступні стратегії розгортання — «Recreate» або «RollingUpdate», але він не вміє керувати трафіком використовуючи istio. Ми хотіли б, щоб при оновленні програмного забезпечення працювало наступним чином:

  1. Система проінсталювала один Pod з новою версією програмного забезпечення.
  2. Направили на цей новий Pod 1% трафіку.
  3. Ми побачили, що все гаразд, проаналізували відповіді на запити, рівень використання ресурсів Pod-ом та інше.
  4. Потрохи запустили усі Pod-и та направили на нову версію увесь трафік.
  5. Видалили Pod старої версії.

Цю можливість нам дає Argo Rollout. Цей компонент може керувати Istio Virtual Service.

Argo Rollout має таку ж специфікацію, як і Kubernetes Deployment, але значно розширену. Після заміни Kubernetes Deployment на ArgoRollout розгортання нашого застосунку зміниться як наведено нижче.

Але це не все, ми можемо автоматизувати процес контролю розгортання нового деплойменту. Якщо якісь метрики будуть виходити за порогові значення, система повернеться до попередньої версії автоматично та відправить про це повідомлення командам DevOps та розробників.

До речі, метрики про запити та коди відповідей ми отримуємо завдяки Istio. Ми можемо використовувати будь-які метрики, які є у Prometheus. Для аналізу метрик до нашого сервісу додається AnalysisTemplate. Під час оновлення версії програмного застосунку створюється компонент AnalysysRun, який робить запити до Prometheus. Якщо параметри виходять за порогові значення, AnalysisRun відправляє команду до ArgoRollout повернутися до попередньої версії. Якщо параметри не виходять за порогові значення, то оновлення версії буде завершено.

Нижче наведено опис частини ArgoRollout, яка відповідає за Deployment Strategy.

apiVersion: argoproj.io/v1alpha1  
kind: Rollout                     
………………………………….
 strategy:
   canary:
     analysis:
       templates:
       - templateName: success-rate
       startingStep: 0
       args:
       - name: service-name
         value: demo-app-canary-svc.default.svc.cluster.local
     canaryService: demo-app-canary-svc  # required
     stableService: demo-app-stable-svc  # required
     trafficRouting:
       istio:
         virtualService:
           name: demo-app   # required
           routes:
           - primary
     steps:
     - setWeight: 20
     - pause: {}
     - setWeight: 40
     - pause: {duration: 10}
     - setWeight: 60
     - pause: {duration: 10}
     - setWeight: 80
     - pause: {duration: 10}

де

  • templateName — назва Analysis Template;
  • startingStep — це параметр, який вказує, з якого кроку починати аналізувати метрики
  • args — ця секція відповідає за передачу параметрів до Analysis Template, у нашому прикладі ми передаємо назву сервісу demo-app-canary-svc.default.svc.cluster.local через який спрямовуються запити до нової версії застосунку;
  • canaryService — це назва сервісу, через який запити спрямовують до Pod-ів зі новою версією застосунку;
  • stableService — це назва сервісу, через який запити спрямовують до Pod-ів зі старою версією застосунку;
  • virtualService — цей параметр вказує на назву Istio Virtual Service, який виконує керування трафіком;
  • steps — це секція, яка описує кроки розгортання. У нашому прикладі спочатку 20% запитів спрямовується до Pod-ів з новою версією застосунку; потім очікується ручна дія від користувача; потім спрямовується 40%, пауза 10 секунд, 60%, пауза 10 секунд, 80%, пауза 10 секунд, 100% запитів спрямовується до нової версії застосунку.

Приклад Analysis Template наведено нижче:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
 name: success-rate
spec:
 args:
 - name: service-name
 metrics:
 - name: success-rate
   interval: 5s
   successCondition: result[0] >= 0.95
   failureLimit: 10
   provider:
     prometheus:
       address: http://prometheus.istio-system:9090
       query: |
         sum(irate(
           istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m]
         )) /
         sum(irate(
           istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
         ))

Цей AnalysisTemplate має назву success-rate, який як аргумент приймає назву сервісу (service-name) та використовує його у запиті query до Prometheus-серверу. Цей запит до Prometheus-серверу обраховує співвідношення вдалих запитів до нової версії застосунку до загальної кількості запитів, тобто якщо в нас буде кількість успішних запитів менше ніж 95%, новий деплоймент з новою версією буде вважатися невдалим та система повернеться до попередньої версії застосунку.

Крім базових параметрів цей шаблон містить:

  • interval — це параметр, який описує, як часто робити запити до Prometheus серверу;
  • successCondition — описує умову, за якої розгортання буде вважатися вдалим;
  • failureLimit — це параметр, який визначає кількість запитів до Prometheus серверу, в результаті яких successCondition не виконався та розгортання вважається невдалим;
  • address — це URL-адреса Prometheus серверу.

Три сценарії розгортання нової версії застосунку

Перший сценарій

Уявімо, що наша команда оновлює застосунку, у якого є багато endpoint, з яких нас цікавить два /probe, який використовується Kubernetes cluster та endpoint /get_data. Запит /get_data повертає якісь дані користувачу, але з невстановлених причин цей запит перестав працювати у новій версії застосунку.

У першому сценарію розгортання ми використовуємо:

  • Kubernetes Deployment;
  • застосунко має метрики про запити та відповіді.

Кроки:

  1. Команда розробників розпочинає оновлення програмного забезпечення та вносить зміни до git repository.
  2. Зміни з git repository застосовуються у Kubernetes Cluster завдяки ArgoCD.
  3. Метрики з Kubernetes Cluster потрапляють до Prometheus Server.
  4. Команда розробників аналізує метрики застосунку в Prometheus Server, використовуючи Grafana.
  5. Команда розробників бачить велику кількість невдалих відповідей від застосунку, вирішує повернутися до попередньої версії та вносить зміни до Git repository.
  6. Зміни з Git repository застосовуються у Kubernetes Cluster завдяки ArgoCD.

Як результат цього сценарію — усі користувачі цього застосунку будуть спостерігати проблему приблизно 30 хвилин.

Другий сценарій

У другому сценарії розгортання ми використовуємо:

  • Kubernetes Deployment;
  • застосунок має метрики про запити та відповіді.

Якщо наші метрики не покривають запит get_data у застосунку або в нас немає цих метрик або ми не помітили цю проблему, нам прийде повідомлення від користувачів через нашу технічну підтримку, що в нас щось не працює. Час, коли у нас буде зламаний URL get_data, буде ще більшим.

Результат цього сценарію — усі користувачі цього застосунку будуть спостерігати проблему понад 30 хвилин.

Третій сценарій

У третьому сценарії розгортання ми використовуємо:

  • ArgoRollout;
  • Istio;
  • Analisys Template;
  • застосунок може не мати метрики про запити та відповіді, система отримує ці метрики завдяки Istio.

Кроки:

  1. Команда розробників розпочинає оновлення програмного забезпечення та вносить зміни до Git repository.
  2. Зміни з Git repository застосовуються у Kubernetes Cluster завдяки ArgoCD.
  3. Метрики з Kubernetes Cluster потрапляють до Prometheus Server.
  4. Метрики з Prometheus Server потрапляють до AnalisysRun.
  5. Якщо метрики не будуть відповідати умовам, які задані в Analysis Template, система вирішить повернутися до попередньої версії застосунку.
  6. Зміни застосовуються у Kubernetes Cluster завдяки Argo Rollout.

Результати трьох сценаріїв

У таблиці нижче наведений час спостереження проблем для нашого тестового сервісу. Також тут наведена кількість запитів, які отримують помилки під час оновлення до проблемної версії та повернення до попередньої версії.

З цієї таблиці можна зробити висновок, що застосування Canary Deployment Stretegy разом з Istio, Argo Rollout та аналізом метрик значно підвищує рівень обслуговування користувачів вашого застосунку. На жаль, функція Istio retries не працює у випадку оновлення до нової версії, але ми сподіваємось, що це буде виправлено у наступних версіях Istio та впливу на рівень обслуговування користувачів взагалі не буде.

Висновки

  1. Istio розширює функціональність системи без внесення змін до програмного забезпечення.
  2. Istio дає можливість підвищити рівень обслуговування користувачів.
  3. Argo Rollout і Istio дають можливість автоматизувати процес повернення до попередньої версії програмним забезпеченням у випадку виникнення проблем з новим програмним забезпеченням.

Корисні посилання

  1. istio.io/...​tasks/traffic-management
  2. istio.io/...​cepts/traffic-management
  3. istio.io/...​reference/config/metrics
  4. argoproj.github.io/argo-rollouts
  5. argoproj.github.io/...​rollouts/features/canary
  6. argoproj.github.io/...​outs/analysis/prometheus
  7. github.com/tomahkvt/demoapp
👍ПодобаєтьсяСподобалось14
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікава технічна стаття. Але схоже такі тут вже не в моді. Треба щоб гівно на вентилятор, образи один одного в коментарях і т. п.

Дякую за статтю. Було цікаво прочитати про практичне застосування Istio та ArgoRollout

Доброго дня дуже дякую вам за запитання. У цій статі описана частина функціоналу яку ми використовуємо.
Нажаль я не мав досвіду роботи з Ingress Contour. Я ознайомився з документацією Contour. Contour має дуже цікавий функціонал.
Ниже у моєму коментарі я намагатимусь відповісти на ваше питання.
Я можу помилятися у своїх міркуваннях щодо Contour будь ласка виправте мене.

1) За допомогою функції Upstream Weighting, яка є в Ingress Contour згідно з документацією, можна керувати трафіком. Проте ArgoRollout не підтримує Ingress Contour для керування трафіком згідно з документацією ArgoRollout.
2) Істіо дає можливість керувати запитами, які надходять від інших Pod-ів без залучення Ingress. Це означає, що якщо ми використовуватимемо Ingress Contour та хочемо керувати усіма запитами за допомогою функції Upstream Weighting, нам потрібно переслати всі запити через Ingress Contour, тобто pod-и інших сервісів мають надсилати запити на зовнішні домени, які пов’язані з Ingress Contour. Також слід врахувати можливу затримку, яку додає Ingress Contour.
3) -Істіо дає можливість збирати статистику про запити з усіх сервісів, навіть якщо вони надходять безпосередньо до іншого сервісу без залучення Ingress. Для отримання цієї статистики не потрібно вносити зміни до застосунків. Ця статистика потрапляє до Prometheus та використовується для аналізу успішності оновлення програмного забезпечення та рівня обслуговування (SLI).
— Бачу що Contour завдяки Envoy може надавати метрики про запити які проходять через Igress але не може надати статистику про запити які надходять до сервісів напряму від інших сервісів не через ingress.
4) — Istio забезпечує trace запитів.
— В документації Ingress Contour я не знайшов інформації про можливість trace запитів. Ingress Contour використовує Envoy proxy і тому я думаю є можливість включити цей функціонал.

Не зовсім зрозуміло що у вашому випадку дає такого Istio Service Mesh чого не вміє Contour ingress на основі того самого Envoy? сам інгрес вміє в ваги і керування трафіком між сервісами, в хелсчеки і розумне балансування...

Доброго дня дуже дякую вам за запитання. У цій статі описана частина функціоналу яку ми використовуємо.
Нажаль я не мав досвіду роботи з Ingress Contour. Я ознайомився з документацією Contour. Contour має дуже цікавий функціонал.
Ниже у моєму коментарі я намагатимусь відповісти на ваше питання.
Я можу помилятися у своїх міркуваннях щодо Contour будь ласка виправте мене.

1) За допомогою функції Upstream Weighting, яка є в Ingress Contour згідно з документацією, можна керувати трафіком. Проте ArgoRollout не підтримує Ingress Contour для керування трафіком згідно з документацією ArgoRollout.
2) Істіо дає можливість керувати запитами, які надходять від інших Pod-ів без залучення Ingress. Це означає, що якщо ми використовуватимемо Ingress Contour та хочемо керувати усіма запитами за допомогою функції Upstream Weighting, нам потрібно переслати всі запити через Ingress Contour, тобто pod-и інших сервісів мають надсилати запити на зовнішні домени, які пов’язані з Ingress Contour. Також слід врахувати можливу затримку, яку додає Ingress Contour.
3) -Істіо дає можливість збирати статистику про запити з усіх сервісів, навіть якщо вони надходять безпосередньо до іншого сервісу без залучення Ingress. Для отримання цієї статистики не потрібно вносити зміни до застосунків. Ця статистика потрапляє до Prometheus та використовується для аналізу успішності оновлення програмного забезпечення та рівня обслуговування (SLI).
— Бачу що Contour завдяки Envoy може надавати метрики про запити які проходять через Igress але не може надати статистику про запити які надходять до сервісів напряму від інших сервісів не через ingress.
4) — Istio забезпечує trace запитів.
— В документації Ingress Contour я не знайшов інформації про можливість trace запитів. Ingress Contour використовує Envoy proxy і тому я думаю є можливість включити цей функціонал.

На скільки я зрозумів у вашому випадку istio надає можливість моніторити трафік між інтернал сервісами, це по суті дуже опосередковано пов’язано з канарейками (і з заголовком статті), без такої вимоги інфраструктура значно спрощується. По суті проблема тільки з заголовком)), а сама стаття досить цікава. Дякую

Підписатись на коментарі