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

qixrx8ivoqcrvtjmvm-2anlhm0y.png

В предыдущих статьях о 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:

41rt93czjz9dihrk_gbgb-t7hsi.png

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

  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 не выше версии 17.09.0 и 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, группу и имя нового проекта.

d3oksn3tgk1tyqukmbirpv1qz0u.png

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

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

orrcxld5448iqzmhnjcv7vxoah8.png

Задание 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 --verbose
  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:

x3n1m28cxchcifihvcheb4p0dhs.png
ixvszhdt4-nudxqcmks_snnvnyo.png

Резюмируя


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

P.S.


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

  • «Практика с dapp. Часть 1: Сборка простых приложений»;
  • «Практика с dapp. Часть 2. Деплой Docker-образов в Kubernetes с помощью Helm»;
  • «Лучшие практики CI/CD с Kubernetes и GitLab (обзор и видео доклада)»;
  • «Инфраструктура с Kubernetes как доступная услуга».

© Habrahabr.ru