Привет!

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

  • При построении пайплайна CI/CD в GitLab или Jenkins для создания образов Docker и их публикации в реджестри (реестре образов)

  • При использовании Jenkins Dynamic Agents (динамических Docker-контейнеров) 

  • При необходимости построения изолированной среды от хоста, где будет выполнятся стендовая работа, а после проведения можно будет легко удалить её.   

Существует три способа запуска docker в docker:
  1. Запуск docker путём монтирования docker.sock (метод DooD)

  2. Использовать дочерний контейнер внутри контейнера  (метод dind)

  3. Использование среды выполнения Nestybox sysbox Docker

Для эксперимента создадим виртуальную машину в облаке Cloud4Y из готового шаблона ubuntu-server-2204 по мануалу. Далее установим docker (https://docs.docker.com/engine/install/ubuntu/). Я воспользуюсь скриптом:

#curl -sSL https://get.docker.com/ | sh

Проверим версию 

# docker –v
Docker version 20.10.22, build 3a2c30b

Метод 1: Docker в Docker с использованием [/var/run/docker.sock]

Что такое /var/run/docker.sock

/var/run/docker.sock является сокетом Unix по умолчанию (IPC-сокет). Сокеты предназначены для обмена данными между процессами на одном хосте. Демон Docker по умолчанию прослушивает docker.sock. Если вы находитесь на том же хосте, на котором запущен демон Docker, вы можете использовать /var/run/docker.sock для управления контейнерами.

Например, если мы запустим следующую команду: 

# curl --unix-socket /var/run/docker.sock http://localhost/version

она вернёт версию docker engine, такую же, как мы проверяли ранее:

{"Platform":{"Name":"Docker Engine -
Community"},"Components":[{"Name":"Engine","Version":"20.10.22","Details":{"ApiVersion":"1.41","A
rch":"amd64","BuildTime":"2022-12-
15T22:25:49.000000000+00:00","Experimental":"false","GitCommit":"42c8b31","GoVersion":"go1.18.9
","KernelVersion":"5.15.0-27-
generic","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"containerd","Version":"1.6.13","Details":{"Git
Commit":"78f51771157abb6c9ed224c22013cdf09962315d"}},{"Name":"runc","Version":"1.1.4","Detail
s":{"GitCommit":"v1.1.4-0-g5fd4c4d"}},{"Name":"dockerinit","
Version":"0.19.0","Details":{"GitCommit":"de40ad0"}}],"Version":"20.10.22","ApiVersion":"1.41",
"MinAPIVersion":"1.12","GitCommit":"42c8b31","GoVersion":"go1.18.9","Os":"linux","Arch":"amd64","
KernelVersion":"5.15.0-27-generic","BuildTime":"2022-12-15T22:25:49.000000000+00:00"}

Теперь давайте запустим Docker in Docker с помощью /var/run/docker.sock. Для этого нам нужно запустить docker с сокетом Unix по умолчанию и передать docker.sock в качестве тома:

# docker run -v /var/run/docker.sock:/var/run/docker.sock -ti docker

Этот метод иногда называют DooD (Docker outside of Docker), потому что он использует Docker вне Docker.  Поскольку он монтирует только сокет хост-среды, используемый образ Docker не используется dind, и не требует привилегированный -privileged режим, как это описано далее в методе 2. 

Тут нужно понимать риски безопасности, что если ваш контейнер получает доступ к docker.sock, это означает, что у него будут повышенные привилегии по сравнению с docker. 

Теперь из контейнера мы имеем возможность выполнять команды docker для создания и отправки изображений в реджестри. Фактически, в данном случае операции docker выполняются на хосте виртуальной машины, на которой запущен ваш базовый контейнер docker, а не внутри контейнера. То есть, если вы выполняете команды docker из контейнера, вы даёте указание docker подключиться к docker-engine хоста виртуальной машины через docker.sock. 

Воспользуемся примером выше, где мы используем официальный образ docker из docker hub и запускаем контейнер Docker в интерактивном режиме. 

# docker run --name dood -v /var/run/docker.sock:/var/run/docker.sock -ti docker

Внутри запущенного контейнера скачаем образ docker —  alpine, командой # docker pull  alpine

И посмотрим доступные локальные образы в контейнере 

# docker images

REPOSITORY   TAG       IMAGE ID       CREATED       SIZE

docker       latest    81a4721a045e   8 days ago    152MB

alpine       latest    49176f190c7e   3 weeks ago   7.05MB

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

Метод 2: Docker в Docker с использованием dind

Этот метод фактически создаёт дочерний контейнер внутри контейнера. Используйте этот метод, только если вы действительно хотите, чтобы контейнеры и изображения находились внутри контейнера.  Для этого нужно использовать официальный образы docker с тэгом dind. Изображение dind загружается с необходимыми утилитами для запуска Docker внутри контейнера Docker и требуют для запуска привилегированный режим. Запустим контейнер:

# docker run --privileged -d --name dind docker:dind

И войдем интерактивно: 

# docker exec -it dind /bin/sh

повторим, что делали ранее, но скачаем теперь другой образа docker —  busybox

# docker pull busybox

И посмотрим доступные локальные образы  в контейнере 

# docker images

REPOSITORY   TAG       IMAGE ID       CREATED       SIZE

busybox      latest    334e4a014c81   7 days ago    4.86MB

Теперь выйдем из контейнера и посмотрим на хостовой машине

# docker images

REPOSITORY   TAG       IMAGE ID       CREATED       SIZE

docker       dind      d74418bd2227   8 days ago    322MB

docker       latest    81a4721a045e   8 days ago    152MB

alpine       latest    49176f190c7e   3 weeks ago   7.05MB

Поскольку Docker на хост-машине и Docker в контейнере docker: dind являются отдельными, образы в контейнере (busybox) не видны с хост-машины, и наоборот. 

 С этим методом связано много проблем:

  • Он плохо работает с модулями безопасности Linux (LSM)

  • Есть проблемы с драйвером хранилища и возможным несоответствии между файловой системой, используемой для контейнеров созданных внутри родительского контейнера, с файловой системой хоста. Могут возникать комбинации, когда это не будет работать. 

  • С безопасностью — из-за необходимости использования привилегированного режима. 

  • Отсутствием поддержки docker-compose в установке по умолчанию и необходимости установки https://docs.docker.com/compose/install/ 

Метод 3: Docker в Docker с использованием среды выполнения Sysbox

Методы 1 и 2 имеют недостатки с точки зрения безопасности из-за запуска базовых контейнеров в привилегированном режиме. Nestybox пытается решить эту проблему, используя среду выполнения Sysbox Docker. Sysbox — это бесплатная среда выполнения контейнеров с открытым исходным кодом, которая расширяет возможности контейнеров двумя ключевыми способами:

  • Улучшает изоляцию контейнера:

    • Пространство имён пользователей Linux во всех контейнерах (т. е. пользователь root в контейнере не имеет привилегий на хосте).

    • Виртуализирует части procfs и sysfs внутри контейнера.

    • Скрывает информацию о хосте внутри контейнера и другие.

  • Позволяет контейнерам работать подобно виртуальным машинам:

    • С помощью Sysbox контейнеры могут запускать программное обеспечение системного уровня, такое как systemd, Docker, Kubernetes, K3s, buildx, устаревшие приложения и т. д. беспрепятственно и безопасно.

    • Это программное обеспечение может работать внутри контейнеров Sysbox без модификации и без использования специальных версий программного обеспечения.

    • Никаких привилегированных контейнеров, специальных монтирований томов и т. д.

Sysbox достигает этого, используя контейнер максимально похожий на виртуальную среду, с помощью передовых методов виртуализации ОС. Для этого метода установим Sysbox по документации.

# wget https://downloads.nestybox.com/sysbox/releases/v0.5.0/sysbox-ce_0.5.0-0.linux_amd64.deb
# sha256sum sysbox-ce_0.5.0-0.linux_amd64.deb
(Должен быть eeacd9ae0e08ee5e5637e3b93e4f0cf78f20f9590ef2e7ab08347700682422f0  sysbox-ce_0.5.0-0.linux_amd64.deb)
# docker rm $(docker ps -a -q) -f
# sudo apt-get install -y jq
# sudo apt-get install -y ./sysbox-ce_0.5.0-0.linux_amd64.deb
# sudo systemctl status sysbox -n20
● sysbox.service - Sysbox container runtime
     Loaded: loaded (/lib/systemd/system/sysbox.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-12-14 13:39:54 UTC; 14s ago
       Docs: https://github.com/nestybox/sysbox
   Main PID: 14708 (sh)
      Tasks: 2 (limit: 4579)
     Memory: 408.0K
        CPU: 61ms
     CGroup: /system.slice/sysbox.service
             ├─14708 /bin/sh -c "/usr/bin/sysbox-runc --version && /usr/bin/sysbox-mgr --version && /usr/bin/sysbox-fs --version && /bin/sleep infinity"
             └─14726 /bin/sleep infinity

Теперь мы можем запустить наш контейнер с использованием среда выполнения sysbox при помощи # docker run --runtime=sysbox-runc --name sysbox -d docker:dind

И войти интерактивно: 

# docker exec -it sysbox /bin/sh

Рекомендации по использованию

  • Используйте Docker в Docker только в том случае, если это действительно необходимо вам. Проводите достаточное тестирование, прежде чем переносить любые продуктовые решения на метод Docker-in-Docker.

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

  • Использовании Docker-in-Docker не влияет на производительность контейнера, но определяется производительностью хоста. 

Пример использования Docker-in-Docker для gitlab-runner 

В качестве примера рассмотрим работу gitlab-runner с использованием всех методов. Для этого на нашей виртуальной машине установим gitlab-runner с executor-м docker: 

# docker run -d --name gitlab-runner --restart always \
	-v /srv/gitlab-runner/config:/etc/gitlab-runner \
	-v /var/run/docker.sock:/var/run/docker.sock \
	gitlab/gitlab-runner:latest 

зарегистрируем три раннера с разными тэгами: dood-tag,  dind-tag, sysbox-tag для каждого метода и образом по умолчанию docker:stable. 

# docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register

В конфигурацию соответствующего раннера в /srv/gitlab-runner/config/config.toml внесём правки:

  • В раннер dood-tag с методом 1, ставим настройки  

    • volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"] 

  • В раннер dind-tag с методом 2, ставим настройки  

    • privileged = true

  • В раннер sysbox-tag с методом 3, ставим настройки 

    • runtime = "sysbox-runc"

В итоге CI/CD Settings нашего проекта мы увидим 

Напишем конфигурацию для  GitLab .gitlab-ci.yml 

stages:
  - DooD
  - dind
  - Sysbox   

DooD_Job:
  stage: DooD

  script:
    - docker images

  tags:
    - dood-tag

dind_Job:
  stage: dind
 
  variables:
      DOCKER_TLS_CERTDIR: ""

  services:
    - docker:dind

  script:
    - docker images

  tags:
    - dind-tag


Sysbox_Job:
  stage: Sysbox

  variables:
    DOCKER_TLS_CERTDIR: ""

  services:
    - docker:dind
    
  script:
    - docker images

  tags:
    - sysbox-tag

с тремя стейджами и заданием для каждого метода, запускаемого на своем раннере, и посмотрим результат docker images

Результаты по каждому раннеру: 

  1. DooD_Runner

  1. Dind_runner

  1. Sysbox_runner

Мы видим сходство результата по второму и третьему методу, при этом мы не использовали привилегированный режим в третьем.  

Подробности по раннерам смотрите в документации GitLab: 

Написано на базе этой статьи. Спасибо за внимание!


Что ещё интересного есть в блоге Cloud4Y

→ Информационная безопасность и глупость: необычные примеры

→ NAS за шапку сухарей

→ Как распечатать цветной механический телевизор на 3D-принтере

→ Создание e-ink дисплея с прогнозом погоды

→ Аналоговый компьютер Telefunken RA 770

Подписывайтесь на наш Telegram-канал, чтобы не пропустить очередную статью. Пишем только по делу. А ещё напоминаем про второй сезон нашего сериала ITить-колотить. Его можно посмотреть на YouTube и ВКонтакте.

Комментарии (9)


  1. nspickiy
    17.01.2023 17:59
    +4

    Лучше же таких ситуаций избегать, использовать например kaniko https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#docker-alternatives


  1. Ares_ekb
    17.01.2023 18:25

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

    .deploy:
      stage: deploy
      image: docker:20.10.16-alpine3.16
      before_script:
        - eval $(ssh-agent -s)
        - echo "$DEPLOYMENT_PRIVATE_KEY" | tr -d '\r' | ssh-add -
        - mkdir -p ~/.ssh
        - chmod 700 ~/.ssh
        - ssh-keyscan $DEPLOYMENT_HOST >> ~/.ssh/known_hosts
        - chmod 644 ~/.ssh/known_hosts
      script:
        - DOCKER_HOST="ssh://$DEPLOYMENT_USER@$DEPLOYMENT_HOST" docker-compose -f docker-compose.yml --env-file .env.$CI_ENVIRONMENT_NAME up --build -d
    
    deploy-staging:
      extends: .deploy
      only:
        - master
      environment: staging


  1. NightShad0w
    17.01.2023 22:29
    +2

    Качество и вычитка статьи удручает.

    "изображений в реджестри.", хотя отсутствует пометка про перевод.
    И в этом же абзаце аж 7 раз слово docker без явной необходимости.


  1. dimkus
    18.01.2023 11:47

    В данный момент пытаюсь разобраться как правильно настроить и использовать gitlab-runnerы для сборки docker образов, публикации их в registry, и последующий деплой на DEV, TEST, PROD. Интересует у кого есть реальный опыт как делать не надо и как по вашему мнению правильнее ? хотелось бы получить изоляцию, чтоб могли одновременно отработаться несколько pipeline и друг другу не мешали, и после завершения pipeline, чтоб не осталось мусора (в том числе и docker контейнеры, которые могли стартануть для интеграционых тестов)


    1. Ares_ekb
      18.01.2023 20:13

      А есть смысл публиковать образы в docker registry? Почему бы сразу не деплоить на стенд?

      Не очень понимаю как pipeline могут мешать друг другу... У нас, например, есть следующий сценарий. В каждой ветке собирается приложение, деплоится на тестовый стенд и на нём запускаются e2e-тесты. При этом если две ветки будут собираться одновременно, то, да, возможна ситуация, что они одновременно будут деплоить приложение и запускать тесты. Чтобы избежать этого мы используем параметр resource_group. Благодаря этому сначала запускаются тесты в одном pipeline, затем в другом. Но это нужно если разные pipeline используют один и тот же сервер. А если они просто собирают приложение и деплоят его на свой стенд, то и так всё работает.

      Насчет удаления мусора... У вас runner запускаются в docker? Или без docker в самой ОС (в шеле)? Если в docker, то весь мусор в нём и остаётся и можно время от времени просто чистить эти контейнеры и образы. Если в шеле, то наверное это не очень и лучше запускать в докере. Мы у себя время от времени запускаем docker system prune, этого хватает.


      1. dimkus
        19.01.2023 03:35

        А есть смысл публиковать образы в docker registry? Почему бы сразу не деплоить на стенд?

        Конечно! Суть тестов, что вы тестируете определённую версию приложения и если всё ок, отправляете в продакшен. А если не использовать docker container registry, то получается для теста собирается образ и при деплои на ПРОД также собирается образ. И тут может произойти не хорошая ситуация, что ваши образы могут отличаться. Вышли какие-то обновления зависимостей и т.д. и т.п. и может оказаться, что в тестах всё хорошо, а в продакшене не работает. В этом то и вся суть тестирования, что протестировали, то в продакшен и отправили. т.е. собрали образ, повесили на него тег версии и запушили в registry. Из реестра взяли, запустили в тестовом окружении, всё гут, тот-же докер образ деплоится в продакшен.

        Не очень понимаю как pipeline могут мешать друг другу...

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

        • serviceA - одно приложение

        • serviceB - второе приложение

        • serviceApp - некий репозиторий с предписанием деплоймента

        мы коммитнули изменения в serviceA и serviceB и ждём их сборки, чтоб через serviceApp потом их задеплоить на DEV и TEST окружение. и на обеих репозиториях serviceA и serviceB запустились pipelines. В рамках unit тестов запускаются тестовые контейнеры например с postgresql, kafka, ещё что-то. и если эти pipeline будут работать в одном и том-же окружении, то потенциально возникнет конфликт интересов, что обеим pipeline надо будет запустить одинаковые образы и возможно может остаться мусор в виде непогашенного docker контейнера, запущеного для тестов, например с kafka.
        Хотелось бы получить для каждой pipeline изолированное окружение, чтоб они не могли друг другу помешать. чтоб допустим могло спокойно одновременно отработаться 5 различных pipeline. по сути логика docker in docker.

        У вас runner запускаются в docker? Или без docker в самой ОС (в шеле)?

        Ситуация следующая. У нас в компании работаем в Azure, и там с CI/CD всё чётко, проблем особо нету, используются Microsoft агенты. Каждый запуск pipeline в изолированном окружении. В общем всё как требуется.

        GitLab я установил для себя, на своём сервере, для своих личных проектов. GitLab Runner пока не настраивал, пытаюсь разобраться как правильнее сделать. CI/CD в гитлабе отличается от того, как это решено в Azure. плодить виртуалки для обеспечения изоляции среды для pipeline мне кажется немного странным. Использование докера более правильный подход, но вот каким образом реализовать, т.к. если замапить docker socket в контейнер, это в принципе не безопасно и приведёт к конфликтами при параллельном запуске pipeline.


        1. Ares_ekb
          19.01.2023 07:56
          +1

          А если не использовать docker container registry, то получается для теста собирается образ и при деплои на ПРОД также собирается образ. И тут может произойти не хорошая ситуация, что ваши образы могут отличаться.

          Я сначала пытался настроить сборку полностью через Docker. Сделал многошаговый Dockerfile, в котором отдельными шагами прописал скачивание зависимостей, сборку бэкенда, фронтенда, запуск тестов и остальное. И у меня была идея, что можно в GitLab сделать несколько шагов (stage), на каждом из которых вызывать соответствующие шаги из Dockerfile. Тогда весь этот процесс был бы описан в одном месте (в Dockerfile), использовался бы механизм кэширования Docker.

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

          1) Либо мы описываем шаги сборки в GitLab. А Docker используем только в конце для деплоя уже собранного приложения на стенд. Иными словами мы основную логику описываем в .gitlab-ci.yml, а Dockerfile просто копирует уже собранное приложение в образ, который потом деплоится

          2) Либо мы описываем сборку в Dockerfile, но при этом в .gitlab-ci.yml у нас грубо говоря 1-2 шага, которые просто запускают Dockerfile целиком

          Попытка сделать какой-то гибридный вариант - это мучение, в том числе описанное в этой статье. Всё это docker in docker, docker registry и т.п.

          У нас эта проблема решается просто:

          1) Процесс сборки прописан в .gitlab-ci.yml

          2) В .gitlab-ci.yml прописаны правила кэширования, зависимости между шагами. Всякие resource_group, чтобы не шёл одновременный деплой на один стенд.

          3) А в Dockerfile тупо копируются уже собранные файлы в образ и всё. Соответственно на разные стенды будет деплоиться один и тот же код, потому что он собран GitLab раннером, сохранен в качестве артефакта, для которого можно задать какое угодно время хранения.

          В общем перефразирую эту мысль иначе. Не нужно пытаться скрестить между собой GitLab и Docker. Только что-то одно из этого можно использовать на полную, а второй инструмент будет вспомогательным. У нас основной инструмент это GitLab, а Docker используется на столько минимально на сколько это возможно. Поэтому мы не используем Docker Registry, да и в нём просто нет необходимости, нам достаточно хранилища артефактов GitLab.

          Вышли какие-то обновления зависимостей и т.д. и т.п. и может оказаться, что в тестах всё хорошо, а в продакшене не работает.

          У нас такой проблемы нет, потому что приложение собрано GitLab ранером, сохранено как артефакт, его можно скачать, посмотреть, задать время хранения и т.п., а Docker его только копирует на стенд. Поэтому на разные стенды копируется одна и та же версия приложения.

          Плюс конечно же у нас жестко прописаны все версии зависимостей. И то, что при пересборке они изменятся - это какая-то невероятная ситуация, баг. Это прямо очень строгое правило, что ни при каких обстоятельствах нельзя указывать плавающие версии зависимостей.

          потенциально возникнет конфликт интересов, что обеим pipeline надо будет запустить одинаковые образы и возможно может остаться мусор в виде непогашенного docker контейнера, запущеного для тестов, например с kafka

          В GitLab конфликты решаются с помощью resource_group.

          Удаление контейнеров можно или прописать в .gitlab-ci.yml (и если используется resource_group, то по идее не должно быть конфликтов, что один pipeline удалил контейнер, созданный другим pipeline). Или можно просто забить на это и время от времени запускать на стенде docker system prune.

          плодить виртуалки для обеспечения изоляции среды для pipeline мне кажется немного странным. Использование докера более правильный подход

          И не нужны виртуалки. Каждый раннер и так запускается в своём Docker контейнере и pipeline никак не пересекаются. Пока вы прописываете процесс сборки, запуска тестов, деплоя и остальное в .gitlab-ci.yml, то всё работает нормально. Всё это и так уже запускается в Docker, в изолированном окружении. А когда вы пытаетесь существенную часть логики перенести в Dockerfile, то начинаются все эти танцы с бубнами вокруг Docker in Docker, для которых GitLab изначально не предназначен, это костыли. Возможно я ошибаюсь, но я потратил на это определенное время и пришёл к тому, что ну его нафиг этот Docker in Docker. Проблем полно, профита ноль.


          1. dimkus
            19.01.2023 15:31
            +1

            За наводку по поводу resource_group благодарю, посмотрю.

            В остальном я возможно не достаточно смог объяснить в чём трудность. Тестирование через docker так себе затея, для этого описываются шаги в CI/CD. Живой пример. Допустим у нас есть приложение на Java (Spring Boot + Maven). Для того , чтобы нам собрать приложение мы должны запустить команду

            mvn clean compile package
            

            Maven тянет зависимости, компилирует код и запускает unit тесты, после собирает готовый jar, а после уже собираем образ и ложем его в registry. В юнит тестах используется test containers для запуска postgresql, и стартует оно в docker контейнере, отпрабатывает тестовые сценарии и всё гасит за собой.

            Если Gitlab Runner запущен в своём контейнере, что в принципе логично, то получаем ситуацию docker in docker. Если мы в докер пробрасываем socket, чтоб всё сработало как надо, то получаем конфликтную ситуацию, когда одновременно отрабатываются две pipeline с тестовыми сценариями. Из этого и возникает вопрос, а как правильно использовать Gitlab Runnerы ?

            Далее про docker container registry. Из личного опыта, это как минимум удобно и по моему мнению правильно. Вы можете разворачивать всю инфраструктуру через IaC и можете управлять версиями приложения в любом отрезке времени, в любом окружении.

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

            Живой пример из опыта. Java приложение, которое через JDBC общается с Microsoft SQLServer (не самой свежей версии, изза определённых причин заказчик не мог себе позволить upgrade). Java приложение было написано на java8. Всё отлично работает. Спустя какое-то время, ничего не меняли, собираем docker образ, а приложение уже не работает. Почему? А потому-что приехало обновление java 8, где было по-умолчанию отключен TLS 1.1, и всё, приложение с базой уже общаться не смогло. Образ из docker container registry, продолжал работать, т.к. его состояние осталось не изменным.


            1. Ares_ekb
              19.01.2023 16:45

              Maven тянет зависимости, компилирует код и запускает unit тесты, после собирает готовый jar, а после уже собираем образ и ложем его в registry. В юнит тестах используется test containers для запуска postgresql, и стартует оно в docker контейнере, отпрабатывает тестовые сценарии и всё гасит за собой.

              Если Gitlab Runner запущен в своём контейнере, что в принципе логично, то получаем ситуацию docker in docker.

              С test containers у нас были сложности... Изначально проект собирался очень просто - это shell-скрипт, который вызывал maven, npm, копировал разные файлы и т.п. Для тестов использовался test containers.

              Затем вместо shell-скриптов решили перенести всё это в Docker. Написали Dockerfile, который делал примерно то же самое, но зато теперь с кэшированием, в управляемом окружении. Если я правильно помню, то уже на этом этапе пришлось сначала помучиться с Docker in Docker, а потом отказаться от test containers и заменить их на zonky. О чём я не жалею, потому что всё стало гораздо проще.

              Потом мы начали переносить сборку в GitLab. И тут я снова столкнулся с Docker in Docker. Помучился и в итоге пришел к тому, что проще перенести правила сборки в конфиг GitLab. Чтобы Dockerfile просто копировал уже собранный jar-файл и остальное. Таким образом у нас нет Docker in Docker и на мой взгляд это всё упрощает.

              Вообще если использовать test containers, то это будет уже Docker in Docker in Docker :) В первом Docker запускается GitLab runner, во втором - сборка приложения, в третьем - тесты на test containers. Для меня это было перебором.

              Нюанс в том, что ваше окружение, оно другое, отличное от того, в котором вы тестировали и могут возникнуть нюансы.

              Java приложение было написано на java8. Всё отлично работает. Спустя какое-то время, ничего не меняли, собираем docker образ, а приложение уже не работает. Почему? А потому-что приехало обновление java 8, где было по-умолчанию отключен TLS 1.1, и всё, приложение с базой уже общаться не смогло.

              Чтобы избежать этого у нас везде жестко прописаны версии: 1) в .gitlab-ci.yml версии docker-образов 2) в Dockerfile версии docker-образов 3) в зависимостях приложения. На некоторых проектах в pom.xml даже прописан вендор и версия JDK, версия Maven, чтобы никто не пытался их собирать с другими версиями.

              Если Gitlab Runner запущен в своём контейнере, что в принципе логично, то получаем ситуацию docker in docker. Если мы в докер пробрасываем socket, чтоб всё сработало как надо, то получаем конфликтную ситуацию, когда одновременно отрабатываются две pipeline с тестовыми сценариями. Из этого и возникает вопрос, а как правильно использовать Gitlab Runnerы ?

              Как я уже описал у нас не используется Docker in Docker, чтобы избежать разные проблемы и костыли. Но я всё равно не очень понимаю какая конфликтная ситуация здесь возникает... Каждый pipeline запустит свой docker-контейнер со своим экземпляром тестовой базы на test containers. Они вроде никак не должны мешать друг другу.

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