company_banner

Как с tcpserver и netcat открыть туннель в Kubernetes pod или контейнер

Автор оригинала: Colin Chartier
  • Перевод
Прим. перев.: Эта практическая заметка от создателя LayerCI — отличная иллюстрация так называемых tips & tricks для Kubernetes (и не только). Предлагаемое здесь решение — лишь одно из немногих и, пожалуй, не самое очевидное (для некоторых случаев может подойти уже упомянутый в комментариях «родной» для K8s kubectl port-forward). Однако оно позволяет как минимум посмотреть на проблему с позиции применения классических утилит и их дальнейшего комбинирования — одновременно простого, гибкого и мощного (см. «другие идеи» в конце для вдохновения).



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

Возможные сценарии использования


  1. Проверить, что возвращает HTTP endpoint /healthz pod'а в production-кластере.
  2. Подключить TCP-отладчик к pod'у на локальной машине.
  3. Получить доступ к production-базе из локальных инструментов для работы с БД без необходимости возиться с аутентификацией (обычно у localhost'а есть права root'а).
  4. Запустить одноразовый скрипт миграции для данных в staging-кластере без необходимости создавать для него контейнер.
  5. Подключить сессию VNC к pod'у с запущенным виртуальным рабочим столом (см. XVFB).

Несколько слов о необходимых инструментах


Tcpserver — Open Source-утилита, доступная в большинстве репозиториев пакетов Linux. Она позволяет открыть локальный порт и перенаправить на него трафик, получаемый через stdin/stdout от любой указанной команды:

colin@colin-work:~$ tcpserver 127.0.0.1 8080 echo -e 'HTTP/1.0 200 OK\r\nContent-Length: 19\r\n\r\n<body>hello!</body>'&
[1] 17377
colin@colin-work:~$ curl localhost:8080
<body>hello!</body>colin@colin-work:~$

(asciinema.org)

Netcat делает обратное. Она позволяет подключиться к открытому порту и передать полученный от него ввод/вывод на stdin/stdout:

colin@colin-work:~$ nc -C httpstat.us 80
GET /200 HTTP/1.0
Host: httpstat.us
HTTP/1.1 200 OK
Cache-Control: private
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.1
Access-Control-Allow-Origin: *
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
Date: Fri, 01 Nov 2019 17:53:04 GMT
Connection: close
Content-Length: 0

^C
colin@colin-work:~$

(asciinema.org)

В приведенном выше примере netcat запрашивает страницу по HTTP. Флаг -C заставляет его добавлять CRLF в конец строки.

Связка с kubectl: слушайте на хосте и подключайтесь к pod'у


Если объединить представленные выше инструменты с kubectl, мы получим команду вроде этой:

tcpserver 127.0.0.1 8000 kubectl exec -i web-pod nc 127.0.0.1 8080

По аналогии, для доступа к порту 80 внутри pod'а достаточно будет сделать curl "127.0.0.1:80":

colin@colin-work:~$ sanic kubectl exec -it web-54dfb667b6-28n85 bash
root@web-54dfb667b6-28n85:/web# apt-get -y install netcat-openbsd
Reading package lists... Done
Building dependency tree
Reading state information... Done
netcat-openbsd is already the newest version (1.195-2).
0 upgraded, 0 newly installed, 0 to remove and 10 not upgraded.
root@web-54dfb667b6-28n85:/web# exit
colin@colin-work:~$ tcpserver 127.0.0.1 8000 sanic kubectl exec -i web-54dfb667b6-28n85 nc 127.0.0.1 8080&
[1] 3232
colin@colin-work:~$ curl localhost:8000/healthz
{"status":"ok"}colin@colin-work:~$ exit

(asciinema.org)


Схема взаимодействия утилит

В обратную сторону: слушайте в pod'е и подключайтесь к хосту


nc 127.0.0.1 8000 | kubectl exec -i web-pod tcpserver 127.0.0.1 8080 cat

Эта команда позволяет pod'у получить доступ к порту 8000 на локальной машине.

Скрипт для Bash


Я написал специальный скрипт для Bash, позволяющий управлять production-кластером Kubernetes LayerCI, используя описанный выше метод:

kubetunnel() {
    POD="$1"
    DESTPORT="$2"
    if [ -z "$POD" -o -z "$DESTPORT" ]; then
        echo "Usage: kubetunnel [pod name] [destination port]"
        return 1
    fi
    pkill -f 'tcpserver 127.0.0.1 6666'
    tcpserver 127.0.0.1 6666 kubectl exec -i "$POD" nc 127.0.0.1 "$DESTPORT"&
    echo "Connect to 127.0.0.1:6666 to access $POD:$DESTPORT"
}

Если добавить эту функцию в ~/.bashrc, можно легко открывать туннель в pod командой kubetunnel web-pod 8080 и делать curl localhost:6666.

  • Для туннеля в Docker можно заменить основную строку на:

    tcpserver 127.0.0.1 6666 docker exec -i "$CONTAINER" nc 127.0.0.1 "$DESTPORT"
  • для туннеля в K3s — поменяйте её на:

    tcpserver 127.0.0.1 6666 k3s kubectl exec …
  • и т.д.

Другие идеи


  • Перенаправить UDP-трафик можно командами netcat -l -u -c вместо tcpserver и netcat -u вместо netcat соответственно.
  • Посмотреть ввод/вывод через pipe viewer:

    nc 127.0.0.1 8000 | pv --progress | kubectl exec -i web-pod tcpserver 127.0.0.1 8080 cat
  • Можно сжимать и распаковывать трафик на обоих концах с помощью gzip.
  • Подключиться по SSH к другому компьютеру с соответствующим файлом kubeconfig:

    tcpserver ssh workcomputer "kubectl exec -i my-pod nc 127.0.0.1 80"
  • Можно соединить два pod'а в разных кластерах с помощью mkfifo и запустить две отдельные команды kubectl.

Возможности безграничны!

P.S. от переводчика


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

Флант
458,93
Специалисты по DevOps и Kubernetes
Поделиться публикацией

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

      +1
      Да, а ещё бывают целые приложения для решения той же задачи. В какой-то мере это костыль, но поскольку он собран по частям из разных утилит, позволяет делать и другие «обходные» вещи (вроде тех, что описаны в «других идеях»).
      0
      Отличная идея, спасибо!
        +1

        и тут вы открываете для себя kubectl port-forward и понимаете что целая статья костылей посвящена уже готовому решению из коробки

          +1
          С kubectl port-forward есть проблема: он не поддерживает forward-proxy (с компьютера на под). Также в разделе «Другие идеи» есть другие примеры использования tcpserver в рамках k8s
            0
            если автор статьи знает про этот инструмент, то ему просто было необходимо сказать что дескать вот существует готовое решение и сказать что его функционала не хватает (чего конкретно не хватает) и предложить собственное. т.е. обязательно с указанием того какую проблему он решает. В текущей ситуации читателю придется садиться и реально анализировать чем ему описанный в статье подход поможет
          0

          VPN через k8s кластер: https://github.com/kayrus/kuttle

            0

            tl;dr, зачем это все, если можно задеплотить socat в pod? Есть даже готовый хельм. Кажется, называется, socat-tuneller. У него есть один фатальный недостаток, но, в целом, методика рабочая.
            Ну, и, конечно, kubectl port-forward

              0

              Миллениалы изобрели…
              Представляю следующую статью на хабре "Шок! Сенсация! Отправка емейл с помощью telnet!"


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


              P.S. Представляю что будет когда девопсы узнают про iptables. Сколько статей появится про dnat

                0
                В этом и «фишка» данного рецепта. Он использует традиционные утилиты и предлагает довольно универсальное решение, которое хорошо ложится на k8s, но легко распространяется и на другие контейнеры, и не только на них, конечно.
                0
                Вот есть удобная тулза с UI github.com/pixel-point/kube-forwarder
                  0

                  На статью про нее есть ссылка в ответе на первый комментарий :)

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

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