Распространенный сценарий: команда разработчиков поддерживает множество Jenkinsfile, каждый из которых соответствует требованиям проекта. Однако эти сценарии непрерывной интеграции (CI) непригодны для повторного использования в другом хранилище. Причина заключается в том, что каждый проект обладает собственным технологическим стеком, версией, зависимостями для инструментов и т. д.
В таком случае многоэтапные сборки в docker— идеальное решение.
Эта технология уже давно известна, однако многие пользователи не осознают мощности ее потенциала.
Идея заключается в объединении нескольких Dockerfiles в один Dockerfile. Каждый файл будет выполнять определенную задачу в процессе разработки.
Рассмотрим на примере. Для демонстрации передовых концепций я добавил несколько сложных структур. Для начала ознакомьтесь с официальной документацией, чтобы узнать о возможностях CI.
Начнем с тестирования sonar:
FROM newtmitch/sonar-scanner AS sonar COPY src src RUN sonar-scanner
Затем устанавливаем зависимости и создаем приложение:
FROM node:11 AS build WORKDIR /usr/src/app COPY . . RUN yarn install yarn run lint yarn run build yarn run generate-docs LABEL stage=build
Затем выполняем модульное тестирование:
FROM build AS unit-tests RUN yarn run unit-tests LABEL stage=unit-tests
Теперь переместите документацию в S3 с помощью метода push:
FROM containerlabs/aws-sdk AS push-docs ARG push-docs=false COPY --from=build docs docs RUN [[ "$push-docs" == true ]] && aws s3 cp -r docs s3://my-docs-bucket/
Наконец переходим к последней стадии, которая будет отражена в результирующем образе. На этой стадии используется базовый образ и присутствуют лишь необходимые артефакты:
FROM node:11-slim EXPOSE 8080 WORKDIR /usr/src/app COPY --from=build /usr/src/app/node_modules node_modules COPY --from=build /usr/src/app/dist dist USER node CMD ["node", "./dist/server/index.js"]
Jenkinsfile значительно упрощается (пример для Jenkins на Kubernetes):
#!/usr/bin/env groovy podTemplate(label: "example", name: "example", containers: [ containerTemplate(name: 'dind', privileged: true, image: 'docker:18.06-dind', command: 'dockerd-entrypoint.sh'), containerTemplate(name: 'docker', image: 'docker:18.06', command: 'cat', ttyEnabled: true) ], envVars: [ envVar(key: 'DOCKER_HOST', value: 'tcp://localhost:2375') ] ){ node('example'){ container('docker'){ stage('checkout'){ checkout scm } stage('Docker build + push'){ sh """#!/bin/bash docker build -t test --build-arg push-docs=true . docker push test """ } stage('deploy'){ ....... } } } }
Этот способ можно использовать практически для каждого проекта!
Основные преимущества:
- Возможность повторного использования из одной системы CI в другую (например, переход с Jenkins на действия GitHub). Идеально подходит для open source проектов.
- Тестирование можно выполнить локально с помощью команды docker build.
- Все, что относится к созданию и тестированию исходного кода, находится в Dockerfile. Следовательно, сценарии CI хранятся отдельно от исходного кода.
- Меньше возможностей для совершения ошибок: каждый шаг осуществляется в непривилегированном контейнере docker. Избежать docker daemon можно с помощью таких инструментов, как Kaniko.
Таким образом, все детали проекта изолированы, а сценарии CI можно использовать повторно в разных хранилищах, что упрощает инфраструктуру и повышает поддерживаемость.
Примечания
Пропуск шагов
К примеру, я использовал стадию переноса сгенерированной документации в области памяти (bucket) на S3. Этот шаг необходим в том случае, если сборка выполняется в системе CI, в которой предоставлены учетные данные для записи в этой области памяти.
Для этого устанавливаем аргумент build с командой ARG. По умолчанию установлено значение false, однако на своем CI-сервере я запустил docker build —build-arg push-docs=true, после которого выполняется команда aws s3 cp.
Экспорт отчета о тестировании и других артефактов
Главное условие при работе в docker build заключается в том, что артефакты хранятся в промежуточных образах docker. Например, результаты тестирования удобно хранить в рабочем пространстве Jenkins для генерирования статистики.
Любой артефакт можно с легкостью извлечь из любой промежуточной стадии с помощью меток.
Я отметил вторую стадию с помощью stage=test. Таким образом, после docker build можно запустить небольшой сценарий для получения файла test-results.xml
docker cp $(docker image ls --filter label=stage=test -q | head -1):/usr/src/app/tests-results.xml .
docker image ls используется для получения ID образа этой стадии, а docker cp для копирования файла.
Лучшее решение — это использование большого количества МЕТОК для фильтрации определенной сборки от других подобных сборок.
И последний совет: используйте BuildKit 😉
Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming