我对Docker很新,我想在我的计算机上映射node_modules文件夹(用于调试目的)。
这是我的docker-compose.yml
web:
build: .
ports:
- "3000:3000"
links:
- db
environment:
PORT: 3000
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
db:
image: mongo:3.3
ports:
- "27017:27017"
command: "--smallfiles --logpath=/dev/null"
我使用Docker for Mac。当我运行docker-compose up -d
时,一切正常,但它在我的计算机上创建了一个node_modules文件夹,但它是空的。我进入容器的bash和ls node_modules,所有包都在那里。
如何在计算机上获取容器上的内容?
谢谢
答案 0 :(得分:10)
TL; DR工作示例,克隆并尝试: https://github.com/xbx/base-server
您需要在计算机中使用node_modules(外部图像)以便首先进行调试(在运行容器之前)。
如果您只想调试node_modules:
volumes:
- /path/to/node_modules:/usr/src/app/node_modules
如果要调试代码和node_modules:
volumes:
- .:/usr/src/app/
请记住,您需要在容器外至少运行一次npm install
(或复制docker build
生成的npm_modules)。如果您需要更多详细信息,请立即告诉我。
修改的。因此,在OSX中不需要npm,您可以:
docker build
然后docker cp <container-id>:/path/to/node-modules ./local-node-modules/
。然后在您的docker-compose.yml中安装这些文件并麻烦您想要的任何内容。docker build
和那里(Dockerfile)在另一个目录中执行npm install
。然后在你的命令(CMD或docker-compose命令)中将副本(cp
)复制到正确的目录,但是这个目录从你的计算机(docker-compose.yml中的一个卷)中挂起,然后麻烦无论你想要什么。修改2.(选项2)工作示例,克隆并尝试: https://github.com/xbx/base-server 我在你的这个仓库里自动完成了这一切。
Dockerfile
FROM node:6.3
# Install app dependencies
RUN mkdir /build-dir
WORKDIR /build-dir
COPY package.json /build-dir
RUN npm install -g babel babel-runtime babel-register mocha nodemon
RUN npm install
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN ln -s /build-dir/node_modules node_modules
# Bundle app source
COPY . /usr/src/app
EXPOSE 1234
CMD [ "npm", "start" ]
搬运工-compose.yml
web:
build: .
ports:
- "1234:1234"
links:
- db # liaison avec la DB
environment:
PORT: 1234
command: /command.sh
volumes:
- ./src/:/usr/src/app/src/
- ./node_modules:/usr/src/app/node_modules
- ./command.sh:/command.sh
db:
image: mongo:3.3
ports:
- "27017:27017"
command: "--smallfiles --logpath=/dev/null"
command.sh
#!/bin/bash
cp -r /build-dir/node_modules/ /usr/src/app/
exec npm start
请克隆我的回购并执行docker-compose up
。它做你想要的。
PS:可以改进以更好的方式做同样的事情(即最佳实践等)
我在OSX,它对我有用。
答案 1 :(得分:5)
首先,有一个操作顺序。构建映像时,不会装入卷,只有在运行容器时才会装入卷。因此,当您完成构建时,所有更改将仅存在于图像内,而不是存在于任何卷中。如果在目录上安装卷,它会覆盖该位置图像中的任何内容,从视图中隐藏这些内容(有一个初始化异常,请参见下文)。
接下来是卷语法:
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
告诉docker-compose从当前目录创建一个主机卷到容器内的/usr/src/app
,然后将/usr/src/app/node_modules
映射到由docker维护的匿名卷。后者将显示为docker volume ls
中的一个卷,其长字符串相对无用。
要将/usr/src/app/node_modules
映射到主机上的文件夹,您需要在上面的行中包含一个文件夹名称和冒号。例如。 /host/dir/node_modules:/usr/src/app/node_modules
。
命名卷与主机卷略有不同,其中docker使用您在docker volume ls
中可以看到的名称维护它们。您只使用名称而不是路径来引用这些卷。所以node_modules:/usr/src/app/node_modules
会创建一个名为node_modules
的卷,您可以将其装入一个只有该名称的容器中。
我分歧来描述命名卷,因为它们带有一个功能,变成了主机卷的问题。 Docker通过使用该位置的图像内容初始化命名卷来帮助您解决命名卷。因此,在上面的示例中,如果命名卷node_modules
为空(或新),它将首先将/ usr / src / app / node_modules`中的图像内容复制到此卷,然后将其挂载到您的卷中容器。
使用主机卷,您将永远不会看到任何初始化,无论是什么 您在容器中看到的就是位置,甚至是空目录。无法从该目录位置的映像获取内容,以便首先复制到该位置的主机卷。这也意味着容器内所需的目录权限不会自动继承,您需要手动设置将在容器内工作的主机目录的权限。
最后,有一个针对Windows和Mac的docker的小问题,它们在VM内部运行,并且您的主机卷已安装到VM。要将卷装入主机,必须将应用程序配置为将主机中的文件夹共享到VM,然后将VM中的卷装入容器中。默认情况下,在Mac上,包含/ Users文件夹,但如果您使用其他目录,例如a / Projects目录,甚至是小写/用户(unix和bsd区分大小写),您将无法在容器内看到Mac中的内容。
有了这些基础知识,一种可能的解决方案是重新设计工作流程,以便将图像中的目录内容复制到主机。首先,您需要将文件复制到图像内的其他位置。然后,您需要将保存的映像位置中的文件复制到容器启动时的卷装入位置。当您执行后者时,您应该注意到您正在破坏拥有卷(持久性)的目的,并且可能需要考虑添加一些逻辑以便在运行副本时更具选择性。首先,将一个entrypoint.sh添加到您的构建中,如下所示:
#!/bin/sh
# copy from the image backup location to the volume mount
cp -a /usr/src/app_backup/node_modules/* /usr/src/app/node_modules/
# this next line runs the docker command
exec "$@"
然后更新您的Dockerfile以包含入口点和备份命令:
FROM node:6.3
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install -g babel babel-runtime babel-register mocha nodemon
RUN npm install
# Bundle app source
COPY . /usr/src/app
RUN cp -a /usr/src/app/. /usr/src/app_backup
EXPOSE 1234
ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ]
CMD [ "npm", "start" ]
然后从docker-compose.yml中删除额外的音量:
volumes:
- .:/usr/src/app
答案 2 :(得分:1)
我在@Robert's answer上加上了一些内容,因为其中有些事情没有考虑在内。即:
cp
花费的时间太长,用户无法查看进度。node_modules
是通过主机安装的,我希望将其覆盖。git pull
处于运行状态并且不运行容器,并相应地更新node_modules
。为解决第一个问题,我在映像上安装了rsync
以及pv
(因为我也想在删除时查看进度)。由于我使用高山,所以在apk add
中使用了Dockerfile
:
# Install rsync and pv to view progress of moving and deletion of node_modules onto host volume.
RUN apk add rsync && apk add pv
然后我将entrypoint.sh
更改为如下形式(可以将yarn.lock
替换为package-lock.json
)
#!/bin/ash
# Declaring variables.
buildDir=/home/node/build-dir
workDir=/home/node/work-dir
package=package.json
lock=yarn.lock
nm=node_modules
#########################
# Begin Functions
#########################
copy_modules () { # Copy all files of build directory to that of the working directory.
echo "Calculating build folder size..."
buildFolderSize=$( du -a $buildDir/$nm | wc -l )
echo "Copying files from build directory to working directory..."
rsync -avI $buildDir/$nm/. $workDir/$nm/ | pv -lfpes "$buildFolderSize" > /dev/null
echo "Creating flag to indicate $nm is in sync..."
touch $workDir/$nm/.docked # Docked file is a flag that tells the files were copied already from the build directory.
}
delete_modules () { # Delete old module files.
echo "Calculating incompatible $1 direcotry $nm folder size..."
folderSize=$( du -a $2/$nm | wc -l )
echo "Deleting incompatible $1 directory $nm folder..."
rm -rfv $2/$nm/* | pv -lfpes "$folderSize" > /dev/null # Delete all files in node_modules.
rm -rf $2/$nm/.* 2> /dev/null # Delete all hidden files in node_modules.node_modules.
}
#########################
# End Functions
# Begin Script
#########################
if cmp -s $buildDir/$lock $workDir/$lock >/dev/null 2>&1 # Compare lock files.
then
# Delete old modules.
delete_modules "build" "$buildDir"
# Remove old build package.
rm -rf $buildDir/$package 2> /dev/null
rm -rf $buildDir/$lock 2> /dev/null
# Copy package.json from working directory to build directory.
rsync --info=progress2 $workDir/$package $buildDir/$package
rsync --info=progress2 $workDir/$lock $buildDir/$lock
cd $buildDir/ || return
yarn
delete_modules "working" "$workDir"
copy_modules
# Check if the directory is empty, as it is when it is mounted for the first time.
elif [ -z "$(ls -A $workDir/$nm)" ]
then
copy_modules
elif [ ! -f "$workDir/$nm/.docked" ] # Check if modules were copied from build directory.
then
# Delete old modules.
delete_modules "working" "$workDir"
# Copy modules from build directory to working directory.
copy_modules
else
echo "The node_modules folder is good to go; skipping copying."
fi
#########################
# End Script
#########################
if [ "$1" != "git" ] # Check if script was not run by git-merge hook.
then
# Change to working directory.
cd $workDir/ || return
# Run yarn start command to start development.
exec yarn start:debug
fi
我添加pv
至少是向用户显示正在发生的事情的进度。另外,我添加了一个标志,以表明已通过容器安装了node_modules
。
无论何时安装软件包,我都会利用postinstall
文件的postuninstall
和package.json
钩子从工作目录中复制package.json
和yarn.lock
文件目录到构建目录,以使其保持最新状态。我还安装了postinstall-postinstall
软件包,以确保postuninstall
挂钩正常工作。
"postinstall" : "if test $DOCKER_FLAG = 1; then rsync -I --info=progress2 /home/node/work-dir/package.json /home/node/build-dir/package.json && rsync -I --info=progress2 /home/node/work-dir/yarn.lock /home/node/build-dir/yarn.lock && echo 'Build directory files updated.' && touch /home/node/work-dir/node_modules/.docked; else rm -rf ./node_modules/.docked && echo 'Warning: files installed outside container; deleting docker flag file.'; fi",
"postuninstall": "if test $DOCKER_FLAG = 1; then rsync -I --info=progress2 /home/node/work-dir/package.json /home/node/build-dir/package.json && rsync -I --info=progress2 /home/node/work-dir/yarn.lock /home/node/build-dir/yarn.lock && echo 'Build directory files updated.' && touch /home/node/work-dir/node_modules/.docked; else rm -rf ./node_modules/.docked && echo 'Warning: files installed outside container; deleting docker flag file.'; fi",
我使用了一个名为DOCKER_FLAG
的环境变量,并将其设置为1
文件中的docker-compose.yml
。这样,当有人将其安装在容器外部时,它将不会运行。另外,我确保删除了.docked
标志文件,以便脚本知道已使用主机命令安装了该文件。
对于每次发生拉动时都同步node_modules
的问题,我使用了git hook;即post-merge钩子。每次我拉时,如果容器正在运行,它将尝试运行entrypoint.sh
脚本。它还将给脚本git
提供一个参数,脚本检查该脚本是否运行exec yarn:debug
,因为容器已在运行。这是我在.git/hooks/post-merge
处的脚本:
#!/bin/bash
if [ -x "$(command -v docker)" ] && [ "$(docker ps -a | grep <container_name>)" ]
then
exec docker exec <container_name> sh -c "/home/node/build-dir/entrypoint.sh git"
exit 1
fi
如果容器未运行,并且我获取了更改,那么entrypoint.sh
脚本将首先检查锁定文件之间是否存在任何差异,如果存在,它将重新安装在build目录中,并执行创建图像和首次运行容器时的操作。这个tutorial可以用来与队友共享钩子。
注意:请务必使用docker-compose run...
,因为docker-compose up...
不会显示进度指示器。
答案 3 :(得分:1)
使用 Docker Compose 和带有 Volume 的本地 Bind Mount 驱动程序将 node_modules 卷配置为使用本地 node_modules 目录作为其存储位置。
首先,将您的 node_modules 卷添加到您的服务中:
ui:
volumes:
- node_modules:/path/to/node_modules
然后,在命名卷部分配置卷:
volumes:
node_modules:
driver: local
driver_opts:
type: none
o: bind
device: ./local/path/to/node_modules
只要确保您始终在 Docker 容器内进行 node_module 更改,它将完美同步并可在主机上用于 IDE、代码完成、调试等。
答案 4 :(得分:0)
变化:
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
以强>
volumes:
- .:/usr/src/app
它会将node_modules放在本地映射的卷中。您拥有它的方式,/usr/src/app/node_modules
将存储在docker inspect {container-name}
找到位置所需的不同卷中。如果您确实要指定位置,请将其指定为:
- /path/to/my_node_modules:/usr/src/app/node_modules