Ответ 1
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
У меня есть файл Docker, который я собираю, чтобы установить среду python для ванили (в которую я буду устанавливать приложение, но позже).
FROM ubuntu:12.04
# required to build certain python libraries
RUN apt-get install python-dev -y
# install pip - canonical installation instructions from pip-installer.org
# http://www.pip-installer.org/en/latest/installing.html
ADD https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py /tmp/ez_setup.py
ADD https://raw.github.com/pypa/pip/master/contrib/get-pip.py /tmp/get-pip.py
RUN python /tmp/ez_setup.py
RUN python /tmp/get-pip.py
RUN pip install --upgrade pip
# install and configure virtualenv
RUN pip install virtualenv
RUN pip install virtualenvwrapper
ENV WORKON_HOME ~/.virtualenvs
RUN mkdir -p $WORKON_HOME
RUN source /usr/local/bin/virtualenvwrapper.sh
Строка работает нормально до последней строки, где я получаю следующее исключение:
[previous steps 1-9 removed for clarity]
...
Successfully installed virtualenvwrapper virtualenv-clone stevedore
Cleaning up...
---> 1fc253a8f860
Step 10 : ENV WORKON_HOME ~/.virtualenvs
---> Running in 8b0145d2c80d
---> 0f91a5d96013
Step 11 : RUN mkdir -p $WORKON_HOME
---> Running in 9d2552712ddf
---> 3a87364c7b45
Step 12 : RUN source /usr/local/bin/virtualenvwrapper.sh
---> Running in c13a187261ec
/bin/sh: 1: source: not found
Если я ls
в этот каталог (просто чтобы проверить, что были выполнены предыдущие шаги), я вижу, что файлы существуют как ожидалось:
$ docker run 3a87 ls /usr/local/bin
easy_install
easy_install-2.7
pip
pip-2.7
virtualenv
virtualenv-2.7
virtualenv-clone
virtualenvwrapper.sh
virtualenvwrapper_lazy.sh
Если я попробую запустить команду source
, я получаю ту же ошибку "не найден", как указано выше. Если я запускаю сеанс интерактивной оболочки, источник работает:
$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]
Я могу запустить script отсюда, а затем с радостью получить доступ к workon
, mkvirtualenv
и т.д.
Я сделал несколько копий, и изначально было похоже, что проблема может заключаться в различии между bash как оболочкой входа Ubuntu и тире как системная оболочка Ubuntu, тире не поддерживает команду source
.
Тем не менее, ответ на этот вопрос заключается в использовании '. вместо source
, но это просто приводит к тому, что время выполнения Docker взорвано с помощью исключения паники.
Каков наилучший способ запустить оболочку script из инструкции Dockerfile RUN, чтобы обойти это (я запускаю базовое изображение по умолчанию для Ubuntu 12.04 LTS).
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
Это должно работать для каждого базового образа докера Ubuntu. Я обычно добавляю эту строку для каждого Dockerfile, который я пишу.
Если вы хотите получить эффект "использовать bash
вместо sh
во всем этом Dockerfile", не изменяя и не повреждая * ОС внутри контейнера, вы можете просто сообщить Docker о своем намерении. Это делается так:
SHELL ["/bin/bash", "-c"]
* Возможный ущерб состоит в том, что многие скрипты в Linux (на новой установке Ubuntu
grep -rHInE '/bin/sh'/
возвращает более 2700 результатов) ожидают полностью POSIX-оболочку в/bin/sh
. Оболочка bash - это не просто POSIX плюс дополнительные встроенные функции. Есть встроенные функции (и даже больше), которые ведут себя совершенно иначе, чем в POSIX. Я ПОЛНОСТЬЮ поддерживаю избегание POSIX (и заблуждение, что любой скрипт, который вы не тестировали в другой оболочке, сработает, потому что вы думаете, что вы избежали басмизм), и просто использование bashism. Но вы делаете это с правильным шебангом в вашем сценарии. Не вытаскивая оболочку POSIX из-под всей ОС. (Если у вас нет времени, чтобы проверить все 2700 и более скриптов, которые поставляются с Linux, плюс все в любых пакетах, которые вы устанавливаете.)
Более подробно в этом ответе ниже. fooobar.com/questions/46731/...
У меня была такая же проблема, и для выполнения установки pip внутри virtualenv мне пришлось использовать эту команду:
RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh \
&& mkvirtualenv myapp \
&& workon myapp \
&& pip install -r /mycode/myapp/requirements.txt"
Надеюсь, это поможет.
Самый простой способ - использовать оператор точки вместо источника, что эквивалентно команде bash source
:
Вместо:
RUN source /usr/local/bin/virtualenvwrapper.sh
Использование:
RUN . /usr/local/bin/virtualenvwrapper.sh
Оболочкой по умолчанию для инструкции RUN
является ["/bin/sh", "-c"]
.
RUN "source file" # translates to: RUN /bin/sh -c "source file"
Используя инструкцию SHELL, вы можете изменить оболочку по умолчанию для последующих инструкций RUN
в Dockerfile:
SHELL ["/bin/bash", "-c"]
Теперь оболочка по умолчанию изменилась, и вам не нужно явно определять ее в каждой инструкции RUN
RUN "source file" # now translates to: RUN /bin/bash -c "source file"
Дополнительное примечание: Вы также можете добавить --login
которая запускает оболочку входа. Это означает, что ~/.bachrc
например, будет прочитан, и вам не нужно явно указывать его перед вашей командой
Основываясь на ответах на этой странице, я бы добавил, что вам нужно знать, что каждый оператор RUN работает независимо от других с помощью /bin/sh -c
и, следовательно, не будет получать каких-либо окружающих vars, которые, как правило, будут получены в оболочках входа.
Лучший способ, который я нашел до сих пор, - добавить script в /etc/bash.bashrc
, а затем вызвать каждую команду как bash login.
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"
Вы могли бы, например, установить и настроить virtualenvwrapper, создать виртуальный env, активировать его при использовании входа bash, а затем установить ваши модули python в это env:
RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "mkvirtualenv myapp"
RUN echo "workon mpyapp" >> /etc/bash.bashrc
RUN /bin/bash --login -c "pip install ..."
Чтение руководства по bash файлам-автозагрузкам помогает понять, что происходит, когда.
Если вы используете Docker 1.12 или новее, просто используйте SHELL
!
Общие:
SHELL ["/bin/bash", "-c"]
для python vituralenv:
SHELL ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
из https://docs.docker.com/engine/reference/builder/#/shell
SHELL ["executable", "parameters"]
Инструкция SHELL позволяет использовать оболочку по умолчанию, используемую для формы оболочки команд, подлежащих переопределению. По умолчанию оболочка в Linux [ "/bin/sh", "-c" ], а в Windows - [ "cmd", "/S", "/C" ]. SHELL инструкция должна быть записана в форме JSON в файле Docker.
Инструкция SHELL особенно полезна в Windows, где являются двумя обычно используемыми и совершенно разными родными оболочками: cmd и powershell, а также альтернативные оболочки, включая sh.
Инструкция SHELL может появляться несколько раз. Каждый SHELL команда отменяет все предыдущие инструкции SHELL и влияет на все последующие инструкции. Например:
FROM microsoft/windowsservercore # Executed as cmd /S /C echo default RUN echo default # Executed as cmd /S /C powershell -command Write-Host default RUN powershell -command Write-Host default # Executed as powershell -command Write-Host hello SHELL ["powershell", "-command"] RUN Write-Host hello # Executed as cmd /S /C echo hello SHELL ["cmd", "/S"", "/C"] RUN echo hello
Следующие инструкции могут быть изменены инструкцией SHELL когда их форма оболочки используется в файле Dockerfile: RUN, CMD и Entrypoint.
Следующий пример - это общий шаблон, найденный в Windows, который может оптимизироваться с помощью инструкции SHELL:
... RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt" ...
Команда, вызываемая докером, будет:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
Это неэффективно по двум причинам. Во-первых, нет необходимости Вызывается командный процессор cmd.exe(aka shell). Во-вторых, каждый RUN инструкции в форме оболочки требуется дополнительная команда powershell -command префикс команды.
Чтобы сделать это более эффективным, можно использовать один из двух механизмов. Один из них - использовать форму JSON команды RUN, например:
... RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""] ...
Хотя форма JSON недвусмысленна и не использует ненужные cmd.exe, это требует большей многословности посредством двойного цитирования и побега. Альтернативным механизмом является использование инструкции SHELL и форма оболочки, создавая более естественный синтаксис для пользователей Windows, особенно в сочетании с директивой анализа парсера:
# escape=` FROM microsoft/nanoserver SHELL ["powershell","-command"] RUN New-Item -ItemType Directory C:\Example ADD Execute-MyCmdlet.ps1 c:\example\ RUN c:\example\Execute-MyCmdlet -sample 'hello world'
Результат:
PS E:\docker\build\shell> docker build -t shell . Sending build context to Docker daemon 4.096 kB Step 1/5 : FROM microsoft/nanoserver ---> 22738ff49c6d Step 2/5 : SHELL powershell -command ---> Running in 6fcdb6855ae2 ---> 6331462d4300 Removing intermediate container 6fcdb6855ae2 Step 3/5 : RUN New-Item -ItemType Directory C:\Example ---> Running in d0eef8386e97 Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 10/28/2016 11:26 AM Example ---> 3f2fbf1395d9 Removing intermediate container d0eef8386e97 Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\ ---> a955b2621c31 Removing intermediate container b825593d39fc Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world' ---> Running in be6d8e63fe75 hello world ---> 8e559e9bf424 Removing intermediate container be6d8e63fe75 Successfully built 8e559e9bf424 PS E:\docker\build\shell>
Инструкция SHELL может также использоваться для изменения способа, которым оболочка работает. Например, используя SHELL cmd/S/C/V: ON | OFF on Windows, семантика расширения переменной задержки с задержкой может быть изменен.
Инструкция SHELL может также использоваться на Linux, если альтернативный оболочки, такие как zsh, csh, tcsh и другие.
Функция SHELL была добавлена в Docker 1.12.
Согласно https://docs.docker.com/engine/reference/builder/#run оболочкой [Linux] по умолчанию для RUN
является /bin/sh -c
. Вы, кажется, ожидаете bashisms, поэтому вы должны использовать "exec форму" RUN
чтобы указать вашу оболочку.
RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
В противном случае использование "формы оболочки" RUN и указание другой оболочки приводит к появлению вложенных оболочек.
# don't do this...
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
# because it is the same as this...
RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]
Если у вас более одной команды, для которой требуется другая оболочка, вам следует прочитать https://docs.docker.com/engine/reference/builder/#shell и изменить оболочку по умолчанию, поместив ее перед командами RUN:
SHELL ["/bin/bash", "-c"]
Наконец, если вы поместили что-либо в файл .bashrc
корневого пользователя, который вам нужен, вы можете добавить флаг -l
к команде SHELL
или RUN
чтобы сделать его оболочкой входа и убедиться, что он получен.
Примечание. Я намеренно проигнорировал тот факт, что бессмысленно указывать сценарий в качестве единственной команды в RUN.
Согласно документации Docker
Чтобы использовать другую оболочку, отличную от '/bin/sh, используйте форму exec, проходящую в нужной оболочке. Например,
RUN ["/bin/bash", "-c", "echo hello"]
У меня также были проблемы при запуске source
в Dockerfile
Это отлично работает для создания контейнера CentOS 6.6 Docker, но дает проблемы в контейнерах Debian.
RUN cd ansible && source ./hacking/env-setup
Вот как я справился с этим, возможно, не изящным, но это то, что сработало для меня.
RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
Это может произойти, потому что source
является встроенным в bash, а не двоичным файлом где-то в файловой системе. Ваше намерение использовать script для поиска контейнера после этого?
Возможно, вы захотите запустить bash -v
, чтобы узнать, что источник.
Я бы сделал следующее вместо игры с символическими ссылками:
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
Если вы просто пытаетесь использовать pip для установки чего-то в virtualenv, вы можете изменить PATH env, чтобы сначала посмотреть в папку виртуального диска bin
ENV PATH="/path/to/venv/bin:${PATH}"
Затем любые команды pip install
, которые следуют в файле Docker, сначала найдут /path/to/venv/bin/pip и будут использовать это, которое будет установлено в этот virtualenv, а не системный python.
Я закончил тем, что поместил свой материал env в .profile
и мутировал SHELL
что-то вроде
SHELL ["/bin/bash", "-c", "-l"]
# Install ruby version specified in .ruby-version
RUN rvm install $(<.ruby-version)
# Install deps
RUN rvm use $(<.ruby-version) && gem install bundler && bundle install
CMD rvm use $(<.ruby-version) && ./myscript.rb
Если у вас есть SHELL
вы должны пойти с этим ответом - не используйте принятый ответ, который заставляет вас поместить остальную часть файла docker в одну команду для каждого комментария.
Если вы используете старую версию Docker и не имеете доступа к SHELL
, это будет работать до тех пор, пока вам ничего не нужно из .bashrc
(что является редким случаем в Dockerfiles):
ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]
Обратите внимание, что -i
необходим, чтобы bash вообще читал rcfile.