Envoy proxy#

Envoy proxy - настраиваемый прокси-сервер, разработанный для микросервисной архитектуры со следующими основными функциями:

  • L4-L7 балансирование нагрузки;

  • динамическая маршрутизация;

  • управление трафиком между микросервисами.

Envoy proxy легко обрабатывает сложные сценарии маршрутизации и, при возникновении проблем с сетью и приложениями, оперативно определяет источник проблемы, а также может использоваться как обратный прокси-сервер для HTTP и TCP-трафика.

Envoy proxy - автономный процесс, который предназначен для работы на каждом сервере приложений. Все Envoy proxy образуют прозрачную коммуникационную сеть, в которой каждое приложение отправляет и получает сообщения на локальный хост и обратно, и не имеет доступа к топологии сети.

Преимущества такой внепроцессной архитектуры по сравнению с традиционным библиотечным подходом к взаимодействию между сервисами:

  • Envoy proxy работает с любым прикладным языком. Одно развертывание может объединить Java, C++, Go, PHP, Python и т.д.;

  • Envoy proxy быстро и прозрачно развертывается и обновляется во всей инфраструктуре, в отличие от сервис-ориентированных архитектур, для которых развертывание обновлений библиотек может быть длительным и трудоемким процессом.

Envoy proxy поддерживает:

  • функции сетевого проксирования уровня L3/L4. Подключаемый механизм цепочки фильтров позволяет создавать фильтры для выполнения различных задач прокси TCP/UDP и накладывать их на основной сервер. Существуют фильтры для поддержки различных задач, таких как необработанный TCP-прокси, UDP-прокси, HTTP-прокси, проверка подлинности сертификата клиента TLS, Redis, MongoDB, Postgres и т.д;

  • дополнительный уровень фильтра HTTP L7. HTTP-фильтры могут быть подключены к подсистеме управления HTTP-соединениями, которые выполняют различные задачи, такие как буферизация, ограничение скорости, маршрутизация/переадресация;

  • в режиме HTTP Envoy - HTTP/1.1, HTTP/2, HTTP/3. Envoy proxy работает как прозрачный прокси-сервер с HTTP/1.1, HTTP/2 и HTTP/3 в исходящем и входящем потоке данных. Любая комбинация клиентов HTTP/1.1 и HTTP/2 и целевых серверов может быть объединена в мост;

  • в режиме HTTP Envoy подсистему маршрутизации, способную маршрутизировать и перенаправлять запросы на основе пути, полномочий, типа контента, значений времени выполнения и т. д. Функция востребована при использовании Envoy proxy в качестве Frontend или пограничного сервера прокси, а также при создании сервисной сетки;

  • систему удаленного вызова процедур gRPC. gRPC - это платформа RPC, которая использует HTTP/2 или более позднюю версию в качестве базового мультиплексированного транспорта. Envoy поддерживает все функции HTTP/2;

  • обнаружение сервисов и динамическую конфигурацию. Использует многоуровневый набор API динамической конфигурации для централизованного управления. Уровни предоставляют Envoy proxy динамические обновления: о хостах в Backend-кластере, о Backend-кластерах, маршрутизации HTTP, прослушивающих сокетах и криптографическом материале. Для простого развертывания обнаружение серверного хоста может быть выполнено с помощью разрешения DNS, а дополнительные уровни заменены статическими файлами конфигурации;

  • функцию автоматической проверки работоспособности вышестоящих сервисных кластеров. Envoy proxy использует объединение информации об обнаружении сервисов и проверке работоспособности для определения объектов балансировки нагрузки. Envoy proxy также поддерживает пассивную проверку работоспособности с помощью подсистемы обнаружения выбросов;

  • методы балансировки нагрузки и осуществляет доступ к балансировке приложениям. Envoy proxy поддерживает автоматические повторные попытки подключения, отслеживание разрыва цепи, глобальное ограничение скорости посредством внешней службы ограничения скорости, теневое копирование запросов и обнаружение выбросов;

  • функции Frontend/пограничного проксирования: использование одного и того же программного обеспечения на периферии позволяет упростить наблюдаемость, управление, обеспечить идентичность алгоритмов обнаружения сервисов и балансировки нагрузки;

  • сбор статистики для всех подсистем.

Архитектура#

Envoy proxy использует единый процесс с многопоточной архитектурой.

Один основной поток управляет различными задачами координации, в то время как некоторое количество рабочих потоков выполняет прослушивание, фильтрацию и пересылку.

Как только слушатель принимает соединение, оно остается привязанным к одному рабочему потоку. Это позволяет большинству экземпляров Envoy proxy быть в основном однопоточными, что приводит к трудностям распараллеливания, с небольшим количеством сложной координации обработки кода между рабочими потоками.

Балансировка подключений слушателя#

По умолчанию координация между рабочими потоками отсутствует. Это означает, что все рабочие потоки независимо пытаются принимать соединения от каждого слушателя и полагаются на ядро для выполнения надлежащей балансировки между потоками. Envoy proxy позволяет настраивать различные типы балансировки подключений для каждого слушателя.

TCP#

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

Отдельная цепочка фильтров состоит из одного или нескольких фильтров сетевого уровня (L3/L4).

Когда слушатель получает новое соединение, выбирается соответствующая цепочка filter_chain, и создается экземпляр настроенного стека фильтров connection-local, который начинает обрабатывать последующие события.

Фильтры слушателя#

Фильтры слушателя Envoy proxy могут использоваться для манипулирования метаданными соединения.

Основная цель фильтров слушателя - упростить добавление дополнительных функций системной интеграции, не требуя внесения изменений в основные функциональные возможности Envoy proxy, а также сделать взаимодействие между несколькими такими функциями более понятным.

API для фильтров слушателей относительно прост, поскольку в конечном итоге эти фильтры работают с вновь принятыми сокетами.

Фильтры в цепочке могут останавливать и впоследствии продолжать итерацию для других фильтров. Это позволяет использовать более сложные сценарии, такие как вызов службы, ограничивающей скорость, и т.д.

Цепочки фильтров#

Сетевые фильтры объединяются в упорядоченный список FilterChain.

У каждого слушателя может быть несколько цепочек фильтров и необязательная default_filter_chain.

При получении запроса используется цепочка фильтров с наиболее подходящими критериями соответствия параметров запроса.

Сценарий развертывания#

Сценарий развертывания Envoy proxy в качестве балансировщика нагрузки перед службой:

  1. Создайте сервис headless service со следующим yaml:

    apiVersion: v1
    kind: Service
    metadata:
      name: myapp
    spec:
      clusterIP: None
      ports:
      - name: HTTP
        port: 80
        targetPort: HTTP
        protocol: TCP
      selector:
        app: myapp
    

    Pods имеют метку appсо с именем myapp.

    Сервис headless service не предоставляет один IP-адрес и балансировку нагрузки для pods, имеет конфигурацию DNS с IP-адресом для всех pods, соответствующих селектору меток. Этот тип службы предназначен для использования в сценариях реализации балансировки нагрузки и самостоятельной поддержки соединения с pods.

  2. Проверьте записи DNS для сервиса внутри кластера DropApp:

    nslookup myapp
    Server:         00.00.0.00
    Address:        00.00.0.10#53
    
    Non-authoritative answer:
    Name:   myapp.namespace.svc.cluster.local
    Address: 000.000.000.000
    Name:   myapp.namespace.svc.cluster.local
    Address: 000.000.000.000
    Name:   myapp.namespace.svc.cluster.local
    Address: 000.000.000.000
    

    Примечание

    IP адреса являются ненастоящими и приведены в качестве примера.

    В приведенном сценарии выведены отдельные записи с IP-адресами для трех pods в сводке DNS.

  3. Примените файл манифеста, выполните команду:

    kubectl apply -f ./dapp-envoy-proxy-deployment.yaml
    

    Содержание манифеста будет следующим:

    dapp-envoy-proxy-deployment.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: app-test-configmap
      labels:
        app: app-test-envoy
    data:
      envoy.yaml: |
        # Простая завершающая конфигурация Sidecar Envoy TLS 1.2.
        static_resources:
          listeners:
          - address:
              socket_address:
                address: 0.0.0.0
                port_value: 8080
            filter_chains:
            - filters:
              - name: envoy.filters.network.HTTP_connection_manager
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.network.HTTP_connection_manager.v3.HTTPConnectionManager
                  codec_type: AUTO
                  stat_prefix: ingress_HTTP
                  route_config:
                    name: local_route
                    virtual_hosts:
                    - name: service
                      domains: ["*"]
                      routes:
                      - match: { prefix: "/" }
                        route: { cluster: local_service_app }
                  HTTP_filters:
                  - name: envoy.filters.HTTP.router
                    typed_config: 
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          clusters:
          - name: local_service_app
            connect_timeout: 0.25s
            type: STRICT_DNS
            lb_policy: ROUND_ROBIN
            load_assignment:
              cluster_name: local_service_app
              endpoints:
              - lb_endpoints:
                - endpoint:
                    address:
                      socket_address:
                        address: app-test-service.default.svc.cluster.local
                        port_value: 80
        admin:
          access_log_path: /dev/null
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 8082
    

    Флаг type: STRICT_DNS указывает тип обнаружения службы.

    Флаг lb_policy: LEAST_REQUEST позволяет выбрать различные алгоритмы балансировки из типов ROUND_ROBIN и LEAST_REQUEST.

    Флаг hosts: [{ socket_address: { address: myapp, port_value: 80 }}] указывает в поле address на доменное имя, с которого Envoy proxy должен получить записи для маршрутизации.

  4. Убедитесь в том, что deployment успешно получен:

    kubectl rollout status deployment.apps/sidecar-test-deploy -n default
    
  5. Внесите содержание манифестов:

    Ниже приведен фрагмент кода YAML-файла для развертывания приложения с названием app-test с тремя репликами. В этом приложении используется образ Docker nginx, и контейнер имеет порт HTTP на порту 80.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: app-test-deploy
      labels:
        app: app-test
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: app-test
      template:
        metadata:
          name: app-test-pod
          labels:
            app: app-test
        spec:
          containers:
            # Приложение было спроксировано.
            - name: app-test
              image: "nginx:1.14.2"
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
    

    Ниже представлен фрагмент кода из YAML-файла, который описывает кластерную службу для приложения app-test. Служба предоставляет доступ к приложению через порт 80 и имеет тип ClusterIP, что означает, что она доступна только внутри кластера. В файле также указаны метки и селектор для службы, которые позволяют ей быть найденной и управляемой в DropApp.

    apiVersion: v1
    kind: Service
    metadata:
      name: app-test-envoy-service
      labels:
        app: app-test-envoy
    spec:
      type: NodePort
      selector:
        app: app-test-envoy
      ports:
        - port: 8080
          targetPort: 8080
          nodePort: 3000
    

    Ниже приведен фрагмент кода YAML-файла для развертывания приложения envoy. Приложение использует образ Docker с именем example и контейнером app-test-envoy. Контейнер имеет один порт HTTP, который настроен на прослушивание на порту 8080. Контейнер также использует файл конфигурации /etc/envoy, который хранится в конфигурации DropApp.

    example-app-test-envoy.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sidecar-test-deploy
      labels:
        app: sidecar-test
    spec:
      selector:
        matchLabels:
          app: sidecar-test
      template:
        metadata:
          name: sidecar-test-pod
          labels:
            app: sidecar-test
        spec:
          containers:
            # The application being proxied.
            - name: app
              image: "nginx:1.14.2"
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
            # The sidecar.
            - name: sidecar
              image: dappregistry/envoy:distroless-v1.25.2
              ports:
                - name: http
                  containerPort: 8080
                  protocol: TCP
              volumeMounts:
                - name: sidecar-config
                  mountPath: "/etc/envoy"
                  readOnly: true
          volumes:
            - name: app-config
              configMap:
                name: sidecar-test-configmap
    

    Ниже представлена конфигурация для Envoy, которая используется для обработки запросов к серверу.

    Где:

    • apiVersion указывает на версию API, на которую должна соответствовать конфигурация. В данном случае это v1;

    • kind указывает на тип объекта конфигурации. В данном случае это ConfigMap;

    • metadata содержит информацию о конфигурации, такую как имя и метки;

    • data содержит содержимое конфигурации, в данном случае файл envoy.yaml;

    • static_resources - это раздел конфигурации, который определяет статические ресурсы, такие как фильтры и слушатели;

    • listeners - это список слушателей, которые обрабатывают запросы. Каждый слушатель определяет адрес, на который будет отправлен запрос, и настройки фильтрации;

    • filter_chains - это раздел, который определяет, какие фильтры должны быть применены к запросу. В данном примере используется фильтр Envoy для обработки HTTP-запросов;

    • name и typed_config определяют параметры фильтра;

    • route_config - это раздел, определяющий маршрутизацию запросов;

    • domains - это список доменов, к которым будет применяться маршрутизация;

    • routes - это список маршрутов, которые будут применены к запросам. Каждый маршрут определяет префикс запроса, который должен соответствовать, и действия, которые должны быть выполнены.

    В целом, данная конфигурация определяет настройки Envoy для обработки запросов к серверу, который расположен на адресе 0.0.0.0 и прослушивает порт 8080.

    Настройки Envoy для обработки запросов к серверу
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: sidecar-test-configmap
      labels:
        app: sidecar-test
    data:
      envoy.yaml: |
        static_resources:
          listeners:
          - address:
              socket_address:
                address: 0.0.0.0
                port_value: 8080
            filter_chains:
            - filters:
              - name: envoy.filters.network.http_connection_manager
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                  codec_type: AUTO
                  stat_prefix: ingress_http
                  route_config:
                    name: local_route
                    virtual_hosts:
                    - name: service
                      domains: ["*"]
                      routes:
                      - match: { prefix: "/" }
                        route: { cluster: local_service }
                  http_filters:
                  - name: envoy.filters.http.router
                    typed_config: 
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          clusters:
          - name: local_service
            connect_timeout: 0.25s
            type: STATIC
            lb_policy: ROUND_ROBIN
            load_assignment:
              cluster_name: local_service
              endpoints:
              - lb_endpoints:
                - endpoint:
                    address:
                      socket_address:
                        address: 127.0.0.1
                        port_value: 80
        admin:
          access_log_path: /dev/null
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 8082
    
  6. Перенаправьте существующий порт на локальный:

    kubectl port-forward service/sidecar-test-service -n default --address localhost 8099:8080
    
  7. Введите команду для просмотра локального порта:

    Curl localhost:8099
    
  8. Проверьте, что на выходные данные возвращают страницу nginx а прокси-сервер является Envoy proxy.