Хабрахабр

Kubernetes tips & tricks: о локальной разработке и Telepresence

Разработчики, особенно интерпретируемых языков, хотят быстро поправить код в любимой IDE и без ожидания сборки/деплоя увидеть результат — по простому нажатию на F5. Нас все чаще спрашивают про разработку микросервисов в Kubernetes. С распиливанием монолитов на микросервисы и приходом Kubernetes, с появлением зависимостей друг от друга, всё стало немного сложнее. И когда речь шла про монолитное приложение, достаточно было локально поднять базу данных и веб-сервер (в Docker, VirtualBox…), после чего — сразу же наслаждаться разработкой. Чтобы вновь насладится разработкой, нужно поднять уже не один и не два Docker-контейнера, а иногда — даже не один десяток… В общем, на всё это может уходить достаточно много времени, поскольку требуется ещё и поддерживать в актуальном состоянии. Чем больше этих микросервисов, тем больше проблем.

И начну я с накопленных workarounds или попросту «костылей». В разное время мы пробовали разные решения проблемы.

1. Костыли

Большинство IDE имеет возможность править код прямо на сервере с помощью FTP/SFTP. Такой путь весьма очевиден и мы сразу решили им воспользоваться. Суть его сводится к следующему:

  1. В pod’е у окружений для разработки (dev/review) запускается дополнительный контейнер с доступом по SSH и пробросом публичного SSH-ключа того разработчика, что будет коммитить/деплоить приложение.
  2. На init-стадии (в рамках контейнера prepare-app) переносим код в emptyDir, чтобы иметь доступ к коду из контейнеров с приложением и SSH-сервера.

Для лучшего понимания технической реализации такой схемы приведу фрагменты задействованных YAML-конфигураций в Kubernetes.

Конфигурации

1.1. values.yaml

ssh_pub_key: vasya.pupkin: <ssh public key in base64>

Здесь vasya.pupkin — это значение переменной $.

1.2. deployment.yaml

...
{{ if eq .Values.global.debug "yes" }} volumes: - name: ssh-pub-key secret: defaultMode: 0600 secretName: {{ .Chart.Name }}-ssh-pub-key - name: app-data emptyDir: {} initContainers: - name: prepare-app
{{ tuple "backend" . | include "werf_container_image" | indent 8 }} volumeMounts: - name: app-data mountPath: /app-data command: ["bash", "-c", "cp -ar /app/* /app-data/" ]
{{ end }} containers:
{{ if eq .Values.global.debug "yes" }} - name: ssh image: corbinu/ssh-server volumeMounts: - name: ssh-pub-key readOnly: true mountPath: /root/.ssh/authorized_keys subPath: authorized_keys - name: app-data mountPath: /app ports: - name: ssh containerPort: 22 protocol: TCP
{{ end }} - name: backend volumeMounts:
{{ if eq .Values.global.debug "yes" }} - name: app-data mountPath: /app
{{ end }} command: ["/usr/sbin/php-fpm7.2", "--fpm-config", "/etc/php/7.2/php-fpm.conf", "-F"]
...

1.3. secret.yaml

{{ if eq .Values.global.debug "yes" }}
apiVersion: v1
kind: Secret
metadata: name: {{ .Chart.Name }}-ssh-pub-key
type: Opaque
data: authorized_keys: "{{ first (pluck .Values.global.username .Values.ssh_pub_key) }}"
{{ end }}

Финальный штрих

После этого останется только передать нужные переменные gitlab-ci.yml:

dev: stage: deploy script: - type multiwerf && source <(multiwerf use 1.0 beta) - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose) - werf deploy --namespace ${CI_PROJECT_NAME}-stage --set "global.env=stage" --set "global.git_rev=${CI_COMMIT_SHA}" --set "global.debug=yes" --set "global.username=${GITLAB_USER_LOGIN}" tags: - build

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

Это вполне рабочее решение, однако с точки зрения реализации имеет очевидные минусы:

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

2. Telepresence

Проект Telepresence известен достаточно давно, однако всерьёз попробовать его на деле у нас, что называется, «не доходили руки». Однако спрос сделал своё дело и теперь мы рады поделиться опытом, который может оказаться полезным читателям нашего блога — тем более, что на хабре до сих пор не было других материалов про Telepresence.

Все действия, которые требуют выполнения со стороны разработчика, мы разместили в текстовом файле Helm-чарта, названном NOTES.txt. Если вкратце, то всё оказалось не так страшно. Таким образом, разработчик после деплоя сервиса в Kubernetes видит инструкцию по запуску локального dev-окружения в логе job’а GitLab:

!!! Разработка сервиса локально, в составе Kubernetes !!! * Настройка окружения
* * Должен быть доступ до кластера через VPN
* * На локальном ПК установлен kubectl ( https://kubernetes.io/docs/tasks/tools/install-kubectl/ )
* * Получить config-файл для kubectl (скопировать в ~/.kube/config)
* * На локальном ПК установлен telepresence ( https://www.telepresence.io/reference/install )
* * Должен быть установлен Docker
* * Необходим доступ уровня reporter или выше к репозиторию https://gitlab.site.com/group/app
* * Необходимо залогинится в registry с логином/паролем от GitLab (делается один раз): #########################################################################
docker login registry.site.com
######################################################################### * Запуск окружения #########################################################################
telepresence --namespace {{ .Values.global.env }} --swap-deployment {{ .Chart.Name }}:backend --mount=/tmp/app --docker-run -v `pwd`:/app -v /tmp/app/var/run/secrets:/var/run/secrets -ti registry.site.com/group/app/backend:v8
#########################################################################

Что же происходит во время запуска Telepresence? Не будем подробно останавливаться на описанных в этой инструкции шагах… за исключением последнего.

Работа с Telepresence

При старте (по последней команде, указанной в инструкции выше) мы задаём:

  • пространство имён (namespace), в котором запущен микросервис;
  • имена deployment’а и контейнера, в который хотим проникнуть.

Остальные аргументы опциональны. Если наш сервис взаимодействует с Kubernetes API и для него создан ServiceAccount, нам необходимо смонтировать сертификаты/токены на свой десктоп. Для этого используется опция --mount=true (или --mount=/dst_path), которая смонтирует корень (/) из контейнера в Kubernetes к нам на desktop. После этого мы можем (в зависимости от ОС и способа запуска приложения) воспользоваться «ключами» от кластера.

Для этого воспользуемся ключом --docker-run и примонтируем директорию с кодом в контейнер: -v `pwd`:/app В начале рассмотрим самый универсальный вариант запуска приложения — в Docker-контейнере.

Код приложения будет смонтирован в директорию /app в контейнере. Обратите внимание, что здесь подразумевается запуск из каталога с проектом.

Далее: -v /tmp/app/var/run/secrets:/var/run/secrets — для монтирования директории с сертификатом/токеном в контейнер.

NB: При сборке образа надо обязательно указать CMD или ENTRYPOINT! За этой опцией следует, наконец, образ, в котором будет запускаться приложение.

Что же, собственно, произойдет дальше?

  • В Kubernetes для указанного Deployment’а количество реплик будет изменено на 0. Вместо него запустится новый Deployment — с подменённым контейнером backend.
  • На десктопе запустятся 2 контейнера: первый — с Telepresence (он будет осуществлять проксирование запросов из/в Kubernetes), второй — с разрабатываемым приложением.
  • Если exec’нуться в контейнер с приложением, то нам будут доступны все ENV-переменные, переданные Helm’ом при деплое, а также доступны все сервисы. Остаётся только править код в любимой IDE и наслаждаться результатом.
  • В конце работы достаточно просто закрыть терминал, в котором запущен Telepresence (оборвать сессию по Ctrl+C), — на десктопе остановятся Docker-контейнеры, а в Kubernetes все вернётся в начальное состояние. Останется лишь коммитнуть, оформить MR и передать его на review/merge/… (в зависимости от ваших рабочих процессов).

В случае, если мы не хотим запускать приложение в Docker-контейнере — например, мы разрабатываем не на PHP, а на Go, и всё-таки собираем его локально, — запуск Telepresence будет ещё проще:

telepresence --namespace {{ .Values.global.env }} --swap-deployment {{ .Chart.Name }}:backend --mount=true

Если приложение обращается к Kubernetes API, потребуется смонтировать директорию с ключами (https://www.telepresence.io/howto/volumes). Для Linux есть утилита proot:

proot -b $TELEPRESENCE_ROOT/var/run/secrets/:/var/run/secrets bash

После запуска Telepresence без опции --docker-run все переменные окружения будут доступны в текущем терминале, поэтому запуск приложения необходимо делать именно в нём.

NB: При использовании, например, PHP, нужно не забывать отключать для разработки различные op_cache, apc и прочие акселераторы — иначе правка кода не будет приводить к желаемому результату.

Итоги

Локальная разработка с Kubernetes — проблема, потребность в решении которой растёт пропорционально распространению этой платформы. Получая соответствующие запросы со стороны разработчиков (от наших клиентов), мы начали их решать первыми доступными средствами, которые, однако, не зарекомендовали себя на длинной дистанции. Благо, это стало очевидно не только сейчас и не только нам, поэтому в мире уже появились более подходящие средства, и Telepresence — самое известное из них (к слову, есть ещё skaffold от Google). Наш опыт его использования ещё не так велик, но уже даёт основания рекомендовать «коллегам по цеху» — попробуйте!

P.S.

Другое из цикла K8s tips & tricks:

Показать больше

Похожие публикации

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

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

Кнопка «Наверх»