链接 Docker 个图像并按顺序执行

Chaining Docker Images and execute in order

我正在用我自己的图像扩展 APIMan / Wildfly Docker 图像,这将做两件事:

1) Drop my .war file application into the Wildfly standalone/deployments directory

2) Execute a series of cURL commands that would query the Wildfly server in order to configure APIMan.

最初,我尝试创建两个 Docker 图像(第一个放入 .war 文件,第二个用于执行 cURL 命令),但是我错误地认为 CMD 指令在最里面的图像将首先执行,然后 CMD 将执行 outward.

例如:

ImageA:
FROM jboss/apiman-wildfly:1.1.6.Final
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent
COPY /RatedRestfulServer/target/RatedRestfulServer-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/

CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c", "standalone-apiman.xml"]

ImageB:
FROM ImageA
COPY /configure.sh /opt/jboss/wildfly/

CMD ["/opt/jboss/wildfly/configure.sh"]

我最初假设在 运行 时间内将首先启动 Wildfly / APIMAN(根据 ImageA CMD 指令),然后我的自定义脚本将是 运行(根据 ImageB CMD 指令)。我假设这是不正确的,因为在整个层次结构中,只执行了 1 条 CMD 指令(链中最外层 Docker 文件中的最后一条指令)?

所以,然后我尝试将所有内容合并到 1 个 Docker 文件中,该文件将(在构建过程中)启动 Wildfly / APIMAN,运行 cURL 命令,关闭 wildfly 服务器,然后CMD 命令将在 运行 时间内启动它,并配置 Wildfly / APIMan。但是,这不起作用,因为当我启动 Wildfly(作为构建的一部分)时,它控制控制台并等待显示日志消息,因此构建永远不会完成。如果我在 运行 命令末尾附加一个“&”,它不会 运行 (Dockerfile : RUN results in a No op).

这是我的 Docker这次尝试的文件:

FROM jboss/apiman-wildfly:1.1.6.Final
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent
COPY /RatedRestfulServer/target/RatedRestfulServer-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/
COPY /configure.sh /opt/jboss/wildfly/
RUN /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 -c standalone-apiman.xml
RUN /opt/jboss/wildfly/configure.sh
RUN /opt/jboss/wildfly/bin/jboss-cli.sh --connect controller=127.0.0.1 command=:shutdown

CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c", "standalone-apiman.xml"]

有什么解决办法吗?在 Wildfly / APIMan 启动后,我试图让我的 "configure.sh" 脚本 运行。这对我来说是在构建过程中还是在 运行 时间完成并不重要,但是我在构建过程中看不到任何方法,因为 Wildfly 没有守护进程模式。

only 1 CMD instruction is executed (the last one in the outermost Dockerfile within the chain)?

是的,这是正确的,请记住 CMD 不是 运行 在构建时而是在实例化时。本质上,当您从 ImageB

实例化容器时,您在第二个 Docker 文件的 CMD 中所做的是覆盖第一个文件

如果您正在做某种 Rest API 或 cli 或 cURL 来连接到您的 Wildfly 服务器,我建议您在容器实例化之后进行配置,而不是在容器构建之后进行配置。这样:

CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c", "standalone-apiman.xml"]`

永远是你最后的命令。 如果您需要一些额外的文件或更改配置文件,您可以将它们放在 Dockerfile 中,以便在 CMD 在实例化时调用之前在构建时复制它们。

总而言之:

1) 使用此 Dockerfile(docker 构建)构建您的 Docker 容器:

FROM jboss/apiman-wildfly:1.1.6.Final
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent
COPY /RatedRestfulServer/target/RatedRestfulServer-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/
COPY /configure.sh /opt/jboss/wildfly/
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c", "standalone-apiman.xml"]

2) 运行(从您新创建的镜像中实例化您的容器)

docker run <image-id>

3) 运行 以下内容来自容器或以相同方式配置 Wildfly 的主机。这假设您正在使用一些 Rest API 来配置东西(即使用 cURL):

/opt/jboss/wildfly/configure.sh

您可以实例化第二个容器到 运行 这个命令,像这样:

docker run -ti <image-id> /bin/bash

构建一张图片:

FROM jboss/apiman-wildfly:1.1.6.Final
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent
COPY /RatedRestfulServer/target/RatedRestfulServer-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/
COPY /configure.sh /opt/jboss/wildfly/
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c", "standalone-apiman.xml"]

启动它。启动完成后,使用 docker exec 命令在 运行 容器中启动配置脚本。

docker run -d --name wildfly <image name>
docker exec wildfly /opt/jboss/wildfly/configure.sh

我的问题背后的原始前提(尽管在原始 post 中没有明确说明)是在镜像内配置 APIMan,而无需在镜像外进行任何干预。

这有点麻烦,但我可以通过创建 3 个脚本来解决这个问题。一个用于启动 Wildfly,一个用于 运行 配置脚本,第三个用于执行它们。希望这可以让其他可怜的人免于花一天的时间来解决所有这些问题。

由于 Dockerfile 的性质只允许在 运行 时间执行 1 次执行调用,因此该调用需要针对自定义脚本。

以下是带有注释的文件。

Dockerfile

FROM jboss/apiman-wildfly:1.1.6.Final
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent
COPY /RatedRestfulServer/target/RatedRestfulServer-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/
COPY /configure.sh /opt/jboss/wildfly/
COPY /execute.sh /opt/jboss/wildfly/
COPY /runWF.sh /opt/jboss/wildfly/

CMD ["/opt/jboss/wildfly/execute.sh"]

请注意,所有 3 个脚本都内置在图像中。 execute.sh 脚本在 运行 时间(实例化)而非构建时间执行。

execute.sh

#!/bin/sh

/opt/jboss/wildfly/configure.sh &
/opt/jboss/wildfly/runWF.sh

注意,configure.sh 脚本被发送到后台,这样我们就可以在 configure.sh 仍然 运行ning 时继续执行 runWF.sh 脚本)

configure.sh

#!/bin/sh

done=""
while [ "$done" != "200" ]
do
        done=$(curl --write-out %{http_code} --silent --output /dev/null -u username:password -X GET -H "Accept: application/json" http://127.0.0.1:8080/apiman/system/status)
        sleep 3
done

# configuration curl commands
curl ...
curl ...

上面的 configure.sh 脚本 运行 在一个循环中通过 curl 每 3 秒检查一次 wildfly / apiman 服务器查询其状态。一旦它返回 HTTP 状态代码 200(表示 "up and running" 状态),它就会退出循环并自由移动到配置。请注意,这应该通过提供另一种退出循环的方式(例如,在一定数量的查询等之后)来稍微 'safer' 。我想这会让生产开发人员心悸,我不建议在生产中部署它,但它暂时有效。

runWF.sh

#!/bin/sh

/opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 -c standalone-apiman.xml

此脚本只是启动服务器。参数将各种模块绑定到 0.0.0.0 并指示 wildfly 使用 apiman 独立 xml 文件进行配置。

在我的机器上,wildfly + apiman(使用我的自定义 war 文件)需要大约 10-15 秒(取决于我 运行 它在哪台计算机上)才能完全加载,但是一次它确实如此,配置脚本将能够成功查询它,然后继续配置 curl 命令。同时,wildfly 仍然控制控制台,因为它是最后启动的,您可以监视 activity 并使用 ctrl-c 终止进程。