Кубернетес для сетевых инженеров
Оглавление:
Сетевая связность и пропускная способность
CNI и CRI
DNS, L4/L7
Захват трафика
Сетевая связность и пропускная способность
Кубернетес использует стандартное сетевое оборудование и примитивы ОС Linux в качестве фундамента для построения различных типов SND (Software-defined networking). Способы отладки сетей в Кубернетес, принципиально не отличается, от привычных методов.
iperf
https://iperf.fr
iperf — это хорошо известный для сетевых инженеров и системных администраторов инструмент,
предназначенный для тестирования сетевого оборудования пропускной способности сетевых каналов.
Также как и обычными хостами Linux, его можно использовать для тестирования сетей в кластере Кубернетес.
Для того, чтобы развернуть iperf в Кубернетес необходимо создать манифест развертывания использующий нужный образ:
apiVersion: apps/v1
kind: Deployment
metadata:
name: iperf3-deployment
spec:
replicas: 3
selector:
matchLabels:
app: iperf3
template:
metadata:
labels:
app: iperf3
spec:
containers:
- name: iperf3
image: leodotcloud/swiss-army-knife
ports:
- containerPort: 5201
Для того, чтобы развернуть созданный манифест в кластере:
kubectl apply -f iperf3-deployment.yaml
Планируемый результат:
~$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
iperf3-deployment-64c767596d-9r4jm 1/1 Running 0 3h36m 10.244.0.13 minikube
iperf3-deployment-64c767596d-jgqx9 1/1 Running 0 3h36m 10.244.0.14 minikube
iperf3-deployment-64c767596d-l5tg8 1/1 Running 0 3h36m 10.244.0.15 minikube
Сначала нужно запустить iperf в одном из подов, в режиме сервера:
kubectl exec -it -- iperf3 -s -p 12345
В данном случае необходимо поменять [pod-name] на имя одного из подов в развертывании:
kubectl exec -it [pod-name] — iperf3 -s -p 12345
Планируемый результат:
~$ kubectl exec -it pod/iperf3-deployment-64c767596d-l5tg8 -- iperf3 -s -p 12345
-----------------------------------------------------------
Server listening on 12345
-----------------------------------------------------------
В другой сессии терминала нужно запустить Iperf в режиме клиента, и подключится из этого пода к серверу:
~$ kubectl exec -it iperf3-deployment-64c767596d-9r4jm -- iperf3 -c 10.244.0.15 -p 12345
Connecting to host 10.244.0.15, port 12345
[ 4] local 10.244.0.13 port 43842 connected to 10.244.0.15 port 12345
[ ID] Interval Transfer Bandwidth Retr Cwnd
[ 4] 0.00-1.00 sec 2.78 GBytes 23.9 Gbits/sec 1296506932 0.00 Bytes
[ 4] 1.00-2.00 sec 2.56 GBytes 22.0 Gbits/sec 0 0.00 Bytes
[ 4] 2.00-3.00 sec 3.02 GBytes 25.9 Gbits/sec 0 0.00 Bytes
[ 4] 3.00-4.00 sec 3.17 GBytes 27.2 Gbits/sec 0 0.00 Bytes
[ 4] 4.00-5.00 sec 2.91 GBytes 25.0 Gbits/sec 0 0.00 Bytes
[ 4] 5.00-6.00 sec 2.76 GBytes 23.7 Gbits/sec 0 0.00 Bytes
[ 4] 6.00-7.00 sec 2.47 GBytes 21.2 Gbits/sec 0 0.00 Bytes
[ 4] 7.00-8.00 sec 2.43 GBytes 20.9 Gbits/sec 0 0.00 Bytes
[ 4] 8.00-9.00 sec 2.43 GBytes 20.9 Gbits/sec 0 0.00 Bytes
[ 4] 9.00-10.00 sec 2.82 GBytes 24.2 Gbits/sec 2998460368 0.00 Bytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-10.00 sec 27.3 GBytes 23.5 Gbits/sec 4 sender
[ 4] 0.00-10.00 sec 27.3 GBytes 23.5 Gbits/sec receiver
Основное предназначение iperf — это анализ пропускной способности сети: между географически удаленными узлами в одном кластере Кубернетес
или между хостом внутри кластера и машиной которая находится за его пределами.
Iperf может тестировать не только TCP, но и UDP:
~$ kubectl exec -it iperf3-deployment-64c767596d-9r4jm -- iperf3 -c 10.244.0.15 -u -p 12345
Connecting to host 10.244.0.15, port 12345
warning: Unable to set socket pacing, using application pacing instead
[ 4] local 10.244.0.13 port 51612 connected to 10.244.0.15 port 12345
[ ID] Interval Transfer Bandwidth Total Datagrams
[ 4] 0.00-1.00 sec 120 KBytes 983 Kbits/sec 15
[ 4] 1.00-2.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 2.00-3.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 3.00-4.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 4.00-5.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 5.00-6.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 6.00-7.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 7.00-8.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 8.00-9.00 sec 128 KBytes 1.05 Mbits/sec 16
[ 4] 9.00-10.00 sec 128 KBytes 1.05 Mbits/sec 16
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 4] 0.00-10.00 sec 1.24 MBytes 1.04 Mbits/sec 0.207 ms 0/159 (0%)
[ 4] Sent 159 datagrams
В случаи если при тестировании наблюдаются потери пакетов, то это может говорить о:
Неисправности «железа» (CRC ошибки NIC проблемы с коммутаторами)
Не правильно настроенный межсетевой экран
Не правильно настроенный протокол (например MTU)
Link congestion
CNI и CRI
Кубернетес построен на принципах UNIX: Делайте что-то одно, но делайте это хорошо. Каждый компонент Кубернетес — это отдельная программа, которая выполняет определенный набор операций и не имеет жесткой привязки (decoupling) к другим компонентам.
CRI
Кубернетес занимается оркестрацией контейнеров, но за работу непостредственно связаной с запуском и остановкой контейнеров на узлах кластера отвечает другой компонент — CRI (Container Runtime Interface). CRI использует примитивы Linux (cgroups и пространства имен) для работы с образами контейнеров. Kubelet — это компонент Кубернтес, который управляет Container Runtime.
CRI (это может быть Docker\containerd) отвечает за запуск контейнеров. Kubelet отправляет инструкции CNI, как присоединить сетевой интерфейс и настроить сеть для Пода.
Для того, чтобы быстро посмотреть какие с какими параметрами был запущен сервис kubelet можно использовать команду strings /proc/$(pgrep kubelet)/cmdline
:
$ strings /proc/$(pgrep kubelet)/cmdline
/usr/bin/kubelet
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
--kubeconfig=/etc/kubernetes/kubelet.conf
--config=/var/lib/kubelet/config.yaml
--container-runtime=remote
--container-runtime-endpoint=/run/containerd/containerd.sock
--node-ip=192.168.61.11
--pod-infra-container-image=k8s.gcr.io/pause:3.4.1
Для отладки проблем связанных с сетевым доступом на узлах Кубернетес, можно использовать бинарные файлы CRI (Container Runtime Interface).
crio:
https://github.com/cri-o/cri-o/blob/main/docs/crio.8.md
$ crictl pull docker.io/library/busybox:latest
containerd:
https://github.com/projectatomic/containerd/blob/master/docs/cli.md
$ ctr images pull docker.io/library/busybox:latest
crictl inspect выводит информацию о контейнере в формате JSON. Можно использовать утилиту jq для того, чтобы отфильтровать PID:
crictl ps
crictl inspect $CONTAINER_ID
PID=$(crictl inspect $CONTAINER_ID | jq '.info.pid')
ps -ef | grep $PID | grep -v grep
CNI
У CNI плагина есть две главные обязанности: аллоцировать и назаначять уникальные IP адреса для Подов и добавлять маршруты в кластере Кубернетес для каждого IP адреса Пода.
Это означает, что сеть в которой находится узлы кластера, диктует поведение CNI плагина. Существует два категории в сетевой модели CNI: flat networks
и overlay networks
. flat networks
использует IP из сети кластера, для чего обычно требуется большое количество свободных адресов. В сетях overlay
, драйвер CNI создает свою сеть в Кубернетес, и затем использует кластерную сеть (которая называется ]underlay network]) для передачи пакетов.
Пример вывода команды ip route
на узле кластера использующего CNI плагин Calico:
$ ip route
default via 10.6.16.1 dev eth0
10.6.16.0/21 dev eth0 proto kernel scope link src 10.6.22.111
111.97.95.0/26 via 10.6.145.224 dev tunl0 proto bird onlink
111.98.108.64/26 via 10.6.144.128 dev tunl0 proto bird onlink
111.98.163.0/26 via 10.6.147.100 dev tunl0 proto bird onlink
111.101.172.128/26 via 10.6.86.141 dev tunl0 proto bird onlink
111.103.57.192/26 via 10.6.17.44 dev eth0 proto bird
111.103.80.128/26 via 10.6.85.178 dev tunl0 proto bird onlink
111.105.231.0/26 via 10.6.23.120 dev eth0 proto bird
111.115.208.128/26 via 10.6.80.11 dev tunl0 proto bird onlink
blackhole 111.126.117.128/26 proto bird
111.126.117.129 dev cali8934275ty scope link
111.126.117.132 dev cali983hfsdf4 scope link
111.126.117.140 dev cali443gfby45 scope link
kubelet подключает Поды к CNI. Сетевой трафик CNI предстваляет из себя стандартный TCP/IP (здесь могут быть особенности реализации конкертного плагина CNI). Шифрованием трафика занимается CNI, приложение или service mesh. Если трафик передается в незашифрованном виде, то его можно прослушивать из скомпрометированного Пода или непостредственно на узле Кубернетес.
По-умолчанию kubelet читает конифиг CNI из директории /etc/cni/net.d/
и ожидает найти бинарный файл CNI в директории /opt/cni/bin/.
Эти параметры может использовать в командной строки с помощью фалгов --cni-config-dir=[directory]
и --cni-bin-dir=[directory]
.
Cilium
Cilium — это одна из реализаций CNI. Это первый плагин использующий Berkeley Packet Filter (eBPF). Это означает, что обработка сетевых пакетов происходит в ядре (Linux kernel space). При использовании вместе с технологией Xpress Data Path (XDP), это позволяет маршрутизировать трафик сразу после передачи сетевых пакетов. Cilium имеет ряд преимуществ по сравнению с другими плагинами. При этом выбор CNI это одно из ключевых решений, которое должно приниматься на этапе планирования кластера администраторами, сетевыми инженерами и разработчиками.
NetworkPolicy
По-умолчанию Кубернетес открывает доступ для любого трафика между Подами в кластерной сети.
NetworkPolicy Это ресурс Кубернетес, который позволяет настраивать правила доступа аналогичные правилам для межсетевого экрана.
Ресурсы NetworkPolicy используется используются для конфигурации плагинов CNI — это компонент который отвечает за сетевую связность между Подами.
Iptables
Большинство имплементаций CNI используют kube-proxy и iptables для фильтрации сетевых пакетов. Исключением является Cilium и другие плагины использующие eBPF.
Некоторые компоненты Кубернетес (kube-proxy) создают правила iptables для выполнения определенных операций. Пример таких правил можно увидеть, выполнив команду на узле в кластере Кубернетес:
$ sudo iptables -t nat -L KUBE-SERVICES
Эти правила предназначены для внутреннего использования в Кубернетес. Они создаются автоматически и не предназначены для редактирования пользователями.
DNS, L4/L7
По-умолчанию Кубернетес автоматически настраивает DNS сервис, который используется для обнаружения сервисов в кластере. Кубернетес это динамическая инфраструктура: Поды и контейнеры создаются, удаляются и перемещаются между узлами кластера в автоматическом режиме. Встроенное обнаружение сервисов необходимо для коммуникации между приложениями в условиях динамической инфраструктуры: кубернетес постоянно создает, удаляет и перемещает Поды и контейнеры между узлами кластера в автоматическом режиме.
CoreDNS
https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution
Кубернетес использует CoreDNS в качестве внутрикластерного DNS сервера для Подов.
CoreDNS отслеживает состояние ресурсов API Сервера. Для каждого Сервиса Кубернетес CoreDNS создает запись следующего формата: [service-name].[namespace-name].svc.cluster.local.
К примеру Сервис, который называется nginx в пространстве имен default, получит запись — nginx.default.svc.cluster.local.
Во время выполнения процедуры запуска Подов, kubelet сохраняет в контейнере файл /etc/resolv.conf
. Если изучить содержимое этого файла, то в качестве nameserver там будет указан CoreDNS. Каждый раз когда Под запрашивает Сервис Кубернетес по имени — этот запрос отправляется в CoreDNS:
cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
Сервис kube-dns подключается к развертыванию (Deployment) CoreDNS:
kubectl -n kube-system get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP 2d3h
Сервис kube-dns не имеет отношения к легковесной реализации DNS сервера, которая устанавливалась по-умолчанию в предыдущих версиях Кубернетес. Сервис kube-dns является частью CoreDNS и устанавливается на этапе установки кластера.
CoreDNS настраивается с помощью плагинов.
CoreDNS использует так называемый Corefile для того, чтобы конфигурировать поведение DNS сервера.
Конфиг CoreDNS нужно читать сверху вниз — каждая новая строчка это один из используемых плагинов.
Для того, чтобы изучить конфигурацию CoreDNS можно воспользоваться следующей командой:
kubectl get configmaps coredns -n kube-system -o yaml
dnstools
Для отладки сетей в Кубернетес, зачастую не хватает привычных для сетевых инженеров и системных администраторов инструментов: dig, nslookup, curl, ping, nc и так далее. Для того, чтобы получить доступ к этим необходимым инструментам, можно собрать служебный образ контейнера с набором предустановленных программ, или использовать один из существующих публично доступных образов Docker, например — dnstools:
dnstools.yaml:
apiVersion: v1
kind: Pod
metadata:
name: dnstools
spec:
containers:
- name: dnstools
image: infoblox/dnstools
command: ['sh', '-c', 'while true; do sleep 1; done']
Для того, чтобы развернуть созданный манифест в кластере:
kubectl apply -f dnstools.yaml
Планируемый результат:
kubectl get pods dnstools
NAME READY STATUS RESTARTS AGE
dnsutils 1/1 Running 0 36s
С помощью dnstools можно проверить L4\L7 соединение между Подами, порты и доступ в Интернет из кластера:
~$ kubectl exec -i -t dnstools -- nc -z -vv 10.244.0.6 80
nc: 10.244.0.6 (10.244.0.6:80): Connection refused
sent 0, rcvd 0
command terminated with exit code 1
~$ kubectl exec -i -t dnstools -- nc -z -vv 10.244.0.6 8080
10.244.0.6 (10.244.0.6:8080) open
sent 0, rcvd 0
~$ kubectl exec -i -t dnstools -- ping google.com -c 4
PING google.com (64.233.165.100): 56 data bytes
64 bytes from 64.233.165.100: seq=0 ttl=36 time=200.956 ms
64 bytes from 64.233.165.100: seq=1 ttl=36 time=18.517 ms
64 bytes from 64.233.165.100: seq=2 ttl=36 time=31.825 ms
64 bytes from 64.233.165.100: seq=3 ttl=36 time=16.783 ms
layer 7 HTTP API:
~$ kubectl exec -i -t dnstools -- wget -qO- 10.244.0.6:8080
Hello, world!
Проверить разрешение доменных имен в кластере:
$ kubectl exec -i -t dnstools -- nslookup kubernetes.default
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
$ kubectl exec -i -t dnstools -- dig hello-world.default.svc.cluster.example.com
Для того, чтобы быстро получить доступ к контейнеру с dnstools:
kubectl run --restart=Never -it --image infoblox/dnstools dnstools dnstools#
Сделать снимок сетевого трафика DNS (UDP 53) в контейнере dnstools:
kubectl exec -it dnstools sh
dnstools# tcpdump -ni eth0 udp and port 53
В случае проблем с DNS в кластере Кубернетес, сначала следует проверить содержимое /etc/resolv.conf:
dnstools# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
https://linux.die.net/man/5/resolv.conf
Захват трафика
Wireshark и tcpdump — являются де-факто стандартными инструментами в арсенале сетевого инженера и хотя контейнеры значительно меняют подход к проектированию и отладки сетей, эти две не заменимые программы можно использовать для анализа сетевого трафика в Кубернетес.
tcpdump собирает данные между интерфейсом драйвера сетевой карты (NIC) и вторым уровнем (layer 2) OSI
ksniff
https://github.com/eldadru/ksniff
ksniff — это плагин kubectl, который использует tcpdump для захвата сетевого трафика.
Трафик в формате PCAP можно затем импортировать в Wireshark для последующего анализа.
Для установки ksniff лучше использовать krew — менеджер плагинов для kubectl:
kubectl krew install sniff
Инструкции по установке krew для разных ОС:
https://krew.sigs.k8s.io/docs/user-guide/setup/install
Ksniff устанавливается на пользовательской машине с настроенным kubectl.
При запуске ksniff использует эфемерный контейнер с tcpdump для подключения к нужному Поду. Для работы с PCAP файлами, на машине должен быть установлен Wireshark.
Интерфейс кsniff достаточно прост:
kubectl sniff [-n ] [-c ] [-i ] [-f ] [-o OUTPUT_FILE] [-l LOCAL_TCPDUMP_FILE] [-r REMOTE_TCPDUMP_FILE]
Следующая команда сохранит перехваченные tcpdump сететвые пакеты в файл out.pcap:
kubectl sniff -n -p -f "port 80" -o out.pcap
В данном случае мы отправляем tcpdump инструкции — отслеживать только запросы POST протокола HTTP на порту 8080:
kubectl sniff -p -n -f 'tcp dst port 8080 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354)' -o - | tshark -r -
С помощью флагов --image
и --tcpdump-image
можно указывать кастомизированные образы Docker. Эта функция может быть использована для работы в закрытом контуре.
Kubeshark
https://docs.kubeshark.co
Инструкции по установке:
https://docs.kubeshark.co/en/install
Это развитие идей, которые были апробированы в ksniff. Принцип работы похож на ksniff — для захвата трафика используется контейнер с tcpdump.
Кроме этого Kubeshark добавляет графический веб-интерфейс и ряд возможностей, которые доступены лишь в системах для анализа трафика класса enterprise.
kubeshark позволяет:
добавлять кастомизированные пользовательские функции и триггеры написанные на Javascript. Это необходимо для отслеживания и реагирования на инциденты и подозрительное поведение в сети Кубернетес.
cохранять снимок сетевого трафика в формате PCAP для последующего импорта в Wireshark.
С kubeshark можно в режиме реального времени отслеживать перемещение трафика (REST, gRPC, Kafka, AMQP и Redis) в кластере,
и визуализировать связи между Сервисами Кубернетес — https://docs.kubeshark.co/en/service_map