GO приложение в кластере k8s

Kate

Administrator
Команда форума

Golang приложение в кластере kubernetes​


Привет! Я — golang разработчик в Каруне. Kubernetes сегодня — звезда среди систем оркестровки и контейнеризации приложений. Важно понимать, как с ним работать. Поделюсь примером демонстрационного api приложения, которое написано на golang, и покажу способы взаимодействия с ним.

Но сначала — страшная история о том, как я жил без него. Много лет назад, в начале моей карьеры, когда ещё не появился kubernetes, доставка кода до production была весьма проблемной задачей. Бывало так, что создание нового приложения для меня начиналось не с работы в моей любимой IDE, а с заказа процессора, материнской платы, и всё это собиралось вручную. Затем я вставлял сервер в стойку, настраивал порты на cisco L3 маршрутизаторе. И это была только начальная фаза. После этого приходилось новоиспеченную node подключать к кластеру proxmox, накатывать операционную систему, ставить nginx и проводить ещё множество разных настроек.

После череды очень утомительных манипуляций можно было попасть на ip адрес хоста и увидеть, что всё работает, но пока ещё без кода, т.е. nginx отдавал default страничку. Совсем забыл: не всегда стоит в продакшене держать default приложение и вообще открытые порты, а значит нужно было не забыть настроить файрвол. Теперь можно было писать приложение и определяться с доставкой кода. Например, изменения попадали в master ветку, а затем отправлялись в продакшн методом копирования файлов по ssh. Звучит страшно, но тем не менее, такое бывало. Из-за скорости доставки файлов изменения могли скопироваться, скажем так, не консистентно. И пока шло копирование файлов, клиенты получали непредсказуемые ошибки. По этой причине такие действия выполнялись сначала на одну реплику, а в конфигурационный файл nginx вносились необходимые изменения, чтобы отключить реплику с новой версией приложения. Затем, когда файлы были скопированы, nginx снова настраивался — но так, чтобы большая часть запросов шла на старую версию, а на новую версию попадало меньшинство запросов.

В общем, схем достаточно много, в разных местах работы использовали свою специфическую, адаптированную под текущие реалии. Т.е. все решали проблемы наиболее актуальным способом: использовали “сине-зеленый деплой” или, например, “канареечный деплой”, а где-то и вовсе свои особые схемы. И это только те проблемы, которые касались процесса deploy. Можно сказать, что процедура доставки новой версии приложения в продакшен представляла серьёзную проблему, и по пятницам эту процедуру было запрещено выполнять. Kubernetes решил множество проблем, и процесс отправки новой версии приложения в production стал намного комфортней.

Хочу отметить: моё приложение носит демонстрационный характер, потому оно умеет отдавать ip адрес конкретной реплики. Это сделано для того, чтобы визуализировать балансировку нагрузки между инстансами приложения. Думаю, статья может быть полезной тем разработчикам, которые либо только знакомятся с kubernetes, либо тем, кто не до конца понимает большинство моментов, связанных с kubernetes. Далее я планирую на наглядном примере продемонстрировать работы нескольких объектов kubernetes: services и ingress controller.

В качестве тестовой среды буду использовать minikube и gcloude. В данной статье я не планирую акцентировать внимание на процедуре установки этих приложений, т.к. в сети достаточно материала, и сам процесс установки тривиален. Я перечислю инструменты, которые нам понадобятся: docker, docker-compose, kubectl, gcloud.

Использовать для эксперимента приложение minikube крайне удобно: оно позволяет делать всё необходимое на локальной «машине» без каких-либо материальных вложений в платформы типа google cloud или amazon aws. Например, в моей Linux системе это приложение устанавливается одной командой. Да, конечно, если есть желание, можно усложнить процесс, скачать исходники и собрать приложение из них. Ещё в программе minikube мне нравится великолепная документация (“https://minikube.sigs.k8s.io/docs/”) и возможность одной командой включать дополнения. Это очень удобный инструмент. Ниже я перечислю примеры управления minikube:


minikube -h
minikube start — запустить minikube
minikube stop — остановить minikube
minikube ssh — залогинится под пользователем docker внутри контейнера minikube
minikube addons list — список дополнений

Google предлагает свои услуги и за них требует некоторую плату. Кстати, платить необязательно, если вы собираетесь просто провести эксперимент или познакомиться ближе с их продуктом. Для этого они предлагают trial версию, где выдаётся некоторая сумма и промежуток времени, в течение которого можно воспользоваться их предложением. Затем, конечно же, придётся платить. Ниже можно посмотреть основные команды, которые могут понадобиться для работы с kubernetes кластером на платформе google cloud:


gcloud init — подготовительная стадия и авторизация на платформе
gcloud config set project go-echo
gcloud projects list — показать список проектов
gcloud container cluster create dev-go-echo --num-nodes=3 — создаём кластер с тремя nodes
gcloud container cluster get-credentials cluster-1 — переключиться на кластер
gcloud container cluster list — список кластеров

Образ целевого golang приложения можно скачать так:


docker pull yvv4docker/goecho:v1.0.0

Исходные коды приложения находятся здесь: https://github.com/yvv4git/go-echo
В директории k8s можно найти манифесты для kubernetes, которые описывают различные объекты kubernetes.


Кластер gke​


GKE — это система управления и оркестровки, используемая в google cloud. Есть множество способов установить утилиту gcloude, но лично мне ближе установка из репозитория snap:


snap install google-cloud-sdk --classic.
gcloud init
gcloud container clusters get-credentials cluster-1 --zone us-central1-c

Последняя команда создаст соответствующий файл в ~/.kube/config, что позволит использовать утилиту kubectl для управления кластером и позволит выполнить deploy.


Создадим кластер:


gcloud container clusters create goecho --num-nodes=3

После того, как кластер создан, можно применить манифесты kubernetes, которые создадут deployment и можно будет взаимодействовать с приложением.


n4xx4dgwb6wnr82v2goq4ktgoke.png



Запуск приложения​


Чтобы развернуть наше приложение в kubernetes кластере, необходимо выполнить следующую команду:


kubectl apply -f k8s/deployment_goecho.yaml.

В результате мы получим deployment, в котором будет 3 реплики этого приложения. Количество реплик я задал в файле «deployment_goecho.yaml». Чтобы проверить, что приложение запущено, можно сделать так:


kubectl get pods.

cqd6ajpiwfltjwazmu_sv1ruyai.png

Зайдём внутрь контейнера и проверим, работает ли вообще приложение:


kubectl exec -it echo-54c8b57455-4jd9p — sh

rflvcrbkzy07j-mrp5nanrn3uia.png

Чтобы проверить, работает ли приложение, можно поднять специальный pod, содержащий контейнер с различными полезными утилитами: ping, curl и т.д. Это может быть полезно, если в целевом контейнере отсутствуют необходимые утилиты.


Внутри контейнера можно запустить утилиту netstat и сделать выводы о том, что приложение физически запущено, и узнать, на каком порту оно работает. Всё, как в файлах манифеста kubernetes. К сожалению, в данном контейнере отсутствует утилита curl, соответственно из него постучаться в приложение не получится. Есть, конечно, методы, но я их здесь рассматривать не буду. Я сделаю иначе: запущу вспомогательный контейнер в отдельном pod, который будет напичкан разными сетевыми утилитами. Это некоторый инструмент, который может прийти на помощь, чтобы целевой контейнер был «лёгким» и безопасным. Например, на случай, если моё приложение будет скомпрометировано, т.е. в целях безопасности.


Для этого выполним следующую команду:


kubectl apply -f k8s/pod_watcher.yaml — так появится новый pod с приложением watcher.

u-q-b5qmrjskbjjqzrxk-2vzvd8.png

Заходим в контейнер, который содержит множество сетевых инструментов:


kubectl exec -it watcher -- bash и пробуем достучаться до нашего целевого приложения.

98eoonqu7yz-fnqoymuo2e5_sxw.png

На screenshot прекрасно видно, что приложение отвечает на запрос. Всё замечательно. Но какой толк от этого? Как можно развернуть приложение и предоставить клиентам или другим приложениям в кластере доступ к нему? Для этого необходимо на практике рассмотреть понятие сервиса.


Service ClusterIP​


Далее мы попробуем использовать три типа сервисов: ClusterIP, NodePort и LoadBalancer. Для начала рассмотрим ClusterIP. Нам нужно выполнить следующую команду:


kubectl apply -f k8s/svc_cluster_ip.yaml
kubectl get svc

okf9bkdwtr22-uyjb02n85m7at8.png

На screenshot выдан ip адрес внутри кластера. Попасть на него можно, например, с любого другого приложения в кластере. Воспользуемся приложением, которое было запущено для таких целей — watcher. Попытки связаться с приложением проходят удачно. Ещё можно заметить, что в ответе мы получаем разные ip ардеса. Это подтверждает тот факт, что имеет место балансировка запросов между репликами приложения. В файле манифеста kubernetes, который отвечает за deployment моего приложения, указано 3 реплики. И, например, если мы целенаправленно или случайно сломаем одну из реплик, система поднимет новую. Это позволяет поддерживать приложение в рабочем состоянии и балансировать нагрузку.


jy9km__jlrkycomgyb6iumgmuaa.png

cd7g45wycxj17b2vot18kc7sx9i.png

Service NodePort​


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


kubectl delete -f k8s/svc_cluster_ip.yaml
kubectl apply -f k8s/svc_node_port.yaml
kubectl get svc

o2-maknffwu3tu4enuddt0ikeay.png

По указанному cluster ip можно слать запросы приложению, но особенность NodePort в другом. Данный тип сервиса открывает порт на nodes и позволяет связаться с приложением снаружи, например, из public internet. Внешний порт можно узнать следующей командой:


kubectl describe nodes gke-goecho-default-pool-5cee0175-9kmp |grep -i external

Затем отправляем запрос к нашему приложению:


curl 'http://34.147.0.120:31124/v1/address'

Аналогично ClusterIP ip адрес в возвращаемом ответе будет отличаться, что говорит о том, что имеет место балансировка нагрузки. Кстати, если вы используете не google cloud, а развернули всё это локально в minikube, то может возникнуть вопрос — а где взять node ip? Всё просто — данный ip адрес можно узнать командой:


minikube ip

kevhhbm_dr15dkcjbgtncxusp98.png

Service LoadBalancer​


Далее я бы хотел рассмотреть другой тип сервиса Load Balancer. В указанном репозитории имеется соответствующий файл манифеста kubernetes, необходимо им воспользоваться:


kubectl delete -f k8s/svc_cluster_ip.yaml
kubectl apply -f k8s/svc_load_balancer.yaml

p3gkfvrt2svx_zdqogdk2nqvkwo.png

Важно: «external-ip» появится не сразу, надо подождать. Например, в google cloud время ожидания может быть несколько секунд. Если же мы имеем дело с minikube, то здесь ip адрес автоматически не появится. Сначала надо узнать ip адрес интерфейса, на котором работает minikube, а затем использовать его как «external-ip»:


minikube ip
kubectl patch service echo -n default -p '{"spec": {"type": "LoadBalancer", "externalIPs":["192.168.49.2"]}}"

После этого можно будет обращаться к приложению через «external-ip». Например, так:


curl 'http://echo.local:30722/v1/address'

Можно заметить, что я обращаюсь не к ip 192.168.49.2, а к hostname, который прописал в /etc/hosts файле. В случае с GKE можно обращаться к нашему приложение из public internet. Это очень удобно, когда мы имеем одно приложение, которое нужно открыть для клиентов. Но если приложений много, и между ними требуется более сложная логика маршрутизации запросов, использовать только сервисы будет неудобно и накладно.


vd-2nmb6xd7pddwenno6cz75odi.png


Ingress controller​


Представим, что всё-таки у нас несколько приложений, и между ними нужно маршрутизировать запросы. Например, когда запросы с префиксом «/goapp» нужно отправлять на приложение go-app, а запросы с префиксом «watcher» требуется отправить в другое приложение. Для решения этой проблемы понадобится другая сущность kubernetes, которая называется «ingress controller».


Выполняем следующие команды:


kubectl delete -f k8s/pod_watcher.yaml
kubectl apply -f k8s/deployment_watcher.yaml
kubectl apply -f k8s/ingress.yaml
kubectl get ingress -o wide

vuevemwn1vctczo7raxaofdwa7a.png

В списке ingress контроллеров можно увидеть наш контроллер. Попробуем теперь достучаться до приложения watcher:


curl -v -X GET ‘http://echo.local/watcher’'

ikjygkxy7k0i-pk6f7_4tldnizi.png

Теперь попробуем отправить запрос на echo сервис:


curl -X GET ‘http://echo.local/goapp/v1/address’

Таким образом мы попадаем на нужный нам сервис внутри кластера. Это может быть крайне полезно, если внутри кластера работает множество приложений, и необходимо как-то централизовано маршрутизировать запросы между ними.


Заключение​


Сегодня процесс доставки кода до production сильно упростился и стал намного безопаснее. Kubernetes позволяет из коробки решить массу проблем, на решение которых раньше уходило очень много времени. Теперь можно сразу приступать к разработке приложения, минуя лишние рутинные этапы. В данной статье я написал простенькое web приложение, на примере которого golang разработчик может ближе познакомиться с доставкой кода в production средствами kubernetes. В демонстрационном приложении можно внести правки в конфигурационные файлы, настроить процесс deployment, задать требуемое количество реплик и выбрать способ взаимодействия с сервисом по сети: ClusterIP, NodePort, LoadBalancer, а так же Ingress controller.

 
Сверху