Gitlab CI runner 无法暴露嵌套 Docker 容器的端口
Posted
技术标签:
【中文标题】Gitlab CI runner 无法暴露嵌套 Docker 容器的端口【英文标题】:Gitlab CI runner not able to expose ports of nested Docker containers 【发布时间】:2017-05-24 09:37:54 【问题描述】:当使用 GitLab CI 以及 gitlab-ci-multi-runner
时,我无法让内部启动的 Docker 容器将它们的端口暴露给“主机”,这是运行构建的 Docker 映像。
我的.gitlab-ci.yml
文件:
test:
image: docker
stage: test
services:
- docker:dind
script:
- APP_CONTAINER_ID=`docker run -d --privileged -p "9143:9143" appropriate/nc nc -l 9143`
- netstat -a
- docker exec $APP_CONTAINER_ID netstat -a
- nc -v localhost 9143
我的命令:
gitlab-ci-multi-runner exec docker --docker-privileged test
输出:
$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 runner--project-1-concurrent-0:54664 docker:2375 TIME_WAIT
tcp 0 0 runner--project-1-concurrent-0:54666 docker:2375 TIME_WAIT
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
$ docker exec $APP_CONTAINER_ID netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:9143 0.0.0.0:* LISTEN
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
$ nc -v localhost 9143
ERROR: Build failed: exit code 1
FATAL: exit code 1
我在这里做错了什么?
以下是原始问题 - 上面是一个更短、更易于测试的示例
我有一个侦听端口9143
的应用程序映像。它的启动和配置是通过docker-compose.yml
管理的,并且在我的本地机器上使用docker-compose up
运行良好-我可以毫无问题地访问localhost:9143
。
但是,当通过共享运行器在 GitLab CI(gitlab.com
版本)上运行时,端口似乎没有暴露。
我.gitlab-ci.yml
的相关部分:
test:
image: craigotis/buildtools:v1
stage: test
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com/craigotis/myapp
- docker-compose up -d
- sleep 60 # a temporary hack to get the logs
- docker-compose logs
- docker-machine env
- docker-compose port app 9143
- netstat -a
- docker-compose ps
- /usr/local/bin/wait-for-it.sh -h localhost -p 9143 -t 60
- cd mocha
- npm i
- npm test
- docker-compose down
输出是:
$ docker-compose logs
...
app_1 | [Thread-1] INFO spark.webserver.SparkServer - == Spark has ignited ...
app_1 | [Thread-1] INFO spark.webserver.SparkServer - >> Listening on 0.0.0.0:9143
app_1 | [Thread-1] INFO org.eclipse.jetty.server.Server - jetty-9.0.z-SNAPSHOT
app_1 | [Thread-1] INFO org.eclipse.jetty.server.ServerConnector - Started ServerConnector@6919dc5HTTP/1.10.0.0.0:9143
...
$ docker-compose port app 9143
0.0.0.0:9143
$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 runner-e11ae361-project-1925166-concurrent-0:53646 docker:2375 TIME_WAIT
tcp 0 0 runner-e11ae361-project-1925166-concurrent-0:53644 docker:2375 TIME_WAIT
tcp 0 0 runner-e11ae361-project-1925166-concurrent-0:53642 docker:2375 TIME_WAIT
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
$ docker-compose ps
stty: standard input: Not a tty
Name Command State Ports
----------------------------------------------------------------------------------------
my_app_1 wait-for-it.sh mysql_serve ... Up 8080/tcp, 0.0.0.0:9143->9143/tcp
mysql_server docker-entrypoint.sh --cha ... Up 3306/tcp
$ /usr/local/bin/wait-for-it.sh -h localhost -p 9143 -t 60
wait-for-it.sh: waiting 60 seconds for localhost:9143
wait-for-it.sh: timeout occurred after waiting 60 seconds for localhost:9143
我的docker-compose.yml
的内容:
version: '2'
networks:
app_net:
driver: bridge
services:
app:
image: registry.gitlab.com/craigotis/myapp:latest
depends_on:
- "db"
networks:
- app_net
command: wait-for-it.sh mysql_server:3306 -t 60 -- java -jar /opt/app*.jar
ports:
- "9143:9143"
db:
image: mysql:latest
networks:
- app_net
container_name: mysql_server
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
似乎我的应用程序容器正在侦听9143
,并且它已正确地暴露给共享的 GitLab 运行器,但它似乎并没有真正暴露。它在我的本地机器上运行良好 - 是否需要一些特殊的解决方法/调整才能使这项工作在在 GitLab 上运行的 Docker 容器中工作?
【问题讨论】:
在您的本地机器上运行它的主机地址..以及您在本地机器上的操作系统是什么 你发现了吗? 我制作了一个最小的工作示例,供其他人使用:gitlab.com/mslot/dind.sandbox 【参考方案1】:当使用docker:dind
时,会创建一个容器,并在其中设置您的 docker-compose 容器。它将端口暴露给docker:dind
容器内的本地主机。您无法在代码执行的环境中以 localhost
的身份访问它。
为您设置了主机名docker
以引用此docker:dind
容器。您可以使用cat /etc/hosts
查看。
您应该使用docker:9143
,而不是引用localhost:9143
。
【讨论】:
我检查了/etc/hosts,确实只有特殊名称“docker”可用(没有“mysql”等)。 如果我用docker run
启动一个内部容器,它是否也可以工作?因为docker
对我不起作用(在 GitLab CI 内部)。
我还检查了cat /etc/hosts
。它不包含docker
名称。【参考方案2】:
官方gitab-ci on gitlab.com documentation指的是example of PostgreSQL
Its working CI 不会尝试连接到 localhost,而是尝试连接到 service name
services
关键字定义了另一个在构建期间运行的 docker 镜像,并链接到 image 关键字定义的 docker 镜像。这允许您在构建期间访问服务映像。可以在主机名
mysql
下访问 MySQL 的服务容器。因此,为了访问您的数据库服务,您必须连接到名为mysql
的主机,而不是套接字或 @ 987654329@.
您可以检查这是否适用于您的情况,并尝试在app:9143
而不是localhost:9143
中访问您的应用程序服务。
【讨论】:
我实际上在我的.gitlab-ci.yml
中使用services
来处理docker:dind
等其他内容。然而,我想在测试阶段使用 docker-compose
的原因是,我的 MySQL、应用程序和其他相关图像(在测试环境中)的编排不需要在 docker-compose.yml
和 @987654337 中复制@。我已经有一个可以工作的 Compose 文件,我花了一些时间完善它——它可以在开发人员机器上运行(而 .gitlab-ci.yml
没有用),所以如果可能的话,我真的很想重用它。
@CraigOtis 我同意,但是,只是为了测试,你能检查一下使用服务名称是否效果更好吗?
不幸的是,仔细观察后,我无法以这种方式将我的图像作为服务运行 - 它们需要为与测试相关的配置设置特定的环境变量,而我的应用程序图像(例如)需要等待 MySQL 启动并运行,然后再启动。 (而且 GitLab CI 配置似乎要求服务只是一个字符串数组......)
@CraigOtis 您的应用程序能否以正确的方式启动,并且作为 CMD,使用等待它的脚本来等待 SQL 启动? (如docs.docker.com/compose/startup-order)
我担心的是,应用映像现在与 MySQL 紧密耦合,而它目前由环境变量 STORAGE_TYPE
驱动,可以设置为 memory
、mysql
或其他一些数据库。但我绝对可以更新应用程序 self 以在3306
上进行轮询,并且在可以访问之前不连接。无论如何,这可能是一个很好的改进 - 我会尝试一下。【参考方案3】:
您的docker-compose.yml
似乎没问题。
但我认为您的 ip 或端口路由有错误。 正如我从您的共享信息中看到的那样,您的应用程序正在 port 9143 上以 0.0.0.0 作为 0.0.0.0:9143 运行.
您正在以localhost:9143
的身份访问它,
可以解释为127.0.0.1:9143
。
根据this.
127.0.0.1 is the loopback address (also known as localhost).
0.0.0.0 is a non-routable meta-address used to designate an invalid, unknown, or non-applicable target (a ‘no particular address’ place holder).
您能否尝试在127.0.0.1:9143
上运行您的应用,然后分享结果。
更新
或者你可以使用 service 通过服务名来运行它 正如documentation 建议:
services 关键字定义了另一个在构建期间运行的 docker 镜像,并链接到 image 关键字定义的 docker 镜像。这允许您在构建期间访问服务映像。
可以在 主机名 mysql
下访问 MySQL 的服务容器。
因此,为了访问您的数据库服务,您必须连接到名为 mysql 而不是套接字或localhost
的主机。
【讨论】:
相信您对0.0.0.0的理解是错误的,继续阅读您链接的文章,您会发现“在服务器的上下文中,0.0.0.0表示本地机器上的所有IPv4地址。”在这种情况下,它只是意味着“监听所有接口”(绑定到 127.0.0.1 的服务器无法从机器外部访问)。【参考方案4】:通常,docker 机器不会在 localhost 上运行,而是在具有其他 IP 地址的 docker 主机上运行。尝试使用docker-machine ip
获取您的 docker 主机 ip。
【讨论】:
问题不在于我确定了它运行的主机名——而是它是否通过使用 Docker Compose 或带有-p "...:..."
的常规 Docker 运行在 localhost
上,或者我是否配置我的应用程序映像在 GitLab 配置中作为 service:
运行,当它实际在 GitLab CI 上运行时,应用程序映像无法访问 - 即使它在我的本地计算机上运行良好,并且似乎开始正常(并保持运行)根据应用程序日志。
也许我没有关注你(从未使用过 GitLab CI),但我只是说,因为任何 docker 客户端都可以配置为针对 VM 或任何其他远程 docker 主机(docker- machine env 用于设置 DOCKER_HOST),可能是 GitLab 的 docker 客户端配置为针对 localhost 以外的主机运行,在这种情况下,容器被创建并运行,但在不同的主机上,具有不同的 ip (并且在您正在等待的本地主机上不可用)。所以,只是想知道你是否尝试过检查。
感谢 Yoav,我的问题已更新为更简单的示例。【参考方案5】:
如果您 GitLab CI Runner 通过套接字绑定使用 Docker-executor 运行,请使用 host.docker.internal
-host,因为您的应用程序在主机上运行但不在客人。
# Run and ssh into dind-container
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock docker:20.10-dind /bin/sh
# Next commands run in dind-container
docker run --rm -d -p 8888:80 nginx:alpine
...
wget -qO- "http://localhost:8888/"
wget: can't connect to remote host (127.0.0.1): Connection refused
wget -qO- "http://127.0.0.1:8888/"
wget: can't connect to remote host (127.0.0.1): Connection refused
wget -qO- "http://docker:8888/"
wget: bad address 'docker:8888'
wget -qO- "http://host.docker.internal:8888/"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
【讨论】:
以上是关于Gitlab CI runner 无法暴露嵌套 Docker 容器的端口的主要内容,如果未能解决你的问题,请参考以下文章
Gitlab CI持续集成 - GitLab Runner 安装与注册