无法连接到在 VSTS 中运行的 Docker 容器

Chr*_*jer 4 docker azure-devops azure-pipelines

我有一个测试,启动 Docker 容器,执行验证(与 Docker 容器中的 Apache httpd 通信),然后停止 Docker 容器。

\n\n

当我在本地运行此测试时,此测试运行得很好。但是,当它在托管 VSTS(即托管构建代理)上运行时,它无法连接到 Docker 容器中的 Apache httpd。

\n\n

这是.vsts-ci.yml文件:

\n\n
queue: Hosted Linux Preview\n\nsteps:\n- script: |\n    ./test.sh\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是test.sh重现问题的 shell 脚本:

\n\n
#!/bin/bash\nset -e\nset -o pipefail\n\nfunction tearDown {\n    docker stop test-apache\n    docker rm test-apache\n}\ntrap tearDown EXIT\n\ndocker run -d --name test-apache -p 8083:80 httpd\nsleep 10\n\ncurl -D - http://localhost:8083/\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我在本地运行此测试时,得到的输出是:

\n\n
$ ./test.sh \n469d50447ebc01775d94e8bed65b8310f4d9c7689ad41b2da8111fd57f27cb38\nHTTP/1.1 200 OK\nDate: Tue, 04 Sep 2018 12:00:17 GMT\nServer: Apache/2.4.34 (Unix)\nLast-Modified: Mon, 11 Jun 2007 18:53:14 GMT\nETag: "2d-432a5e4a73a80"\nAccept-Ranges: bytes\nContent-Length: 45\nContent-Type: text/html\n\n<html><body><h1>It works!</h1></body></html>\ntest-apache\ntest-apache\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个输出完全符合我的预期。

\n\n

但是当我在 VSTS 上运行此测试时,我得到的输出是(不相关的部分替换为\xe2\x80\xa6)。

\n\n
2018-09-04T12:01:23.7909911Z ##[section]Starting: CmdLine\n2018-09-04T12:01:23.8044456Z ==============================================================================\n2018-09-04T12:01:23.8061703Z Task         : Command Line\n2018-09-04T12:01:23.8077837Z Description  : Run a command line script using cmd.exe on Windows and bash on macOS and Linux.\n2018-09-04T12:01:23.8095370Z Version      : 2.136.0\n2018-09-04T12:01:23.8111699Z Author       : Microsoft Corporation\n2018-09-04T12:01:23.8128664Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkID=613735)\n2018-09-04T12:01:23.8146694Z ==============================================================================\n2018-09-04T12:01:26.3345330Z Generating script.\n2018-09-04T12:01:26.3392080Z Script contents:\n2018-09-04T12:01:26.3409635Z ./test.sh\n2018-09-04T12:01:26.3574923Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/02476800-8a7e-4e22-8715-c3f706e3679f.sh\n2018-09-04T12:01:27.7054918Z Unable to find image \'httpd:latest\' locally\n2018-09-04T12:01:30.5555851Z latest: Pulling from library/httpd\n2018-09-04T12:01:31.4312351Z d660b1f15b9b: Pulling fs layer\n[\xe2\x80\xa6]\n2018-09-04T12:01:49.1468474Z e86a7f31d4e7506d34e3b854c2a55646eaa4dcc731edc711af2cc934c44da2f9\n2018-09-04T12:02:00.2563446Z   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n2018-09-04T12:02:00.2583211Z                                  Dload  Upload   Total   Spent    Left  Speed\n2018-09-04T12:02:00.2595905Z \n2018-09-04T12:02:00.2613320Z   0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (7) Failed to connect to localhost port 8083: Connection refused\n2018-09-04T12:02:00.7027822Z test-apache\n2018-09-04T12:02:00.7642313Z test-apache\n2018-09-04T12:02:00.7826541Z ##[error]Bash exited with code \'7\'.\n2018-09-04T12:02:00.7989841Z ##[section]Finishing: CmdLine\n
Run Code Online (Sandbox Code Playgroud)\n\n

关键是这个:

\n\n
curl: (7) Failed to connect to localhost port 8083: Connection refused\n
Run Code Online (Sandbox Code Playgroud)\n\n

10 秒足以让 Apache 启动。\n为什么无法curl与 Apache 的 8083 端口通信?

\n\n

附:

\n\n

我知道像这样的硬编码端口是垃圾,我应该使用临时端口。我想让它首先使用硬编码端口运行,因为这比使用临时端口更简单,然后在硬编码端口工作后立即切换到临时端口。如果硬编码端口由于端口不可用而无法工作,则错误看起来应该有所不同,在这种情况下,docker run应该失败,因为无法分配端口。

\n\n

更新:

\n\n

sleep 100为了确定,我已经使用而不是重新运行了测试sleep 10。结果没有变化,curl无法连接到localhost端口8083

\n\n

更新2:

\n\n

当扩展脚本来执行时docker logsdocker logs显示 Apache 正在按预期运行。\n当扩展脚本来执行时docker ps,它显示以下输出:

\n\n
2018-09-05T00:02:24.1310783Z CONTAINER ID        IMAGE                                                          COMMAND                  CREATED              STATUS              PORTS                  NAMES\n2018-09-05T00:02:24.1336263Z 3f59aa014216        httpd                                                          "httpd-foreground"       About a minute ago   Up About a minute   0.0.0.0:8083->80/tcp   test-apache\n2018-09-05T00:02:24.1357782Z 850bda64f847        microsoft/vsts-agent:ubuntu-16.04-docker-17.12.0-ce-standard   "/home/vsts/agents/2\xe2\x80\xa6"   2 minutes ago        Up 2 minutes                               musing_booth\n
Run Code Online (Sandbox Code Playgroud)\n

Chr*_*jer 6

问题是 VSTS 构建代理在 Docker 容器中运行。当 Apache 的 Docker 容器启动时,它与 VSTS 构建代理 Docker 容器在同一级别运行,而不是嵌套在 VSTS 构建代理 Docker 容器内。

有两种可能的解决方案:

  • 替换localhost为docker主机的ip地址,保留端口号8083
  • 替换localhost为docker容器的ip地址,将主机端口号改为8083容器端口号80

通过 Docker 主机访问

这种情况,解决办法就是替换localhost为docker主机的ip地址。下面的 shell 片段可以做到这一点:

host=localhost
if grep '^1:name=systemd:/docker/' /proc/1/cgroup
then
    apt-get update
    apt-get install net-tools
    host=$(route -n | grep '^0.0.0.0' | sed -e 's/^0.0.0.0\s*//' -e 's/ .*//')
fi
curl -D - http://$host:8083/
Run Code Online (Sandbox Code Playgroud)

检查if grep '^1:name=systemd:/docker/' /proc/1/cgroup脚本是否在 Docker 容器内运行。如果是这样,它会安装net-tools以访问该route命令,然后从route命令中解析默认 gw 以获取主机的 IP 地址。请注意,只有当容器的网络默认网关实际上是主机时,这才有效。

直接访问Docker容器

启动docker容器后,可以使用以下命令获取其ip地址:

docker container inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' <container-id>
Run Code Online (Sandbox Code Playgroud)

替换<container-id>为您的容器 ID 或名称。

所以,在这种情况下,它会是(假设第一个 IP 地址没问题):

ips=($(docker container inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' nuance-apache))
host=${ips[0]}
curl http://$host/
Run Code Online (Sandbox Code Playgroud)