当Docker镜像运行时如何运行命令?

3

我有一个Consul Docker镜像,它是docker-compose环境的一部分。

我需要在Docker容器内运行命令consul acl bootstrap,我相信将其提及到commandentrypoint中会覆盖为Consul设置的默认命令,我该如何在保留默认命令的同时执行它?

2个回答

0

在docker-compose中没有选项可以让您在容器启动后运行命令。

您可以构建自己的镜像,在启动时执行所需的操作。要做到这一点,您需要:

  1. 找出容器的默认启动方式(即ENTRYPOINTCMD的组合)。
  2. 创建一个shell脚本,该脚本将使用所需的参数调用入口点,然后调用您的命令。
  3. 创建一个基于原始镜像的Dockerfile,将shell脚本复制到镜像中,并将入口点更改为您的脚本
  4. 将您的镜像和Dockerfile添加到docker-compose中(将当前consul镜像更改为指向您的镜像和构建脚本)

以下是入口点shell脚本的示例,可用于启动您的特定脚本。将您的代码放在execute_after_start()函数中。

entrypoint.sh

#!/bin/bash
set -e

execute_before_start() {
    echo "Execute befor start" > /running.txt
}

execute_after_start() {
    sleep 1
    echo "Execute after start" >> /running.txt
}

execute_before_start
echo "CALLING ENTRYPOINT WITH CMD: $@"
exec /old_entrypoint.sh "$@" &
daemon_pid=$!
execute_after_start
wait $daemon_pid
echo "Entrypoint exited" >> running.txt

脚本将启动execute_before_start。当这些命令完成后,将使用提供的CMD参数并在并行(这是&execute末尾)的情况下启动原始入口点,并同时启动execute_after_start。当execute_after_start完成后,它将等待原始入口点停止。
我在示例中使用sleep作为一种简单的方式来确保有些延迟,以便入口点可以接受命令。根据入口点,可能有更聪明的方法来确保入口点准备好接受命令。

1
谢谢您的回复,但是如果我要覆盖默认的cmd,为什么不直接将其添加到docker-compose文件中呢? - Ajay Sabarish
根据您的附加命令,您肯定可以覆盖它们,但是如果您开始有更多要执行的命令,很快在 docker-compose.yaml 中进行的覆盖将变得更加复杂和难以维护。另一方面,基于原始图像创建新的 Docker 映像更易于维护,并允许更复杂的命令、更改和其他文件。您可以创建 Dockerfile 并将其添加到 docker-compose.yaml 中以自动构建映像,因此除了创建 Dockerfile 外,不需要额外的工作。 - jordanvrtanoski
好的,那么我应该创建另一个入口文件,比如说 consul-entrypoint,它应该使用所需的参数调用 docker-entrypoint(consul镜像的默认入口点),并在Dockerfile中设置 ENTRYPOINT ["/consul-entrypoint.sh"] - Ajay Sabarish
是的,您可以创建另一个shell脚本myentrypoint.sh,该脚本首先调用consul-entrypoint.sh,然后运行您的命令(例如consul acl bootstrap)。 - jordanvrtanoski
“Cmd”: [“agent”, “-dev”, “-client”, “0.0.0.0”] 是 Consul 镜像的 CMD,这将作为参数传递给 Consul 镜像的 docker-entrypoint。我该如何确保它从我的 docker-entrypoint 传递? - Ajay Sabarish
@AjaySabarish 我给你提供了一个样板,这样你就可以开始编写你的脚本了。 - jordanvrtanoski

0

正如其他人所评论的那样,在Compose中无法在容器启动后运行其他命令。

或者,您可以通过在Consul服务器的配置中指定acl.tokens.master来显式指定主令牌的密钥ID。使用Docker镜像执行此操作的最简单方法是使用CONSUL_LOCAL_CONFIG环境变量传递配置(请参见Docker-Consul: 使用容器)。

例如:

---
version: "3.8"
services:
  consul:
    image: consul
    ports:
      - "8500:8500/tcp"
    environment:
      CONSUL_LOCAL_CONFIG: >
        {"acl": {"enabled": true, "tokens": {"master": "A8D89992-A3DB-46E3-A528-8F97CFEDB183"} }}

这种方法的明显缺点是将一个令牌硬编码到Compose配置中,但也避免了尝试使用自定义入口点运行consul acl bootstrap时的复杂性。

只有在集群首次初始化时才需要提供此环境变量。之后,Consul会将此令牌数据持久化存储在挂载在/consul/data的Docker卷中。对令牌(包括主令牌)进行任何修改都需要使用consul acl token/v1/acl/token API端点执行。


我如何在不运行 consul acl bootstrap 的情况下知道要传递的令牌?我应该手动复制/粘贴吗? - Ajay Sabarish
@AjaySabarish,token只是一个随机UUID。您可以使用CLI工具“uuidgen”自己生成一个。 - Blake Covarrubias
谢谢,现在我明白了。 - Ajay Sabarish
如果我的回答解决了您的问题,您是否介意将其标记为您问题的答案? - Blake Covarrubias

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