Docker容器启动后如何自动运行脚本

Posted

技术标签:

【中文标题】Docker容器启动后如何自动运行脚本【英文标题】:How can I run script automatically after Docker container startup 【发布时间】:2018-05-20 04:11:52 【问题描述】:

我正在使用 Search Guard 插件来保护由多个节点组成的弹性搜索集群。 这是我的 Dockerfile:

#!/bin/sh
FROM docker.elastic.co/elasticsearch/elasticsearch:5.6.3

USER root

# Install search guard
RUN bin/elasticsearch-plugin install --batch com.floragunn:search-guard-5:5.6.3-16 \
    && chmod +x \
        plugins/search-guard-5/tools/hash.sh \
        plugins/search-guard-5/tools/sgadmin.sh \
        bin/init_sg.sh \
    && chown -R elasticsearch:elasticsearch /usr/share/elasticsearch

USER elasticsearch

初始化 SearchGuard(创建内部用户并分配角色)。我需要在容器启动后运行脚本init_sg.sh。 问题是:除非 elasticsearch 正在运行,否则脚本不会初始化任何安全索引。

脚本的内容是:

sleep 10
plugins/search-guard-5/tools/sgadmin.sh -cd config/ -ts config/truststore.jks -ks config/kirk-keystore.jks -nhnv -icl

现在,我只是在容器启动后手动运行脚本,但由于我在 Kubernetes 上运行它。Pod 可能会因某种原因被杀死或失败并自动重新创建。在这种情况下,插件必须在容器启动后自动初始化!

那么如何实现呢?任何帮助或提示将不胜感激。

【问题讨论】:

每个集群只需要初始化 SG 一次。每次重新启动 Pod 时重新初始化 SG 将覆盖任何未保存的自定义角色/权限。 @nafooesi 绝对,你是对的!每个集群应该只初始化 SG 一次。 这个问题也解决了here 【参考方案1】:

镜像本身在 Dockerfile 中有一个入口点ENTRYPOINT ["/run/entrypoint.sh"]。您可以用自己的脚本替换它。例如,创建一个新脚本,挂载它并首先调用/run/entrypoint.sh,然后在运行init_sg.sh 之前等待elasticsearch 启动。

【讨论】:

我正在尝试做同样的事情,但容器中不存在/run/entrypoint.sh。我找不到它的位置。有什么帮助吗? 在 /entrypoint.sh 中找到了 elasticsearch:1.5.2 你能指定如何mount it吗?【参考方案2】:

不确定这是否能解决您的问题,但值得检查我的repo'sDockerfile

我创建了一个简单的 run.sh 文件,复制到 docker 映像,并在 Dockerfile 中写了 CMD ["run.sh"]。以同样的方式在run.sh 中定义您想要的任何内容并写入CMD ["run.sh"]。您可以在下面找到另一个示例

Dockerfile

FROM java:8

RUN apt-get update && apt-get install stress-ng -y 
ADD target/restapp.jar /restapp.jar 
COPY dockerrun.sh /usr/local/bin/dockerrun.sh 
RUN chmod +x /usr/local/bin/dockerrun.sh 
CMD ["dockerrun.sh"]

dockerrun.sh

#!/bin/sh
java -Dserver.port=8095 -jar /restapp.jar &
hostname="hostname: `hostname`"
nohup stress-ng --vm 4 &
while true; do
  sleep 1000
done

【讨论】:

感谢回复,elasticsearch镜像已经有entrypoint.sh文件了,像这样使用CMD会覆盖掉elasticsearch search根本不会启动:/【参考方案3】:

我试图解决确切的问题。这是对我有用的方法。

    创建一个单独的 shell 脚本来检查 ES 状态,并且只有在 ES 准备好时才开始初始化 SG:

Shell 脚本

#!/bin/sh

echo ">>>>  Right before SG initialization <<<<"
# use while loop to check if elasticsearch is running 
while true
do
    netstat -uplnt | grep :9300 | grep LISTEN > /dev/null
    verifier=$?
    if [ 0 = $verifier ]
        then
            echo "Running search guard plugin initialization"
            /elasticsearch/plugins/search-guard-6/tools/sgadmin.sh -h 0.0.0.0 -cd plugins/search-guard-6/sgconfig -icl -key config/client.key -cert config/client.pem -cacert config/root-ca.pem -nhnv
            break
        else
            echo "ES is not running yet"
            sleep 5
    fi
done

在 Dockerfile 中安装脚本

您需要将脚本安装在容器中,以便在启动后可以访问它。

COPY sginit.sh /
RUN chmod +x /sginit.sh

更新入口点脚本

您将需要编辑 ES 映像的入口点脚本或运行脚本。以便在启动 ES 进程之前在后台启动 sginit.sh。

# Run sginit in background waiting for ES to start
/sginit.sh &

这样sginit.sh会在后台启动,只有在ES启动后才会初始化SG。

让这个 sginit.sh 脚本在后台 ES 之前启动的原因是它不会阻止 ES 启动。同样的逻辑,如果你把它放在 ES 启动之后,它永远不会运行,除非你把 ES 的启动放在后台。

【讨论】:

如何等待ES启动,然后初始化SG? 正如在原始问题下方评论的那样,您不想每次都初始化 SG。当 pod 重新启动时,它将覆盖任何修改过的配置。 SG 应该只在每个集群开始时初始化一次。如果需要在 ES 启动后运行其他一些脚本,可以以我的脚本为例,在启动脚本之前检测 ES 是否在后台运行。 这里描述的一般方法非常有用。我有一个类似的问题(不是 ES),我想在主进程有机会启动后运行一个脚本。这种模式就像一个魅力!【参考方案4】:

这在此处的文档中得到解决:https://docs.docker.com/config/containers/multi-service_container/

如果您的某个进程依赖于主进程,则首先使用 wait-for-it 之类的脚本启动您的辅助进程,然后启动主进程 SECOND 并删除 fg %1 行。

#!/bin/bash
  
# turn on bash's job control
set -m
  
# Start the primary process and put it in the background
./my_main_process &
  
# Start the helper process
./my_helper_process
  
# the my_helper_process might need to know how to wait on the
# primary process to start before it does its work and returns
  
  
# now we bring the primary process back into the foreground
# and leave it there
fg %1

【讨论】:

这里重复***.com/questions/45371521/…【参考方案5】:

我建议将 CMD 放在你的 docker 文件中,以便在容器启动时执行脚本

FROM debian
RUN apt-get update && apt-get install -y nano && apt-get clean
EXPOSE 8484
CMD ["/bin/bash", "/opt/your_app/init.sh"]

还有其他方法,但在使用之前先看看你的要求,

    ENTRYPOINT "put your code here" && /bin/bash
    #exemple ENTRYPOINT service nginx start && service ssh start &&/bin/bash "use && to separate your code"

【讨论】:

这个解决方案覆盖了 Elasticsearch Image 提供的入口点 我没有研究过 elasticseacrh 镜像安装,你的意思是这对你不起作用吗?【参考方案6】:

您也可以使用wait-for-it 脚本。它将等待主机和 TCP 端口的可用性。它对于同步相互依赖的服务的启动很有用,并且像容器的魅力一样工作。它没有任何外部依赖项,因此您可以将其作为 RUN 命令运行而无需执行任何其他操作。

一个基于thread的Dockerfile示例:

FROM elasticsearch

# Make elasticsearch write data to a folder that is not declared as a volume in elasticsearchs' official dockerfile.
RUN mkdir /data && chown -R elasticsearch:elasticsearch /data && echo 'es.path.data: /data' >> config/elasticsearch.yml && echo 'path.data: /data' >> config/elasticsearch.yml

# Download wait-for-it
ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/e1f115e4ca285c3c24e847c4dd4be955e0ed51c2/wait-for-it.sh /utils/wait-for-it.sh

# Copy the files you may need and your insert script

# Insert data into elasticsearch
RUN /docker-entrypoint.sh elasticsearch -p /tmp/epid & /bin/bash /utils/wait-for-it.sh -t 0 localhost:9200 -- path/to/insert/script.sh; kill $(cat /tmp/epid) && wait $(cat /tmp/epid); exit 0;

【讨论】:

以上是关于Docker容器启动后如何自动运行脚本的主要内容,如果未能解决你的问题,请参考以下文章

Docker容器启动后如何自动运行脚本

如何在不同的(依赖的)容器启动后对 docker 容器运行 .sql 脚本?

docker容器启动自动启动shell脚本

docker-compose,容器启动后运行脚本?

docker 容器启动后运行自定义脚本

docker容器启动Mysql执行初始化sql脚本实现