company_banner

Сборка и дeплой приложений в Kubernetes с помощью dapp и GitLab CI

  • Tutorial


В предыдущих статьях о dapp было рассказано про сборку приложений и про запуск в Minikube. При этом dapp запускался локально на машине разработчика. Однако инструмент задумывался для поддержки процессов непрерывной интеграции (CI) и сами мы используем его в основном в связке с GitLab. Чем dapp помогает в процессах CI/CD?

Во-первых, конечно же, это сборка. Dapp позволяет ускорить инкрементальную сборку приложений, привязывая команды сборки к изменениям между коммитами в Git-репозитории (подробнее об этом и других оптимизациях на этапе сборки см. в докладе «Собираем Docker-образы для CI/CD быстро и удобно вместе с dapp»: статья, видео).

Во-вторых, dapp помогает выкатывать приложение в кластер Kubernetes с помощью Helm. Помимо некоторого упрощения работы с секретами и проверки Helm-шаблонов, dapp помогает ожиданию выката Deployments, указанных в шаблонах.

В-третьих, в dapp реализована логика очистки кэша как в локальном, так и в удалённом Registry с Docker-образами. Очистка была улучшена в последних версиях: теперь dapp удаляет образы, созданные по веткам Git, если эти ветки удалены из Git-репозитория. С образами по тегам немного сложнее: остаётся не более 10 образов не старше 30 дней. В следующих версиях планируется сделать настраиваемые политики очистки.

Всё перечисленное критично в большей степени для сервера сборки, поэтому далее с помощью несложного стенда GitLab + Minikube покажу пример внедрения dapp в процессы непрерывной интеграции и доставки (CI/CD).

Стенд GitLab + Minikube + dapp


Стенд состоит из установленного GitLab, GitLab Runner, Registry и кластера Kubernetes:



Схема приближена к варианту, который используется нами в реальных проектах. Вкратце всё работает так:

  1. Разработчик push'ит свои изменения в Git-репозиторий.
  2. GitLab запускает задачу сборки, GitLab Runner запускает dapp, который собирает образ и push'ит образ в Registry.
  3. Чтобы выкатить приложение в кластер, запускается задача деплоя: GitLab Runner (с доступом к kubectl и Helm) выполняет dapp kube deploy.
  4. Кластер Kubernetes при получении обновлённых ресурсов проверяет, есть ли новый образ в Registry, скачивает его и запускает поды с новым образом.

Стенд — демонстрационный, поэтому, чтобы не плодить виртуальные машины, вместе с GitLab нужно установить Registry, GitLab Runner, dapp, Docker, kubectl и Helm. В качестве Kubernetes-кластера используется Minikube как самый простой способ запустить K8s на локальной машине.

В качестве приложения опять используется symfony-demo. Сборка этого проекта в локальном варианте была описана в статье «Практика с dapp. Часть 1: Сборка простых приложений», а пример выката приложения в Minikube был описан в статье «Практика с dapp. Часть 2. Деплой Docker-образов в Kubernetes с помощью Helm». Отличие от второй статьи будет в том, что Registry для Minikube становится внешним (расположен в виртуальной машине с GitLab) и команда dapp kube minikube setup не требуется.

Подготовка хост-системы


Перед созданием виртуальных машин лучше заранее добавить имена хостов, например, в /etc/hosts:

192.168.33.20 gitlab.example.com # доступ к интерфейсу gitlab
192.168.33.20 registry.gitlab.example.com # доступ к registry
192.168.33.100 cluster.example.com # доступ к приложению и к api k8s


GitLab в виртуальной машине


Установка GitLab с помощью готового Vagrantfile (https://github.com/rgl/gitlab-vagrant) была упомянута во второй части «Практики с dapp», но теперь опишу подробнее. Чтобы запустить виртуальную машину, понадобится отредактировать Vagrantfile, сменить базовый образ на xenial64 и увеличить память с 2048 до 4096.

После vagrant up нужно установить GitLab Runner по инструкции проекта. Также потребуется инсталляция Docker и dapp. И для деплоя — скачать бинарники kubectl и Helm.

Чтобы запустить Registry, нужно отредактировать /etc/gitlab/gitlab.rb, раскомментировав там строчки:

gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "registry.gitlab.example.com"
gitlab_rails['registry_port'] = "5000"
gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
gitlab_rails['registry_api_url'] = "http://localhost:5000"
gitlab_rails['registry_key_path'] = "/var/opt/gitlab/gitlab-rails/certificate.key"
gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
registry['registry_http_addr'] = "0.0.0.0:5000"

После сохранения — запустить gitlab-ctl reconfigure. Omnibus изменит конфигурацию и перезапустит GitLab.

Всё вышеперечисленное уже есть в форке gitlab-vagrant — достаточно его склонировать и выполнить vagrant up.

Minikube


Время запустить и настроить Minikube. Инструкция довольно проста: скачать бинарник проекта и вызвать команду:

minikube start --insecure-registry registry.gitlab.example.com:5000 --host-only-cidr 192.168.33.1/24

К сожалению, для работы в составе стенда придётся остановить кластер (minikube stop) и вручную отредактировать ~/.minikube/machines/minikube/config.json, включив HostDNSResolver, чтобы виртуальная машина воспринимала /etc/hosts хоста. Также, если Minikube был установлен после прочтения второй части, проверьте ключ InsecureRegistry.

{
  "Driver": {
  …
    "HostDNSResolver": true,
…
  “HostOptions”: {
    "EngineOptions": {
      "InsecureRegistry": [
        "registry.gitlab.example.com:5000"
      ],
…

Чтобы проверить доступность кластера после запуска minikube start, можно выполнить команду kubectl get all.

Настройка GitLab Runner


Кластер запущен — нужно настроить связь с кластером для gitlab-runner. Для этого в виртуальную машину копируется локальный конфиг из .kube и сертификаты для доступа к Kubernetes API:

$ cd ~
$ tar zcf kube-config.tar.gz .kube/config .minikube/ca.crt .minikube/apiserver.*
$ cp kube-config.tar.gz $GITLAB_VM_DIR
$ cd $GITLAB_VM_DIR
$ vagrant ssh
ubuntu@gitlab:~$ sudo su - gitlab-runner
gitlab-runner@gitlab:~$ tar zxf /vagrant/kube-config.tar.gz

Следующий шаг — отредактировать .kube/config, чтобы был правильный путь к файлам:

  • certificate-authority: /home/gitlab-runner/.minikube/ca.crt
  • client-certificate: /home/gitlab-runner/.minikube/apiserver.crt
  • client-key: /home/gitlab-runner/.minikube/apiserver.key

Далее можно проверить связь и настроить Helm:

gitlab-runner@gitlab:~$ kubectl get all

Если Minikube уже был установлен ранее по второй части, то tiller уже есть и достаточно команды:

gitlab-runner@gitlab:~$ helm init --client-only

Если minikube не был ранее установлен, то нужно, чтобы установился tiller:

gitlab-runner@gitlab:~$ helm init

Для dapp необходимо добавить plugin template:

gitlab-runner@gitlab:~$ helm plugin install https://github.com/technosophos/helm-template

Импорт проекта и pipeline


Теперь можно импортировать репозиторий symfony-demo в GitLab. В списке проектов нажать New Project, выбрать Import, затем Repo by URL, ввести URL, группу и имя нового проекта.



В проекте уже есть ветка с Dappfile и шаблонами в .helm, созданными при работе над предыдущими статьями, — dapp_deploy_minikube.

Для демонстрации работы CI/CD нужно добавить конфигурацию CI — .gitlab-ci.yml. Это будет простой pipeline из двух заданий:



Задание Build


Build:
  stage: build
  script:
    - dapp --version
    - dapp dimg build --build-dir ~/dapp_build/${CI_PROJECT_NAME}
    - dapp dimg push --build-dir ~/dapp_build/${CI_PROJECT_NAME} ${CI_REGISTRY_IMAGE} --tag-ci
  tags:
    - build

Задание 3 раза запускает dapp. Первый раз — информативный, чтобы видеть версию dapp. Второй запуск — сборка образов, описанных в Dappfile. Третий — push собранных образов в Registry, при этом теги образам будут проставлены на основании переменных CI_* (документация по опциям тегирования).

Задание Deploy


Deploy:
  stage: deploy
  script:
  - dapp --version
  - dapp kube deploy
    --tag-ci
    --namespace ${CI_PROJECT_NAME}-stage
    --set "global.env=stage"
    --set "global.git_rev=${CI_COMMIT_SHA}"
    $CI_REGISTRY_IMAGE
  tags:
    - build

Здесь тоже первым запуском выводится версия dapp. Второй запуск — выкат приложения в кластер.

Можно заметить, что в пространстве имён используется суффикс stage — это сделано, чтобы показать, что имя namespace можно задать любое. В полноценном варианте pipeline потребуется создать несколько заданий для нужных окружений.

Helm-шаблоны


Registry, установленный в GitLab, в отличие от Registry, устанавливаемого командой dapp kube minukube setup, имеет разграничение прав и поэтому в Helm-шаблоны нужно добавить registrysecret с логином и паролем для Registry. Можно сформировать секрет вручную с помощью команды base64, а можно воспользоваться подсказкой.

В итоге, в .helm/values.yaml добавятся такие значения:

imageCredentials:
  registry: registry.gitlab.example.com:5000
  username: root
  password: password

А в backend.yaml — такой ресурс:

apiVersion: v1
kind: Secret
type: kubernetes.io/dockercfg
metadata:
  name: registrysecret
data:
  .dockercfg: {{ printf "{\"%s\": {\"auth\": \"%s\"}}" .Values.imageCredentials.registry (printf "%s:%s" .Values.imageCredentials.username .Values.imageCredentials.password | b64enc) | b64enc }}

… и imagePullSecrets в spec шаблонов контейнеров:

spec:
  template:
    ...
    spec:
      imagePullSecrets:
      - name: registrysecret
       containers:
       - command: [ '/demo/start.sh' ]
         image: {{ tuple "symfony-demo-app" . |  include "dimg" }}

Выкат по таким Helm-шаблонам уже должен быть успешен — результат можно наблюдать в браузере по адресу http://cluster.example.com/symfony-demo:




Резюмируя


В целом можно считать, что развёрнут стенд с процессом CI/CD, очень приближенным к тому, что работает у наших клиентов. Следующими шагами будет усложнение pipeline (см. «GitLab CI для непрерывной интеграции и доставки в production. Часть 1: наш пайплайн» и «Часть 2: преодолевая трудности»), введение динамических окружений (обзорная статья коллеги), добавление очистки Registry по расписанию, добавление запуска интеграционных тестов. Вопросы про описанный стенд и про dapp можно задать в комментариях и в нашем Telegram-чате.

P.S.


Читайте также в нашем блоге:

  • +24
  • 12.5k
  • 4
Флант
357.38
Специалисты по DevOps и Kubernetes
Share post

Comments 4

    0
    Ваша утилита (dapp) пытается перекрыть штатный функционал GitLab, в котором есть и кэширование, и интеграция с Kubernetes (консоль + rollback).
    Так же кажется странной установка компонентов GitLab НЕ в Kubernetes а на отдельные ВМ.
      +1

      Кэширование есть и в docker-е, но в Dappfile можно определить зависимость пересборки от изменений в определённых файлах — такого нет в gitlab. И ещё проблема — чистка ненужных образов, gitlab тут не сильно помогает, даже немного вредит, не давая прав через CI_JOB_TOKEN.


      С выкатом через интеграцию с kubernetes не сталкивался вплотную, но из документации складывается впечатление, что это некий свой выкат через использование API или kubectl. Второй вариант — Clusters использует helm, но ещё не совсем готов. Если clusters в Gitlab CE будет уметь ожидать успешности выката (запуск helm это выдача задания в tiller и выход), то будем брать на вооружение.


      GitLab НЕ в Kubernetes

      Некоторым заказчикам мы ставим gitlab в Kubernetes, эту тему можно даже в отдельную статью превратить. А для демонстрационного стенда проще настроить две ВМ — gitlab в vagrant-е + minikube. Есть особенность, что если собирать образы тем же docker, который установлен в minikube, то kubelet будет уничтожать образы стадий по мере сборки. Поэтому для сборки нужен свой отдельный docker, настройка получается сложнее. Опять же можно использовать ВМ по отдельности, например, запустить только gitlab, чтобы отладить пайплайн.

        +1
        Спасибо за подробный ответ.

        На всех DevOps проектах с которыми мне приходилось сталкиваться, в качестве репозитория артефактов (в т.ч. Docker Registry) использовали Nexus либо Artifactory, в которых нет пробоем с вычисткой и с доступом. К тому же такой подход позволяет организовать хранение всех проектных артефактов в одном месте (npm, pypi, docker, maven, etc).

        Выкатка через интеграцию k8s возможна через Deployment update (replace) либо patch. Всё это отлично ложится на CI GitLab`a.
        По поводу использования helm всё зависит как Вы организуете CI/CD для выкатки чартов, но тут так-же всё можно сделать достаточно красиво.

        По поводу установки GitLab в k8s — быстрее поставить в minikube (helm install… буквально в одну команду) чем описывать процесс с vargant и отдельными ВМ, раз уж это не являляется целью данной публикации. Но это безусловно ИМХО

        А можно поподробнее про «kubelet будет уничтожать образы стадий по мере сборки»??
        Речь про DIND?
          0

          Попробую разъяснить сразу два момента. Упрощённо dapp работает таким образом, что сборка разделена на 4 стадии. Каждая стадия это образ (с тэгом), который можно запушить в registry. Таким образом, для одной сборки в registry попадёт несколько тэгов. По мере разработки появляется дерево образов, когда сборка, например, новой ветки основана на стадии предыдёщей сборки — это и есть кэширование.
          Проблем с очисткой как таковой в целом нет — dapp умеет смотреть что есть в registry и давать команды на удаление. Это скорее неудобство, что gitlab при запуске задачи ci не даёт токен с правами на удаление образов в Registry, хотя пользователь такие права имеет и приходится создавать токен отдельно — такая же ситуация будет с любым registry.


          Про kubelet очень просто — он чистит локальный registry от тэгов, которых нет в ресурсах. И, если собирать dapp-ом на том же docker-е, то возникает ситуация, что dapp собрал стадию, а kubelet её тут же удалил.


          По поводу использования helm всё зависит как Вы организуете CI/CD для выкатки чартов, но тут так-же всё можно сделать достаточно красиво.

          Критичная фича — ожидание выката helm релиза. dapp следит за этим, а Clusters надо пробовать. Это всё конечно «исторически сложилось» — выкат через helm был реализован в dapp, ещё когда много клиентов были на 9-ой версии gitlab, где интеграции с k8s не было.


          По поводу установки GitLab в k8s — быстрее поставить в minikube

          Тут полностью согласен, установка в куб больших приложений очень проста. Цель статьи показать как у нас в большинстве проектов настроен CI и получить стенд для экспериментов с gitlab + k8s.
          dapp умеет работать с remote docker, есть успешные попытки запускать сборку в pod-е с docker-dind и dapp. Но основное препятствие использовать gitlab в кубе для сборки — как быть с монтированием. Т.е. вариант с двумя ВМ заведомо рабочий для всех вариантов сборки. А вариант с gitlab в кубе это более сложная конструкция, которая тянет на отдельную статью.

    Only users with full accounts can post comments. Log in, please.