На нескольких проектах я сталкивался с ситуацией, когда есть 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-инженеры не против.
На этом все, надеюсь, было интересно и, может, кому-то полезно.
vasyakrg
Все хорошо, следил за руками, но так и не понял: вроде есть ci\cd, кластера и все на прод катается. От куда взялся докер-композ, который пришлось трансформить через kompose ? Почему просто не взять готовые хельм-чарты с прода ?
А вот телепрезенс и в правду хорош.
shoytov Автор
docker-compose.yml с описанным самими сервисом и нужными ему службами по дефолту находится в репозитории с сервисом для быстрого "поднятия" его (сервиса) локально, все тесты гоняются в контенере. Откуда он взялся я писал в разделе "мы имеем"