Grails 监视文件在 Vagrant 虚拟机中运行的 Docker 容器中不起作用
Posted
技术标签:
【中文标题】Grails 监视文件在 Vagrant 虚拟机中运行的 Docker 容器中不起作用【英文标题】:Grails watch files doesn't work inside Docker container running inside a Vagrant virtual machine 【发布时间】:2014-10-16 13:49:00 【问题描述】:我有一个相当嵌套的结构:
-
MacOSX 工作站运行...
带有
ubuntu/trusty64
的Vagrant VirtualBox 虚拟机运行...
Docker 容器正在运行...
我用 Grails 编写的应用程序
每个层的配置方式都是共享上一层文件系统的一部分。这样:
Vagrant,在Vagrantfile
中带有 config.vm.synced_folder
指令
Docker,在Dockerfile
中使用-v
类似switch 的命令和VOLUME
指令
这样我可以在我的工作站上进行开发,并且底部的 Grails 应用程序应该(理想情况下)检测更改并即时重新编译/重新加载。当我直接在 MacOSX 上运行同一个应用程序时,这个功能曾经可以工作,但现在 grails 似乎完全不知道文件更改。当然,如果我使用编辑器(在 Docker 容器内)打开文件,它们确实会发生变化,事实上,如果我停止/重新启动 grails 应用程序,则会使用新代码。
我不知道 grails 如何实现监视策略,但如果它依赖于某些操作系统级别的功能,我怀疑文件更改通知会在链中的某个位置丢失。
任何人都知道可能是什么原因和/或我该如何进行调试?
【问题讨论】:
您是否尝试过从 docker 容器中修改文件并查看它是否正确重新加载? 您是在 Docker 上以生产模式还是开发模式运行? @DavidC 我不确定。如何查看我运行的是什么模式? 如果您在 IDE 中或在命令行上以grails runApp
运行,那么默认应该是开发模式,并且它应该获取文件更改。但是,如果您有任何机会使用生产模式,它不会,但由于您不确定您似乎处于开发模式。
就像我指定的那样,我在 Docker 容器中运行它,而不是在 IDE 中。我用一个简单的grails runApp
运行它。顺便说一句,我的直觉告诉我,问题取决于 NFS 共享而不是传播更改事件,就像@roy-willemse 指出的那样。
【参考方案1】:
有两种方法可以检测文件更改(据我所知):
轮询,即每隔一定时间检查文件夹中所有文件的时间戳。进行“近乎即时”的更改检测需要非常短的时间间隔。这是 CPU 和磁盘密集型的。
OS 事件(Linux 上的 inotify,OS X 上的 FSEvents),因为文件操作通过 OS 子系统,所以可以检测到更改。这在 CPU 和磁盘上很容易。
网络文件系统 (NFS) 等不会生成事件。由于文件更改不通过客户操作系统子系统,操作系统不知道更改;只有进行更改的操作系统 (OS X) 知道它们。
Grails 和许多其他 File Watcher 工具依赖于 FSEvents 或 inotify(或类似)事件。
那该怎么办?考虑到可能产生的流量,在正常情况下将 NFS 更改从主机“广播”到所有来宾是不切实际的。但是,我认为 VirtualBox 共享应该算作一个特殊例外......
弥合这一差距的机制可能涉及监视主机的更改并触发来宾同步的过程。
查看这些文章,了解一些有趣的想法和解决方案,涉及某种类型的 rsync 操作:
http://drunomics.com/en/blog/syncd-sync-changes-vagrant-box (Linux) https://github.com/ggreer/fsevents-tools (OS X)
Rsync-ing 到您的来宾 (Docker) 实例上的非 NFS 文件夹具有额外的优势,即 I/O 性能显着提高。 VirtualBox 的共享速度非常缓慢。
更新!
这就是我所做的。首先安装 lsyncd(以 OS X 为例,更多信息在 http://kesar.es/tag/lsyncd/):
brew install lsyncd
在我的 Mac 上的 Vagrant 文件夹中,我创建了文件 lsyncd.lua:
settings
logfile = "./lsyncd.log",
statusFile = "./lsyncd.status",
nodaemon = true,
pidfile = "./lsyncd.pid",
inotifyMode = "CloseWrite or Modify",
sync
default.rsync,
delay = 2,
source = "./demo",
target = "vagrant@localhost:~/demo",
rsync =
binary = "/usr/bin/rsync",
protect_args = false,
archive = true,
compress = false,
whole_file = false,
rsh = "/usr/bin/ssh -p 2222 -o StrictHostKeyChecking=no"
,
这样做的目的是将我的 Vagrant 文件夹中的文件夹 demo
同步到 /home/vagrant/demo
中的来宾操作系统。请注意,您需要使用 SSH 密钥设置登录以使此过程顺畅。
然后,随着 vagrant VM 的运行,我启动了 lsyncd 进程。 -log Exec
是可选的;它将其活动记录到标准输出:
sudo lsyncd lsyncd.lua -log Exec
在 vagrant VM 上,我在同步文件夹中启动了 Grails (2.4.4):
cd /home/vagrant/demo
grails -reloading run-app
回到我在 IntelliJ 中的 Mac 上,我编辑了一个 Controller 类。它几乎立即触发了 lsyncd(延迟 2 秒),然后很快我确认 Grails 重新编译了该类!
总结一下:
在 Mac 上编辑项目文件,在 VM 上执行 使用 lsyncd 将您的更改重新同步到 VM 内的文件夹中 Grails 注意到更改并触发重新加载 不使用 VirtualBox 共享,磁盘性能更快问题: Textmate 触发了一种 lsyncd (尚未)识别的 FSEvent,因此不会检测到更改。不过,Vim 和 IntelliJ 都很好。
希望这对某人有所帮助!我花了一天时间才弄清楚这些东西。
【讨论】:
我正在通过 docker 运行 grails 应用程序,该应用程序可以使用 Atom/TextEdit/Sublime 重新编译,但如果使用 IntelliJ 进行更改,则无法正常工作(加上中断重新编译)。如果我要直接通过 Docker(不在 Vagrant 上)运行但能够通过 IntelliJ 进行更改,我将如何更改 .lua 文件??【参考方案2】:我发现在容器中显示文件系统通知的最佳方法如下:
-
创建两个文件夹,一个用于映射项目,一个用于“镜像”
在第一个文件夹中映射项目
在容器中保持后台脚本运行,将项目文件夹同步到“镜像”
从“镜像”运行项目
这可能不是最有效或最优雅的方式,但这种方式对容器的用户是透明的。无需运行其他脚本。
未在更大的项目中进行测试,但在我的情况下,我没有意识到性能问题。
https://github.com/altieres/docker-jekyll-s3
【讨论】:
【参考方案3】:Vagrant 已经包含了一些 rsync 选项,因此无需在主机上安装特殊程序。
在 Vagrantfile 中,我配置了 rsync:
config.vm.synced_folder ".", "/vagrant", type: "rsync", rsync__exclude: [ "./build", ".git/" ]
然后在命令行(在主机中),我运行:
vagrant rsync
从主机到来宾执行单次同步。
或
vagrant rsync-auto
当检测到主机上的更改时自动运行。
在Vagrant rsync Documentation 和rsync-auto 上查看更多信息
【讨论】:
以上是关于Grails 监视文件在 Vagrant 虚拟机中运行的 Docker 容器中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
xdebug远程调试Vagrant虚拟机中web程序的注意点