444 Stimmen

Die Verwendung der RUN-Anweisung in einem Dockerfile mit 'source' funktioniert nicht.

Ich habe ein Dockerfile, das ich zusammenstelle, um eine einfache Python-Umgebung zu installieren (in die ich später eine App installieren werde).

FROM ubuntu:12.04

# erforderlich zum Bauen bestimmter Python-Bibliotheken
RUN apt-get install python-dev -y

# installiere pip - kanonische Installationsanweisungen von 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 

# installiere und konfiguriere 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 OK, bis zur letzten Zeile, wo ich die folgende Ausnahme erhalte:

[vorherige Schritte 1-9 für Klarheit entfernt]
...
virtualenvwrapper virtualenv-clone stevedore erfolgreich installiert
Aufräumen...
 ---> 1fc253a8f860
Schritt 10 : ENV WORKON_HOME ~/.virtualenvs
 ---> In 8b0145d2c80d ausgeführt
 ---> 0f91a5d96013
Schritt 11 : RUN mkdir -p $WORKON_HOME
 ---> In 9d2552712ddf ausgeführt
 ---> 3a87364c7b45
Schritt 12 : RUN source /usr/local/bin/virtualenvwrapper.sh
 ---> In c13a187261ec ausgeführt
/bin/sh: 1: source: nicht gefunden

Wenn ich in dieses Verzeichnis ls mache (nur um zu testen, ob die vorherigen Schritte ausgeführt wurden), sehe ich, 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 versuche, nur den source Befehl auszuführen, erhalte ich den gleichen 'nicht gefunden' Fehler wie oben. Wenn ich jedoch eine interaktive Shell-Sitzung mit RUN starte, funktioniert source:

$ docker run 3a87 bash
source
bash: Zeile 1: source: Dateiname Argument erforderlich
source: Verwendung: source Dateiname [Argumente]

Ich kann das Skript von hier aus ausführen und danach problemlos auf workon, mkvirtualenv usw. zugreifen.

Ich habe ein wenig recherchiert, und anfangs schien das Problem möglicherweise in der Unterscheidung zwischen bash als Ubuntu login shell und dash als Ubuntu system shell zu liegen, wobei dash den source Befehl nicht unterstützt.

Die Antwort darauf scheint jedoch zu sein, stattdessen '.' anstelle von source zu verwenden, doch das führt nur dazu, dass die Docker-Runtime mit einer go panic Ausnahme ausfällt.

Wie kann ein Shell-Skript aus einer Dockerfile RUN-Anweisung heraus ausgeführt werden, um dieses Problem zu umgehen (läuft auf dem Standard-Basisimage für Ubuntu 12.04 LTS)?

30voto

Bruno Bronosky Punkte 60135

Gemäß https://docs.docker.com/engine/reference/builder/#run ist die Standard-[Linux]-Shell für RUN /bin/sh -c. Es scheint, dass du bashismen erwartest, daher solltest du die "exec-Form" von RUN verwenden, um deine Shell anzugeben.

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

Ansonsten führen das Verwenden der "Shell-Form" von RUN und das Angeben einer anderen Shell zu verschachtelten Shells.

# mach das nicht...
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
# denn es ist dasselbe wie das hier...
RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]

Wenn du mehr als 1 Befehl hast, der eine andere Shell benötigt, solltest du https://docs.docker.com/engine/reference/builder/#shell lesen und deine Standardshell ändern, indem du dies vor deine RUN-Befehle platzierst:

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

Schließlich, wenn du irgendetwas in die .bashrc Datei des Root-Benutzers platziert hast, das du benötigst, kannst du die -l Flagge zum SHELL oder RUN Befehl hinzufügen, um es zu einem Anmelde-Shell zu machen und sicherzustellen, dass es ausgeführt wird.

Hinweis: Ich habe bewusst ignoriert, dass es sinnlos ist, ein Skript als einzigen Befehl in einem RUN zu sourcen.

28voto

TomDotTom Punkte 5377

Basierend auf den Antworten auf dieser Seite würde 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 erhalten, die normalerweise in Login-Shells bezogen werden.

Der beste Weg, den ich bisher gefunden habe, ist das Hinzufügen des Skripts zu /etc/bash.bashrc und dann das Aufrufen jeder Befehlsfolge als bash login.

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

Sie könnten zum Beispiel virtualenvwrapper installieren und einrichten, das virtuelle Environment erstellen, es aktivieren, wenn Sie sich mit einem bash login anmelden, und dann Ihre Python-Module in diesem Environment 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 myapp" >> /etc/bash.bashrc
RUN /bin/bash --login -c "pip install ..."

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

12voto

Gianluca Casati Punkte 2495

Laut der Docker-Dokumentation

Um eine andere Shell als '/bin/sh' zu verwenden, verwenden Sie das ausführende Formular und geben Sie die gewünschte Shell ein. Zum Beispiel,

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

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

6voto

vikas027 Punkte 4402

Außerdem hatte ich Probleme beim Ausführen von source in einem Dockerfile

Dies funktioniert einwandfrei beim Erstellen eines CentOS 6.6 Docker-Containers, aber es gab Probleme in Debian-Containern

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

So habe ich es gelöst, es mag nicht elegant sein, aber so hat es für mich funktioniert

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

6voto

Mohan Punkte 6094

Wenn Sie SHELL zur Verfügung haben, sollten Sie mit dieser Antwort gehen - verwenden Sie nicht die akzeptierte, die Sie zwingt, den Rest des Dockerfiles in einem Befehl pro diesen Kommentar zu setzen.

Wenn Sie eine alte Docker-Version verwenden und keinen Zugriff auf SHELL haben, funktioniert dies, solange Sie nichts aus .bashrc benötigen (was ein seltener Fall in Dockerfiles ist):

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

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X