如何编写一个可以启动服务并运行 shell 并接受 shell 参数的 Dockerfile?
Posted
技术标签:
【中文标题】如何编写一个可以启动服务并运行 shell 并接受 shell 参数的 Dockerfile?【英文标题】:How to write a Dockerfile which I can start a service and run a shell and also accept arguments for the shell? 【发布时间】:2016-04-14 00:05:43 【问题描述】:在一个 Dockerfile 中,最新的指令是:
CMD sudo chown -R user:user /home/user/che && \
sudo service docker start && \
cd /home/user/che/bin/ && ./che.sh run
它有效,但我无法将更多参数传递给./che.sh
。
che.sh
检查内部docker
是否在执行其他任务后启动。它可以接受几个可选参数,例如-r:111.111.111.111
。
我尝试将指令修改为:
RUN sudo chown -R user:user /home/user/che && \
sudo service docker start
ENTRYPOINT ["/home/user/che/bin/che.sh"]
为了像docker run -it --priviledged my/che -r:111.111.111.111 run
一样调用它,但是che.sh
shell 会报告内部docker
工作不正常。
我也试过了:
ENTRYPOINT ["sudo service docker start", "&&", "/home/user/che/bin/che.sh run"]
甚至:
ENTRYPOINT ["sh", "-c" "sudo service docker start && /home/user/che/bin/che.sh run"]
但它会报告sudo service docker start
is not found in $PATH,或者che.sh
没有运行。
正确的写法是什么?
sudo service docker start
应该在调用 che.sh
时运行
我需要将参数从外部传递给che.sh
,例如docker run -it --priviledged my/che -r:111.111.111.111 run
【问题讨论】:
【参考方案1】:您必须在 Docker 容器内使用 supervisord,以便在创建容器时使用更复杂的 shell 语法。
关于 supervisord 的 Docker 文档:https://docs.docker.com/engine/articles/using_supervisord/
当您使用 $ docker run
命令创建新容器时,您可以使用更复杂的 shell 语法(您想要使用的),但是这在 systemd 服务文件(由于 systemd 的限制)和 docker-compose 中不起作用。 yml 文件和 Dockerfiles 也是。
首先,您必须在 Dockerfile 中安装 supervisord:
RUN apt-get -y update && apt-get -y dist-upgrade \
&& apt-get -y install \
supervisor
RUN mkdir -p /var/log/supervisord
将其放在 Dockerfile 的末尾:
COPY etc/supervisor/conf.d/supervisord.conf /etc/supervisor/conf.d/
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
在您的 Dockerfile 旁边的 etc/supervisor/conf.d/supervisord.conf
中创建一个文件:
[unix_http_server]
file=/var/run/supervisord.sock
chmod=0777
chown=root:root
username=root
[supervisord]
nodaemon=true
user=root
environment=HOME="/root",USER="root"
logfile=/var/log/supervisord/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisord
logfile_maxbytes=10MB
loglevel=info
[program:keepalive]
command=/bin/bash -c 'echo Keep Alive service started... && tail -f /dev/null'
autostart=true
autorestart=true
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/var/log/supervisord/keepalive-stdout.log
stdout_logfile_maxbytes=1MB
stderr_logfile=/var/log/supervisord/keepalive-stderr.log
stderr_logfile_maxbytes=1MB
[program:dcheck]
command=/bin/bash -c 'chmod +x /root/dcheck/repo/dcheck.sh && cd /root/dcheck/repo && ./dcheck.sh'
autostart=true
autorestart=true
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/var/log/supervisord/dcheck-stdout.log
stdout_logfile_maxbytes=10MB
stderr_logfile=/var/log/supervisord/dcheck-stderr.log
stderr_logfile_maxbytes=1MB
这是一个更复杂的 supervisord.conf,您可能不需要这里的许多命令,另外您必须根据需要更改文件位置。但是,您可以看到如何从脚本的 bash 输出创建日志文件。
稍后您必须在该容器中docker exec
,您可以通过以下方式实时查看日志:
docker exec -it your_running_container /bin/bash -c 'tail -f /var/log/supervisord/dcheck-stdout.log'
您可以选择使用loglevel=debug
在主 supervisord 日志中显示子进程日志,但是这充满了时间戳和 cmets,而不是像直接运行脚本时那样的纯 bash 输出。
正如您在我的 scipt 中看到的那样,我使用 tail -f /dev/null
保持容器处于活动状态,但是这是一种不好的做法。 .sh 脚本应该自己让你的容器保持活动状态。
当您将 scipt 作为 ENTRYPOINT ["sudo service docker start", "&&", "/home/user/che/bin/che.sh run"]
发送到 ENTRYPOINT 时,您希望将默认 docker ENTRYPOINT 从 /bin/sh -c
更改为 sudo
(另外,使用完整的位置名称)。
有两种方法可以在 Dockerfile 中更改 docker ENTRYPOINT。一种是将其放在 Dockerfile 的 head 部分:
RUN ln -sf /bin/bash /bin/sh && ln -sf /bin/bash /bin/sh.distrib
或者把它放在底部:
ENTRYPOINT ['/bin/bash', '-c']
当你向这个 Dockerfile 发送任何CMD
之后,它将通过/bin/bash -c
命令运行。
还有一点需要注意的是,第一个命令使用 PID1,所以如果你想在我的 supervisord 脚本中运行没有 tail -f /dev/null
的 .sh 脚本,它将占据 PID1 进程位置,并且 CTRL+C 命令将不起作用.您必须从另一个 shell 实例中关闭容器。
但是如果你运行命令:
[program:dcheck]
command=/bin/bash -c 'echo pid1 > /dev/null && chmod +x /root/dcheck/repo/dcheck.sh && cd /root/dcheck/repo && ./dcheck.sh'
echo pid1 > /dev/null
将使用 PID1 和 SIGTERM,SIGKILL 和 SIGINT 将再次与您的 shell 脚本一起使用。
我尽量避免使用 --privileged
标志运行 Docker。您有更多选择可以摆脱这些限制。
我对您的堆栈一无所知,但通常最好不要在容器中 dockerise Docker。 sudo service docker start
在您的 Dockerfile 中是否有特定原因?
我对这个容器一无所知,它必须是活的吗?因为如果没有,有一个更简单的解决方案,仅在必须从命令行处理某些内容时才运行容器。将此文件放在名为run
的主机上,假设在/home/hostuser
文件夹中,并给它chmod +x run
:
#!/bin/bash
docker run --rm -it -v /home/hostuser/your_host_shared_folder/:/root/your_container_shared_folder/:rw your_docker_image "echo pid1 > /dev/null && chmod +x /root/script.sh && cd /root && ./script.sh"
在这种情况下,ENTRYPOINT 最好是ENTRYPOINT ['/bin/bash', '-c']
。
在主机上运行此脚本:
$ cd /home/hostuser
$ ./run -flag1 -flag2 args1 args2 args3
【讨论】:
按照您的回答和示例,我花了几个小时才终于完成工作,我想说非常非常感谢!我并没有真正意识到 docker 容器不是 vm,而更像是一个单一的进程。感谢您多次强调它,现在我终于明白了。 我的最终解决方案是跟随你最后一个“简单”案例,既不写ENTRYPOINT
也不写CMD
,只写RUN sudo chown -R user:user /home/user/che
,并像这样调用它:docker run -it --rm --privileged --net=host my/che bash -c " sudo service docker start && cd /home/user/che/bin/ && ./che.sh -r:159.203.211.163 run"
,它就像我一样工作预期的,虽然看起来有点丑
先解释一下为什么docker里面有docker。 Che 是一个 web ide,它使用 docker 镜像来运行不同类型的项目。但是由于某种原因,我想运行多个 che 实例,所以我想在 docker 容器中运行它们中的每一个以使它们独立
可能不适用于这个特定的项目,但对于未来的项目可能非常有用,docker-compose
客户端有助于轻松构建 Docker 容器服务。 Compose 没有内置 Docker,但您可以从 github 安装它。这是我的 Dockerised LEMP 堆栈:github.com/DJviolin/LEMP/blob/master/install-lemp.sh 我可以用一个命令启动一切,这很棒。在链接的安装脚本中查找docker-compose.yml
文件。 Docker-compose 参考:docs.docker.com/compose 现在我非常喜欢 Docker + Docker-compose,因此我将它用于每个项目。
感谢您推荐 docker-compose,我一定会研究一下【参考方案2】:
尝试: 1。在 Dockerfile 中
RUN sudo chown -R user:user /home/user/che && \
sudo service docker start
ENTRYPOINT ["/bin/sh", "-c", "/home/user/che/bin/che.sh run"]
-
启动容器
docker run -it --priviledged my/che -r:111.111.111.111
【讨论】:
我刚试过,但失败了。che.sh
中的“docker”检查失败。
当你 RUN
和 service
拥有 sudo
特权时,这并不是出于所有可能的原因。 1:service
仅在您从映像启动容器时启动。例如。而不是RUN service sshd start
,您必须使用CMD ["/usr/bin/sshd", "-D"]
。所以RUN sudo service docker start
命令不会在$ docker run
上执行。 2:在 Dockerfile 中,每个命令都以 root 身份运行,事实上,在大多数 dockerised linux 发行版中,默认情况下没有其他用户,只有超级用户。在 Dockerfile 中安装或使用 sudo
无用。 3:ENTRYPOINT正确用法说明在我的帖子里。
另外,我在上面的评论中使用了sshd
示例,仅用于演示目的,最好不要将任何 SSH 服务放在 docker 镜像中。请改用docker exec
,并在您的主机操作系统上只保留一个 SSH 实例。
Docker 不是虚拟机。而是想像诺基亚 Symbian S40 平台上的移动 Java 应用程序。它看起来像一个可执行文件,但如果你用 Winrar 打开,你会看到一堆文本文件。好吧,如果你去主机上/var/lib/docker
的Docker主文件夹,你可以看到图像和容器是未解压的文件。以上是关于如何编写一个可以启动服务并运行 shell 并接受 shell 参数的 Dockerfile?的主要内容,如果未能解决你的问题,请参考以下文章
如何实现在运行shell程序时添加选项-h能提示程序的用法?