wake-up-neo.net

Die Verwendung der RUN-Anweisung in einer Docker-Datei mit 'source' funktioniert nicht

Ich habe eine Docker-Datei, die ich zusammenstelle, um eine Vanilla python Umgebung (in die ich eine App installieren werde, aber zu einem späteren Zeitpunkt) zu installieren.

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

Der Build läuft bis zur letzten Zeile in Ordnung, wobei ich die folgende Ausnahme erhalte:

[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

Wenn ich ls in dieses Verzeichnis gehe (nur um zu testen, ob die vorherigen Schritte ausgeführt wurden), kann ich feststellen, dass die Dateien wie erwartet vorhanden sind:

$ 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

Wenn ich nur den Befehl source ausführe, wird derselbe Fehler wie oben angezeigt, der nicht gefunden wurde. Wenn ich jedoch eine interaktive Shell-Sitzung ausführe, funktioniert source:

$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]

Ich kann das Skript von hier aus ausführen und dann glücklich auf workon, mkvirtualenv usw. zugreifen.

Ich habe ein bisschen gegraben und anfangs sah es so aus, als ob das Problem im Unterschied zwischen bash und der Ubuntu Login-Shell liegen könnte. -) und Bindestrich wie die Ubuntu -System-Shell , Bindestrich unterstützt das source Befehl.

Die Antwort auf diese Frage scheint jedoch die Verwendung von '.' anstelle von source zu sein. Dies führt jedoch nur dazu, dass die Docker-Laufzeit mit einer Go-Panic-Ausnahme in die Luft sprengt.

Was ist der beste Weg, um ein Shell-Skript von einer Dockerfile-RUN-Anweisung auszuführen, um dies zu umgehen (ich verwende das Standard-Basis-Image für Ubuntu 12.04 LTS)?.

218

RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"

274
chobo

Ursprüngliche Antwort

FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

Dies sollte für jedes Ubuntu Docker-Basis-Image funktionieren. Im Allgemeinen füge ich diese Zeile für jede Docker-Datei hinzu, die ich schreibe.

Editieren durch einen betroffenen Zuschauer

Wenn Sie den Effekt erhalten möchten, dass "bash anstelle von sh in der gesamten Docker-Datei verwendet wird", ohne zu ändern und möglicherweise * das Betriebssystem im Container beschädigen, können Sie einfach Docker Ihre Absicht mitteilen . Das geht so:

Shell ["/bin/bash", "-c"]

* Der mögliche Schaden ist, dass viele Skripte unter Linux (auf einer neuen Ubuntu-Installation grep -rHInE '/bin/sh' / liefert über 2700 Ergebnisse) Erwarten Sie eine vollständige POSIX-Shell bei /bin/sh. Die Bash-Shell ist nicht nur POSIX plus zusätzliche integrierte Funktionen. Es gibt Builtins (und mehr), die sich völlig anders verhalten als die in POSIX. Ich unterstütze VOLLSTÄNDIG die Vermeidung von POSIX (und den Irrtum, dass jedes Skript, das Sie nicht auf einer anderen Shell getestet haben, funktionieren wird, weil Sie glauben, Sie hätten Basmismen vermieden) und verwende nur Bashismus. Aber das machst du mit einem richtigen Shebang in deinem Drehbuch. Nicht durch Herausziehen der POSIX-Shell unter dem gesamten Betriebssystem. (Es sei denn, Sie haben Zeit, alle 2700-plus-Skripte zu überprüfen, die mit Linux geliefert werden, sowie alle Skripte in den von Ihnen installierten Paketen.)

Weitere Details in dieser Antwort unten. https://stackoverflow.com/a/45087082/117471

136
Anubhav Sinha

Ich hatte das gleiche Problem und um pip install in virtualenv auszuführen, musste ich diesen Befehl verwenden:

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"

Ich hoffe, es hilft.

43
Andrea Grandi

Die Standard-Shell für die Anweisung RUN ist ["/bin/sh", "-c"].

RUN "source file"      # translates to: RUN /bin/sh -c "source file"

Mit Shell-Anweisung können Sie die Standard-Shell für nachfolgende RUN -Anweisungen in Dockerfile ändern:

Shell ["/bin/bash", "-c"] 

Jetzt hat sich die Standard-Shell geändert, und Sie müssen sie nicht in jedem RUN-Befehl explizit definieren

RUN "source file"    # now translates to: RUN /bin/bash -c "source file"

Zusätzlicher Hinweis : Sie können auch --login Option, die eine Login-Shell starten würde. Das heisst ~/.bachrc würde zum Beispiel gelesen und Sie müssen es nicht explizit vor Ihrem Befehl angeben

41

Am einfachsten ist es, den Punktoperator anstelle des Quellcodes zu verwenden, der dem Befehl bash source sh entspricht:

Anstatt von:

RUN source /usr/local/bin/virtualenvwrapper.sh

Verwenden:

RUN . /usr/local/bin/virtualenvwrapper.sh
41
mixja

Aufbauend auf den Antworten auf dieser Seite möchte ich hinzufügen, dass Sie sich bewusst sein müssen, dass jede RUN-Anweisung unabhängig von den anderen mit /bin/sh -c Ausgeführt wird und daher keine Umgebungsvariablen erhält, die normalerweise in Login-Shells bereitgestellt werden.

Die beste Möglichkeit, die ich bisher gefunden habe, besteht darin, das Skript zu /etc/bash.bashrc Hinzuzufügen und dann jeden Befehl als Bash-Login aufzurufen.

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"

Sie können beispielsweise virtualenvwrapper installieren und einrichten, die virtuelle Umgebung erstellen, bei Verwendung eines Bash-Logins aktivieren lassen und dann Ihre python Module in dieser Umgebung installieren:

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 ..."

Das Lesen des Handbuchs zu Bash-Startdateien hilft zu verstehen, was wann bezogen wird.

20
TomDotTom

Wenn Sie Docker 1.12 oder neuer verwenden, verwenden Sie einfach Shell!

Kurze Antwort:

allgemeines:

Shell ["/bin/bash", "-c"] 

für python vituralenv:

Shell ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]

Lange Antwort:

from https://docs.docker.com/engine/reference/builder/#/Shell

Shell ["executable", "parameters"]

Mit der Shell-Anweisung kann die Standard-Shell, die für die Shell-Befehlsform verwendet wird, überschrieben werden. Die Standard-Shell unter Linux ist ["/ bin/sh", "-c"] und unter Windows ["cmd", "/ S", "/ C"]. Die Shell-Anweisung muss in JSON-Form in einer Docker-Datei geschrieben sein.

Die Shell-Anweisung ist besonders unter Windows nützlich, wo es zwei häufig verwendete und sehr unterschiedliche native Shells gibt: cmd und powershell sowie alternative verfügbare Shells, einschließlich sh.

Die Shell-Anweisung kann mehrfach auftreten. Jede Shell-Anweisung überschreibt alle vorherigen Shell-Anweisungen und wirkt sich auf alle nachfolgenden Anweisungen aus. Zum Beispiel:

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

Die folgenden Anweisungen können von der Shell-Anweisung betroffen sein, wenn ihre Shell-Form in einer Docker-Datei verwendet wird: RUN, CMD und ENTRYPOINT.

Das folgende Beispiel ist ein unter Windows übliches Muster, das mithilfe der Shell-Anweisung optimiert werden kann:

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...

Der vom Docker aufgerufene Befehl lautet:

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

Dies ist aus zwei Gründen ineffizient. Erstens wird ein nicht notwendiger Befehlsprozessor für cmd.exe (auch bekannt als Shell) aufgerufen. Zweitens erfordert jeder RUN-Befehl im Shell-Formular einen zusätzlichen Powershell-Befehl, der dem Befehl vorangestellt wird.

Um dies effizienter zu gestalten, kann einer von zwei Mechanismen eingesetzt werden. Eine besteht darin, die JSON-Form des RUN-Befehls zu verwenden, beispielsweise:

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

Das JSON-Formular ist zwar eindeutig und verwendet nicht die nicht erforderliche Datei cmd.exe, erfordert jedoch mehr Ausführlichkeit durch doppelte Anführungszeichen und Escapezeichen. Der alternative Mechanismus ist die Verwendung der Shell-Anweisung und des Shell-Formulars, wodurch Windows-Benutzern eine natürlichere Syntax geboten wird, insbesondere in Kombination mit der Escape-Parser-Direktive:

# 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'

Ergebend:

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>

Der Shell-Befehl kann auch verwendet werden, um die Funktionsweise einer Shell zu ändern. Wenn Sie beispielsweise Shell cmd/S/C/V: ON | OFF unter Windows verwenden, kann die Semantik für die verzögerte Erweiterung von Umgebungsvariablen geändert werden.

Die Shell-Anweisung kann auch unter Linux verwendet werden, wenn eine alternative Shell erforderlich ist, zsh, csh, tcsh und andere.

Die Shell-Funktion wurde in Docker 1.12 hinzugefügt.

19
Mithril

Laut https://docs.docker.com/engine/reference/builder/#run ist die Standard [Linux] Shell für RUN/bin/sh -c. Sie erwarten anscheinend Bashismen, daher sollten Sie die "exec-Form" von RUN verwenden, um Ihre Shell anzugeben.

RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]

Andernfalls führt die Verwendung der "Shell-Form" von RUN und die Angabe einer anderen Shell zu verschachtelten Shells.

# 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"]

Wenn Sie mehr als einen Befehl haben, für den eine andere Shell erforderlich ist, lesen Sie https://docs.docker.com/engine/reference/builder/#Shell und ändern Sie die Standard-Shell, indem Sie diese voranstellen Ihre RUN-Befehle:

Shell ["/bin/bash", "-c"]

Wenn Sie irgendetwas in die Datei .bashrc Des Root-Benutzers gestellt haben, das Sie benötigen, können Sie dem Befehl Shell oder RUN das Flag -l Hinzufügen Machen Sie es zu einer Login-Shell und stellen Sie sicher, dass es bezogen wird.

Hinweis: Ich habe absichtlich die Tatsache ignoriert, dass es sinnlos ist, ein Skript als einzigen Befehl in einem RUN zu verwenden.

14
Bruno Bronosky

Laut Docker-Dokumentation

Um eine andere Shell als "/ bin/sh" zu verwenden, verwenden Sie das Exec-Formular, das in der gewünschten Shell übergeben wird. Zum Beispiel,

RUN ["/bin/bash", "-c", "echo hello"]

Siehe https://docs.docker.com/engine/reference/builder/#run

9
Gianluca Casati

Ich hatte auch Probleme beim Ausführen von source in einer Docker-Datei

Dies funktioniert einwandfrei für die Erstellung von CentOS 6.6 Docker-Containern, führte jedoch zu Problemen bei Debian-Containern

RUN cd ansible && source ./hacking/env-setup

So bin ich damit umgegangen, vielleicht nicht auf elegante Weise, aber genau das hat bei mir funktioniert

RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
3
vikas027

Dies kann vorkommen, weil source ein integrierter Bash ist und nicht eine Binärdatei irgendwo im Dateisystem. Soll das Skript, das Sie verwenden, den Container anschließend ändern?

2
Paul Morie

Möglicherweise möchten Sie bash -v Ausführen, um zu sehen, was bezogen wird.

Ich würde Folgendes tun, anstatt mit Symlinks zu spielen:

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc

2
vimdude

Am Ende habe ich mein env-Zeug in .profile und mutierte Shell so etwas wie

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
1
mattexx

Wenn Sie nur versuchen, mit pip etwas in das virtuelle Gerät zu installieren, können Sie die PATH-Umgebung so ändern, dass sie zuerst im bin-Ordner des virtuellen Geräts nachschaut

ENV PATH="/path/to/venv/bin:${PATH}"

Dann kann jeder pip install-Befehle, die in der Docker-Datei folgen, suchen zuerst nach/path/to/venv/bin/pip und verwenden diesen Befehl, der in diese virtuelle Umgebung und nicht in den System-Python installiert wird.

0
shadfc

Wenn Sie Shell zur Verfügung haben, sollten Sie mit diese Antwort - nicht verwenden akzeptiere eins, was dich zwingt, den Rest der Docker-Datei in einem Befehl pro dieser Kommentar abzulegen.

Wenn Sie eine alte Docker-Version verwenden und keinen Zugriff auf Shell haben, funktioniert dies, solange Sie nichts von .bashrc (was in Dockerfiles selten vorkommt):

ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]

Beachten Sie das -i wird benötigt, damit bash die rcfile überhaupt liest.

0
Mohan