如何检查应用程序是否已在容器内启动

时间:2015-08-05 15:10:02

标签: docker

我们有一个集成测试套件,可以在开始任何测试用例之前启动容器。我们曾经在执行任何测试之前等待端口可用,这表明应用程序已准备好接收请求。但是,在应用程序甚至在容器内部启动之前,1.7.1版本的端口立即可用。

是否可以选择推迟停靠端口转发,直到容器内的端口打开?

或者是否有其他可靠的方法来检查应用程序是否已在容器内启动?

2 个答案:

答案 0 :(得分:7)

  

但是,在应用程序甚至在容器内启动之前,1.7.1版本的端口立即可用。

我不认为这是真的 - 也就是说,我认为这取决于你如何尝试联系港口。例如,考虑一个像这样的容器:

$ docker run -it -p 8888:80 alpine sh

这里我们设置了从主机端口8888到容器端口80的端口转发,但是我们还没有设置任何内容来监听容器内部。尝试连接到localhost上的端口8888会导致连接成功立即关闭:

$ telnet localhost 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.

这正是您所遇到的。但是,如果我们使用主机的IP地址代替localhost,我们会看到不同的行为:

$ telnet 192.168.1.55 8888
Trying 192.168.1.55...
telnet: connect to address 192.168.1.55: Connection refused

如果在容器内我启动了一个Web服务器:

/ # apk add mini_httpd
[...]  
/ # mini_httpd 
mini_httpd: started as root without requesting chroot(), warning only

然后我可以成功连接:

$ telnet 192.168.1.55 8888
Trying 192.168.1.55...
Connected to 192.168.1.55.
Escape character is '^]'.

这是因为通过localhost的连接由Docker userland代理处理,该代理绑定到端口8888:

# netstat -tlnp | grep 8888
tcp6       0      0 :::8888                 :::*                    LISTEN      2809/docker-proxy   

但是到另一个接口ip的连接 - 以及来自另一个主机的任何连接 - 将由iptables nat表中的规则处理:

# iptables -t nat -S DOCKER
-N DOCKER
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8888 -j DNAT --to-destination 172.17.0.138:80
  

或者是否有其他可靠的方法来检查应用程序是否已在容器内启动?

您有几个选择:

  • 只需直接连接到容器ip,而不是依赖端口转发。例如,在上面的例子中,我开始的容器被分配了地址172.17.0.138。我可以连接到那个而不是主机地址。很容易找到docker容器的ip地址:

    $ docker inspect --format'{{.NetworkSettings.IPAddress}}'my-container 172.17.0.138

  • 等到您的应用程序实现成功连接。在这个我最终启动Web服务器的示例中,我可以等到curl成功连接:

    while ! curl -sf http://localhost:8888/; do
      sleep 1
    done
    

    -f标志告诉curl如果无法成功获取URL,则会退出并显示错误代码。

答案 1 :(得分:1)

我遇到了使用此Python脚本解决的相同问题:

    def is_port_connected_in_container(host, port):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(0.1)
            # try to connect; if it fails, port is closed
            try:
                s.connect((host, port))
            except socket.timeout:
                return False

            # try to receive some data
            try:
                received = s.recv(1)
            except socket.timeout:
                # no data, but this only times out if port was open
                return True

            # no timeout, so port open depends on whether we received something
            return len(received) > 0