Знать, как использовать инструмент с открытым исходным кодом

В этой статье рассматривается создание Docker-образа минималистического приложения и его развертывание в кластере Kubernetes с помощью инструмента с открытым исходным кодом под названием werf. Я также покажу, как вы можете дополнительно внести изменения в код вашего приложения и инфраструктуру, в которой оно выполняется.

В качестве примера приложения я буду использовать небольшой эхо-сервер, основанный на сценарии оболочки. Этот сервер возвращает строку Hello, werfer! в ответ на запрос конечной точки /ping.

Примечание. Вы можете изучить все файлы этого минималистического приложения и загрузить их из этого репозитория.

Кластер Kubernetes, который мы будем использовать в этой статье, основан на minikube, поэтому вам не нужно какое-то специальное оборудование, чтобы следовать инструкциям: подойдет ваш обычный настольный компьютер/ноутбук.

werf: Краткое введение

Для тех, кто не знаком с этой утилитой CLI, werf реализует полный рабочий процесс доставки приложений в Kubernetes. Он использует Git как единый источник кода приложения и конфигурации:

  • Каждая фиксация отражает определенное состояние приложения;
  • werf синхронизирует его с реестром контейнеров (путем создания недостающих слоев финальных образов Docker) и запуском приложения в Kubernetes (путем повторного развертывания измененных ресурсов);
  • werf также очищает реестр контейнеров от устаревших артефактов с помощью уникального алгоритма, основанного на истории Git и определяемых пользователем политиках.

Отличительной чертой werf является интеграция многих известных инструментов для разработчиков и инженеров DevOps/SRE, таких как Git, Docker, реестр контейнеров, система CI, Helm и Kubernetes. Объединение этих компонентов обеспечивает продуманный рабочий процесс CI/CD для доставки ваших приложений в Kubernetes. Их объединение сводит к минимуму усилия, необходимые для реализации CI/CD.

Давайте посмотрим на это в действии.

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

Перед запуском установите в свою систему последнюю стабильную версию werf (v1.2 из канала стабильного релиза) (см. официальную документацию).

Все команды и действия, приведенные в статье, относятся к операционной системе Linux (проверено на Ubuntu 20.04.03). Хотя команды в целом одинаковы для других систем, таких как Windows и macOS, могут существовать небольшие различия. Если у вас возникли проблемы с какими-либо конкретными инструкциями для вашей ОС, перейдите по ссылкам в конце этой статьи.

Создание образа

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

mkdir app

Создайте сценарий hello.sh в каталоге со следующим содержимым:

Инициализируйте новый репозиторий Git в каталоге и зафиксируйте первые изменения — скрипт, который мы только что создали:

cd ~/app
git init
git add .
git commit -m initial

Поскольку наше приложение будет собрано и запущено в Docker, давайте также создадим Dockerfile с инструкциями по сборке образа приложения:

Чтобы werf использовал Dockerfile для сборки, нам нужно создать werf.yaml конфигурационный файл в корне проекта с описанием Dockerfile:

Репозиторий с созданными к этому моменту файлами доступен в этой директории репозитория werf/first-steps-example.

Теперь мы готовы создать наше приложение. Обратите внимание, что вам необходимо зафиксировать все изменения в репозитории проекта (файл Dockerfile и т. д.) перед сборкой, т. е. сначала выполнить следующие команды:

git add .
git commit -m FIRST

Запустите сборку с помощью команды ниже:

werf build

Вы должны увидеть следующий вывод:

Чтобы проверить, прошла ли сборка успешно, запустите приложение командой:

werf run app --docker-options="-ti --rm -p 8000:8000" -- /app/hello.sh

Давайте подробнее рассмотрим приведенную выше команду. Параметр —-docker-options указывает набор параметров, связанных с Docker, а команде для выполнения в контейнере (в конце) предшествуют два дефиса.

Давайте проверим, все ли работает правильно. Для этого перейдите по адресу http://127.0.0.1:8000/ping в браузере или используйте следующий CURL-запрос в другом терминале:

curl http://127.0.0.1:8000/ping

Вы должны увидеть сообщение «Hello, werfer!» приветствие. Кроме того, в логах работающего контейнера должно появиться следующее сообщение:

GET /ping HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/7.68.0
Accept: */*

Подготовка к развертыванию

Создание приложения — половина проблемы (или даже третья). В конце концов, вам все равно придется развернуть его на рабочих серверах. Для этого создадим локальный «рабочий» кластер Kubernetes и настроим werf для его использования. Вот список шагов, которые нужно предпринять:

  • установить и запустить minikube, минимальный дистрибутив Kubernetes (идеально подходит для тестирования);
  • установить NGINX Ingress Controller — компонент кластера, отвечающий за маршрутизацию трафика;
  • отредактируйте файл /etc/hosts, чтобы включить доступ к кластеру с использованием доменного имени приложения;
  • войдите в Docker Hub и настройте секрет с необходимыми учетными данными;
  • разверните приложение в Kubernetes.

1. Установка и запуск миникуба

Сначала установите minikube, как описано в официальной документации. Если он у вас уже установлен, убедитесь, что ваша версия самая последняя.

Давайте запустим кластер Kubernetes с помощью minikube:

# Delete the existing minikube cluster (if there is one).
minikube delete
# Start a new minikube cluster.
minikube start --driver=docker

Установите пространство имен Kubernetes по умолчанию, чтобы вам не приходилось вводить его каждый раз при использовании kubectl (обратите внимание, что мы настраиваем только имя по умолчанию, а не создаем само пространство имен — мы сделаем это позже):

kubectl config set-context minikube --namespace=werf-first-app

Если у вас не установлен kubectl, установить его можно двумя способами:

  • Установите его вручную, используя официальную документацию;
  • Используйте бинарный файл kubectl, поставляемый с minikube. Для этого выполните следующие команды:
alias kubectl="minikube kubectl --"
echo 'alias kubectl="minikube kubectl --"' >> ~/.bash_aliases

Если вы выберете второй вариант, утилита будет загружена и установлена ​​при первом вызове kubectl с указанным выше псевдонимом.

Давайте проверим, работает ли kubectl, перечислив все поды, работающие во вновь созданном кластере:

kubectl get --all-namespaces pod

Под — это эфемерная сущность Kubernetes, в которой размещается один или несколько контейнеров приложений и ресурсов, совместно используемых этими контейнерами.

Выполнение этой команды должно привести к выводу, подобному приведенному ниже:

Посмотрите внимательно на столбцы READY и STATUS. Если все поды имеют статус Running , а числа в столбце Ready равны 1/1 (обратите внимание, что число слева должно совпадать с числом справа), то наш кластер готов к использованию. Если вы не видите вывод, аналогичный приведенному выше, попробуйте подождать немного дольше и повторно запустить вышеуказанную команду (вероятно, некоторые поды не успели запуститься).

2. Установка контроллера входящего трафика NGINX

Следующим шагом будет установка и настройка контроллера NGINX Ingress. Он будет направлять внешние HTTP-запросы в наш кластер.

Используйте следующую команду для его установки:

minikube addons enable ingress

Этот процесс может занять некоторое время, в зависимости от производительности вашего ПК. Например, на установку этого дополнения моей машине потребовалось около четырех минут.

После завершения процесса вы должны увидеть следующее сообщение об успешном завершении:

The 'ingress' addon is enabled

Дождитесь запуска надстройки и проверьте, работает ли она:

kubectl -n ingress-nginx get pod

Вы должны увидеть вывод, аналогичный приведенному ниже:

Нас интересует последняя строка. Статус Running означает, что все в порядке, контроллер работает.

3. Отредактируйте файл hosts

Последним шагом в настройке среды является редактирование файла hosts, чтобы все запросы к тестовому домену попадали в локальный кластер.

В нашем случае мы будем использовать адрес werf-first-app.test. Запустите команду minikube ip в терминале, чтобы убедиться, что она выводит действительный IP-адрес. Если вывод не выглядит как действительный IP-адрес (в моем случае 192.168.49.2), вернитесь и переустановите кластер minikube.

Затем выполните следующую команду:

echo "$(minikube ip) werf-first-app.test" | sudo tee -a /etc/hosts

Вы можете проверить, была ли приведенная выше команда успешной, просмотрев файл hosts. Должна быть такая строка: 192.168.49.2 werf-first-app.test.

Теперь давайте посмотрим, все ли работает так, как ожидалось. Для этого отправим CURL-запрос на конечную точку приложения:

curl http://werf-first-app.test/ping

В этом случае NGINX Ingress Controller должен вернуть страницу 404, указывающую, что конечная точка еще недоступна:

4. Вход в Docker Hub

Теперь нам нужно настроить репозиторий для встроенных образов. Мы предлагаем использовать частный репозиторий Docker Hub. Для удобства мы будем использовать имя приложения (werf-first-app) в качестве имени репозитория.

Войдите в Docker Hub, выполнив следующую команду:

docker login
Username: <DOCKER HUB USERNAME>
Password: <DOCKER HUB PASSWORD>

Вы должны увидеть сообщение Login Succeeded.

5. Создание секрета для доступа к реестру

Чтобы использовать частный реестр для хранения изображений, необходимо создать секрет с учетными данными для входа в реестр. Обратите внимание, что секрет должен находиться в том же пространстве имен, что и приложение.

Поэтому вам нужно заранее создать пространство имен для приложения:

kubectl create namespace werf-first-app

Вы должны увидеть сообщение о том, что новое пространство имен создано (namespace/werf-first-app created).

Затем создайте секрет с именем registrysecret:

kubectl create secret docker-registry registrysecret \
  --docker-server='https://index.docker.io/v1/' \
  --docker-username='<DOCKER HUB USERNAME>' \
  --docker-password='<DOCKER HUB PASSWORD>'

В случае успеха вы должны увидеть сообщение secret/registrysecret created. Если вы ошиблись при создании секрета, удалите его командой kubectl delete secret registrysecret и создайте заново.

Обратите внимание, что описанный выше метод — это стандартный способ создания секретов в Kubernetes.

На этом подготовка среды для развертывания приложения в кластере завершена.

Мы будем использовать созданный выше секрет для извлечения образов приложений из реестра, указав его в поле imagePullSecrets при настройке подов.

Развертывание приложения в кластере

Перед развертыванием приложения мы должны создать манифесты Kubernetes, определяющие необходимые нам ресурсы. Для этой цели мы будем использовать формат диаграммы Helm. Диаграммы Helm (или пакеты Helm) содержат все определения ресурсов, необходимые для запуска приложения или службы в кластере Kubernetes.

Для нашего приложения нам понадобятся три ресурса K8s. В то время как Deployment отвечает за запуск приложения в контейнерах, Ingress и Service направляют внешний и внутренний трафик в кластере соответственно.

В итоге получаем следующую файловую структуру:

Мы поместим упомянутые выше манифесты в подкаталог templates скрытого каталога .helm.

Примечание: вам нужно добавить каталог с манифестами в файл .dockerignore, чтобы исключить эти файлы из контекста сборки образа Docker:

/.helm/

Давайте подробнее рассмотрим наши манифесты ресурсов.

1. Развертывание

Ресурс развертывания создает набор модулей для запуска приложения. Это выглядит так:

Здесь переменная шаблона {{ .Values.werf.image.app }} используется для вставки полного имени образа Docker приложения. Обратите внимание, что вы должны использовать то же имя компонента, которое использовалось в werf.yaml (в нашем случае app).

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

werf пересобирает образы только при изменении добавленных файлов (используемых в инструкциях Dockerfile COPY/ADD) или при изменении самого werf.yaml. Перестроение приводит к изменению тега образа, что автоматически приводит к обновлению развертывания. Если в этих файлах нет изменений, образ приложения и связанное с ним развертывание останутся без изменений, что означает, что состояние приложения в кластере актуально.

2. Сервис

Ресурс службы позволяет другим приложениям в кластере подключаться к нашему приложению. Это выглядит так:

3. Вход

В отличие от предыдущего ресурса, Service Ingress открывает доступ к нашему приложению из за пределами кластера. Его цель — перенаправить трафик с общедоступного домена werf-first-app.test на нашу службу Kubernetes. Это выглядит так:

Развертывание приложения

Давайте зафиксируем наши изменения конфигурации (ресурсы K8s, необходимые для развертывания приложения) в Git:

git add .
git commit -m FIRST

Репозиторий с созданными к этому моменту файлами доступен в этой директории репозитория werf/first-steps-example.

Запустите процесс развертывания с помощью следующей команды:

werf converge --repo <DOCKER HUB USERNAME>/werf-first-app

Посмотрим, прошел ли процесс успешно:

Запустите снова:

curl http://werf-first-app.test/ping

Вы должны увидеть следующий ответ:

Hello, werfer!

Поздравляем, вы успешно развернули приложение в кластере Kubernetes!

Внесение изменений в приложение

Давайте попробуем модифицировать наше приложение и посмотрим, как werf пересоберет и повторно развернет его в кластере.

Масштабирование

Наш веб-сервер работает как часть развертывания веб-приложения. Посмотрим, сколько реплик запущено:

На данный момент у нас есть только одна работающая реплика (та, которая запускается с werf-first-app). Увеличьте их количество до четырех:

kubectl edit deployment werf-first-app

Откроется текстовый редактор с содержимым файла манифеста. Найдите строку spec.replicas и измените количество реплик на четыре: spec.replicas=4. Подождите немного и проверьте количество запущенных реплик приложения:

В данном случае мы вручную увеличили количество реплик в кластере, напрямую отредактировав манифест и минуя Git. Теперь запустите команду werf converge:

werf converge --repo <DOCKER HUB USERNAME>/werf-first-app

Еще раз проверьте количество реплик:

Как видите, количество запущенных реплик соответствует указанному в манифесте, хранящемся в Git (мы его не редактировали). Это связано с тем, что werf вернул состояние кластера обратно к состоянию, описанному в текущем коммите Git. Этот механизм называется гитерминизм (Git + детерминизм).

Чтобы соблюсти этот принцип и сделать все правильно, нужно изменить количество реплик в файлах проекта в репозитории. Итак, давайте отредактируем файл deployment.yaml и зафиксируем изменения в репозиторий:

Зафиксируйте изменения и перестройте приложение с помощью следующей команды:

werf converge —-repo <DOCKER HUB USERNAME>/werf-first-app

Теперь давайте снова проверим количество реплик:

Как видите, реплик четыре. Давайте уменьшим их количество обратно до одного. Для этого отредактируйте файл deployment.yaml, зафиксируйте изменения и повторно разверните приложение с помощью команды werf converge.

Изменение кода

В настоящее время наше приложение отвечает Hello, werfer! Давайте изменим ответ и повторно развернем обновленное приложение в кластере. Откройте hello.sh в редакторе и замените существующую строку чем-то другим (например, Say hello one more time!):

Теперь зафиксируйте изменения и запустите werf converge. Что мы имеем в итоге?

curl http://werf-first-app.test/ping
Say hello one more time!

Поздравляю, все в порядке и работает как положено!

Выводы

В этой статье мы создали и развернули базовое приложение в кластере Kubernetes с помощью werf. Надеюсь, это поможет вам познакомиться с werf и получить некоторый опыт развертывания приложений на K8s.

Статья основана на главе Первые шаги онлайн-руководства для самостоятельного изучения. Делая его максимально кратким, я решил не углубляться в теоретические вопросы, освещенные в полном руководстве, такие как шаблоны и манифесты Kubernetes, основные ресурсы K8s для запуска приложений (Deployment, Service, Ingress), режимы работы werf и Giterminism, особенности использование Helm в werf и т. д. Подробнее о них можно узнать в вышеупомянутом руководстве. Там же доступны более конкретные инструкции, в том числе для других операционных систем.

Любые вопросы и предложения приветствуются в комментариях к статье или в Telegram-чате werf_io.

Ресурсы

  • werf.io — официальный сайт утилиты werf;
  • Гитерминизм — О гитерминизме, принципе, используемом утилитой;
  • GitHub — репозиторий исходного кода.