在Dockerfile脚本中source脚本

我在构建 Debian镜像(tini进程管理器) 时,尝试使用 RVM 方式来安装一个ruby运行环境。简单来说,在通常服务器上执行以下命令来使用 rvm :

安装rvm,并通过rvm安装ruby和RoR
gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | bash -s stable
source /home/admin/.rvm/scripts/rvm
rvm install 3.3.4
gem install rails

当我将上述命令嵌入到 Dockerfile :

最初直接使用rvm安装命令嵌入到Dockerfile
...
RUN gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
RUN curl -sSL https://get.rvm.io | bash -s stable
RUN source /home/admin/.rvm/scripts/rvm 
RUN rvm install 3.3.4
RUN gem install rails
...

Dockerfile使用 /bin/sh 执行

首先遇到的错误是执行 source 指令报错:

/bin/sh 不支持 source (bash) 内置指令
 => ERROR [26/35] RUN source /home/admin/.rvm/scripts/rvm       0.2s 
------
 > [26/35] RUN source /home/admin/.rvm/scripts/rvm:
0.165 /bin/sh: 1: source: not found
------
Dockerfile:51
--------------------
  49 |     RUN gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
  50 |     RUN curl -sSL https://get.rvm.io | bash -s stable
  51 | >>> RUN source /home/admin/.rvm/scripts/rvm
  52 |     RUN rvm install 3.3.4
  53 |     RUN gem install rails
--------------------
ERROR: failed to solve: process "/bin/sh -c source /home/admin/.rvm/scripts/rvm" did not complete successfully: exit code: 127

原因是Dockerfile是使用 /bin/sh 来执行的,这个 sh SHELL不支持 BASH 中常用的内置指令 source ,需要改成 .

不过后面又遇到一个问题 rvm 脚本需要 bashbuiltin 工具,使用 /bin/sh 会报错。见下文

Dockerfile每个 RUN 命令都是一个独立的容器

接下来的报错是,即使使用了 . 来引入 rvm 脚本,但是在下一个命令中使用 rvm 却找不到指令:

由于Dockerfile每个RUN命令都是一个容器,所以下一步命令找不到 rvm
 => [26/35] RUN . /home/admin/.rvm/scripts/rvm
 => ERROR [27/35] RUN rvm install 3.3.4
------
 > [27/35] RUN rvm install 3.3.4:
0.162 /bin/sh: 1: rvm: not found
------
Dockerfile:53
--------------------
  51 |     # /bin/sh 不支持source,需要使用 .
  52 |     RUN . /home/admin/.rvm/scripts/rvm
  53 | >>> RUN rvm install 3.3.4
  54 |     RUN gem install rails
  55 |     # python program: virtualenv
--------------------
ERROR: failed to solve: process "/bin/sh -c rvm install 3.3.4" did not complete successfully: exit code: 127

原因是每个 RUN 命令实际上是一个容器,所以下一个命令是无法看到上一个命令的引入环境变量或者脚本的

尝试 . 引入环境和运行指令在一行 RUN

我尝试改动了一下Dockerfile:

. 引入脚本和后续执行命令合并成一行
...
RUN . /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails
...

这样解决问题了么?

还有报错:

出现新的报错显示 rvm 脚本执行时没有找到 buildin
 => ERROR [26/29] RUN . /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails              0.2s
------
 > [26/29] RUN . /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails:
0.153 /bin/sh: 10: /home/admin/.rvm/scripts/rvm: builtin: not found
0.153 /bin/sh: 1: rvm: not found
------
Dockerfile:52
--------------------
  50 |     RUN curl -sSL https://get.rvm.io | bash -s stable
  51 |     # /bin/sh 不支持source,需要使用 . 并且要直接执行rvm,否则每次RUN重新进入/bin/sh会丢失环境
  52 | >>> RUN . /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails
  53 |     # python program: virtualenv
  54 |     RUN python3 -m venv venv3
--------------------
ERROR: failed to solve: process "/bin/sh -c . /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails" did not complete successfully: exit code: 127

这里可以看到BASH内置的 buildin 工具不能被 /bin/sh 支持

解决之道: 使用 bash -c

既然 RVM 必须使用BASH,而且BASH又可以使用习惯的 source 指令,那么最终的解决方法是:

  • 在 RUN 中使用 bash -c 'xxx && yyy && zzz' 来完成BASH运行环境

  • 并且在一行中使用多个命令,确保共享了同一个运行SHELL环境

改进后验证成功的Dockerfile:

最终改进成一行运行多个指令,并且使用 bash -c 运行确保环境共享
...
USER admin

# ruby program: rvm install ruby in $HOME
RUN gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
RUN curl -sSL https://get.rvm.io | bash -s stable
# /bin/sh 不支持source,需要使用 . 
# 每个RUN都是一个容器,所以为了继承环境变量来运行多个命令,将这些命令写成一行RUN
# rvm需要使用BASH,所以最终改成 bash -c 'xxx && yyy && zzz'
RUN bash -c 'source /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails'

# python program: virtualenv
RUN bash -c 'cd /home/admin && python3 -m venv venv3'
RUN bash -c 'source /home/admin/venv3/bin/activate && pip install sphinx && pip install sphinx_rtd_theme && pip install sphinxnotes-strike'
RUN bash -c 'source /home/admin/venv3/bin/activate && pip install sphinxcontrib-video && pip install sphinxcontrib-youtube'

参考