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 驱动,可以设置为 memorymysql 或其他一些数据库。但我绝对可以更新应用程序 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/CD Runner

gitlab-ci-runner安装

Gitlab CI持续集成 - GitLab Runner 安装与注册

更改 Gitlab CI Runner 用户

如何让 gitlab-runner 从指定文件中读取而不是 .gitlab-ci.yml?

Gitlab-ci - 管道无法运行