Плагин kubectl-debug для отладки в pod'ах Kubernetes



    В конце прошлого года на Reddit представили плагин к kubectl, помогающий производить отладку в pod'ах кластера Kubernetes — kubectl-debug. Эта идея сразу же показалась интересной и полезной нашим инженерам, так что мы решили посмотреть на её воплощение и рады поделиться своими результатами с читателями хабры.

    Зачем это вообще нужно?


    На данный момент существует серьезное неудобство в процессе отладки чего-либо в рамках pod'ов. Основная цель при сборке образа контейнера — минимизировать его, т.е. сделать как можно меньшим в размере и содержащим как можно меньше «лишнего» внутри. Однако когда доходит дело до проблем в работе конечного софта в контейнерах либо отладки его коммуникации с другими сервисами в кластере/снаружи… минимализм играет с нами злую шутку — ведь в контейнерах ничего нет для собственно процесса поиска проблем. Как правило, недоступны такие утилиты, как netstat/ip/ping/curl/wget и т.п.

    И зачастую всё заканчивается тем, что инженер на скорую руку ставит необходимый софт прямо в работающем контейнере, чтобы «прозреть» и увидеть проблему. Именно для таких случаев плагин kubectl-debug и показался весьма полезным инструментом — ведь он спасает от насущной боли.

    С его помощью можно одной командой запустить контейнер со всеми необходимыми инструментами на борту в контексте проблемного pod'а и изучать все процессы «со стороны», находясь внутри. Если вы уже когда-либо сталкивались с troubleshooting'ом в Kubernetes, то звучит привлекательно, не так ли?

    Что представляет собой данный плагин?


    В общих чертах архитектура данного решения выглядит как связка из плагина для kubectl и агента, запускающегося с помощью контроллера DaemonSet. Плагин обслуживает команды, начинающиеся с kubectl debug …, и взаимодействует с агентами на узлах кластера. Агент в свою очередь запускается в хостовой сети, а также в pod агента монтируется хостовый docker.sock для полного доступа к контейнерам на этом сервере.

    Соответственно, при запросе на запуск отладочного контейнера в указанном pod'е:
    происходит процесс по выявлению hostIP pod'а, а также отправляется запрос агенту (работающему на подходящем хосте) о запуске отладочного контейнера в пространствах имён (namespaces), соответствующих целевому pod'у.

    Более детальное представление об этих этапах доступно в документации проекта.

    Что требуется для работы?


    Автор kubectl-debug заявляет о наличии совместимости с версиями клиента/кластера Kubernetes 1.12.0+, однако у меня под рукой оказался K8s 1.10.8, на котором всё заработало без видимых проблем… с единственным примечанием: для того, чтобы команда kubectl debug работала именно в таком виде, требуется версия kubectl именно 1.12+. В ином же случае все команды аналогичны, но вызываются только через kubectl-debug ….

    При запуске описанного в README шаблона DaemonSet'а стоит не забывать про используемые вами taint'ы на узлах: без соответствующих toleration'ов pod'ы агента туда не поселятся и, как следствие, к pod'ам, живущим на таких узлах, вы не сможете подключиться отладчиком.

    Help у отладчика весьма полный и, похоже, описывает все текущие возможности по запуску/конфигурированию плагина. В целом утилита радует большим количеством директив для запуска: можно подкладывать сертификаты, указывать контекст kubectl, указывать отдельный kubectl config или адрес API-сервера кластера и другое.

    Работа с отладчиком


    Установка до момента «всё работает» сводится к двум этапам:

    1. выполнить kubectl apply -f agent_daemonset.yml;
    2. непосредственно установить сам плагин — в целом, всё как описано здесь.

    Как же им пользоваться? Допустим, у нас следующая проблема: не происходит сбор метрик одного из сервисов в кластере — и нам хочется проверить, есть ли сетевые проблемы между Prometheus и целевым сервисом. Как легко догадаться, в образе Prometheus не хватает требуемых инструментов.

    Попробуем подключиться в контейнер с Prometheus (если в pod'е несколько контейнеров — потребуется указать, к какому конкретно подключаться, а иначе отладчик выберет первый по умолчанию):

    kubectl-debug --namespace kube-prometheus  prometheus-main-0                                    
    Defaulting container name to prometheus.
    pulling image nicolaka/netshoot:latest... 
    latest: Pulling from nicolaka/netshoot
    4fe2ade4980c: Already exists 
    ad6ddc9cd13b: Pull complete 
    cc720038bf2b: Pull complete 
    ff17a2bb9965: Pull complete 
    6fe9f5dade08: Pull complete 
    d11fc7653a2e: Pull complete 
    4bd8b4917a85: Pull complete 
    2bd767dcee18: Pull complete 
    Digest: sha256:897c19b0b79192ee5de9d7fb40d186aae3c42b6e284e71b93d0b8f1c472c54d3
    Status: Downloaded newer image for nicolaka/netshoot:latest
    starting debug container...
    container created, open tty...
     [1]   → 
    
    root @ / 

    Предварительно мы выяснили, что проблемный сервис живет на адресе 10.244.1.214 и слушает порт 8080. Конечно, мы можем проверять доступность и с хостов, однако для достоверного процесса отладки эти операции необходимо воспроизводить в идентичных (или максимально приближенных к этому) условиях. Поэтому проверка из pod'а/контейнера с Prometheus — лучший вариант. Начнём с простого:

     [1]   → ping 10.244.1.214
    PING 10.244.1.214 (10.244.1.214) 56(84) bytes of data.
    64 bytes from 10.244.1.214: icmp_seq=1 ttl=64 time=0.056 ms
    64 bytes from 10.244.1.214: icmp_seq=2 ttl=64 time=0.061 ms
    64 bytes from 10.244.1.214: icmp_seq=3 ttl=64 time=0.047 ms
    64 bytes from 10.244.1.214: icmp_seq=4 ttl=64 time=0.049 ms
    ^C
    --- 10.244.1.214 ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 61ms
    rtt min/avg/max/mdev = 0.047/0.053/0.061/0.007 ms

    Всё хорошо. Может, порт недоступен?

     [1]   → curl -I 10.244.1.214:8080
    HTTP/1.1 200 OK
    Date: Sat, 12 Jan 2019 14:01:29 GMT
    Content-Length: 143
    Content-Type: text/html; charset=utf-8

    И тут нет проблем. Тогда проверим, происходит ли собственно общение между Prometheus и endpoint'ом с метриками:

     [2]   → tcpdump host 10.244.1.214
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:04:19.234101 IP prometheus-main-0.prometheus-operated.kube-prometheus.svc.cluster.local.36278 > 10.244.1.214.8080: Flags [P.], seq 4181259750:4181259995, ack 2078193552, win 1444, options [nop,nop,TS val 3350532304 ecr 1334757657], length 245: HTTP: GET /metrics HTTP/1.1
    14:04:19.234158 IP 10.244.1.214.8080 > prometheus-main-0.prometheus-operated.kube-prometheus.svc.cluster.local.36278: Flags [.], ack 245, win 1452, options [nop,nop,TS val 1334787600 ecr 3350532304], length 0
    14:04:19.290904 IP 10.244.1.214.8080 > prometheus-main-0.prometheus-operated.kube-prometheus.svc.cluster.local.36278: Flags [P.], seq 1:636, ack 245, win 1452, options [nop,nop,TS val 1334787657 ecr 3350532304], length 635: HTTP: HTTP/1.1 200 OK
    14:04:19.290923 IP prometheus-main-0.prometheus-operated.kube-prometheus.svc.cluster.local.36278 > 10.244.1.214.8080: Flags [.], ack 636, win 1444, options [nop,nop,TS val 3350532361 ecr 1334787657], length 0
    ^C
    4 packets captured
    4 packets received by filter
    0 packets dropped by kernel

    Запросы-ответы приходят. По итогу этих операций можно заключить, что проблем на уровне сетевого взаимодействия нет, а значит (скорее всего) — смотреть надо с прикладной стороны. Подключаемся к контейнеру с exporter'ом (тоже, конечно, с помощью рассматриваемого отладчика, т.к. exporter'ы всегда имеют крайне минималистичные образы) и… с удивлением обнаруживаем, что есть проблема в конфигурации сервиса — например, забыли направить exporter на правильный адрес конечного приложения. Дело раскрыто!

    Разумеется, в описанной здесь ситуации возможны и другие пути отладки, но их мы оставим за рамками статьи. Итог же таков, что у kubectl-debug предостаточно возможностей для использования: ведь в работу можно запустить совершенно любой образ, а при желании — даже собрать какой-то свой специфичный (с необходимым набором инструментария).

    Какие ещё варианты применения сразу приходит в голову?

    • «Молчаливое» приложение, которому вредные разработчики не реализовали нормальное логирование. Зато у него есть возможность подключаться к служебному порту и проводить отладку специфичным инструментом, который в конечный образ, конечно же, класть не стоит.
    • Запуск рядом с боевым приложением идентичного в «ручном» режиме, но с включённым дебагом — для проверки взаимодействия с соседними сервисами.

    В целом же очевидно, что ситуаций, в которых такой инструмент может пригодиться, сильно больше. Инженеры, сталкивающиеся с ними в работе каждый день, смогут оценить потенциал утилиты в плане «живой» отладки.

    Выводы


    Kubectl-debug — полезный и перспективный инструмент. Конечно, есть кластеры Kubernetes и приложения, для которых он не имеет большого смысла, но всё же вероятнее случаи, когда он окажет неоценимую помощь в отладке — в особенности, если речь заходит о боевом окружении и необходимости быстро, прямо здесь и сейчас, найти причины возникшей проблемы.

    Первый опыт использования выявил острую потребность в возможности подключения к pod'у/контейнеру, который запускается не до конца (например, «висит» в CrashLoopbackOff), как раз с целью на ходу проверять причины «незапуска» приложения. По этому поводу я создал соответствующий issue в репозитории проекта, на что разработчик откликнулся положительно и пообещал реализацию в ближайшее время. Очень порадовала быстрая и адекватная обратная связь. Так что будем с нетерпением ждать новых возможностей утилиты и её дальнейшего развития!

    P.S.


    Читайте также в нашем блоге:

    Флант
    265,00
    Специалисты по DevOps и высоким нагрузкам в вебе
    Поделиться публикацией

    Комментарии 5

      0
      Ну, я так понял, что в сам исследуемый контейнер в поде эта утилита никакие ping/curl etc. не устанавливает. Т.е. в под инжектируется именно отдельный отладочный контейнер. Т.о. получается, что это не интерфейс к самому проблемному контейнеру в поде, а отдельная штука, которая может отладку только запутать (хотя действительно отмечу, что если есть сетевые проблемы, то они скорее всего распространяются на ВЕСЬ под, а не на конкретный контейнер сервиса, запущенный в поде — и этот кейс вышеописанный в статье инструментарий отлавливает). И в чем тогда преимущество от прямого выполнения команд в проблемном контейнере!?
        +1
        Ну вообще-то не совсем так.
        В документации сообщается, что:
        The agent runs a debug container with tty and stdin opened, the debug contaienr will join the pid, network, ipc and user namespace of the target container.


        Соответственно мы разделяем все основные окружения с целевым контейнером, т.е. находимся в том же контексте, включая сеть, процессы и тд.
        Так что не вижу вариантов, при которых это может запутать отладку.

        Когда проблемный под крашится — тут да, новый функционал утилиты будет «форкать» по сути существующий под, запускать отладочный под/контейнер и в него монтировать ФС проблемного контейнера. Но при этом доступен тот же chroot который поможет запускать процессы «как бы» в конечном контейнере.
          0
          Спасибо за развернутый комментарий, но тем не менее Вы не ответили на вопрос
          И в чем тогда преимущество от прямого выполнения команд в проблемном контейнере!?
            0
            В возможности подтянуть любой желаемый функционал, инструментарий и прочее полезное в рабочий образ (включая боевой) не запихивая его непосредственно в рабочий (основной) образ.
              0
              *и прочее полезное в рабочий контейнер конечно же.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое