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)?

0voto

DeusXMachina Punkte 1114

Hier ist ein Beispiel Dockerfile, das mehrere clevere Techniken nutzt, um Ihnen zu ermöglichen, für jede RUN-Anweisung eine vollständige Conda-Umgebung auszuführen. Sie können einen ähnlichen Ansatz verwenden, um beliebige Vorbereitungen in einer Skriptdatei auszuführen.

Hinweis: Es gibt viele Feinheiten, wenn es um Anmeldung/interaktive vs. nicht-interaktive Shells, Signale, exec, die Art und Weise, wie mehrere Argumente behandelt werden, das Zitieren, wie CMD und ENTRYPOINT interagieren und noch eine Million andere Dinge geht, also lassen Sie sich nicht entmutigen, wenn beim Herumspielen mit diesen Dingen mal etwas schief geht. Ich habe viele frustrierende Stunden damit verbracht, in allen Arten von Literatur zu graben, und ich verstehe immer noch nicht ganz, wie alles zusammenpasst.

## Conda mit benutzerdefiniertem Entry-Point vom Basis-Ubuntu-Image
## Erstellen Sie z.B. mit `docker build -t monoconda .`
## Führen Sie `docker run --rm -it monoconda bash` aus, um direkt in
## die Umgebung `foo` einzutauchen!
VON ubuntu:18.04

## Installieren Sie Dinge, die wir benötigen, um weitere Dinge zu installieren
RUN apt-get update -qq &&\
    apt-get install -qq curl wget git &&\
    apt-get install -qq --no-install-recommends \
        libssl-dev \
        software-properties-common \
    && rm -rf /var/lib/apt/lists/*

## Miniconda installieren
RUN wget -nv https://repo.anaconda.com/miniconda/Miniconda3-4.7.12-Linux-x86_64.sh -O ~/miniconda.sh && \
    /bin/bash ~/miniconda.sh -b -p /opt/conda && \
    rm ~/miniconda.sh && \
    /opt/conda/bin/conda clean -tipsy && \
    ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh

## Fügen Sie Conda dem Pfad hinzu, damit wir es mit seinem Namen ausführen können
ENV PATH=/opt/conda/bin:$PATH

## Erstellen Sie /entry.sh, das unser neuer Shell-Einstiegspunkt sein wird. Dies führt Aktionen zur Konfiguration der Umgebung
## vor dem Start einer neuen Shell aus (die die Umgebung erbt).
## Das exec ist wichtig! Dies ermöglicht das Weiterleiten von Signalen
RUN     (echo '#!/bin/bash' \
    &&   echo '__conda_setup="$(/opt/conda/bin/conda shell.bash hook 2> /dev/null)"' \
    &&   echo 'eval "$__conda_setup"' \
    &&   echo 'conda activate "${CONDA_TARGET_ENV:-base}"' \
    &&   echo '>&2 echo "EINSTIEGSPUNKT: CONDA_DEFAULT_ENV=${CONDA_DEFAULT_ENV}"' \
    &&   echo 'exec "$@"'\
        ) >> /entry.sh && chmod +x /entry.sh

## Teilen Sie dem Docker-Build-Prozess mit, dass dies für RUN verwendet werden soll.
## Die Standard-Shell unter Linux lautet ["/bin/sh", "-c"], und unter Windows ["cmd", "/S", "/C"]
SHELL ["/entry.sh", "/bin/bash", "-c"]
## Jetzt wird jeder folgende Aufruf von RUN mit dem Einstiegsskript beginnen
RUN     conda update conda -y

## Erstellen Sie eine Dummy-Umgebung
RUN     conda create --name foo

## Ich habe diese Variable hinzugefügt, damit das Einstiegsskript eine bestimmte Umgebung aktiviert
ENV CONDA_TARGET_ENV=foo

## Dies wird in der Umgebung foo installiert, da sie zu Beginn der RUN-Anweisung aktiviert wird
RUN  conda install pip

## Konfigurieren Sie .bashrc, um in eine Conda-Umgebung zu wechseln und sofort unsere ZIEL-Umgebung zu aktivieren
RUN conda init && echo 'conda activate "${CONDA_TARGET_ENV:-base}"' >>  ~/.bashrc
ENTRYPOINT ["/entry.sh"]

0voto

Manuel Lazo Punkte 507

Ich habe ein ähnliches Szenario für eine Anwendung erlebt, die mit dem Django-Webframework entwickelt wurde, und das sind die Schritte, die für mich perfekt funktioniert haben:

  • Inhalt meiner Dockerfile

    [mlazo@srvjenkins project_textile]$ cat docker/Dockerfile.debug FROM malazo/project_textile_ubuntu:latest

    ENV PROJECT_DIR=/proyectos/project_textile PROJECT_NAME=project_textile WRAPPER_PATH=/usr/share/virtualenvwrapper/virtualenvwrapper.sh

    COPY . ${PROJECT_DIR}/ WORKDIR ${PROJECT_DIR}

    RUN echo "source ${WRAPPER_PATH}" > ~/.bashrc SHELL ["/bin/bash","-c","-l"] RUN mkvirtualenv -p $(which python3) ${PROJECT_NAME} && \ workon ${PROJECT_NAME} && \ pip3 install -r requirements.txt

    EXPOSE 8000

    ENTRYPOINT ["tests/container_entrypoint.sh"] CMD ["public/manage.py","runserver","0:8000"]

  • Inhalt der ENTRYPOINT-Datei "tests/container_entrypoint.sh":

    [mlazo@srvjenkins project_textile]$ cat tests/container_entrypoint.sh

    !/bin/bash

    - encoding : UTF-8 -

    sh tests/deliver_env.sh source ~/.virtualenvs/project_textile/bin/activate exec python "$@"

  • Zuletzt, die Art und Weise wie ich den Container bereitstelle, war:

    [mlazo@srvjenkins project_textile]$ cat ./tests/container_deployment.sh

    !/bin/bash

    CONT_NAME="cont_app_server" IMG_NAME="malazo/project_textile_app" [ $(docker ps -a |grep -i ${CONT_NAME} |wc -l) -gt 0 ] && docker rm -f ${CONT_NAME} docker run --name ${CONT_NAME} -p 8000:8000 -e DEBUG=${DEBUG} -e MYSQL_USER=${MYSQL_USER} -e MYSQL_PASSWORD=${MYSQL_PASSWORD} -e MYSQL_HOST=${MYSQL_HOST} -e MYSQL_DATABASE=${MYSQL_DATABASE} -e MYSQL_PORT=${MYSQL_PORT} -d ${IMG_NAME}

Ich hoffe wirklich, dass dies für jemand anderen hilfreich sein wird.

Grüße,

0voto

unlimitedfox Punkte 396

Ich hatte dasselbe Problem. Wenn Sie auch ein Python-Basisbild verwenden, können Sie die Shebang-Zeile in Ihrem Shell-Skript in #!/bin/bash ändern. Siehe zum Beispiel das container_entrypoint.sh von Manuel Lazo.

0voto

shadfc Punkte 5428

Wenn Sie versuchen, mit Pip etwas in das Virtualenv zu installieren, können Sie die PATH-Umgebung anpassen, um zuerst im bin-Ordner des Virtualenv zu suchen

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

Dann werden alle pip install-Befehle, die im Dockerfile folgen, zuerst /path/to/venv/bin/pip finden und verwenden, was in dieses Virtualenv installiert und nicht das System-Python.

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