Настройте производительность приложений Kubernetes с помощью небольшой конфигурации DNS


Введение

Одним из наиболее распространенных узких мест при взаимодействии стручков Kubernetes является разрешение DNS. Анализ сетевого трафика в Kubernetes не является тривиальной задачей.

Эта статья поможет вам понять, как анализировать и отлаживать сетевой трафик HTTP и DNS в Kubernetes.

С вашими комментариями и помощью я хотел бы улучшить эту небольшую статью, чтобы охватить все профили и помочь любому разработчику, сетевому инженеру или энтузиасту Kubernetes.

Что такое DNS?

DNS — это протокол, который позволяет компьютеру преобразовывать доменное имя (например, dev.to), написанное человеком, в IP-адрес, чтобы браузеры могли загружать интернет-ресурсы. DNS называют телефонной книгой Интернета.

Как отладить сетевой трафик DNS — Wireshark

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

Что происходит при вызове веб-страницы?

Чтобы понять, что происходит с DNS при запросе веб-страницы, можно начать с этой диаграммы от @wassimchegham.

DNS-запрос, TCP-соединение, HTTP-запрос и HTTP-ответ могут быть отображены с помощью различных фильтров и статистики — Flow Graph:

В следующем разделе мы узнаем, как его создать.

Создание графика потока Wireshark

Установите Wireshark на вашу любимую операционную систему (Mac, Windows или Linux) и откройте его.

Начальный экран должен выглядеть следующим образом:

Подготовка фильтра

Прежде чем перехватывать трафик, давайте найдем IP-адрес домена www.example.com.

Откройте терминал или консоль CMD и выполните команду:

nslookup www.example.com
Войти в полноэкранный режим Выйти из полноэкранного режима

Ответ будет выглядеть следующим образом:

Server: 8.8.8.8
Address: 8.8.8.8#53

Non-authoritative answer:
Name: www.example.com
Address: 93.184.216.34
Войти в полноэкранный режим Выйти из полноэкранного режима

Поле Server указывает на DNS-сервер, настроенный в среде, к которому nslookup будет обращаться для разрешения имени www.example.com.

Найдите имя в поле Non-authoritative answer: и связанный с ним IP. В примере 93.184.216.34.

Под опцией «Использовать этот фильтр:» на главном экране введите:

tcp port http or port 53 or dst host 93.184.216.34

93.184.216.34 — это адрес, разрешенный с помощью nslookup.

Перехват трафика

Выберите интерфейс для начала захвата сетевого трафика. Обычно это должен быть интерфейс, где колонка Packet/s постоянно меняется, что указывает на наличие живого трафика).

Нажмите на синий значок плавника, чтобы начать захват трафика.

Запрос URL-адреса в локальном клиенте

Если вы используете Linux или Mac, выполните следующие действия из терминала:

curl http://example.com
Войти в полноэкранный режим Выйти из полноэкранного режима

В Windows используйте Powershell и выполните следующее:

Invoke-WebRequest http://www.example.com
Войти в полноэкранный режим Выйти из полноэкранного режима

Остановите перехват трафика, просто нажав большую красную кнопку. Ваш экран должен выглядеть следующим образом:

Чтобы получить обещанный график потока, выберите меню Статистика и График потока.

Понимание захваченного трафика

При анализе захваченного трафика различают две части. DNS (голубой цвет) и HTTP (зеленый цвет).

Первая строка (голубого цвета) показывает DNS запрос с нашего IP адреса на DNS сервер (8.8.8.8 в примере).

Вторая голубая строка показывает ответ DNS. Выберите его, чтобы найти IP-адрес запрашиваемого домена в окне Packet Details. В разделе Domain Name System (Response) — Ответы, как показано на рисунке ниже.

Трехстороннее рукопожатие (SYN-ACK)

Находится в следующих трех строках (зеленым цветом).

Прежде чем клиент и сервер смогут обмениваться данными (полезной нагрузкой), они должны установить TCP-соединение с помощью трехстороннего рукопожатия TCP.

SYN — Клиент посылает серверу пакет SYN (синхронизация).
SYN-ACK — сервер посылает клиенту пакет SYN-ACK (подтверждение синхронизации).
ACK — клиент посылает серверу пакет ACK (Acknowledge).

На рисунке это происходит после запроса DNS.

Запрос и закрытие соединения (FIN ACK)

После SYN-ACK идет HTTP GET и ACK от сервера с HTTP-ответом.

И, наконец, TCP запрашивает пакеты FIN-ACK для закрытия соединения.

DNS в Kubernetes

Чтобы понять, как доменное имя разрешается в капсуле, сначала создадим одно развертывание:

kubectl create deployment nginx --image nginx
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы проверить, готово ли развертывание, выполните команду:

kubectl get deployments -o wide
Войти в полноэкранный режим Выйти из полноэкранного режима
NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES   SELECTOR
nginx   1/1     1            1           26m   nginx        nginx    app=nginx
Войти в полноэкранный режим Выйти из полноэкранного режима

Помните, что разрешение DNS внутри контейнера — как и в любой системе Linux — управляется конфигурационным файлом /etc/resolv.conf.

kubectl exec deployments/nginx -- cat /etc/resolv.conf
Вход в полноэкранный режим Выход из полноэкранного режима

По умолчанию файл /etc/resolv.conf внутри контейнера выглядит следующим образом:

search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
Войти в полноэкранный режим Выход из полноэкранного режима

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

Обратите внимание на опцию ndots:5. Важно понимать, как оба параметра search и ndots работают вместе.

Чтобы понять обе концепции, обратитесь к странице руководства resolv.conf(5) Linux

Параметры search представляют собой путь поиска для определенного домена. Интересно, что dev.to или example.com не являются FQDN (полностью квалифицированным доменным именем). Стандартное соглашение, которому следует большинство DNS-резолверов, гласит, что если домен заканчивается точкой (.) (представляющей корневую зону), то домен является FQDN. Некоторые преобразователи пытаются действовать умно и сами добавляют точку (.). Поэтому dev.to. является FQDN, а dev.to — нет.

Один важный момент из руководства resolv.conf(5) Linux Для сред с несколькими поддоменами, пожалуйста, прочитайте опции ndots:n, чтобы избежать ненужного трафика для корневых dns-серверов. Обратите внимание, что этот процесс может быть медленным и генерировать много сетевого трафика, если серверы для перечисленных доменов не являются локальными, и что запросы будут выполняться с задержкой, если ни один сервер не доступен для одного из доменов.

ndots представляет собой пороговое значение количества точек в имени запроса, чтобы считать его «полностью квалифицированным» доменным именем.

Если ndots равно пяти (5) (значение по умолчанию в Kubernetes), а имя содержит менее пяти (5) точек внутри, системный вызов попытается сначала разрешить его последовательно через все локальные поисковые домены, и — в случае, если ни один из них не достигнет успеха — разрешит его как абсолютное имя только в последнюю очередь. Например, доменное имя www.example.com содержит две точки (.), а количество точек (.) меньше значения ndots.

Поэтому при запросе доменного имени DNS-запрос итерирует все пути поиска, пока ответ не будет содержать код NOERROR.

  1. www.example.com.<namespace>.svc.cluster.local
  2. www.google.com.svc.cluster.local
  3. www.google.cluster.local
  4. www.google.com

Важно отметить, что DNS запрашивает записи A и AAAA параллельно. Опция single-request в /etc/resolv.conf имеет конфигурацию по умолчанию для выполнения параллельного поиска IPv4 и IPv6. Эту опцию можно отключить с помощью параметра конфигурации single-request в конфигурационном файле /etc/resolv.conf.

option single-request
Вход в полноэкранный режим Выход из полноэкранного режима

Захват трафика в подсистеме Kubernetes

Существуют различные способы захвата трафика в Kubernetes Pod. Все примеры основаны на последней функциональности Kubernetes, использующей Ephemeral Debug Containers.

Сначала создадим отладочный образ tcpdump. Wireshark будет использовать его для отображения трафика подключенных стручков. Процесс сборки создает мультиплатформенный образ, чтобы его можно было запустить в linux/amd64 и linux/arm64.

Dockerfile

ARG build_for=linux/amd64,linux/arm64
FROM alpine:latest as base

LABEL maintainer="<yourusername>"

# Packages to build image requirements
RUN apk add --no-cache 
    tcpdump

ENTRYPOINT [ "tcpdump" ]
CMD [ "-i", "any" ]
Вход в полноэкранный режим Выход из полноэкранного режима

Команда сборки

Для создания мультиплатформенного образа в dockerhub давайте создадим экземпляр builder и воспользуемся расширенными возможностями сборки с помощью BuildKit.

docker buildx create --name buildx --driver-opt network=host --use
docker buildx inspect --bootstrap
docker buildx build -t <yourusername>/tcpdump:v1.0.0 --platform linux/amd64 --platform linux/arm64 --file Dockerfile --push .
docker buildx imagetools inspect <yourusername>/tcpdump:v1.0.0
docker buildx rm buildx
Войти в полноэкранный режим Выход из полноэкранного режима

На выходе показаны оба образа с их платформами:

Name:      docker.io/<yourusername>/tcpdump:v1.0.0
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:9dd8cb1d4b77b7d02d41ff8418cd442c01badfe8ecd0c0a3a58f43f528eba378

Manifests:
  Name:      docker.io/<yourusername>/tcpdump:v1.0.0@sha256:0c341c671566dbc3cdded9da05120bb2216142f46516c14cf3d10b6c38997195
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64

  Name:      docker.io/<yourusername>/tcpdump:v1.0.0@sha256:c6de3ab95521c9e7e07a05d99935d19686b8d6e81ab85ce631312cffe57d2ce3
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm64
Вход в полноэкранный режим Выход из полноэкранного режима

Прикрепление эфемерного контейнера к Nginx Pod

Теперь, когда образ tcpdump готов, создайте эфемерный контейнер под названием debugger:

kubectl debug --image <yourusername>/tcpdump:v1.0.0 -c debugger $(kubectl get pod -l app=nginx -o name)
Войдите в полноэкранный режим Выход из полноэкранного режима

Подключение Wireshark к эфемерному контейнеру

После создания запустите Wireshark и подключите его к только что созданному контейнеру.

kubectl exec -c debugger deployments/nginx -- tcpdump -s 0 -n -w - -U -i any | Wireshark -kni -
Вход в полноэкранный режим Выход из полноэкранного режима

Запрос URL-адреса с POD

Как и на локальной машине, выполните curl http://example.com в Nginx Pod.

kubectl exec deployments/nginx -c nginx -- curl http://example.com
Войти в полноэкранный режим Выход из полноэкранного режима

Анализ трафика

Как и ожидалось, есть восемь (8) запросов к DNS (A и AAAA) с отрицательным ответом No such name на каждую пару запросов, пока не будет достигнут конец списка поиска и попытка с example.com.

Почему ndots:5?

ndots:5 генерирует ненужные DNS-запросы. Так почему же пять (5) является значением по умолчанию?

Два комментария в коде Kubernetes объясняют, почему ndots должен быть равен пяти (5) в Kubernetes:

  1. Компромисс между функциональностью и производительностью
  2. Имена поиска SRV

Причина, по которой ndots равно пяти (5), заключается в том, чтобы позволить искать SRV-записи относительно домена кластера.

Типичная SRV-запись имеет форму _service._protocol.name., а в Kubernetes имя имеет форму service.namespace.svc. Сформированная запись будет выглядеть как _service._protocol.service.namespace.svc. Этот запрос содержит четыре точки. Если ndots равно четырем (4), это будет считаться FQDN и не сможет разрешиться. Если ndots будет пять (5), он не будет считаться FQDN и будет искаться относительно cluster.local.

Проверка теории с ndots меньше пяти (5).

Давайте посмотрим на практике, как ndots ведет себя со службой DNS, и как он ломается, когда его значение меньше пяти (5).

Перезапустите развертывание nginx, чтобы начать с нуля:

kubectl rollout restart deployment nginx
Войдите в полноэкранный режим Выйти из полноэкранного режима

Подготовьте файл для исправления текущего развертывания с новой конфигурацией ndots и примените его.

---
spec:
  template:
    spec:
      dnsConfig:
        options:
          - name: ndots
            value: '4'
Войти в полноэкранный режим Выход из полноэкранного режима
kubectl patch deployments.apps nginx --patch-file ndots-patch.yaml
Войти в полноэкранный режим Выход из полноэкранного режима

Установите nslookup в капсулу для тестирования

kubectl exec deployments/nginx -c nginx -- bash -c "apt-get update && apt-get install dnsutils -y"
Войти в полноэкранный режим Выход из полноэкранного режима

Подготовка эфемерного контейнера для тестирования ndots

Следуя той же процедуре, что и с Wireshark, создайте эфемерный контейнер под названием debugger:

kubectl debug --image <yourusername>/tcpdump:v1.0.0 -c debugger $(kubectl get pod -l app=nginx -o name)
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Подключение Wireshark для тестирования ndots

После создания запустите Wireshark и подключите его к только что созданному контейнеру.

kubectl exec -c debugger deployments/nginx -- tcpdump -s 0 -n -w - -U -i any | Wireshark -kni -
Войти в полноэкранный режим Выход из полноэкранного режима

Разрешение службы DNS

Запустите nslookup _dns._udp.kube-dns.kube-system.svc в Nginx Pod.

kubectl exec deployments/nginx -c nginx -- nslookup _dns._udp.kube-dns.kube-system.svc
Войдите в полноэкранный режим Выйдите из полноэкранного режима

В консоли ошибка ясна:

** server can't find _dns._udp.kube-dns.kube-system.svc: NXDOMAIN
Войти в полноэкранный режим Выход из полноэкранного режима

Также в трафике Wireshark:

Тест с ndots:5 проходит, если изменить патч на «

---
spec:
  template:
    spec:
      dnsConfig:
        options:
          - name: ndots
            value: '5'
Войти в полноэкранный режим Выйти из полноэкранного режима

Примените тот же способ:

kubectl patch deployments.apps nginx --patch-file ndots-patch.yaml
Войти в полноэкранный режим Выйти из полноэкранного режима

Установите nslookup в капсулу для тестирования

kubectl exec deployments/nginx -c nginx -- bash -c "apt-get update && apt-get install dnsutils -y"
Войти в полноэкранный режим Выйти из полноэкранного режима

и повторите процесс:

kubectl exec deployments/nginx -c nginx -- nslookup _dns._udp.kube-dns.kube-system.svc
Войти в полноэкранный режим Выйти из полноэкранного режима

Создайте эфемерный контейнер с именем debugger:

kubectl debug --image <yourusername>/tcpdump:v1.0.0 -c debugger $(kubectl get pod -l app=nginx -o name)
Войти в полноэкранный режим Выйти из полноэкранного режима

Запустите Wireshark:

kubectl exec -c debugger deployments/nginx -- tcpdump -s 0 -n -w - -U -i any | Wireshark -kni -
Войти в полноэкранный режим Выйти из полноэкранного режима

Запустите nslookup _dns._udp.kube-dns.kube-system.svc в Nginx Pod.

kubectl exec deployments/nginx -c nginx -- nslookup _dns._udp.kube-dns.kube-system.svc
Вход в полноэкранный режим Выйдите из полноэкранного режима

В консоли отображается:

Name:   _dns._udp.kube-dns.kube-system.svc.cluster.local
Address: 10.96.0.10
Вход в полноэкранный режим Выход из полноэкранного режима

И Wireshark правильно зацикливается на DNS search до достижения cluster.local.

ndots:5 может негативно повлиять на производительность

Стандартная конфигурация ndots идеально подходит для стандартных сервисов Kubernetes, но она не должна подходить для развернутых позже микросервисов.

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

Тестирование производительности CoreDNS с несколькими запросами

Пока у нас есть богатая теория, но нет данных. Наблюдаемость нуждается как в достаточном количестве данных, так и в теории, в рамках которой эти данные могут быть уточнены.

Давайте создадим тест внутри кластера Kubernetes с помощью небольшого стручка. Он будет запускать от 60 до 80 запросов в минуту. Этого диапазона запросов достаточно, чтобы избежать перегрузки кластера и проверить поведение ndots.

Сборка приложения

Используется код на языке python:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import asyncio
import concurrent.futures
import logging
import sys
import os
import requests

logging.basicConfig(format="%(asctime)s %(levelname)s:%(name)s: %(message)s",
                    datefmt="%d-%b-%y %H:%M:%S", stream=sys.stdout, level=logging.INFO)

logger = logging.getLogger('httprequests')
logging.getLogger("chardet.charsetprober").disabled = True

benchuri = os.environ.get('BENCHURI', 'http://www.example.com')


async def main():

    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

        fetch = asyncio.get_event_loop()
        futures = [
            fetch.run_in_executor(
                executor,
                requests.get,
                benchuri
            )
            for i in range(20)
        ]
        for request in await asyncio.gather(*futures):
            logger.info(request)

if __name__ == '__main__':

    while True:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
Войти в полноэкранный режим Выйти из полноэкранного режима

И Docker-файл:

ARG build_for=linux/amd64,linux/arm64
FROM python:alpine3.10 as base

LABEL maintainer="<yournamehere>"

RUN mkdir /app
COPY dnsrequest.py /app/dnsrequest.py
ADD requirements.txt requirements.txt

RUN pip install --upgrade pip
RUN pip install -r requirements.txt && chmod +x /app/dnsrequest.py && adduser -S none
USER none

WORKDIR /app

ENTRYPOINT ["python", "dnsrequest.py"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Файл python называется dnsrequest.py, а requirements.txt содержит одну строку:

requests
Войти в полноэкранный режим Выйти из полноэкранного режима

Процесс создания Dockerfile такой же, как и в случае с tcpdump выше.

docker buildx create --name buildx --driver-opt network=host --use
docker buildx inspect --bootstrap
docker buildx build -t <yourusername>/dnsbench:v1.0.0 --platform linux/amd64 --platform linux/arm64 --file Dockerfile --push .
docker buildx imagetools inspect <yourusername>/dnsbench:v1.0.0
docker buildx rm buildx
Войти в полноэкранный режим Выйти из полноэкранного режима

Запуск приложения

Для проведения теста достаточно Minikube под Docker. Чтобы создать развертывание, выполните следующие действия:

kubectl create deployment dnsbench --image <yourusername>/dnsbench:v1.0.0
Войти в полноэкранный режим Выйти из полноэкранного режима

После запуска CPU от CoreDNS сразу же увеличится при конфигурации по умолчанию. Журнал DNS показывает всю работу, выполненную от одного запроса.

Примечание: Чтобы активировать журналы CoreDNS, отредактируйте DNS configmap и добавьте слово log в конфигурации coredns:

[INFO] 10.244.0.7:33509 - 64525 "AAAA IN www.example.com.default.svc.cluster.local. udp 59 false 512" NXDOMAIN qr,aa,rd 152 0.000022583s
[INFO] 10.244.0.7:33509 - 64400 "A IN www.example.com.default.svc.cluster.local. udp 59 false 512" NXDOMAIN qr,aa,rd 152 0.00005575s
[INFO] 10.244.0.7:35864 - 8087 "AAAA IN www.example.com.svc.cluster.local. udp 51 false 512" NXDOMAIN qr,aa,rd 144 0.000044s
[INFO] 10.244.0.7:35864 - 8004 "A IN www.example.com.svc.cluster.local. udp 51 false 512" NXDOMAIN qr,aa,rd 144 0.000053792s
[INFO] 10.244.0.7:35569 - 21607 "AAAA IN www.example.com.cluster.local. udp 47 false 512" NXDOMAIN qr,aa,rd 140 0.00004025s
[INFO] 10.244.0.7:35569 - 21524 "A IN www.example.com.cluster.local. udp 47 false 512" NXDOMAIN qr,aa,rd 140 0.000065667s
[INFO] 10.244.0.7:53735 - 22419 "AAAA IN www.example.com. udp 33 false 512" NOERROR qr,aa,rd,ra 76 0.000035041s
[INFO] 10.244.0.7:53735 - 22335 "A IN www.example.com. udp 33 false 512" NOERROR qr,aa,rd,ra 64 0.000027542s
Вход в полноэкранный режим Выйти из полноэкранного режима

Изменив параметр DNS ndots:5 на ndots:1 с

---
spec:
  template:
    spec:
      dnsConfig:
        options:
          - name: ndots
            value: '1'
Войти в полноэкранный режим Выход из полноэкранного режима
kubectl patch deployments.apps dnsbench --patch-file ndots-patch.yaml
Войти в полноэкранный режим Выйти из полноэкранного режима

Или изменение URI запроса с http://www.example.com на http://www.example.com. (Обратите внимание на точку (.) в конце).

Процессор радикально снижается. Журнал CoreDNS показывает:

[INFO] 10.244.0.11:54982 - 63488 "A IN www.example.com. udp 33 false 512" NOERROR qr,aa,rd,ra 64 0.000057167s
[INFO] 10.244.0.11:54982 - 63571 "AAAA IN www.example.com. udp 33 false 512" NOERROR qr,aa,rd,ra 76 0.000119333s
Вход в полноэкранный режим Выход из полноэкранного режима

Найдите загрузку ЦП на диаграмме ЦП CoreDNS ниже:

ЦП приложения не меняется, а запросы имеют одинаковую частоту:

Что касается сетевого трафика в CoreDNS, то при ndots:5 также наблюдается истощение:

И в приложении:

Вывод

Используйте конкретные ndots для вашего приложения в разделе spec - dnsConfig. Помните, что ndots:1 игнорирует список search, поскольку имя запроса удовлетворяет порогу ndots (хотя бы одна точка).

Агрессивный ndots:1 заставляет использовать полный домен для каждого внутриузлового взаимодействия. Использование полных имен может быть описано как «обходной путь» на разных ресурсах. Я считаю это правильной реализацией.

Когда у приложения много DNS-запросов, ndots:2 увеличивает производительность DNS и задержку.

Оцените статью
devanswers.ru
Добавить комментарий