Даниэле Поленчич - технический консультант learnk8s.io и сертифицированный партнер по обучению Kubernetes и Linux Foundation. 5 ноября 2018 года он представит Масштабирование микросервисов с помощью очередей сообщений, Spring Boot и Kubernetes на µCon London 2018. Его следующий курс на Skills Matter - Kubernetes: Master Application Deployment and Scaling 13-14 декабря 2018 года.
Когда вы развертываете приложение в Kubernetes, ваш код запускается на одном или нескольких рабочих узлах. Узел может быть физической машиной или виртуальной машиной, такой как AWS EC2 или Google Compute Engine, и наличие нескольких из них означает, что вы можете эффективно запускать и масштабировать свое приложение между экземплярами. Если у вас есть кластер, состоящий из трех узлов, и вы решили масштабировать свое приложение, чтобы иметь четыре реплики, Kubernetes равномерно распределит реплики по узлам следующим образом:
Описанная выше архитектура особенно хорошо работает в случае сбоев. Если первый узел будет недоступен, два других по-прежнему смогут обслуживать приложение. Между тем у Kubernetes есть достаточно времени, чтобы перенести четвертую реплику на другой узел.
Еще лучше, если все узлы будут изолированы, они все равно смогут обслуживать трафик. Давайте уменьшим масштаб приложения до двух реплик:
Поскольку каждый узел может обслуживать приложение, как третий узел узнает, что он не запускает приложение и должен направлять трафик на один из других узлов?
В Kubernetes есть двоичный файл kube-proxy
, который запускается на каждом узле и отвечает за маршрутизацию трафика к определенному модулю. Вы можете думать о kube-proxy
как о секретарше. Прокси-сервер перехватывает весь трафик, направленный на узел, и направляет его в правый модуль.
Но как kube-proxy
узнать, где находятся все капсулы?
Это не так.
Главный узел знает все и отвечает за создание списка со всеми правилами маршрутизации. kube-proxy
отвечает за проверку и соблюдение правил в списке. В приведенном выше простом сценарии список выглядит так:
- Экземпляр приложения 1 доступен на узле 1
- Экземпляр приложения 2 доступен на узле 2
Неважно, с какого узла идет трафик; kube-proxy
знает, куда должен быть направлен трафик, просматривая список.
Но что происходит, когда kube-proxy
вылетает?
А что, если список правил утерян?
Что произойдет, если нет правила для перенаправления трафика?
У Манабу Сакаи были те же вопросы. Поэтому он решил выяснить.
Предположим, у вас есть двухузловой кластер на GCP:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION node1 Ready <none> 17h v1.8.8-gke.0 node2 Ready <none> 18h v1.8.8-gke.0
И вы развернули приложение Manabu с:
$ kubectl create -f https://raw.githubusercontent.com/manabusakai/k8s-hello-world/master/kubernetes/deployment.yml $ kubectl create -f https://raw.githubusercontent.com/manabusakai/k8s-hello-world/master/kubernetes/service.yml
Приложение простое. Он отображает имя хоста текущего модуля на веб-странице:
Вы должны масштабировать развертывания до десяти реплик с помощью:
$ kubectl scale --replicas 10 deployment/k8s-hello-world
Десять реплик равномерно распределены по двум узлам:
$ kubectl get pods NAME READY STATUS NODE k8s-hello-world-55f48f8c94-7shq5 1/1 Running node1 k8s-hello-world-55f48f8c94-9w5tj 1/1 Running node1 k8s-hello-world-55f48f8c94-cdc64 1/1 Running node2 k8s-hello-world-55f48f8c94-lkdvj 1/1 Running node2 k8s-hello-world-55f48f8c94-npkn6 1/1 Running node1 k8s-hello-world-55f48f8c94-ppsqk 1/1 Running node2 k8s-hello-world-55f48f8c94-sc9pf 1/1 Running node1 k8s-hello-world-55f48f8c94-tjg4n 1/1 Running node2 k8s-hello-world-55f48f8c94-vrkr9 1/1 Running node1 k8s-hello-world-55f48f8c94-xzvlc 1/1 Running node2
Служба была создана для балансировки нагрузки запросов по десяти репликам:
$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE k8s-hello-world NodePort 100.69.211.31 <none> 8080:30000/TCP 3h kubernetes ClusterIP 100.64.0.1 <none> 443/TCP 18h
Служба предоставляется внешнему миру через NodePort
на порту 30000. Другими словами, каждый узел имеет порт 30000, открытый для общедоступного Интернета, и может принимать входящий трафик.
Но как трафик направляется от порта 30000 к моему модулю?
kube-proxy
отвечает за настройку правил для маршрутизации входящего трафика от порта 30000 к одному из десяти модулей.
Вы должны попытаться запросить узел на порту 30000:
Обратите внимание, что вы можете получить IP-адрес узла с помощью
kubectl get nodes -o wide
Приложение отвечает Hello World!, а имя хоста контейнера запущено. В предыдущей команде вас должен приветствовать Hello world! via <hostname>
.
Если вы продолжите запрашивать один и тот же URL-адрес, вы можете заметить, как иногда вы получаете один и тот же ответ, а иногда он меняется. kube-proxy
действует как балансировщик нагрузки, просматривает список маршрутизации и распределяет трафик между десятью модулями.
Что еще интереснее, не имеет значения, какой узел вы запрашиваете. Ответ может прийти из любого модуля, даже если он не размещен на том же узле, который вы запросили.
Для завершения настройки у вас должен быть внешний балансировщик нагрузки, маршрутизирующий трафик на ваши узлы через порт 30000.
Балансировщик нагрузки направит входящий трафик из Интернета на один из двух узлов.
Если вас смущает, сколько у нас есть вещей, похожих на балансировщик нагрузки, давайте быстро резюмируем:
- Трафик, исходящий из Интернета, направляется на основной балансировщик нагрузки.
- Балансировщик нагрузки перенаправляет трафик на один из двух узлов на порт 30000.
- Правила, установленные
kube-proxy
, направляют трафик от узла к поду. - трафик достигает капсулы
Фух! Это было давно!
👍 Чтобы получать новости и статьи из Skills Matter, подпишитесь на нашу рассылку здесь.
Пришло время сломать вещи
Теперь, когда вы знаете, как все взаимосвязано, давайте вернемся к исходному вопросу.
Что делать, если вы нарушите правила маршрутизации?
Будет ли кластер работать?
Пакеты по-прежнему обслуживают запросы?
Давайте удалим правила маршрутизации.
В отдельной оболочке вы должны следить за приложением по времени и потерянным запросам.
Вы можете написать цикл, который каждую секунду печатает время и запрашивает приложение:
$ while sleep 1; do date +%X; curl -sS http://<your load balancer ip>/ | grep ^Hello; done
В этом случае у вас есть время в первом столбце и ответ от модуля в другом:
10:14:41 Hello world! via k8s-hello-world-55f48f8c94-vrkr9 10:14:43 Hello world! via k8s-hello-world-55f48f8c94-tjg4n
Первый вызов модуля k8s-hello-world-55f48f8c94-vrkr9 был сделан в 10:14 и 41 секунда.
Второй вызов был сделан в модуль k8s-hello-world-55f48f8c94-tjg4n в 10:14 и 43 секунды.
Удалим правила маршрутизации с узла.
kube-proxy
может работать в трех режимах: пользовательское пространство, iptables и ipvs. По умолчанию, начиная с Kubernetes 1.2, это iptables.
В режиме iptables kube-proxy
записывает список правил маршрутизации на узел, используя правила iptables.
Таким образом, вы можете войти в систему на одном из серверов узлов и удалить правила iptables с помощью iptables -F
.
Обратите внимание, что
iptables -F
может мешать вашему SSH-соединению.
Если все пойдет по плану, вы должны испытать нечто подобное:
10:14:41 Hello world! via k8s-hello-world-55f48f8c94-xzvlc 10:14:43 Hello world! via k8s-hello-world-55f48f8c94-tjg4n # this is when `iptables -F` was issued 10:15:10 Hello world! via k8s-hello-world-55f48f8c94-vrkr9 10:15:11 Hello world! via k8s-hello-world-55f48f8c94-vrkr9
Как вы заметили, с момента сброса правил iptables и следующего ответа прошло около 27 секунд, с 10:14:43 до 10:15:10.
Что произошло за эти 27 секунд?
Почему все возвращается в нормальное состояние через 27 секунд?
Возможно, это просто совпадение. Давайте снова очистим правила:
11:29:55 Hello world! via k8s-hello-world-55f48f8c94-xzvlc 11:29:56 Hello world! via k8s-hello-world-55f48f8c94-tjg4n # this is when `iptables -F` was issued 11:30:25 Hello world! via k8s-hello-world-55f48f8c94-npkn6 11:30:27 Hello world! via k8s-hello-world-55f48f8c94-vrkr9
Был разрыв в 29 секунд с 11:29:56 до 11:30:25, но кластер вернулся в нормальное состояние.
Почему на ответ уходит около 30 секунд?
Получает ли узел трафик, несмотря на отсутствие таблицы маршрутизации?
Может быть, вы могли бы исследовать, что происходит с узлом за эти 30 секунд.
В другом терминале вы должны написать цикл, чтобы делать запросы к приложению каждую секунду. Но на этот раз вы должны запросить узел, а не балансировщик нагрузки:
$ while sleep 1; printf %"s\n" $(curl -sS http://<ip of the node>:30000); done
И давайте откажемся от правил iptables. Журнал предыдущей команды:
Hello world! via k8s-hello-world-55f48f8c94-xzvlc Hello world! via k8s-hello-world-55f48f8c94-tjg4n # this is when `iptables -F` was issued curl: (28) Connection timed out after 10003 milliseconds curl: (28) Connection timed out after 10004 milliseconds Hello world! via k8s-hello-world-55f48f8c94-npkn6 Hello world! via k8s-hello-world-55f48f8c94-vrkr9
Неудивительно, что время ожидания подключения к узлу истекает после того, как вы отбрасываете правила iptables. Что еще интереснее, curl
выжидает десять секунд, прежде чем сдаться.
Что делать, если в предыдущем примере балансировщик нагрузки ожидает установления соединения?
Это объясняет 30-секундную задержку. Но он не говорит, почему узел готов принять соединение, если вы ждете достаточно долго.
Так почему трафик восстанавливается через 30 секунд?
Кто возвращает правила iptables?
Прежде чем отбросить правила iptables, вы можете проверить их с помощью:
Вскоре после того, как вы отбросите правила, вы должны продолжить выполнение iptables -F
и заметить, что правила вернутся через несколько секунд!
Это ты, kube-proxy
?
Да, это так.
Копаясь в официальной документации для kube-proxy
, можно обнаружить два интересных флага:
--iptables-sync-period
- Максимальный интервал обновления правил iptables (например, «5сек», «1м», «2ч22м»). Должно быть больше 0. (по умолчанию 30 секунд)--iptables-min-sync-period
- минимальный интервал обновления правил iptables при изменении конечных точек и сервисов (например, «5 с», «1 мин», «2 ч 22 мин»). (по умолчанию 10 с)
kube-proxy
обновляет правила iptables каждые 10–30 секунд. Если мы отбросим правила iptables, kube-proxy
потребуется до 30 секунд, чтобы реализовать и восстановить их.
Это объясняет, почему на возврат узла потребовалось 30 секунд!
Он также объясняет, как таблицы маршрутизации распространяются от главного узла к рабочему узлу. kube-proxy
отвечает за их регулярную синхронизацию. Другими словами, каждый раз, когда модуль добавляется или удаляется, главный узел пересчитывает список маршрутизации. Регулярно kube-proxy
синхронизирует правила с текущим узлом.
Давайте вспомним, как Kubernetes и kube-proxy
могут оправиться от несанкционированного вмешательства в правила iptables на узле:
- Правила iptables удаляются с узла
- Запрос перенаправляется на балансировщик нагрузки и направляется на узел.
- Узел не принимает входящие запросы, поэтому балансировщик нагрузки ожидает
- Через 30 секунд
kube-proxy
восстанавливает iptables - Узел снова может обслуживать трафик. Правила iptables перенаправляют запрос от балансировщика нагрузки к поду.
- Модуль отвечает на балансировщик нагрузки с задержкой в 30 секунд.
Ожидание в течение 30 секунд может быть неприемлемым для вашего приложения. Возможно, вас заинтересует настройка интервала обновления по умолчанию для kube-proxy
.
Так где же настройки и как их изменить?
Оказывается, на узле есть агент - кубелет - который отвечает за запуск kube-proxy
как статического модуля на каждом узле. Документация для статических модулей предполагает, что кубелет сканирует определенную папку и создает все ресурсы, содержащиеся в этой папке.
Если вы проверите процесс кублета в узле, вы увидите, что кубелет работает с --pod-manifest-path=/etc/kubernetes/manifests
.
Выполнение простого ls
раскрывает правду:
$ ls -l /etc/kubernetes/manifests total 4 -rw-r--r-- 1 root root 1398 Feb 24 08:08 kube-proxy.manifest
И быстрый cat
из kube-proxy.manifest
раскрывает содержание:
apiVersion: v1 kind: Pod metadata: name: kube-proxy spec: hostNetwork: true containers: - name: kube-proxy image: gcr.io/google_containers/kube-proxy:v1.8.7-gke.1 command: - /bin/sh - -c -> echo -998 > /proc/$$$/oom_score_adj && exec kube-proxy --master=https://35.190.207.197 --kubeconfig=/var/lib/kube-proxy/kubeconfig --cluster-cidr=10.4.0.0/14 --resource-container="" --v=2 --feature-gates=ExperimentalCriticalPodAnnotation=true --iptables-sync-period=30s 1>>/var/log/kube-proxy.log 2>&1
Обратите внимание, что содержание было усечено и не отображается полностью.
Тайна разгадана!
Вы можете увидеть, как --iptables-sync-period=30s
используется для обновления правил iptables каждые 30 секунд. Вы можете пойти дальше и изменить эту команду, чтобы настроить минимальное и максимальное время для обновления правил iptables для этого узла.
Уроки выучены
Удаление правил iptables похоже на отключение узла. Трафик по-прежнему направляется к узлу, но узел не может пересылать его дальше. Kubernetes может восстановиться после подобного сбоя, отслеживая состояние правил маршрутизации и обновляя их при необходимости.
Большое спасибо сообщению в блоге Manabu Sakai, которое стало огромным источником вдохновения, и Valentin Ouvrard за исследование проблемы с распространением iptables.
Вот и все, ребята!
Если вам понравилась эта статья, возможно, вам будет интересно почитать:
- 3 простых приема для создания образов Docker меньшего размера и узнайте, как быстрее создавать и развертывать образы Docker.
- Масштабирование микросервисов с помощью очередей сообщений, Spring Boot и Kubernetes. Узнайте, как использовать Horizontal Pod Autoscaler для динамического изменения размера вашего парка приложений.
Станьте экспертом в развертывании и масштабировании приложений в Kubernetes
Нарушение кластера - один из основных модулей наших учебных курсов.
В конце концов, нужно быть готовым к худшему.
Никогда не знаешь!
Начните с наших практических курсов и узнайте, как добиться масштабируемости в облаке.
Узнайте, как:
- Управляйте сайтами с наибольшей посещаемостью, не беспокоясь
- Масштабируйте свои задания до тысяч серверов и сократите время ожидания с дней до минут
- Наслаждайтесь спокойствием, зная, что ваши приложения высокодоступны благодаря настройке с несколькими облаками
- Сэкономьте кучу денег на счетах за облако, используя только те ресурсы, которые вам нужны
- Усовершенствуйте свой конвейер доставки и развертывайте приложение круглосуточно
Стать экспертом в Kubernetes →
Первоначально опубликовано на learnk8s.io