docker-compose的"up"和"run"命令产生不同的挂载卷。

3

更新2:我已经在Git上创建了一个样例项目来复现这个问题。经过进一步测试,测试用例与我在原帖中描述的略有不同。

以下是我在github仓库中编写的README内容。


使用案例

  • 一个简单的Node.js项目,带有Dockerfile
  • 一个本地NPM依赖项,由上述项目使用(通过Dockerfile复制到容器中)。 该项目通过本地路径引用该依赖项。
  • Node.js项目有一个Web路由(/),打印出其package.json中本地npm依赖项的版本。这用于验证测试用例过程的结果。
  • docker-compose使用此卷技术将主机机器的源树覆盖在容器的源树之上,然后将容器中的node_modules覆盖在第一个卷之上。

重现步骤

  1. 克隆this repo
  2. 通过docker rmdocker rmi清理与此repo项目相关的任何先前的容器和镜像。
  3. 检出test2_run1标签。该状态表示使用本地NPM依赖项1.0.0版本的项目。
  4. 执行docker-compose build。如果正确执行了第2步,则所有步骤都应在不使用缓存的情况下运行。 在npm install命令期间注意本地NPM依赖项的版本,例如+-- my-npm@1.0.0
  5. 执行docker-compose up。浏览到http://localhost:8000。页面应报告版本1.0.0
  6. 停止正在运行的容器。(在发出up命令的终端上按Ctrl-C。)
  7. 检出test2_run2标签。这会向NPM的index.js文件引入a small change,并将其package.json的版本提升为1.0.1
  8. 执行docker-compose build。只有到COPY ./my-npm ...指令的指令应使用缓存。(例如,docker输出打印---> Using cache用于该指令。)所有后续步骤都应由docker运行。这是因为步骤7引入的NPM软件包中的更改应该已经使COPY ./my-npm ...命令的缓存无效,从而导致后续步骤也无效。确认在npm install命令期间,新版本的NPM会在摘要树输出中打印出来,例如+-- my-npm@1.0.1
  9. 执行docker-compose up。浏览到http://localhost:8000。页面应报告版本1.0.1
期望行为:第9步的页面应报告1.0.1。也就是说,本地npm的更改应通过docker-compose up在容器中反映出来。
实际行为:第9步的页面报告1.0.0请注意,Docker本身正在按预期重新构建镜像。观察到的问题不是Docker正在重用缓存的镜像,因为输出显示它重新运行NPM安装并显示本地NPM依赖项的新版本。问题在于docker-compose没有看到组成dctest_service1容器的底层镜像已更新。
事实上,在容器中运行bash可以让我们看到容器具有更新的my-npm模块文件,但node_modules版本已经过时:

# docker exec -it dctest_service1_1 bash app@6bf2671b75c6:~/service1$ grep version my-npm/package.json node_modules/my-npm/package.json my-npm/package.json: "version": "1.0.1", node_modules/my-npm/package.json: "version": "1.0.0" app@6bf2671b75c6:~/service1$

解决方法:使用docker rm删除dctest_service1容器。然后重新运行docker-compose up,这将使用现有镜像重新创建容器。值得注意的是,在此步骤中不会重新构建任何底层镜像。在重新创建容器时,docker-compose似乎会找到使用更新的node_modules的新卷。

请参阅output目录,了解第一次运行(步骤4和5)和第二次运行(步骤8和9)期间打印的输出。


原始帖子

我有一个基于这个教程(“在Docker中构建Node应用程序的经验教训”)nodejsDockerfile。特别注意,此教程使用卷技巧将node_modules目录从容器本身挂载到与主机上等效的目录上方。例如:

volumes:
 - .:/home/app/my-app
 - /home/app/my-app/node_modules

我遇到了一个问题,即更新 package.json 会触发 npm install(而不是使用 docker 缓存),但是当使用 docker-compose up 启动服务时,生成的容器以某种方式具有旧版本的 node_modules 数据,因为新添加的 NPM 包在目录中丢失。然而,如果通过 docker-compose run --rm 手动运行指定的 CMD,则可以看到已更新的卷! 我可以通过几种方式进行确认:
node_modules 时间戳
通过 "up" 启动的容器:
app@88614c5599b6:~/my-app$ ls -l
...
drwxr-xr-x 743 app app 28672 Dec 12 16:41 node_modules

通过"run"启动的容器:

app@bdcbfb37b4ba:~/my-app$ ls -l
...
drwxr-xr-x 737 app app 28672 Jan  9 02:25 node_modules

不同的docker inspect "Mount"条目ID

通过"up"启动的容器:

"Name": "180b82101433ab159a46ec1dd0edb9673bcaf09c98e7481aed7a32a87a94e76a",
"Source": "/var/lib/docker/volumes/180b82101433ab159a46ec1dd0edb9673bcaf09c98e7481aed7a32a87a94e76a/_data",
"Destination": "/home/app/my-app/node_modules",

通过 "run" 启动的容器:

"Name": "8eb7454fb976830c389b54f9480b1128ab15de14ca0b167df8f3ce880fb55720",
"Source": "/var/lib/docker/volumes/8eb7454fb976830c389b54f9480b1128ab15de14ca0b167df8f3ce880fb55720/_data",
"Destination": "/home/app/my-app/node_modules",

HostConfig -> Binds

我不确定这是否相关,但我也注意到(同样在docker inspect中),HostConfig下的Binds部分在两种情况下有所不同:

通过"up"启动的容器:

"Binds": [
   "180b82101433ab159a46ec1dd0edb9673bcaf09c98e7481aed7a32a87a94e76a:/home/app/my-app/node_modules:rw",
  "/Volumes/my-mount/my-app:/home/app/my-app:rw"
],

通过 "run" 启动的容器:

"Binds": [
  "/Volumes/my-mount/my-app:/home/app/my-app:rw"
],

两者都显示了主机源挂载在镜像上,但只有"up"显示了具有node_modules的辅助叠加卷,这似乎是另一个奇怪的问题。

理论

根据docker-compose CLI reference:

如果服务已经存在容器,并且在容器创建后更改了服务的配置或映像,则docker-compose up通过停止和重新创建容器来获取更改

因此,看起来docker-compose up认为配置或映像没有更改。我不确定如何调试以确认。当然,我可以使用--force-recreate来解决这个问题,但我希望解决导致问题的配置错误。

更新: 如果在docker-compose up之前进行显式的docker-compose build,问题仍然存在。因此,我目前对这个理论的信心降低了。

这是整个Dockefile

FROM node:6.9.1

RUN useradd --user-group --create-home --shell /bin/false app

ENV HOME=/home/app
ENV APP=my-app
ENV NPM_CONFIG_LOGLEVEL warn

RUN npm install --global gulp-cli

COPY ./package.json $HOME/$APP/package.json
RUN chown -R app:app $HOME/*

USER app
WORKDIR $HOME/$APP
RUN npm install && npm cache clean

USER root
COPY . $HOME/$APP
RUN chown -R app:app $HOME/* && chmod u+x ./node_modules/.bin/* ./bin/*

USER app

ENTRYPOINT ["/home/app/my-app/bin/entrypoint.sh"]
CMD ["npm", "run", "start:dev"]

你能否也发布一下你的 docker-compose.yml 文件?这样我们就可以将该文件与你的 Dockerfile 进行比较。请参见我下面的回答。 - Pitt
我仔细查看了您提供的教程,如果您的docker-compose.yml文件使用指令build: .,那么它意味着您的Docker容器不仅使用预先构建的镜像,还会构建当前目录下的内容。因此,如果您更改了某些内容,则必须在运行docker-compose run之前运行docker-compose build,否则将使用旧的缓存构建。 - Pitt
另外,如果您只想使用一个命令,可以使用 docker-compose up --build。要真正了解正在发生的事情,您需要发布您使用的完整 docker run 命令,以及您想要运行的服务所定义的 docker-compose 命令和 docker-compose.yml 文件。 - Pitt
@Pitt,感谢你在这里提供帮助,但是让我试着建立一个更简单的项目来帮助调试。 - twelve17
@Pitt 我正在使用 docker-compose up。如果我使用 docker-compose run,它可以工作,因为它会创建一个新的容器并使用正确的镜像。我已经更新了帖子,并提供了一个示例项目的链接和说明以便复现问题。我想我也会尝试联系 Docker 开发人员寻求帮助。 - twelve17
显示剩余3条评论
1个回答

2

命令docker-compose使用docker-compose.yml文件作为容器化service的配置文件。默认情况下,它会在运行docker-compose的目录中查找该.yml配置文件。 因此,当您运行docker-compose up时,您的docker-compose.yml文件可能已经过期,导致其中一个卷未被挂载。

docker run忽略docker-compose.yml文件,只使用Dockerimage构建容器。因此,Dockerimage文件和docker-compose.yml文件之间可能存在配置差异。


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