如何编写一个Dockerfile,我可以启动服务并运行shell并接受shell的参数?

时间:2016-01-09 09:22:27

标签: docker dockerfile entry-point

在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"]

但它会在$ PATH中找不到报告sudo service docker start,或者che.sh没有运行。

编写它的正确方法是什么?

  1. sudo service docker start应在调用che.sh时运行
  2. 我需要将参数从外部传递到che.sh,例如docker run -it --priviledged my/che -r:111.111.111.111 run

2 个答案:

答案 0 :(得分:4)

您必须在Docker容器中使用supervisord,以便在创建容器时能够使用更复杂的shell语法。

关于supervisord的Docker文档:https://docs.docker.com/engine/articles/using_supervisord/

当您使用$ docker run命令创建新容器时,您可以使用更复杂的shell语法(您想要使用),但是这在systemd服务文件中不起作用(由于systemd的限制)和docker-撰写.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日志中显示子进程日志,但这里有时间戳和注释,而不是像直接运行脚本时那样的纯bash输出。

正如你在我的scipt中看到的那样,我使用tail -f /dev/null保持容器,但这是一个不好的做法。 .sh脚本应该让你的容器独立存在。

当你将你的scipt发送到ENTRYPOINT ENTRYPOINT ["sudo service docker start", "&&", "/home/user/che/bin/che.sh run"]时,你想要将默认的泊坞号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。您还有更多选择可以摆脱限制。

我对你的堆栈一无所知,但通常不知道如何不将Docker停靠在Container中。您的Dockerfile中有sudo service docker start的具体原因吗?

我对这个容器一无所知,是否必须活着?因为如果没有,有一个更简单的解决方案,只有在必须从命令行处理某个东西时才运行容器。将此文件放在名为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

答案 1 :(得分:0)

尝试:
1。在Dockerfile中

class Counter implements Runnable {
    private int a = 0;
    private int b = 0;
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    private void incA() {
        ++a;
    }

    private void decA() {
        --a;
    }

    private void incB() {
        ++b;
    }

    private void decB() {
        --b;
    }

    private void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (lock1) {
                incA();
                decA();
            }

            synchronized (lock2) {
                incB();
                decB();
            }

            synchronized (lock1) {
                synchronized (lock2) {
                    printValues();
                }
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 启动容器
    @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE); View childLayout = null; // Handle navigation view item clicks here. int id = item.getItemId(); if (id == R.id.nav_home) { childLayout = inflater.inflate(R.layout.layout1, (ViewGroup) findViewById(R.id.where_to_include)); } else if (id == R.id.nav_menu) { childLayout = inflater.inflate(R.layout.layout2, (ViewGroup) findViewById(R.id.where_to_include)); } else if (id == R.id.nav_hospital) { childLayout = inflater.inflate(R.layout.layout3, (ViewGroup) findViewById(R.id.where_to_include)); } else if (id == R.id.nav_atm) { childLayout = inflater.inflate(R.layout.layout4, (ViewGroup) findViewById(R.id.where_to_include)); } else if (id == R.id.nav_feedback) { childLayout = inflater.inflate(R.layout.layout5, (ViewGroup) findViewById(R.id.where_to_include)); } else if (id == R.id.nav_about) { childLayout = inflater.inflate(R.layout.layout6, (ViewGroup) findViewById(R.id.where_to_include)); // then add childLayout to your row } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; }