Первые шаги с werf: собираем и деплоим простое приложение в Kubernetes

от автора

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

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

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

В качестве кластера Kubernetes мы используем minikube, что позволит легко и быстро попробовать поработать с werf прямо на рабочем компьютере.

werf

Для тех, кто слышит это название впервые, поясним, что werf — это CLI-утилита, организующая полный цикл доставки приложения в Kubernetes. Она использует Git как единый источник, хранящий код и конфигурацию приложения. Каждый коммит — это состояние приложения, которое во время доставки werf синхронизирует с container registry, дособирая несуществующие слои конечных образов, а затем с приложением в Kubernetes, перевыкатывая изменившиеся ресурсы. Также werf позволяет очищать container registry от неактуальных артефактов по уникальному алгоритму, опирающемуся на историю Git и пользовательские политики.

Почему werf? Что в ней такого полезного и особенного? Основная ее фишка — объединение привычных для разработчиков и DevOps-/SRE-инженеров инструментов, таких как Git, Docker, container registry, CI-система, Helm и Kubernetes под одной утилитой. Тесная интеграция компонентов совместно с заложенными в процесс работы лучшими практиками, накопленными нашей компанией за годы работы с кубернетизацией приложений разных клиентов и запуском их в Kubernetes, делает werf достойным претендентом для доставки вашего приложения в Kubernetes.

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

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

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

Сборка образа

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

mkdir app

Создадим внутри каталога скрипт hello.sh, в котором опишем простую логику ответа на запрос:

#!/bin/sh  RESPONSE="Hello, werfer!"  while true; do   printf "HTTP/1.1 200 OK\n\n$RESPONSE\n" | ncat -lp 8000 done

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

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

Т.к. собираться и работать наше приложение будет в Docker, создадим рядом со скриптом Dockerfile, в котором будет описана логика сборки приложения в образ:

FROM alpine:3.14 WORKDIR /app  # Устанавливаем зависимости приложения RUN apk add --no-cache --update nmap-ncat  # Добавляем в образ созданный скрипт для запуска эхо-сервера  # и устанавливаем разрешение на выполнение COPY hello.sh . RUN chmod +x hello.sh

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

project: werf-first-app configVersion: 1  --- image: app dockerfile: Dockerfile

Уже готовое состояние репозитория с нужными сейчас файлами также можно забрать из этого каталога репозитория werf/first-steps-example.

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

git add . git commit -m FIRST

Запустим сборку приложение командой:

werf build

При успешной сборке увидим примерно следующий лог:

┌ ⛵ image app │ ┌ Building stage app/dockerfile │ │ app/dockerfile  Sending build context to Docker daemon  4.096kB │ │ app/dockerfile  Step 1/13 : FROM alpine:3.14 │ │ app/dockerfile   ---> 0a97eee8041e │ │ app/dockerfile  Step 2/13 : WORKDIR /app │ │ app/dockerfile   ---> Running in d4c535c0d754 │ │ app/dockerfile  Removing intermediate container d4c535c0d754 │ │ app/dockerfile   ---> 5a2a81813edc │ │ app/dockerfile  Step 3/13 : RUN apk add --no-cache --update nmap-ncat │ │ app/dockerfile   ---> Running in ce40513872fc │ │ app/dockerfile  fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz │ │ app/dockerfile  fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz │ │ app/dockerfile  (1/3) Installing lua5.3-libs (5.3.6-r0) │ │ app/dockerfile  (2/3) Installing libpcap (1.10.0-r0) │ │ app/dockerfile  (3/3) Installing nmap-ncat (7.91-r0) │ │ app/dockerfile  Executing busybox-1.33.1-r6.trigger │ │ app/dockerfile  OK: 6 MiB in 17 packages │ │ app/dockerfile  Removing intermediate container ce40513872fc │ │ app/dockerfile   ---> 8fffbbd2f295 │ │ app/dockerfile  Step 4/13 : COPY hello.sh . │ │ app/dockerfile   ---> 157c374b6224 │ │ app/dockerfile  Step 5/13 : RUN chmod +x hello.sh │ │ app/dockerfile   ---> Running in e603d67b6a34 │ │ app/dockerfile  Removing intermediate container e603d67b6a34 │ │ app/dockerfile   ---> e6d8a8dcd424 │ │ app/dockerfile  Step 6/13 : LABEL werf=werf-first-app │ │ app/dockerfile   ---> Running in 42b37bd0b1cd │ │ app/dockerfile  Removing intermediate container 42b37bd0b1cd │ │ app/dockerfile   ---> 2f27cc2d99bc │ │ app/dockerfile  Step 7/13 : LABEL werf-cache-version=1.2 │ │ app/dockerfile   ---> Running in 2e003ef0ec0a │ │ app/dockerfile  Removing intermediate container 2e003ef0ec0a │ │ app/dockerfile   ---> f55707dc38e6 │ │ app/dockerfile  Step 8/13 : LABEL werf-docker-image-name=f4059163-28e5-44fe-a53a-a110ed27db2d │ │ app/dockerfile   ---> Running in 4240a40d6032 │ │ app/dockerfile  Removing intermediate container 4240a40d6032 │ │ app/dockerfile   ---> 1ae5fc802a07 │ │ app/dockerfile  Step 9/13 : LABEL werf-image=false │ │ app/dockerfile   ---> Running in 610b776441c8 │ │ app/dockerfile  Removing intermediate container 610b776441c8 │ │ app/dockerfile   ---> 1c9066dcea1f │ │ app/dockerfile  Step 10/13 : LABEL werf-project-repo-commit=4959e91153d14e07133806398a001814b76f83bd │ │ app/dockerfile   ---> Running in ec973f81f08d │ │ app/dockerfile  Removing intermediate container ec973f81f08d │ │ app/dockerfile   ---> c938b7f22f96 │ │ app/dockerfile  Step 11/13 : LABEL werf-stage-content-digest=2c3a4d5dd59c112c3cba8d1ef5590dc16a618489bdcaf276f551f8ef │ │ app/dockerfile   ---> Running in 9acc7cd494df │ │ app/dockerfile  Removing intermediate container 9acc7cd494df │ │ app/dockerfile   ---> 4568493d8f34 │ │ app/dockerfile  Step 12/13 : LABEL werf-stage-digest=11b3f1b95dd6a39a9473a34ba0615c513d799be7e67e48a2deeed05c │ │ app/dockerfile   ---> Running in ac1bf708647a │ │ app/dockerfile  Removing intermediate container ac1bf708647a │ │ app/dockerfile   ---> 1b4d163b491c │ │ app/dockerfile  Step 13/13 : LABEL werf-version=v1.2.40 │ │ app/dockerfile   ---> Running in a1f9e286d870 │ │ app/dockerfile  Removing intermediate container a1f9e286d870 │ │ app/dockerfile   ---> eb03f3a2c600 │ │ app/dockerfile  Successfully built eb03f3a2c600 │ │ app/dockerfile  Successfully tagged a0e1fc05-970b-4b4c-bb8c-31c51a1a991a:latest │ │ ┌ Store stage into :local │ │ └ Store stage into :local (0.02 seconds) │ ├ Info │ │      name: werf-first-app:11b3f1b95dd6a39a9473a34ba0615c513d799be7e67e48a2deeed05c-1638357228902 │ │        id: eb03f3a2c600 │ │   created: 2021-12-01 14:13:48.839066578 +0300 MSK │ │      size: 6.0 MiB │ └ Building stage app/dockerfile (7.45 seconds) └ ⛵ image app (7.47 seconds)  Running time 7.53 seconds

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

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

Рассмотрим подробнее команду запуска. В ней заданы параметры Docker’а при помощи опции --docker-options, а в самом конце указана команда для выполнения внутри контейнера через два дефиса.

Проверим, что все запустилось и работает как нужно. Перейдем в браузере по адресу 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 и настроим секрет с нужными учетными данными;

  • непосредственно задеплоим приложение в K8s.

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

Для начала установим minikube, следуя его официальной документации. Если у вас в системе он уже установлен, убедитесь, что его версия соответствует последней актуальной (v1.23.2 на момент публикации статьи).

Создадим Kubernetes-кластер с помощью minikube:

# удаляем существующий minikube-кластер, если он существует minikube delete # запускаем новый minikube-кластер minikube start --driver=docker

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

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

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

  • Установить ее в систему отдельно, воспользовавшись официальной документацией.

  • Использовать поставляемый с minikube бинарник утилиты. Для этого достаточно выполнить команды:

alias kubectl="minikube kubectl --" echo 'alias kubectl="minikube kubectl --"' >> ~/.bash_aliases

Если вы выбрали второй вариант, то при первом обращении к kubectl по созданному alias’у утилита будет выкачана и доступна к использованию.

Давайте проверим, что все прошло успешно, и посмотрим на список всех Pod’ов, запущенных в свежесозданном кластере:

kubectl get --all-namespaces pod

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

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

NAMESPACE     NAME                               READY   STATUS    RESTARTS      AGE kube-system   coredns-78fcd69978-qldbj           1/1     Running   0             14m kube-system   etcd-minikube                      1/1     Running   0             14m kube-system   kube-apiserver-minikube            1/1     Running   0             14m kube-system   kube-controller-manager-minikube   1/1     Running   0             14m kube-system   kube-proxy-5hrfd                   1/1     Running   0             14m kube-system   kube-scheduler-minikube            1/1     Running   0             14m kube-system   storage-provisioner                1/1     Running   1 (13m ago)   14m

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

2. Установка NGINX Ingress Controller

Следующим шагом в подготовке будет установка и настройка Ingress-контроллера, основной задачей которого будет проброс внешних HTTP-запросов в наш кластер.

Установим его следующей командой:

minikube addons enable ingress

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

Если все прошло успешно, вы увидите сообщение о том, что аддон успешно установлен и активирован.

The 'ingress' addon is enabled

Немного подождем, чтобы он успел запуститься, и убедимся, что он работает:

kubectl -n ingress-nginx get pod

В результате будет отображено нечто похожее на картину ранее:

NAME                                        READY   STATUS      RESTARTS   AGE ingress-nginx-admission-create--1-xn4wr     0/1     Completed   0          6m19s ingress-nginx-admission-patch--1-dzxt6      0/1     Completed   0          6m19s ingress-nginx-controller-69bdbc4d57-bp27q   1/1     Running     0          6m19s

Нас интересует последняя строка — если статус стоит 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

Проверить правильность выполнения можно, посмотрев на содержимое искомого файла — в самом конца должна появиться строка вида 192.168.49.2 werf-first-app.test

Давайте убедимся, что все сделанное нами выше работает как надо. Выполним запрос на адрес http://werf-first-app.test/ping с использованием утилиты curl:

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

Если все работает правильно — NGINX Ingress Controller вернет страницу с кодом 404, сообщающую, что такого endpoint’а в системе нет:

<html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>nginx</center> </body> </html>

4. Авторизация в Docker Hub

Для дальнейшей работы понадобится хранилище собираемых образов, и мы предлагаем использовать для этого приватный репозиторий на Docker Hub. Для удобства используем такое же имя, как у приложения — werf-first-app

Залогинимся на Docker Hub, выполнив следующую команду:

docker login Username: <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB> Password: <ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>

Если введено правильно, увидим сообщение Login Succeeded.

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

Чтобы иметь возможность в процессе работы пользоваться приватным container registry для хранения образов, необходимо создать Secret с учетными данными для входа в registry. Здесь есть одна особенность — Secret должен располагаться в том же namespace’е, что и приложение.

Поэтому необходимо заранее создать namespace для нашего приложения:

kubectl create namespace werf-first-app

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

Далее создадим Secret с именем registrysecret:

kubectl create secret docker-registry registrysecret \   --docker-server='https://index.docker.io/v1/' \   --docker-username='<ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>' \   --docker-password='<ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>'

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

Это  стандартный способ создания Secret из официальной документации Kubernetes.

На этом шаге подготовку окружения к деплою можно считать завершенной.

Далее мы будем использовать созданный Secret для получения образов приложения из registry, указывая поле imagePullSecrets при конфигурации Pod’ов.

Деплой приложения в кластер

Для деплоя приложения в кластер необходимо подготовить Kubernetes-манифесты, которые описывают необходимые для работы ресурсы. Создавать их будем в формате Helm-чартов — пакетов Helm, содержащих все определения ресурсов, необходимых для запуска приложения, инструмента или службы внутри кластера Kubernetes.

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

Для их создания в случае нашего приложения получится следующая структура файлов:

. ├── Dockerfile ├── .dockerignore ├── hello.sh ├── .helm │   └── templates │       ├── deployment.yaml │       ├── ingress.yaml │       └── service.yaml └── werf.yaml

Мы создали скрытый каталог .helm, в который поместим упомянутые выше манифесты (их содержимое будет описано ниже) в подкаталоге templates. Обратите внимание: чтобы исключить эти файлы из контекста сборки Docker-образа, каталог с манифестами мы добавляем в файл .dockerignore:

/.helm/

Рассмотрим манифесты используемых ресурсов более подробно.

1. Deployment

Ресурс Deployment отвечает за создание набора Pod’ов, запускающих приложение. Выглядит он так:

apiVersion: apps/v1 kind: Deployment metadata:   name: werf-first-app spec:   replicas: 1   selector:     matchLabels:       app: werf-first-app   template:     metadata:       labels:         app: werf-first-app     spec:       imagePullSecrets:       - name: registrysecret       containers:       - name: app         image: {{ .Values.werf.image.app }}         command: ["/app/hello.sh"]         ports:         - containerPort: 8000

Здесь с помощью шаблонизации подставляется полное имя Docker-образа нашего приложения: {{ .Values.werf.image.app }}. Важно отметить, что для доступа к этому значению необходимо использовать имя компонента, которое используется в werf.yaml — в нашем случае это app

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

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

2. Service

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

apiVersion: v1 kind: Service metadata:   name: werf-first-app spec:   selector:     app: werf-first-app   ports:   - name: http     port: 8000

3. Ingress

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

apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   annotations:     kubernetes.io/ingress.class: nginx   name: werf-first-app spec:   rules:   - host: werf-first-app.test     http:       paths:       - path: /         pathType: Prefix         backend:           service:             name: werf-first-app             port:               number: 8000

Деплой приложения в Kubernetes

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

git add . git commit -m FIRST

Уже готовое состояние репозитория с нужными сейчас файлами также можно забрать из этого каталога репозитория werf/first-steps-example.

Запускаем деплой командой:

werf converge --repo <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-first-app

Если все прошло успешно — вы увидите примерно такой лог:

│ │ app/dockerfile  Successfully built 558e6bf23bb7 │ │ app/dockerfile  Successfully tagged 96c8cfca-d7db-4bf0-8351-cbdc0777c5b5:latest │ │ ┌ Store stage into .../werf-first-app │ │ └ Store stage into .../werf-first-app (16.85 seconds) │ ├ Info │ │      name: .../werf-first-app:11b3f1b95dd6a39a9473a34ba0615c513d799be7e67e48a2deeed05c-1638438736538 │ │        id: 558e6bf23bb7 │ │   created: 2021-12-02 12:52:16 +0300 MSK │ │      size: 3.0 MiB │ └ Building stage app/dockerfile (31.61 seconds) └ ⛵ image app (39.62 seconds)  Release "werf-first-app" does not exist. Installing it now.  ┌ Waiting for release resources to become ready │ ┌ Status progress │ │ DEPLOYMENT                                                                                                 REPLICAS            AVAILABLE             UP-TO-DATE                            │ │ werf-first-app                                                                                              1/1                 0                     1                                     │ │ │   POD                                      READY          RESTARTS            STATUS                     ---                                                                             │ │ └── first-app-559d4f59b-j5ffv                 0/1            0                   ContainerCreating          Waiting for: available 0->1                                                     │ └ Status progress │  │ ┌ Status progress │ │ DEPLOYMENT                                                                                                 REPLICAS            AVAILABLE             UP-TO-DATE                            │ │ werf-first-app                                                                                              1/1                 0->1                  1                                     │ │ │   POD                                      READY          RESTARTS            STATUS                                                                                                     │ │ └── first-app-559d4f59b-j5ffv                 1/1            0                   ContainerCreating ->        │ │                                                                                 Running                     │ └ Status progress └ Waiting for release resources to become ready (9.40 seconds)  NAME: werf-first-app LAST DEPLOYED: Thu Dec  2 12:52:40 2021 NAMESPACE: werf-first-app STATUS: deployed REVISION: 1 TEST SUITE: None Running time 60.24 seconds

Давайте убедимся, что все прошло успешно:

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

В ответ получим заветное:

Hello, werfer!

Мы успешно задеплоили приложение в Kubernetes-кластер!

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

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

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

Наш веб-сервер запущен в Deployment’е web-first-app. Посмотрим, сколько реплик запущено:

$ kubectl get pod NAME                            READY   STATUS    RESTARTS   AGE werf-first-app-559d4f59b-j5ffv   1/1     Running   0          146m

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

kubectl edit deployment werf-first-app

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

$ kubectl get pod NAME                            READY   STATUS    RESTARTS   AGE werf-first-app-559d4f59b-j5ffv   1/1     Running   0          172m werf-first-app-559d4f59b-l4wxp   1/1     Running   0          10s werf-first-app-559d4f59b-xrnxn   1/1     Running   0          10s werf-first-app-559d4f59b-z48qm   1/1     Running   0          10s

Сейчас мы произвели масштабирование вручную, напрямую указав количество реплик внутри кластера, минуя при этом Git. Если сейчас снова запустим werf converge:

werf converge --repo <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-first-app

А затем снова посмотрим на количество реплик приложения:

$ kubectl get pod NAME                            READY   STATUS    RESTARTS   AGE werf-first-app-559d4f59b-j5ffv   1/1     Running   0          175m

То увидим, что количество снова соответствует тому, что указано в Git-репозитории — в файле-манифесте, который мы не редактировали. Так получилось потому, что werf снова привела состояние кластера к тому, что описано в текущем Git-коммите. Этот принцип называется гитерминизмом (giterminism, от англ. Git + determinism).

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

Новое содержимое файла:

apiVersion: apps/v1 kind: Deployment metadata:   name: werf-first-app spec:   # Меняем количество реплик на 4   replicas: 4   selector:     matchLabels:       app: werf-first-app   template:     metadata:       labels:         app: werf-first-app     spec:       imagePullSecrets:       - name: registrysecret       containers:       - name: app         image: {{ .Values.werf.image.app }}         command: ["/app/hello.sh"]         ports:         - containerPort: 8000

Закоммитим изменения и пересоберем приложение командой:

werf converge --repo <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-first-app

Если теперь посмотреть количество запущенных реплик, их будет четыре:

$ kubectl get pod NAME                            READY   STATUS    RESTARTS   AGE werf-first-app-559d4f59b-2fth5   1/1     Running   0          7m12s werf-first-app-559d4f59b-7jqnv   1/1     Running   0          7m12s werf-first-app-559d4f59b-j5ffv   1/1     Running   0          3h17m werf-first-app-559d4f59b-n9n64   1/1     Running   0          7m12s

Давайте снова вернём одну реплику. Снова исправляем файл deployment.yaml, коммитим изменения и перезапускаем converge.

$ kubectl get pod NAME                            READY   STATUS    RESTARTS   AGE werf-first-app-559d4f59b-j5ffv   1/1     Running   0          3h24m

Меняем само приложение

Сейчас наше приложение отвечает Hello, werfer!. Давайте изменим эту строку и перезапустим обновленное приложение в кластере. Открываем наш hello.sh и меняем строку ответа на любую другую, например на Hello, Habr users!:

#!/bin/sh  RESPONSE="Hello, Habr users!"  while true; do   printf "HTTP/1.1 200 OK\n\n$RESPONSE\n" | ncat -lp 8000 done

Действуем по старой схеме — коммитим изменения, перезапускаем converge и смотрим результат: 

$ curl "http://werf-first-app.test/ping" Hello, Habr users!

Поздравляю, у нас все получилось и работает!

Выводы

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

Эта статья основана на главе «Первые шаги» нашего онлайн-самоучителя. Представляя её как максимально лаконичный практический tutorial, мы не стали останавливаться на некоторых теоретических вопросах, которые раскрыты в полном руководстве:  шаблоны и манифесты Kubernetes, основные ресурсы K8s для запуска приложений (Deployment, Service, Ingress), режимы работы werf и гитерминизм, использование Helm в werf и т. д. Ответы на них можно найти, проходя «Первые шаги» в полном самоучителе.

Надеемся, что эта статья поможет вам сделать ваши первые шаги с werf и приобрести немного опыта в части деплоя приложений в Kubernetes!

С любыми вопросами и предложениями ждем вас в комментариях к статье, а также в Telegram-каналe werf_ru, где уже более 700 участников и всегда рады помочь. Любым issues (и звёздам) всегда рад и GitHub-репозиторий werf.

P.S.

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


ссылка на оригинал статьи https://habr.com/ru/company/flant/blog/597001/


Комментарии

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

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