Какие задачи пользователю нужно выполнять в рамках CI-пайплайна или при локальной разработке? Среди них может быть что угодно, но самое очевидное — это, наверное, запуск линтеров, всевозможных unit-тестов и получение покрытия и других отчетов по результатам выполнения команды. Также при разработке и отладке может быть полезен интерактивный режим, который позволит быстрее разобраться в проблеме или проверить гипотезу.

Мы рассмотрим «классическое» решение этой задачи штатными средствами, а затем — простой пример, как Open Source-утилита werf помогает сократить трудозатраты на выполнение этих действий. Такой подход позволяет перенести нагрузку со сборочной или локальной машины в кластер Kubernetes, что дает возможность упростить масштабирование и обслуживание инфраструктуры, а также избавиться от зависимости от Docker.
Традиционный подход с использованием kubectl run
Попробуем протестировать приложение в контейнере Docker с ручным запуском. Для этого создадим простое приложение на Go, выполняющее одну задачу — вычисление площади прямоугольника:
package square import ( "fmt" "strconv" ) // Функция, вычисляющая площадь прямоугольника. func getArea(x, y int) (res int) { return x * y } func main() { fmt.Println("Площадь прямоугольника: " + strconv.Itoa(getArea(10, 10))) }
Убедимся, что все работает. Скомпилируем программу и запустим:
% go build main.go % ./main Площадь прямоугольника: 100
Теперь добавим простой тест, проверяющий, что функция подсчета площади работает верно. Создадим файл main_test.go:
package square import "testing" func testGetArea(t *testing.T) { got := getArea(3, 2) want := 6 if got != want { t.Errorf("Ожидалось %q, получено %q", got, want) } }
Запустим тест, чтобы убедиться, что все работает корректно:
% go test PASS ok square0.448s
Теперь соберем из нашего тестового приложения Docker-контейнер. Для этого добавим Dockerfile в корень проекта:
FROM golang:1.18-alpine WORKDIR /app ADD . /app/ RUN go build -o main . RUN chmod +x ./main CMD ./main
Соберем контейнер и убедимся, что все работает:
% docker build . % docker run 70df7f451a8b Площадь прямоугольника: 100
Исходные коды приложения можно найти в репозитории.
За’push’им собранный контейнер в container registry:
docker tag 70df7f451a8b <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-kuberun-app docker push <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-kuberun-app
Создадим в Kubernetes-кластере отдельное пространство имен и Secret для доступа к container registry:
% kubectl create namespace werf-kuberun-app namespace/werf-kuberun-app created % kubectl config set-context minikube --namespace=werf-kuberun-app Context "minikube" modified. 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 run gotest --image=<ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-kuberun-app:latest --command -- go test pod/gotest created % kubectl get pods NAME READY STATUS RESTARTS AGE gotest 0/1 Completed 0 5s % kubectl logs gotest testing: warning: no tests to run PASS ok square0.002s
Контейнер запустился в Pod’е, выполнил тесты и до сих пор висит в состоянии Completed. Удалим его:
% kubectl delete pod gotest pod "gotest" deleted
Итак, мы собрали контейнер с приложением, создали пространство имен и Secret с доступами к container registry в нём, затем, используя все данные с предыдущих шагов, запустили Pod с нашим образом и командой запуска тестов. Последним шагом удалили оставшийся Pod.
Теперь давайте сделаем то же самое с помощью werf.
Запуск разовой задачи с помощью werf kube-run
Для этого воспользуемся новой командой werf – werf kube-run. Она отчасти аналогична уже знакомой пользователям утилиты команде werf run, но в отличие от последней создаёт Pod в K8s-кластере, а не запускает локальный контейнер.
Чтобы werf смогла собрать контейнер и задеплоить его в кластер, нужно создать файл werf.yaml в корне проекта:
project: werf-kuberun-app configVersion: 1 --- image: kuberun dockerfile: Dockerfile
Здесь мы указываем название проекта, наименование создаваемого образа и Dockerfile, из которого будут браться инструкции для сборки.
Для работы werf необходимо, чтобы все файлы проекта находились в Git-репозитории. Инициализируем новый репозиторий в корне проекта:
git init git add . git commit -m WIP
Запустим выполнение тестов в кластере командой:
werf kube-run --repo <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-kuberun-app -- go test
Здесь мы указываем репозиторий, в который werf за’push’ит собранный образ, и команду, которую необходимо выполнить: go test. Подробнее о команде и ее настройках (доступных флагах) можно прочитать в официальной документации.
После выполнения увидим примерно следующее:
┌ Getting client id for the http synchronization server │ Using clientID "e100e249-066a-48eb-80e7-52b0e3e6a491" for http synchronization server at address https://synchronization.werf.io/e100e249-066a-48eb-80e7-52b0e3e6a491 └ Getting client id for the http synchronization server (1.70 seconds) ┌ ⛵ image kuberun │ Use cache image for kuberun/dockerfile │ name: <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/werf-kuberun-app:44922a164fd7eceb98659eb4e008f03730a9abc29cf750e16cdc0c99-1653648006124 │ id: 166a97e05613 │ created: 2022-05-27 13:40:04 +0300 MSK │ size: 115.8 MiB └ ⛵ image kuberun (1.42 seconds) Running pod "werf-run-1675291575958117025" in namespace "werf-kuberun-app" ... pod/werf-run-1675291575958117025 created Waiting for pod "werf-run-1675291575958117025" in namespace "werf-kuberun-app" to be ready ... Execing into pod "werf-run-1675291575958117025" in namespace "werf-kuberun-app" ... PASS ok square0.070s Stopping container "werf-run-1675291575958117025" in pod "werf-run-1675291575958117025" in namespace "werf-kuberun-app" ... Cleaning up pod "werf-run-1675291575958117025" ...
Стоит отметить, что в целях демонстрации мы используем Docker-сервер для сборки тестового образа, чтобы не усложнять статью особенностями сборки с Buildah. Однако такой режим работы тоже доступен в werf и позволяет собирать образы без Docker-сервера (подробнее про настройку окружения с Buildah можно прочитать в документации).
Все запустилось и выполнилось. Проверим, что Pod, в котором запускались тесты, удален:
% kubectl get pods No resources found in werf-kuberun-app namespace.
Все действия werf выполняет в рамках одной команды, автоматизируя рутину и позволяя пользователю сосредоточиться на выполняемой задаче.
Как это работает
По умолчанию команда действует по следующему алгоритму:
-
Берёт параметры доступа для указанного
$WERF_REPOиз~/.docker/config.json. -
Создает в кластере Image Pull Secret с этими параметрами (для доступа к приватным container registry).
-
Создаёт в кластере Pod с указанной командой, монтирует к нему созданный Pull Secret.
После завершения работы Pod’а удаляет созданные Image Pull Secret и Pod.
Различные сценарии использования
Мы рассмотрели простой запуск команды во временном Pod’е. Возможны и другие, более сложные, сценарии использования werf kube-run. Взглянем на несколько таких примеров — уже без практики, а с целью лучше раскрыть ее возможности.
Запустить выполнение тестов в ранее созданном Pod’е (например, frontend_image) можно командой:
werf kube-run frontend_image --repo ghcr.io/group/project -- npm test
Запустить тесты в Pod’e, но перед выполнением команды скопировать файл с секретными переменными окружения в контейнер:
werf kube-run frontend_image --repo ghcr.io/group/project --copy-to ".env:/app/.env" -- npm run e2e-tests
Запустить тесты в Pod’е и получить отчет о покрытии:
werf kube-run frontend_image --repo ghcr.io/group/project --copy-from "/app/report:." -- go test -coverprofile report ./...
Выполнить команду по умолчанию для созданного образа в Kubernetes Pod с установленными параметрами CPU:
werf kube-run frontend_image --repo ghcr.io/group/project --overrides='{"spec":{"containers":[{"name": "%container_name%", "resources":{"requests":{"cpu":"100m"}}}]}}'
Выводы
Мы рассмотрели решение проблемы запуска разовых задач в кластере Kubernetes с помощью новой команды утилиты werf. Она позволяет сэкономить немного времени при выполнении таких задач, а также перенести нагрузку, с ними связанную, в кластер.
С вопросами и предложениями ждем вас в комментариях к статье, а также в Telegram-каналe werf_ru, где 700+ участников и всегда готовы помочь. Также мы всегда рады правкам и улучшениям для проекта в виде Pull Request’ов для GitHub-репозитория werf.
P.S.
Читайте также в нашем блоге:
ссылка на оригинал статьи https://habr.com/ru/company/flant/blog/671960/
Добавить комментарий