在Dockerfile中安装Node.js和npm

4

背景

我有一个 Dockerfile,用于创建一个包含 Apache Web 服务器的镜像。然而,我还希望使用 Dockerfile 构建我的网站,这样构建过程就不会依赖于开发者的本地环境。请注意,Docker 容器仅用于本地开发,而不是生产环境。

问题

我有以下 Dockerfile:

FROM httpd
RUN apt-get update -yq
RUN apt-get -yq install curl gnupg
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
RUN apt-get update -yq
RUN apt-get install -yq \
        dh-autoreconf=19 \
        ruby=1:2.5.* \
        ruby-dev=1:2.5.* \
        nodejs

我来构建它:

sudo docker build --no-cache .

构建成功,以下是部分输出内容:
Step 9/15 : RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
 ---> Running in e6c747221ac0
......
......
......
Removing intermediate container 5a07dd0b1e01
 ---> 6279003c1e80
Successfully built 6279003c1e80

然而,当我使用以下命令在容器中运行该镜像时:

sudo docker container run --rm -it --name=debug 6279003c1e80 /bin/bash

然后,在容器内执行apt-cache policy时,它不会显示应该已添加的curl命令所在的存储库。同时,运行apt-cache policy nodejs时显示安装了旧版本。

但是当我在容器内运行以下命令:

curl -sL https://deb.nodesource.com/setup_12.x | bash
apt-cache policy
apt-cache policy nodejs

它向我展示了已添加存储库,以及可用的新版本nodejs。

那么,为什么在docker文件内使用curl命令时似乎无法正常工作,但手动在容器中从shell执行却可以正常工作?如何解决这个问题?

更新

  • 请注意,为了防止缓存问题,我正在使用--no-cache标志。
  • 我还删除了所有容器并进行了sudo docker system prune,并重新构建了镜像,但没有成功。
  • 我尝试将所有内容捆绑在一个RUN命令中,就像用户"hmm"建议的那样(因为这是apt命令的最佳实践):
RUN apt-get update -yq \
    && apt-get -yq install curl gnupg && \
    && curl -sL https://deb.nodesource.com/setup_12.x | bash \
    && apt-get update -yq \
    && apt-get install -yq \
        dh-autoreconf=19 \
        ruby=1:2.5.* \
        ruby-dev=1:2.5.* \
        nodejs \
    && rm -rf /var/lib/apt/lists/*
1个回答

12

您可能遇到了缓存层的问题。在Dockerfile最佳实践文档中,有一个 较长的部分 介绍了使用apt-get。建议您仔细阅读。

要点是,Docker无法识别第一个和第二个RUN apt-get update之间的区别,也不知道apt-get install依赖于新的apt-get update层。

解决方法是将所有这些组合成一个单一的RUN命令(建议)或在构建过程中禁用缓存(docker build --no-cache)。

RUN apt-get update -yq \
    && apt-get -yq install curl gnupg ca-certificates \
    && curl -L https://deb.nodesource.com/setup_12.x | bash \
    && apt-get update -yq \
    && apt-get install -yq \
        dh-autoreconf=19 \
        ruby=1:2.5.* \
        ruby-dev=1:2.5.* \
        nodejs

编辑:在本地运行您的Dockerfile时,我注意到curl命令没有输出。删除-s标志(静默失败),您可以看到它无法验证服务器的SSL证书而导致失败:

Edit: 运行你的 Dockerfile 时,我发现 curl 命令没有任何输出。移除 -s 标记后(静默失败),您可以看到由于无法验证服务器的 SSL 证书而导致失败:

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

那个问题的解决方案是在运行curl之前安装ca-certificates。我已经更新了上面的RUN命令。


谢谢提供的链接,我会去看一下!不过我认为缓存并不是问题,因为我已经使用了--no-cache标志。我甚至尝试不使用docker-compose,而是使用:sudo docker build --no-cache .但仍然遇到同样的问题。即使在一个RUN语句中运行它,我仍然安装了旧的node版本,并且没有添加存储库。 - Stefan
@Stefan 对不起,我没注意到你已经在使用 --no-cache。请查看我的编辑,看看它是否可以解决问题。 - chash
非常感谢!我没有意识到我在静音输出,我以为它已经成功执行了。我不明白为什么有时候当出现错误(比如使用未安装的shell命令)时docker构建会失败,而其他时候它却可以继续愉快地进行。或者是因为我在curl命令中使用了“-s”选项吗? - Stefan
“-s” 隐藏了错误输出,但 Docker 继续运行的原因是 curl https://<url> | bash 命令的退出状态实际上是 bash 的退出状态(在此情况下为 0)。要由于 curl 的非零退出状态导致管道失败,您需要调整默认 shell 以使用例如 bash 并使用 set -o pipefailSHELL ["/bin/bash", "-o", "pipefail", "-c"] - chash
这个命令在2022年不起作用。 - Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接