Локальная разработка с Kubernetes. Немного танцев с бубном

от автора

На нескольких проектах я сталкивался с ситуацией, когда есть Kubernetes с разными окружениями типа dev, stage, prod и т.д.
Код сервисов в эти самые окружения попадает в процессе CI/CD: то есть мы мержим какую-то ветку с разрабатываемой фичей или исправлением бага в ветку, которая “привязана” к окружению и дальше наш код деплоится в кластер. Думаю, для многих — это уже стандартная история.

Давайте представим, что нужно сделать задачу, относящуюся к какому-нибудь микросервису, эта задача подразумевает запрос по сети к другому микросервису, а тот, в свою очередь, посылает запрос к еще другим микросервисам. Как быть, когда мы хотим, чтобы нам были доступны данные из других микросервисов, чтобы протестировать то, что мы сделали не в тестах с моками, а в условиях, похожих на “боевые”. Тут самым очевидным, как мне кажется, является разворачивание локально микросервиса, код которого мы “ковыряем” и проброс портов до целевого микросервиса в dev кластере (или в другом кластере, предназначенным для тестирования), например:

kubectl port-forward service/some-service 8001:8001

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

Так, кажется, что всё, что мы напрограммировали работает. Но теперь мы хотим каким-то образом поменять ответ от микросервиса, к которому мы обращаемся в рамках нашей задачи. Как быть? Можно, конечно, поменять данные, которыми оперирует этот микросервис, например, через интерфейс к БД или какому-то другому хранилищу. Но как быть, если с этой связкой микросервисов работаете не только вы и копание в данных микросервиса может зааффектить работу других разработчиков и тестировщиков, которые работают с этим же контуром k8s?

Для себя я нашел следующее решение: локально поднятый Kubernetes с развертыванием только необходимых в рамках задачи служб и разработкой внутри контейнера в кластере, чтобы все изменения подхватывались, как говорится, налету.

Какую альтернативу тут еще я вижу? Например, можно собрать все нужные микросервисы и их зависимости в большой docker-compose.yml и поднимать их все командой docker compose up. Можно? — Да, конечно, но, как по мне, скучно 🙂 Поэтому, вариант с локальным k8s и разработкой в нем дальше и рассмотрим…

Мы имеем:

  • несколько микросервисов, которые взаимодействуют друг с другом неким образом для процессинга данных: синхронно или асинхронно — неважно;

  • у каждого микросервиса свой репозиторий;

  • для каждого микросервиса есть docker-compose.yml для удобства локальной разработки этого микросервиса;

  • все сторонние интеграции, включая общение с другими микросервисами в тестах “замоканы”.

Нам нужно:

  • развернуть локальный Kubernetes;

  • задеплоить туда наши сервисы;

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

Существует несколько решений для упрощенного поднятия локального Kubernetes. В этой статье есть ссылки на самые распространенные. Мне из этого списка нравится Minikube. Устанавливаем его к себе на машину по документации, там все просто, например, для MacOS — brew install minikube и все готово. Запускаем локальный кластер:

minikube start

Для удобства работы с нашим локальным кластером я буду использовать TUI утилиту k9s. Можете использовать инструменты с графическим интерфейсом наподобие Lens — кому что нравится.

Давайте теперь установим Telepresence — инструмент, который позволит нам работать с сервисом в кластере так, чтобы все изменения применялись “налету”.
Для вашей системы лучше посмотреть документацию на официальном сайте, для MacOS просто:

brew install telepresenceio/telepresence/telepresence-oss

Далее для установки Telepresence в наш кластер выполним:

telepresence helm install

Итак, запускаем k9s и, если все хорошо, то увидим примерно следующее:

Дальше нам надо задеплоить необходимые для работы сервисы. Так как у нас есть готовые docker-compose.yml файлы, мы можем из них сделать деплойменты для кубера при помощи утилиты kompose. Я, правда, этим инструментом пользовался всего один раз, так как у меня есть готовые шаблоны deployment-файлов, которые мне поменять быстрее, чем пользоваться kompose 🙂 Ну, тут дело вкуса и привычки. Приведу пример одного такого deployment:

apiVersion: apps/v1 kind: Deployment metadata:   name: catalog spec:   replicas: 1   selector:     matchLabels:       app: catalog   template:     metadata:       labels:         app: catalog     spec:       containers:       - name: catalog         image: catalog-image:latest  # Используйте имя образа, который вы собрали         imagePullPolicy: IfNotPresent         ports:         - name: catalog-grpc           containerPort: 50051  # Замените на порт, который использует ваше приложение         command: ["python", "src/app.py"]         env:         - name: MONGODB_USER           value: "mongouser"  # Имя сервиса PostgreSQL         - name: MONGODB_PASSWORD           value: "mongopassword"  # Замените на ваше имя пользователя         - name: MONGODB_HOST           value: "mongodb"  # Замените на ваш пароль         - name: MONGODB_PORT           value: "27017"  # Замените на ваше имя базы данных         - name: MONGODB_AUTH_DB           value: "admin"         - name: MONGODB_DATABASE_NAME           value: "catalog"         - name: REDIS_HOST           value: "redis" --- apiVersion: v1 kind: Service metadata:   name: catalog spec:   type: NodePort   ports:   - port: 50051  # Замените на порт, который использует ваше приложение     targetPort: catalog-grpc     nodePort: 30001  # Замените на желаемый NodePort   selector:     app: catalog

Теперь давайте соберем образ приложения. Перейдите в директорию вашего проекта, где находится ваш Dockerfile, и выполните команду для сборки образа:

eval $(minikube docker-env) docker build -t your-image-name .

Для Mac с M-процессорами надо добавить в команду docker build флаг --platform=linux/arm64

Теперь давайте задеплоим наш сервис в кластер:

kubectl apply -f your-deployment-file.yml

Вот такие манипуляции надо провести со всеми нужными нам сервисами. Когда все нужные нам сервисы окажутся в кластере, то увидим похожу картину на ту, что на скриншоте*:

*в моем случае namespace=default

Все, теперь у нас есть рабочий локальный кластер Kubernetes. Для сервисов нужны данные, надеюсь, они у вас есть в дампах, csv или еще в чем-то для импорта в БД.

Инструмент k9s позволяет нам пробросить порт нужного сервиса на локальную машину нажатием shift+f. Таким образом, пробросив порт, например, до Postgres, мы можем подключиться к БД в нашем кластере при помощи любимого инструмента, указав хост — localhost и порт, который вы указали про пробросе, ну, и, конечно, креды для авторизации. Вот пример подключения к Postgres с дефолтными настройками и импорт подготовленного дампа:

psql -U postgres -h postgres postgres < dump.sql

Выполнять команду нужно из директории с файлом дампа, ну это вы поняли, я думаю 🙂 Все, вот по такой аналогии заполняем данными наши сервисы и приступаем, наконец, к разработке…

Далее я рассмотрю пример, когда нужно менять код cms-service, который в свою очередь посылает запрос к cms-api, а тот уже идет к следующему сервису, пусть это будет catalog-service.
CMS_APP написано на Python, на Django. Открываем наш редактор кода с этим проектом. Переходим в терминал:

cd {path_to_project} telepresence connect telepresence intercept cms-app --port 9000:9000 -- python3 manage.py runserver 0.0.0.0:9000

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

✔ Intercepted                                                                                                                                                                                0.1s Using Deployment cms-app    Intercept name    : cms-app    State             : ACTIVE    Workload kind     : Deployment    Intercepting      : 10.244.0.31 -> 127.0.0.1        9000 -> 9000 TCP

Открываем браузер http://127.0.0.1:9000 и там видим наше приложение.

Все, пишем код, смотрим в live-режиме все изменения, радуемся жизни 😉

После того как вы закончили, в терминале нажимаем ctrl+c чтобы прервать работу telepresence и выполняем telepresence quit для отсоединения от кластера. Как только мы нажмем ctrl+c состояние контейнера приложения вернется к исходному, которое было до начала работы и соответствующее состоянию образа. Спокойно коммитим наши изменения, пушим и закрываем таску 😉 Для обновления образа приложения с нашими изменениями надо будет заново его пересобрать и задеплоить в наш локальный кластер. Эти шаги расписаны выше, поэтому не буду еще раз их повторять.

С Telepresence, конечно, можно работать и в dev кластере, если ваши DEVOPS-инженеры не против.

На этом все, надеюсь, было интересно и, может, кому-то полезно.


ссылка на оригинал статьи https://habr.com/ru/articles/926556/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *