更新2:我已经在Git上创建了一个样例项目来复现这个问题。经过进一步测试,测试用例与我在原帖中描述的略有不同。
以下是我在github仓库中编写的README内容。
使用案例
- 一个简单的Node.js项目,带有
Dockerfile
。 - 一个本地NPM依赖项,由上述项目使用(通过Dockerfile复制到容器中)。 该项目通过本地路径引用该依赖项。
- Node.js项目有一个Web路由(
/
),打印出其package.json
中本地npm依赖项的版本。这用于验证测试用例过程的结果。 docker-compose
使用此卷技术将主机机器的源树覆盖在容器的源树之上,然后将容器中的node_modules
覆盖在第一个卷之上。
重现步骤
- 克隆this repo。
- 通过
docker rm
和docker rmi
清理与此repo项目相关的任何先前的容器和镜像。 - 检出
test2_run1
标签。该状态表示使用本地NPM依赖项1.0.0版本的项目。 - 执行
docker-compose build
。如果正确执行了第2步,则所有步骤都应在不使用缓存的情况下运行。 在npm install
命令期间注意本地NPM依赖项的版本,例如+-- my-npm@1.0.0
。 - 执行
docker-compose up
。浏览到http://localhost:8000
。页面应报告版本1.0.0
。 - 停止正在运行的容器。(在发出
up
命令的终端上按Ctrl-C。) - 检出
test2_run2
标签。这会向NPM的index.js
文件引入a small change,并将其package.json
的版本提升为1.0.1
。 - 执行
docker-compose build
。只有到COPY ./my-npm ...
指令的指令应使用缓存。(例如,docker输出打印---> Using cache
用于该指令。)所有后续步骤都应由docker运行。这是因为步骤7引入的NPM软件包中的更改应该已经使COPY ./my-npm ...
命令的缓存无效,从而导致后续步骤也无效。确认在npm install
命令期间,新版本的NPM会在摘要树输出中打印出来,例如+-- my-npm@1.0.1
。 - 执行
docker-compose up
。浏览到http://localhost:8000
。页面应报告版本1.0.1
。
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应用程序的经验教训”)的nodejs
Dockerfile
。特别注意,此教程使用卷技巧将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
进行比较。请参见我下面的回答。 - Pittbuild: .
,那么它意味着您的Docker容器不仅使用预先构建的镜像,还会构建当前目录下的内容。因此,如果您更改了某些内容,则必须在运行docker-compose run
之前运行docker-compose build
,否则将使用旧的缓存构建。 - Pittdocker-compose up --build
。要真正了解正在发生的事情,您需要发布您使用的完整docker run
命令,以及您想要运行的服务所定义的docker-compose
命令和docker-compose.yml
文件。 - Pittdocker-compose up
。如果我使用docker-compose run
,它可以工作,因为它会创建一个新的容器并使用正确的镜像。我已经更新了帖子,并提供了一个示例项目的链接和说明以便复现问题。我想我也会尝试联系 Docker 开发人员寻求帮助。 - twelve17