重启Play应用程序Docker容器会导致“此应用程序已在运行” - RUNNING_PID未被删除

19

编辑:有一个相关的问题正在GitHub上被讨论,但是在另一种部署模式(Typesafe Activator UI而不是Docker)下。

我试图模拟系统重新启动以验证Docker重启策略,该策略声称能够按正确顺序重新运行容器。

我有一个用Java编写的Play框架应用程序。

Dockerfile看起来像这样:

FROM ubuntu:14.04
#
#  [Java8, ...]
#
RUN chmod +x /opt/bin/playapp
CMD ["/bin/bash"]
我使用$ docker run --restart=always -d --name playappcontainer "./opt/bin/playapp"启动它。
当我执行$ service docker stop && service docker restart并执行$ docker attach playappcontainer时,控制台会提示我:
Play server process ID is 7
This application is already running (Or delete /opt/RUNNING_PID file)

编辑: 当我按照Play文档中建议的方式更改文件位置为 /var/run/play.pid 且使用 -Dpidfile.path=/var/run/play.pid 时,结果相同。

Play server process ID is 7
This application is already running (Or delete /var/run/play.pid file).

所以,为什么当docker守护进程停止、重新启动并重新启动之前运行的容器时,包含RUNNING_PID的文件不会被删除?


当我运行$ docker inspect playappcontainer时,它告诉我:

"State": {
    "ExitCode": 255,
    "FinishedAt": "2015-02-05T17:52:39.150013995Z",
    "Paused": false,
    "Pid": 0,
    "Restarting": true,
    "Running": true,
    "StartedAt": "2015-02-05T17:52:38.479446993Z"
},

虽然:

容器内的主要进程将会接收到 SIGTERM 信号,等待一个宽限期后会被强制杀死(SIGKILL)。

引自 $ docker stop 的 Docker 参考文档

为了停止运行中的 Play 服务器,只需向进程发送一个 SIGTERM 信号即可正常关闭应用程序。

引自 Play Framework 关于停止 Play 应用程序的文档

5个回答

30

我刚刚将一个Play!应用程序制作成了Docker映像,并且也遇到了这个问题 - 重启主机会导致Play!应用程序在其容器中无法启动,因为RUNNING_PID文件没有被删除。

我意识到,由于Play!应用程序是其容器中唯一的进程,始终具有相同的PID,并且由Docker负责管理,所以据我所知,实际上并不需要RUNNING_PID文件。

因此,我通过将pidfile.path覆盖为/dev/null来解决了这个问题,方法是:

javaOptions in Universal ++= Seq(
  "-Dpidfile.path=/dev/null"
)

在我的项目的build.sbt文件中加入了这段代码。它能够正常工作——我可以重启主机(和容器),我的Play!应用程序也能够正常启动。

我喜欢这种方法的原因是它不需要改变sbt-native-packager生成镜像的方式,只需要改变应用程序在其中运行的方式。

这种方法适用于sbt-native-packager 1.0.0-RC2及更高版本(因为该版本包含https://github.com/sbt/sbt-native-packager/pull/510)。


+1 这个解决方案对我很有效。或者,更新本地打包器后,您可以添加一个application.ini文件代替将其放在构建脚本中。我选择了那个选项。 - Chris Dail
我真的不明白为什么他们不能在每个新版本中保持Upstart的稳定性而不会出现故障。 - flapjack
1
只是一个确认,将pidfile.path=/dev/null设置在reference.conf / application.conf中是否足够? - Touko
1
我刚刚测试了一下,并确认当我运行一个具有这种配置的容器时,在/opt/docker/RUNNING_PID没有创建RUNNING_PID文件,因此它可以正常工作。然而,不同之处在于像这样在.conf中设置它将使其适用于所有运行/部署模式(即使需要PID文件的模式),而在build.sbt中通过in Universal限定则仅适用于sbt-native-packager部署。 - dhpiggott
application.conf中似乎也可以设置play.server.pidfile.path=/dev/null - James Ward

5

我根据答案和对这个问题的进一步研究整理出一个有效的解决方法。如果我按照以下方式启动容器,它们将在(意外)停止/重启后重新启动。冲突的RUNNING_PID文件不会阻止容器重新启动。

$ sudo docker run --restart=on-failure:5 -d \
--name container my_/container:latest \
sh -c "rm -f /var/run/play.pid && ./opt/bin/start \
-Dpidfile.path=/var/run/play.pid"

它的作用是在每次运行二进制文件之前,使用一个选项将包含进程ID的文件放置在特定位置,并删除该文件。

4
我对docker了解不多,但据我测试,Play在停止服务器时并不会删除RUNNING_PID文件。当我以prod模式部署我的应用程序并尝试通过Ctrl + D和Ctrl + C停止它时,它没有从项目目录中删除RUNNING_PID文件,所以我不得不手动删除它。根据Play文档,通常这个(RUNNING_PID)文件放在play项目的根目录下,但建议将其放在某个可以在重新启动时自动清除的位置,例如“/var/run”:
所以,除了手动删除之外,解决方法是更改RUNNING_PID的路径,并通过一些脚本在每次服务器启动时删除它。
$ /path/to/bin/<project-name> -Dpidfile.path=/var/run/play.pid

请确保目录存在,并且运行Play应用程序的用户具有写入权限。

使用此文件,您可以使用kill命令停止应用程序,例如:

$ kill $(cat /var/run/play.pid)

你也可以尝试使用docker命令:$ sudo docker rm --force redis

也许这能帮到你。

来源1 来源2 来源3


你好,singhakash,感谢你的回答。我已经通过一个外部脚本开发了一个解决方法,它可以在重新启动时杀死容器并重新启动它。但这对我来说并不令人满意。另外,正如你在我的问题中所看到的,我已经更改了RUNNING_PID文件的位置。正如我在期望中指出的那样,我希望有一个能够重新启动Docker容器的方法,而不是杀死它然后再次运行它。它是更大基础设施的一部分,其他组件依赖于它的存在。 - Steven

4
我曾经遇到了同样的问题,并通过手动删除文件来解决它,每次容器运行时都需要这样做。 为了实现这一点,我添加了一个伴随文件start.bash,用于从SBT dist任务的结果启动播放过程,以下是一行代码:
find . -type f -name RUNNING_PID -exec rm -f {} \;

希望这可以帮到您。

1
嗨,朱利安,谢谢你。我实际上使用一个star.sh脚本来杀死和运行容器(应用程序是无状态的,所以在数据方面没有问题)。但这有点不太好。你知道是否有一种方法可以在每次重新启动时自动在容器内运行此行吗? - Steven
好的...实际上这就是我想用那行代码做的事情。我的应用程序也是无状态的,所以每次容器启动时,这个RUNNING_PID文件都会被删除,并创建一个具有不同标识符的新文件。虽然我知道这不是世界上最优雅的方法,但目前我还不知道使用docker以更自然的方式实现相同功能的其他方法。 - Julian Hernandez
但我们都同意这不是Docker的问题,而是Play的问题,对吧? - Steven
1
是的,我们一定会 :) - Julian Hernandez

1
我在执行ctrl+c失败后遇到了同样的问题。我通过运行docker-compose down -v解决了这个问题,然后当然是运行docker-compose up-v选项是用来指示您要删除与容器关联的卷。也许docker-compose down就足够了。
以下是一些down选项的概述:

仅停止服务

docker-compose stop

停止和删除容器、网络等。
docker-compose down 

下载并删除卷

docker-compose down --volumes 

下载并移除图像

docker-compose down --rmi <all|local>`

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