dockerseleniumgitlab-cigitlab-ci-runnerselenium-rc

Why can't I connect to selenium docker-compose service from my GitLab job?


I am running selenium test in Gitlab CI, but have problem with setting remote URL correctly when using the gitlab runner instead of my computer.

The IP address of the runner is 192.168.xxx.xxx. And when I run the pipeline, I got the IP address of selenium hub is 172.19.0.2/16. I tried both, and both failed. I also tried to use the name of the selenium hub container http://selenium__hub, but it also failed.

The docker-compose.yml is:

version: "3"
services:
  chrome:
    image: selenium/node-chrome:4.0.0-20211013
    container_name: chrome
    shm_size: 2gb
    depends_on:
      - selenium-hub
    volumes:
      - ./target:/home/seluser/Downloads
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_GRID_URL=http://localhost:4444
    ports:
      - "6900:5900"
  edge:
    image: selenium/node-edge:4.0.0-20211013
    container_name: edge
    shm_size: 2gb
    depends_on:
      - selenium-hub
    volumes:
      - ./target:/home/seluser/Downloads
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_GRID_URL=http://localhost:4444
    ports:
      - "6901:5900"
  firefox:
    image: selenium/node-firefox:4.0.0-20211013
    container_name: firefox
    shm_size: 2gb
    depends_on:
      - selenium-hub
    volumes:
      - ./target:/home/seluser/Downloads
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_GRID_URL=http://localhost:4444
    ports:
      - "6902:5900"
  selenium-hub:
    image: selenium/hub:4.0.0-20211013
    container_name: selenium-hub
    ports:
      - "4444:4444"

the gitlab runner's config file looks like this:

[[runners]]
  name = "selenium"
  url = "https://gitlab.myhost.at"
  token = "xxxxxxxx"
  executor = "docker"
  privileged = true
  links = ["selenium__hub:hub"]
  [runners.docker]
    image = "docker:stable"
    privileged = true

The remote url I have tried are:

WebDriver driver = new RemoteWebDriver(new URL("http://192.168.xxx.xxx:4444/wd/hub"), cap);
WebDriver driver = new RemoteWebDriver(new URL("http://172.19.0.2:4444/wd/hub"), cap);
WebDriver driver = new RemoteWebDriver(new URL("http://selenium__hub:4444/wd/hub"), cap);

How can I get this to work with GitLab runner?


Solution

  • The issue here is when your job launches containers using docker-compose, the hostnames in the docker network are not known to your job container.

    Assuming you are using the docker:dind service in your job to use docker-compose and you are trying to connect to your services started with docker-cmpose from your job, you would need to use the hostname docker to reach your services through their mapped ports.

    So your corrected code would be as follows:

    WebDriver driver = new RemoteWebDriver(new URL("http://docker:4444/wd/hub"), cap);
    

    Why "docker"?

    The reason this is needed is because your containers are running 'on' a remote docker daemon service -- the docker:dind container. When you invoke docker-compose your job container talks to the docker:dind container which in turn spins up a new docker network and creates the docker containers in your compose file on that network.

    Your job container has no knowledge of (or route to) that network, nor does it know the hostnames of the services. The service daemon itself also is running on a separate network from your runner -- because it is another docker container that was created by the docker executor; so your runner IP won't work either.

    However, the docker executor does create a link to your services: I.E. the docker:dind service. So you can reach that container by the docker hostname. Additionally, your compose file indicates that the hub service should make a port mapping of 4444:4444 from the host -> continaer. The host, in this case, means the docker:dind service. So calling http://docker:4444 from your job reaches the hub service.

    Why doesn't "links" work?

    Finally, to cover one last detail, it looks like in your runner configuration you expected your links to allow you to communicate with the hub container by hostname:

      links = ["selenium__hub:hub"]
    

    In the runner configuration, it's true that the links configuration, in general, would allow your job to communicate with containers by hostname. However, this configuration is errant for two reasons:

    1. This configuration can only apply to containers alongside your runner container. That is other containers registered on the host daemon -- not containers created by other docker daemons, like the ones created with docker-compose in your job by talking to the docker:dind service daemon.
    2. Even if you could reach containers created by other daemons, or your hub container was created by the host daemon, the parameters are wrong, according to the URLS you tried. This configuration basically says "expose the selenium__hub container as the FQDN hub" -- but you never tried the hostname hub.

    There is nothing to fix here because (1) is not a fixable error when using docker-in-docker.

    Alternatives

    Alternatively, you can utilize GitLab's services: capability to run the hub and/or browser containers.

    my_job:
      services:
        - docker:dind
        - name: selenium/hub:4.0.0-20211013
          alias: hub  # this is the hostname
    

    You could even do this as a runner configuration and give it a special tag and jobs needing remote browsers can just add the necessary tags: key(s) to reduce the amount of job configuration needed.

    You may also be interested to see one of my other answers on how FF_NETWORK_PER_BUILD feature flag can affect how networking works between docker containers and jobs/services.