Gitlab CI runner 无法公开嵌套 Docker 容器的端口

Gitlab CI runner not able to expose ports of nested Docker containers

使用 GitLab CI 以及 gitlab-ci-multi-runner 时,我无法让内部启动的 Docker 容器将其端口暴露给 "host" ,这是构建为 运行.

的 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@6919dc5{HTTP/1.1}{0.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 运行器,但它似乎并没有真正暴露.它在我的本地机器上工作正常 - 是否有一些特殊的 workaround/tweak 我需要在 GitLab 上 inside 一个 Docker 容器 运行 中完成这项工作?

官方gitab-ci on gitlab.com documentation refers to the example of PostgreSQL

Its working CI does not try to connect to localhost, but rather to the service name

The services keyword defines just another docker image that is run during your build and is linked to the docker image that the image keyword defines. This allows you to access the service image during build time.

The service container for MySQL will be accessible under the hostname mysql.
So, in order to access your database service you have to connect to the host named mysql instead of a socket or localhost.

您可以检查这是否适用于您的情况,并尝试在 app:9143 而不是 localhost:9143 中访问您的应用程序服务。

通常 docker 机器不会 运行 在本地主机上,而是在具有其他 IP 地址的 docker 主机上。尝试使用 docker-machine ip 获取您的 docker 主机 ip。

你的docker-compose.yml好像没问题。

但我认为您的 ip 或端口路由有误。 正如我从您的共享信息中看到的,您的应用程序 运行ning 在 port 91430.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 然后分享结果。

更新

或者您可以通过服务名称使用服务 运行 documentation 建议:

services 关键字只定义另一个 docker 图像,它在构建期间是 运行,并链接到 image 关键字定义的 docker 图像。这允许您在构建期间访问服务图像。

MySQL 的服务容器可以在 主机名 mysql 下访问。 因此,为了访问您的数据库服务,您必须连接到名为 mysql 的主机,而不是套接字或 localhost.

当使用 docker:dind 时,会创建一个容器,并在其中设置您的 docker-compose 容器。它向 docker:dind 容器中的本地主机公开端口。您无法从执行代码的环境中以 localhost 的形式访问它。

已设置主机名 docker 供您引用此 docker:dind 容器。您可以使用 cat /etc/hosts.

检查

您应该使用 docker:9143.

而不是引用 localhost:9143

如果你 GitLab CI Runner 运行 与 Docker-executor via socket绑定使用 host.docker.internal-host,因为您的应用程序 运行ning 在主机上而不是客户机上。

# 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>
...