Je prépare un fichier Docker pour installer un environnement Vanilla python (dans lequel j'installerai une application, mais à une date ultérieure).
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
La construction fonctionne correctement jusqu'à la dernière ligne, où j'obtiens l'exception suivante:
[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
Si je ls
dans ce répertoire (juste pour vérifier que les étapes précédentes ont été validées), je peux voir que les fichiers existent comme prévu:
$ 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
Si j'essaie simplement d'exécuter la commande source
, j'obtiens la même erreur 'introuvable' que ci-dessus. Si j'exécute une session interactive Shell cependant, la source fonctionne:
$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]
Je peux exécuter le script à partir d'ici, puis accéder avec bonheur à workon
, mkvirtualenv
etc.
J'ai creusé un peu, et au départ, le problème semblait résider dans la différence entre bash comme étant le shell de connexion Ubuntu , et tiret en tant que système Ubuntu Shell , tiret ne prenant pas en charge la source
commande.
Cependant, la solution semble être d'utiliser '.' au lieu de source
, mais cela provoque simplement le temps d'exécution de Docker avec une exception de panique.
Quel est le meilleur moyen d’exécuter un script Shell à partir d’une instruction Dockerfile RUN pour contourner ce problème (j’exécute l’image de base par défaut pour 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
Cela devrait fonctionner pour chaque image de base du docker Ubuntu. J'ajoute généralement cette ligne pour chaque fichier Docker que j'écris.
Si vous voulez obtenir l’effet de "utiliser bash
au lieu de sh
dans tout ce fichier Dockerfile", sans modifier Alt et endommageant éventuellement * le système d'exploitation à l'intérieur du conteneur, vous pouvez simplement indiquer votre intention à Docker . Cela se fait comme suit:
Shell ["/bin/bash", "-c"]
* Les dommages possibles sont que de nombreux scripts sous Linux (sur une nouvelle installation Ubuntu
grep -rHInE '/bin/sh' /
renvoie plus de 2700 résultats) attendent un shell POSIX complet à/bin/sh
. Le shell bash n’est pas seulement POSIX plus des éléments supplémentaires. Il y a des fonctions intégrées (et plus) qui se comportent de manière totalement différente de celles de POSIX. Je suis tout à fait en faveur de l’évitement de POSIX (et de l’erreur selon laquelle tout script que vous n’avez pas testé sur un autre shell va fonctionner parce que vous pensez avoir évité les basmismes) et n’utilisez que le bashisme. Mais vous faites cela avec un Shebang approprié dans votre script. Pas en tirant le shell POSIX de tout le système d'exploitation. (Sauf si vous avez le temps de vérifier tous les 2700 scripts fournis avec Linux, ainsi que ceux de tous les packages que vous installez.)
Plus de détails dans cette réponse ci-dessous. https://stackoverflow.com/a/45087082/117471
J'ai eu le même problème et afin d'exécuter l'installation de pip dans virtualenv, j'ai dû utiliser cette commande:
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"
J'espère que ça aide.
Le shell par défaut pour l'instruction RUN
est ["/bin/sh", "-c"]
.
RUN "source file" # translates to: RUN /bin/sh -c "source file"
En utilisant instruction Shell , vous pouvez modifier le shell par défaut pour les instructions RUN
suivantes dans Dockerfile:
Shell ["/bin/bash", "-c"]
Maintenant, le shell par défaut a changé et vous n'avez pas besoin de le définir explicitement dans chaque instruction RUN.
RUN "source file" # now translates to: RUN /bin/bash -c "source file"
Remarque complémentaire : Vous pouvez également ajouter l’option --login
pour démarrer un shell de connexion. Cela signifie que ~/.bachrc
par exemple serait lu et que vous n'avez pas besoin de le rechercher explicitement avant votre commande
Le moyen le plus simple consiste à utiliser l'opérateur point à la place de source, qui est l'équivalent sh de la commande bash source
:
Au lieu de:
RUN source /usr/local/bin/virtualenvwrapper.sh
Utilisation:
RUN . /usr/local/bin/virtualenvwrapper.sh
En me basant sur les réponses de cette page, j’ajouterais que vous devez être conscient du fait que chaque instruction RUN s’exécute indépendamment des autres avec /bin/sh -c
et qu’elle n’obtiendra donc aucun var d’environnement qui proviendrait normalement de shell de connexion.
Le meilleur moyen que j’ai trouvé jusqu’à présent consiste à ajouter le script à /etc/bash.bashrc
, puis à appeler chaque commande sous le nom bash login.
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"
Vous pouvez par exemple installer et configurer virtualenvwrapper, créer l’environnement virtuel, l’activer lorsque vous utilisez un login bash, puis installer vos modules python dans cet 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 ..."
Lire le manuel sur fichiers de démarrage bash aide à comprendre ce qui est acheté quand.
Si vous utilisez Docker 1.12 ou une version plus récente, utilisez simplement Shell
!
général:
Shell ["/bin/bash", "-c"]
pour python vituralenv:
Shell ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
à partir de https://docs.docker.com/engine/reference/builder/#/Shell
Shell ["executable", "parameters"]
L'instruction Shell permet de remplacer le shell par défaut utilisé pour la forme de commande Shell. Le shell par défaut sous Linux est ["/ bin/sh", "-c"], et sous Windows, ["cmd", "/ S", "/ C"]. L'instruction Shell doit être écrite sous forme JSON dans un fichier Docker.
L'instruction Shell est particulièrement utile sous Windows, où il existe deux shell natifs couramment utilisés et assez différents: cmd et powershell, ainsi que d'autres shells disponibles, y compris sh.
L'instruction Shell peut apparaître plusieurs fois. Chaque instruction Shell remplace toutes les instructions précédentes du shell et affecte toutes les instructions suivantes. Par exemple:
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
Les instructions suivantes peuvent être affectées par l'instruction Shell lorsque leur forme Shell est utilisée dans un fichier Docker: RUN, CMD et ENTRYPOINT.
L'exemple suivant est un modèle commun trouvé sur Windows qui peut être simplifié à l'aide de l'instruction Shell:
... RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt" ...
La commande appelée par docker sera:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
Ceci est inefficace pour deux raisons. Tout d'abord, un processeur de commande cmd.exe non nécessaire (appelé Shell) est appelé. Deuxièmement, chaque instruction RUN sous la forme Shell nécessite une commande powershell supplémentaire préfixant la commande.
Pour rendre cela plus efficace, un des deux mécanismes peut être utilisé. La première consiste à utiliser le formulaire JSON de la commande RUN, tel que:
... RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""] ...
Bien que le formulaire JSON soit sans ambiguïté et n'utilise pas le cmd.exe non nécessaire, il nécessite davantage de verbosité via des guillemets doubles et des échappements. Le mécanisme alternatif consiste à utiliser l'instruction Shell et la forme Shell, rendant ainsi la syntaxe plus naturelle pour les utilisateurs Windows, en particulier lorsqu'elle est combinée à la directive d'échappement Parser:
# 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'
Résultant en:
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>
L'instruction Shell peut également être utilisée pour modifier le fonctionnement d'un shell. Par exemple, à l'aide de Shell cmd/S/C/V: ON | OFF sous Windows, la sémantique d'expansion des variables d'environnement différée peut être modifiée.
L'instruction Shell peut également être utilisée sous Linux si un autre shell, tel que zsh, csh, tcsh et autres, est requis.
La fonctionnalité Shell a été ajoutée dans Docker 1.12.
Selon https://docs.docker.com/engine/reference/builder/#run , le shell [Linux] par défaut pour RUN
est /bin/sh -c
. Vous semblez attendre des bashismes, vous devez donc utiliser le "formulaire" de RUN
pour spécifier votre shell.
RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
Sinon, l'utilisation du "formulaire Shell" de RUN et la spécification d'un shell différent génèrent des shells imbriqués.
# 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"]
Si vous avez plusieurs commandes nécessitant un autre shell, vous devez lire https://docs.docker.com/engine/reference/builder/#Shell et modifier votre shell par défaut en le plaçant avant. vos commandes RUN:
Shell ["/bin/bash", "-c"]
Enfin, si vous avez placé quoi que ce soit dans le fichier .bashrc
de l'utilisateur root dont vous avez besoin, vous pouvez ajouter l'indicateur -l
à la commande Shell
ou RUN
pour en faire un login. Shell et assurez-vous qu'il est recherché.
Remarque: j'ai volontairement ignoré le fait qu'il est inutile de créer un script en tant que commande unique d'un RUN.
Selon la documentation de Docker
Pour utiliser un autre shell, autre que "/ bin/sh", utilisez le formulaire exec en passant dans le shell souhaité. Par exemple,
RUN ["/bin/bash", "-c", "echo hello"]
J'ai également eu des problèmes pour exécuter source
dans un fichier Docker
Cela fonctionne parfaitement pour la construction du conteneur CentOS 6.6 Docker, mais donne des problèmes dans les conteneurs Debian
RUN cd ansible && source ./hacking/env-setup
C’est ainsi que j’ai abordé le sujet, ce n’est peut-être pas une façon élégante mais c’est ce qui a fonctionné pour moi
RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
Cela peut être dû au fait que source
est une commande intégrée à bash plutôt qu'un fichier binaire situé quelque part sur le système de fichiers. Votre intention est-elle que le script que vous sourcez modifie le conteneur par la suite?
Vous voudrez peut-être exécuter bash -v
pour voir ce qui est recherché.
Je voudrais faire ce qui suit au lieu de jouer avec des liens symboliques:
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
J'ai fini par mettre mes fichiers env dans .profile
et muté Shell
quelque chose comme
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
Si vous essayez simplement d’utiliser pip pour installer quelque chose dans virtualenv, vous pouvez modifier l’environnement PATH pour qu’il cherche d’abord dans le dossier bin de virtualenv.
ENV PATH="/path/to/venv/bin:${PATH}"
Ensuite, toutes les commandes pip install
qui suivent le fichier Docker trouveront d’abord/path/to/venv/bin/pip et l’utiliseront, ce qui l’installera dans ce virtualenv et non dans le python système.
Si vous avez Shell
disponible, vous devriez utiliser cette réponse - ne pas utiliser celui qui est accepté, ce qui vous oblige à mettre le reste du fichier docker dans une commande par ce commentaire .
Si vous utilisez une ancienne version de Docker et que vous n'avez pas accès à Shell
, cela fonctionnera tant que vous n'avez besoin de rien de .bashrc
(ce qui est un cas rare dans Dockerfiles):
ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]
Notez que -i
est nécessaire pour que bash lise le fichier rcfile.