Докер в Docker не может установить громкость
Я управляю кластером Jenkins, где в Master и Slave оба работают как контейнеры Docker.
Хост - это последняя загрузочная ВМ, работающая на MacOS.
Чтобы разрешить Jenkins выполнять развертывание с использованием Docker, я смонтировал docker.sock и клиент docker с хоста в контейнер Jenkins следующим образом: -
docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker -v $HOST_JENKINS_DATA_DIRECTORY/jenkins_data:/var/jenkins_home -v $HOST_SSH_KEYS_DIRECTORY/.ssh/:/var/jenkins_home/.ssh/ -p 8080:8080 jenkins
У меня возникают проблемы при подключении тома к контейнерам Docker, которые запускаются внутри контейнера Jenkins. Например, если мне нужно запустить другой контейнер внутри контейнера Jenkins, я делаю следующее:
sudo docker run -v $JENKINS_CONTAINER/deploy.json:/root/deploy.json $CONTAINER_REPO/$CONTAINER_IMAGE
Выше работает контейнер, но файл "deploy.json" НЕ монтируется как файл, а как "Каталог". Даже если я подключу каталог как том, я не смогу просмотреть файлы в результирующем контейнере.
Это проблема из-за прав доступа к файлам из-за Docker в случае Docker?
Ответы
Ответ 1
Контейнер Docker в контейнере Docker использует родительский демон HOST Docker, и, следовательно, все тома, которые монтируются в случае "докер-в-докер", по-прежнему ссылаются на HOST, а не из контейнера.
Следовательно, фактический путь, установленный из контейнера Дженкинса "не существует" в HOST. В связи с этим создается новый каталог в контейнере "docker-in-docker", который пуст. То же самое происходит, когда каталог монтируется в новый контейнер Docker внутри контейнера.
Очень простая и очевидная вещь, которую я пропустил, но понял, как только я набрал вопрос.
Ответ 2
Другой способ сделать это - использовать либо именованные тома, либо контейнеры объема данных. Таким образом, внутри контейнера не нужно ничего знать о хосте и контейнере Jenkins, а контейнер сборки ссылается на объем данных таким же образом.
Я попытался сделать что-то похожее на то, что вы делаете, кроме как с агентом, а с использованием мастера Jenkins. Проблема была такой же, что я не мог смонтировать рабочее пространство Jenkins во внутреннем контейнере. То, что работало на меня, заключалось в использовании подхода к контейнеру объема данных, и файлы рабочей области были видны как контейнеру агента, так и внутреннему контейнеру. Что мне понравилось в этом подходе, так это то, что оба контейнера ссылаются на объем данных таким же образом. Монтаж каталогов с внутренним контейнером будет сложным, так как внутренний контейнер теперь должен знать что-то о хосте, на котором работает его родительский контейнер.
У меня есть подробное сообщение в блоге о моем подходе здесь:
http://damnhandy.com/2016/03/06/creating-containerized-build-environments-with-the-jenkins-pipeline-plugin-and-docker-well-almost/
Как и код здесь:
https://github.com/damnhandy/jenkins-pipeline-docker
В моем конкретном случае не все работает так, как мне хотелось бы, с точки зрения плагина Jenkins Pipeline. Но он затрагивает проблему внутреннего контейнера, который может получить доступ к каталогу рабочей области Jenkins.
Ответ 3
Что касается вашего случая использования, связанного с Jenkins, вы можете просто подделать путь, создав символическую ссылку на хост:
ln -s $HOST_JENKINS_DATA_DIRECTORY/jenkins_data /var/jenkins_home
Ответ 4
Это также работает через docker-compose
и/или именованные тома, поэтому вам не нужно создавать контейнер с данными, но вам все равно нужно иметь пустую директорию на хосте.
Настройка хоста
Сделать каталоги на стороне хоста и установить разрешения для доступа к контейнерам Docker для доступа к sudo mkdir -p/var/jenkins_home/{workspace,builds,jobs} && sudo chown -R 1000/var/jenkins_home && sudo chmod -R a+rwx/var/jenkins_home
докер-compose.yml
version: '3.1'
services:
jenkins:
build: .
image: jenkins
ports:
- 8080:8080
- 50000:50000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- workspace:/var/jenkins_home/workspace/
# Can also do builds/jobs/etc here and below
jenkins-lts:
build:
context: .
args:
versiontag: lts
image: jenkins:lts
ports:
- 8081:8080
- 50001:50000
volumes:
workspace:
driver: local
driver_opts:
type: none
o: bind
device: /var/jenkins_home/workspace/
Когда вы создаете docker-compose up --build jenkins
(вы можете включить это в готовый к запуску пример, например https://github.com/thbkrkr/jks, где скрипты.groovy предварительно настроили Jenkins, чтобы быть полезными при запуске) и тогда вы сможете клонировать свою работу в каталог $ JENKINS_HOME/workspace и не должны получать ошибки о недостающих файлах /etc, потому что будут соответствовать пути к узлу и контейнеру, а затем запускать дополнительные контейнеры из Docker-in- Докер также должен работать.
Dockerfile (для Дженкинса с Докером в Докере)
ARG versiontag=latest
FROM jenkins/jenkins:${versiontag}
ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false"
COPY jenkins_config/config.xml /usr/share/jenkins/ref/config.xml.override
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
USER root
RUN curl -L http://get.docker.io | bash && \
usermod -aG docker jenkins
# Since the above takes a while make any other root changes below this line
# eg 'RUN apt update && apt install -y curl'
# drop back to the regular jenkins user - good practice
USER jenkins
EXPOSE 8080
Ответ 5
Способ обойти эту проблему состоит в том, чтобы смонтировать каталог (внутри вашего контейнера докеров, в котором вы установили свой сокет для докеров), используя тот же самый путь для своего адресата. Затем, когда вы запускаете контейнер из этого контейнера, вы можете смонтировать все, что находится внутри этого пути монтирования, в новый контейнер, используя docker -v
.
Возьмем следующий пример:
# Spin up your container from which you will use docker
docker run -v /some/dir:/some/dir -v /var/run/docker.sock:/var/run.docker.sock docker:latest
# Now spin up a container from within this container
docker run -v /some/dir:/usr/src/app $CONTAINER_IMAGE
Папка /some/dir
теперь монтируется на вашем хосте, промежуточном контейнере, а также в контейнере назначения. Поскольку путь монтирования существует как на хосте, так и на контейнере "почти докер в докере", вы можете использовать docker -v
как и ожидалось.
Это похоже на предложение создания символической ссылки на хосте, но я нашел это (по крайней мере, в моем случае), более чистым решением. Только не забудьте очистить каталог на хосте после этого! ;)
Ответ 6
Если вы похожи на меня и не хотите связываться с установкой Jenkins или слишком ленивы, чтобы пройти через все эти проблемы, вот простой обходной путь, который я сделал, чтобы это сработало для меня.
Шаг 1 - Добавьте следующие переменные в раздел среды конвейера
environment {
ABSOLUTE_WORKSPACE = "/home/ubuntu/volumes/jenkins-data/workspace"
JOB_WORKSPACE = "\${PWD##*/}"
}
Шаг 2 - Запустите свой контейнер с помощью следующей команды Jenkins pipe следующим образом.
steps {
sh "docker run -v ${ABSOLUTE_WORKSPACE}/${JOB_WORKSPACE}/my/dir/to/mount:/targetPath imageName:tag"
}
Обратите внимание на двойные кавычки в приведенном выше утверждении: Jenkins не будет преобразовывать переменные env, если кавычки не отформатированы должным образом или вместо них добавлены одинарные кавычки.
Что означает каждая переменная?
-
ABSOLUTE_WORKSPACE - путь нашего тома Jenkins, который мы смонтировали при запуске Jenkins Docker Container. В моем случае команда docker run была следующей.
sudo docker run\-p 80:8080\-v/home/ubuntu/volumes/jenkins-data: /var/jenkins_home\-v/var/run/docker.sock: /var/run/docker.sock\-d -t jenkinsci/blueocean
Таким образом, переменная ABSOLUTE_WORKSPACE =/home/ubuntu/volume/jenkins -d ata +/workspace
- Команда JOB_WORKSPACE дает нам текущий каталог рабочей области, в которой находится ваш код. Это также корневой каталог вашей кодовой базы. Просто следовал за этим ответом для справки.
Как это работает?
Это очень просто, как уже упоминалось в ответе @ZephyrPLUSPLUS (кредиты, где необходимо), исходный путь для нашего контейнера Docker, который запускается в конвейере Jenkins, не является путем в текущем контейнере, скорее, выбранный путь является путем к хосту. Все, что мы делаем здесь, это прокладываем путь, по которому проходит наш трубопровод Дженкинс. И монтируем его в наш контейнер. Вуаля !!
Вот небольшая иллюстрация, чтобы помочь прояснить...
Ответ 7
У меня та же проблема в Gitlab CI, я решил это с помощью docker cp
, чтобы сделать что-то вроде монтирования
script:
- docker run --name ${CONTAINER_NAME} ${API_TEST_IMAGE_NAME}
after_script:
- docker cp ${CONTAINER_NAME}:/code/newman ./
- docker rm ${CONTAINER_NAME}