Kubernetes tips & tricks: про локальну розробку та Telepresence

Kubernetes tips & tricks: про локальну розробку та Telepresence

Нас все частіше запитують про створення мікросервісів в Kubernetes. Розробники, особливо інтерпретованих мов, хочуть швидко поправити код у улюбленій IDE і без очікування складання/деплою побачити результат — просто натиснувши на F5. І коли йшлося про монолітну програму, достатньо було локально підняти базу даних і веб-сервер (у Docker, VirtualBox ...), після чого - відразу ж насолоджуватися розробкою. З розпилюванням монолітів на мікросервіси і приходом Kubernetes, з появою залежностей один від одного, все стало трохи складніше. Що більше цих мікросервісів, то більше проблем. Щоб знову насолодитися розробкою, потрібно підняти вже не один і не два Docker-контейнери, а іноді навіть не один десяток… Загалом, на все це може витрачатись досить багато часу, оскільки потрібно ще й підтримувати в актуальному стані.

У різні часи ми пробували різні рішення проблеми. І почну я з накопичених workarounds або просто «милиць».

1. Милиці

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

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

Kubernetes tips & tricks: про локальну розробку та Telepresence

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

Зміни

1.1. values.yaml

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

Тут vasya.pupkin - Це значення змінної ${GITLAB_USER_LOGIN}.

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.

Якщо стисло, то все виявилося не так страшно. Усі дії, які вимагають виконання з боку розробника, ми розмістили у текстовому файлі 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-контейнері. Для цього скористаємося ключем --docker-run та примонтуємо директорію з кодом у контейнер: -v `pwd`:/app

Зверніть увагу, що тут мається на увазі запуск із каталогу з проектом. Код програми буде змонтований у директорію /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 -b $TELEPRESENCE_ROOT/var/run/secrets/:/var/run/secrets bash

Після запуску Telepresence без опції --docker-run всі змінні оточення будуть доступні в поточному терміналі, тому запуск програми необхідно робити саме в ньому.

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

Підсумки

Локальна розробка з Kubernetes – проблема, потреба у вирішенні якої зростає пропорційно поширенню цієї платформи. Отримуючи відповідні запити з боку розробників (від наших клієнтів), ми почали їх вирішувати першими засобами, які, однак, не зарекомендували себе на довгій дистанції. Благо, це стало очевидним не тільки зараз і не тільки нам, тому у світі вже з'явилися більш підходящі кошти, і Telepresence — найвідоміший з них (до речі, є ще лісок від Google). Наш досвід його використання ще не такий великий, але вже дає підстави рекомендувати колегам по цеху - спробуйте!

PS

Інше з циклу K8s tips & tricks:

Джерело: habr.com

Додати коментар або відгук