На нескольких проектах я сталкивался с ситуацией, когда есть 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-инженеры не против.

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

Комментарии (2)


  1. vasyakrg
    10.07.2025 16:20

    Все хорошо, следил за руками, но так и не понял: вроде есть ci\cd, кластера и все на прод катается. От куда взялся докер-композ, который пришлось трансформить через kompose ? Почему просто не взять готовые хельм-чарты с прода ?

    А вот телепрезенс и в правду хорош.


    1. shoytov Автор
      10.07.2025 16:20

      docker-compose.yml с описанным самими сервисом и нужными ему службами по дефолту находится в репозитории с сервисом для быстрого "поднятия" его (сервиса) локально, все тесты гоняются в контенере. Откуда он взялся я писал в разделе "мы имеем"