Как запустить контейнер Docker внутри конвейера GitLab CI

от автора

Довольно часто мы используем GitLab CI для докеризации наших приложений. Но как запустить контейнер Docker из GitLab Container Registry? Можно ли использовать Docker Compose? Делимся переводом статьи, в которой автор отвечает на эти вопросы, и рассказывает о функции services keyword в GitLab CI. Она позволяет запустить один или несколько образов Docker и связать их с вашим заданием.

Перед началом

Для этого сценария я создал приложение Node.js, которое предоставляет API.

Таким образом, текущий пайплайн состоит из следующих этапов:

  • build, где устанавливаются все зависимости

  • test, на котором выполняются все модульные тесты

  • package, где приложение докеризируется, и образ отправляется в GitLab Container Registry.

Для справки, вот как выглядит .gitlab-ci.yml на этом этапе:

stages:   - build   - test   - package  build:   stage: build   image: node:14-alpine   script:     - npm ci --only=production   artifacts:     paths:       - node_modules/       - server.js  unit tests:   stage: test   image: node:14-alpine   before_script:     - npm install   script:     - npm test  build docker image:   stage: package   image: docker   services:      - docker:dind   script:      - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin     - docker build -t $CI_REGISTRY_IMAGE .     - docker push $CI_REGISTRY_IMAGE

В этом сценарии используется shared runner инфраструктура GitLab.com, где GitLab раннеры используют Kubernetes executor.

Для некоторых Docker executor может потребоваться указать переменную DOCKER_HOST. Вы узнаете об этом, если получите эту ошибку:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Этот подход НЕ подходит для shell executors.

variables:    DOCKER_HOST: tcp://docker:2375/

или

variables:    DOCKER_HOST: tcp://docker:2376/

Проблема

Следующим логичным шагом было бы проведение приемочных тестов, выполнение нескольких команд cURL в контейнере Docker или, что еще лучше, использование Postman/Newman для тестирования API.

Как же запустить Docker-контейнер в рамках конвейера GitLab CI?

Запуск контейнера с помощью docker run

Локально я собрал и протестировал контейнер с помощью docker build и docker run. То же самое должно быть возможно и в рамках конвейера, верно?

Задание build docker image уже предоставляет очень хороший шаблон для использования Docker в GitLab. Поэтому я войду в GitLab Container Registry и запущу созданный ранее образ.

Чтобы упростить конвейер, я НЕ указывал никаких версий для образов docker и docker:dind и не создавал никаких тегов для созданного образа. В реальном сценарии я бы сделал и то, и другое.

stages:   - build   - test   - package   - acceptance  ...  curl api testing:   stage: acceptance   image: docker   services:      - docker:dind   script:      - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin       - docker run -d -p 3000:3000 $CI_REGISTRY_IMAGE     - apk add curl     - curl http://localhost:3000/status | grep "UP"

К сожалению, следующая установка завершится с ошибкой:

curl: (7) Failed to connect to localhost port 3000 after 5 ms: Connection refused

Но почему? Локально все работало просто отлично. Чтобы объяснить причину, позвольте мне познакомить вас с сервисами GitLab CI.

Что такое GitLab CI services?

Как вы видели, задание, создающее конвейер, использовало ключевое слово services для указания образа docker:dind.

В двух словах, сервисы GitLab предоставляют вам возможность запускать дополнительные контейнеры Docker и связывать их с вашим образом (тем, который вы указали в ключевом слове image).

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

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

  • Образ или образы, указанные в services, могут быть доступны только через сетевое соединение.

  • Образ или образы, указанные в services, должны открывать службу на заданном порту. В противном случае они бесполезны в данном контексте.

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

  • Использование сервисов НЕ похоже на определение файла docker-compose.yaml

Зачем dockerу нужен docker:dind в качестве сервиса?

Если у вас локально установлен Docker, вы можете открыть окно терминала и ввести команду, например:

docker --version

На выходе получится что-то вроде

Однако если я попытаюсь выполнить такие команды, как docker pull, docker build или docker run, то получу ошибку, подобную этой:

Error response from daemon: dial unix docker.raw.sock: connect: connection refusedCannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

На этом этапе позвольте мне процитировать документацию Docker:

Docker использует клиент-серверную архитектуру. Клиент Docker общается с демоном Docker, который выполняет всю работу по созданию, запуску и распространению ваших контейнеров Docker. Клиент Docker и демон могут работать в одной системе, или вы можете подключить клиента Docker к удаленному демону Docker. Клиент Docker и демон взаимодействуют с помощью REST API, через сокеты UNIX или сетевой интерфейс.

На локальном уровне вы не задумываетесь об этом, поскольку и клиент, и демон установлены на одной машине. Но в GitLab все обстоит несколько иначе.

Итак, в GitLab образ docker — это просто клиент. Образ docker:dind — это демон Docker, который запускается как служба, предлагая доступные по сети сервисы. Клиент может взаимодействовать с демоном через сетевой интерфейс.

Доступ к контейнеру Docker, запущенному как служба

В предыдущем примере приложение Node.js, открывающее API на порту 3000, было запущено демоном Docker в контейнере docker:dind.

Поэтому использование localhost не сработает, так как API запущен в другом контейнере. К счастью, GitLab автоматически генерирует имя хоста для соответствующей службы. В данном случае это имя хоста — docker.

Поэтому мы можем получить доступ к приложению с помощью cURL, адаптировав команду для использования docker вместо localhost.

curl http://docker:3000/status | grep "UP"

Однако такой подход громоздок, поскольку теперь мы запускаем Docker в Docker в Docker. Время выполнения только этого задания составляет 61 секунду. Должен быть лучший способ.

Запуск частного контейнера Docker с помощью GitLab services

Ключевое слово services позволяет нам указать как публичный образ Docker, доступный на Dockerhub, так и использовать наш частный GitLab Container Registry.

curl api testing:
stage: acceptance
image: curlimages/curl
services:
— name: $CI_REGISTRY_IMAGE
alias: banking-api
script:
— curl http://banking-api:3000/status | grep «UP»

Я расширил конфигурацию, указав alias. Я не был уверен, какое имя хоста сгенерирует GitLab, и предпочел указать его самостоятельно.

Запуск приемочных тестов Postman/Newman на основе образа Docker

Следуя принципам, использованным в cURL, я могу добавить новое задание, которое использует публичный образ Newman Docker для запуска существующей коллекции Postman.

postman api testing:   stage: acceptance   image:      name: postman/newman     entrypoint: [""]   services:      - name: $CI_REGISTRY_IMAGE       alias: banking-api   script:     - newman run test/collection.json --env-var "baseUrl=banking-api:3000"

Обратите внимание, что я ввел переменную окружения Postman baseUrl во время выполнения.

Устранение неполадок

Не удается подключиться к демону Docker по адресу unix:///var/run/docker.sock. Запущен ли демон Docker?

Для некоторых исполнителей Docker может потребоваться указать переменную DOCKER_HOST на уровне конвейера или задания.

variables:    DOCKER_HOST: tcp://docker:2375/

или 

variables:    DOCKER_HOST: tcp://docker:2376/

Заключение

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

Полезные ссылки:


Окунись в мир практических задач по CI/CD, пополни портфолио реальными кейсами
и вырасти до нового уровня в своей профессии на Навыкуме по CI/CD в Слёрме.

Навыкум позволит тебе:

  • Наработать около 20 часов практики.

  • Добавить в своё резюме и базовые, и реальные кейсы по CI/CD.

  • «Подсмотреть» лучшие практики в свою копилку знаний.

  • Попасть в комьюнити сильных специалистов с чатом поддержки. Доступ к нему навсегда.

? Оставить заявку


ссылка на оригинал статьи https://habr.com/ru/articles/778652/


Комментарии

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

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