Прим. перев.: Эта практическая заметка от создателя 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. от переводчика


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

Комментарии (11)


  1. divanikus
    18.12.2019 13:03

    1. IlyaArens Автор
      18.12.2019 17:30
      +1

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


  1. W001fer
    18.12.2019 14:04

    Отличная идея, спасибо!


  1. bykvaadm
    18.12.2019 16:00

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


    1. IlyaArens Автор
      19.12.2019 02:52

      С kubectl port-forward есть проблема: он не поддерживает forward-proxy (с компьютера на под). Также в разделе «Другие идеи» есть другие примеры использования tcpserver в рамках k8s


  1. kay
    18.12.2019 16:54

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


  1. gecube
    18.12.2019 22:35

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


  1. in_heb
    19.12.2019 00:58
    +1

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


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


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


    1. IlyaArens Автор
      19.12.2019 02:55

      В этом и «фишка» данного рецепта. Он использует традиционные утилиты и предлагает довольно универсальное решение, которое хорошо ложится на k8s, но легко распространяется и на другие контейнеры, и не только на них, конечно.


  1. lasc
    19.12.2019 06:25

    Вот есть удобная тулза с UI github.com/pixel-point/kube-forwarder


    1. shurup
      19.12.2019 08:22

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