В общем, в прошлый раз я упомянул, что другой, совместимый с Kubernetes сервис меш — Conduit, работает по иному принципу. В отличие от Linkerd, он не устанавливает прокси на каждую машину и не заставляет клиентов общаться с ним, задав переменную окружения http_proxy
. Этот кадр мало того, что подключает клиентские сервисы к мешу по-одному, так ещё и совсем другим способом. Мне нравятся такие идеи, ставящие всё с ног на голову, так что я решил разобрать Conduit на части и посмотреть, что же там у него внутри.
Ещё раз, кто такой Conduit?
Как я упомянул абзацем выше, Conduit — это сервис меш для Kubernetes. То есть это компонент, который вместо того, чтобы разрешать сервисам приложения общаться по сети друг с другом напрямую, пропускает весь траффик через себя и даёт некоторые плюшки взамен. Например, статистику по таймингам и успешности запросов, балансировщик нагрузки, перенаправление траффика, логи и кучи других приятностей, которые мешам полагается раздавать.
Установка
Ставится Conduit очень просто. Во-первых, нужно скачать бинарник (есть даже скрипт для этого) и добавить его в PATH:
1 2 |
curl https://run.conduit.io/install | sh export PATH=$PATH:$HOME/.conduit/bin |
Во-вторых, добавить панель управления в Kubernetes-кластер (или тот же minikube), что делается ещё одной командой:
1 2 3 4 5 |
minikube start # Create local k8s cluster conduit install | kubectl apply -f - # namespace "conduit" created # serviceaccount "conduit-controller" created # ... |
Препарируем скрипт установки
Прежде чем идти дальше, я хочу заглянуть внутрь conduit install
, который, походу, просто генерирует YAML конфигурацию для kubectl apply
. Интересно же.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
conduit install | vim - ### Namespace ### kind: Namespace apiVersion: v1 metadata: name: conduit # ... kind: Service apiVersion: v1 metadata: name: prometheus # ... kind: Service apiVersion: v1 metadata: name: grafana # ... |
Круть! Кроме создания аккаунтов, сервисов и деплойментов там внутри встречаются имена Prometheus и Grafana, так что я уже догадываюсь, на что будет похож дашборд и как они собирают метрики.
Смотрим на дашборд
Конечно, данных там не будет, но всё-таки интересно, как он выглядит. Команда conduit dashboard
поможет:
Ха! Всё запустилось и всё работает. Самая интересная часть — внизу. Там написано, что из четырёх пространств имён в текущем кластере, одно — conduit — полностью подключено к сервис мешу. Ещё два — пустые, и одно — kube-system — не подключено никак.
То есть Conduit мониторит сам себя? Уважаю. Это так же означает, что хоть какая-то статистика внутри уже должна быть. Кликаем и смотрим…
Да! Что-то есть. Метрики для деплойментов и подов. Хакей, посмотрим, например, на статистику для деплоймента grafana поближе:
Grafana, показывающая статистику для деплоймента grafana… Ну не рекурсия ли?
Подключаем поды к сети
Вернёмся-ка мы назад на главную страницу, к списку подключённых и неподключённых пространств имён.
Как мы уже поняли, conduit
подключён полностью, default
и kube-public
— пустые, но в kube-system
есть аж девять подов и ни один из низ не подключён к мешу. В продакшене я такое вряд ли бы делал, но что если подключить что-нибудь из kube-system к conduit?
Подключение к мешу делается через командную строку (наверняка есть и API какой-нибудь), через conduit inject
. На вход ему подаётся YAML конфигурация объекта (например, pod или деплоймент), а на выходе получается та же конфигурация, но настроенная так, чтобы её исходящий траффик шёл через меш. Прежде, чем я что-нибудь начну подключать, хочется выяснить, как же именно меняется конфигурация.
Сравнимаем YAML до и после
В пространстве имён kube-system
есть два деплоймента для экспериментов: kube-dns и kubernetes-dashboard.
1 2 3 4 |
kubectl get deployments -n kube-system # NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE # kube-dns 1 1 1 1 31m # kubernetes-dashboard 1 1 1 1 31m |
Я сохранил начальную конфигурацию kube-dns в файле before.yml
:
1 |
kubectl get deployment kube-dns -n kube-system -o yaml > before.yml |
Пропущенную через conduit inject
— в after.yml
:
1 |
kubectl get deployment kube-dns -n kube-system -o yaml | conduit inject - > after.yml |
И пропустил это всё через vim diff:
И какая ж это красота! В общем, conduit добавил в шаблон для пода два новых контейнера. Первый — постоянный, через который, я так полагаю, траффик и будет пропускаться наверх (gcr.io/runconduit/proxy:v0.4.2
, строка 189). Второй же — Init Контейнер, gcr.io/runconduit/proxy-init:v0.4.2
, строка 211 — делает начальную настройку, и я очень хочу заглянуть ему внутрь.
Ну вот почти кроличья нора
Буквально первый гугловый результат сдал мне Dockerfile для proxy-init образа как стеклотару.
1 2 3 4 5 6 7 8 9 10 |
## compile proxy-init utility FROM gcr.io/runconduit/go-deps:23e16ad5 as golang WORKDIR /go/src/github.com/runconduit/conduit COPY ./proxy-init ./proxy-init RUN CGO_ENABLED=0 GOOS=linux go install -v -installsuffix cgo ./proxy-init/ ## package runtime FROM gcr.io/runconduit/base:2017-10-30.01 COPY --from=golang /go/bin/proxy-init /usr/local/bin/conduit-proxy-init ENTRYPOINT ["/usr/local/bin/conduit-proxy-init"] |
Хм. То есть внутренности написаны на Go. Это приятно. Что ещё приятнее, никакого http_proxy
внутри них не встречается. Зато есть iptables. Вот, оказывается, как они собирают траффик из сервисов в свой сервис меш. Кстати, сырцы для второго Докер образа — proxy
— лежат неподалёку и написаны на Rust. Один язык для одной задачи, чего уж там.
Подключаем kube-system к мешу
Моё любопытство пока удовлетворено, так что наступило время подключать деплойменты неймспейса к мешу.
1 2 3 4 |
kubectl get deployments -n kube-system -o yaml | conduit inject - | kubectl apply -f- # Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply # deployment.extensions "kube-dns" configured # deployment.extensions "kubernetes-dashboard" configured |
И-и-и-и-и оно работает! Частично, но всё-таки. kubernetes-dashboard
, походу, догадался, что его надругали, и откатил свою конфигурация назад (сам, или кто-то из его начальников). А вот kube-dns
подчинился:
Бонус: командная строка
Conduit, кстати, неплохо поддерживает командную строку. Эта самая статистика, которую мы только что видели в Grafana, есть и в консоли:
1 2 3 4 |
conduit stat deployments -n kube-system #NAME MESHED SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 SECURED #kube-dns 1/1 100.00% 0.4rps 5ms 9ms 10ms 0% #kubernetes-dashboard 0/1 - - - - - - |
Что ещё круче, можно даже посмотреть данные по индивидуальным запросам. Вот, что, например, бегает по проводам в kube-dns
деплойменте? А я знаю.
1 2 3 4 5 6 |
conduit tap deploy/kube-dns -n kube-system # req id=0:293 src=172.17.0.1:51226 dst=172.17.0.8:10054 :method=GET :authority=172.17.0.8:10054 :path=/metrics secured=no # rsp id=0:293 src=172.17.0.1:51226 dst=172.17.0.8:10054 :status=200 latency=3248µs secured=no # end id=0:293 src=172.17.0.1:51226 dst=172.17.0.8:10054 duration=10µs response-length=4881B secured=no # req id=0:294 src=172.17.0.1:51230 dst=172.17.0.8:10054 :method=GET :authority=172.17.0.8:10054 :path=/healthcheck/kubedns secured=no # ... |
Красотень.
Мораль
Вот такой он, Conduit. Как и в случае с Linkerd или даже сервис мешами в целом, мой мир не пошатнулся и не обрёл истину. Но мне нравятся красивые идеи и их имплементация, и это как раз тот случай. И, как оказалось, удивительно много можно узнать просто заглянув в чужие сырцы. Смешивание языков программирования работает не только в теории, Rust ещё жив, Init Контейнеры прекрасны, iptables — тоже. И ведь это только пара наблюдений. А вот теперь мне интересно, делал ли кто-нибудь бенчмаркинг того, как меш влияет на производительность сети. Вот уверен, как только я попробую продать идею мешей начальству, именно об этом они и спросят в первую очередь.