我正在使用Java / Postgres / Docker / Gradle创建一个Web应用程序。
我的项目是这样设置的:
有一个db容器和一个应用程序容器。该应用程序容器仅运行一个普通的jar文件。 jar文件是使用主机系统中的卷添加的。容器最初运行此jar并监视其更改。如果更改,它将重新部署jar,使开发人员可以立即在其本地计算机上查看更改。这就像在Tomcat上部署/更新War文件。当您“按下按钮”时,实时应用程序将更新。无需停止应用程序,重建并重新运行:只需重建。
使用Dockerhub上的Postgres镜像,我想做类似的事情:我想在gradle中“按下按钮”,这将更新DB容器监视的某些文件并重新部署。
不幸的是,我撞墙了。我最初的想法是创建一个扩展Postgres的Dockerfile。它所要做的就是照常运行Postgres服务,并运行脚本以查找某些文件中的更改并重新部署。
不幸的是,以下Dockerfile失败:
FROM postgres:9.4
WORKDIR /db
COPY monitor.py .
COPY run-pg-and-monitor.sh .
CMD ["run-pg-and-monitor.sh"]
Shell脚本具有以下命令:
# Run monitor in the background
python3 monitor.py &
# Run postgres normally
postgres
不幸的是,这与Postgres不兼容。我得到错误:
db_1 | "root" execution of the PostgreSQL server is not permitted.
db_1 | The server must be started under an unprivileged user ID to prevent
db_1 | possible system security compromise. See the documentation for
db_1 | more information on how to properly start the server.
如果我将命令设置为:
CMD ["postgres"]
这是Postgres容器最初所做的,错误消失了。显然,这不会运行我需要的monitor.py。
奇怪的是,不带方括号运行CMD也会导致此问题。
CMD postgres
从 shell 环境运行它也会失败:
CMD [“sh”,“-c”,“postgres”]
也许这与从shell运行它有关...
我知道postgres应该从postgres用户而不是root运行。我感到奇怪的是,如果我将CMD设置为:
CMD ["whoami"]
我得到:
db_1 | root
因此,它已经以root身份运行postgres,没有任何问题。
我的第二个想法是完成一个gradle任务:
1)更新jar,从而触发app容器的重新部署
2)运行
docker exec ...
手动更新正在运行的数据库容器。这个想法的唯一问题是,如果没有应用程序或数据库容器正在运行,则作业显然会失败。我将不得不添加检查它们是否为逻辑的逻辑。这似乎并不理想。
有人有见识吗?是否存在我不知道的解决方案?
最佳答案
这些奇怪的原因是postgres Dockerfile
将其自己的docker-entrypoint.sh脚本设置为图像的ENTRYPOINT。因此,在容器启动时执行docker-entrypoint.sh
,并将设置为CMD
的任何内容作为参数传递给脚本。
只有在第一个传递的参数等于postgres
(请参阅here和here)之后,入口脚本才会执行postgres所需的初始化步骤,例如删除特权,然后才最终执行通过CMD
传递的任何内容。
覆盖ENTRYPOINT
而不是CMD
,实际上是用您自己的脚本包装docker-entrypoint.sh
,应该可以为您提供所需的行为:
# Run monitor in the background
python3 monitor.py &
# Run postgres normally
docker-entrypoint.sh "$@"