Найти URL / ip контейнера, запущенного в docker-создать в gitlab ci

#docker #networking #docker-compose #gitlab #containers

#docker #сеть #docker-compose #gitlab #контейнеры

Вопрос:

У меня есть приложение, которое запускается в docker-compose (для приемочного тестирования). Приемочные тесты работают локально, но для отправки запросов к нему требуется хост (или IP) контейнера веб-сервиса, запущенного в docker-compose. Это отлично работает локально, но я не могу найти IP контейнера, когда он запущен на сервере gitlab ci. Я попробовал следующие несколько решений (все из которых работают при локальном запуске, но ни одно из которых не работает в gitlab ci), чтобы найти URL контейнера, запущенного в docker-compose на сервере gitlab ci:

  1. используйте «docker» в качестве хоста. Это работает для приложения, запущенного в docker, но не для docker-compose
  2. используйте docker-inspect, чтобы найти IP контейнера (docker inspect -f ‘{{диапазон.Настройки сети.Сети}}{{.IP-адрес}}{{конец}}’понимание прочитанного)
  3. назначьте статический IP контейнеру, используя сеть в docker-compose.yml (последняя попытка).

Файл gitlab ci можно найти здесь:https://gitlab.com/connorbutch/reading-comprehension/-/blob/9-list-all-assessments/.gitlab-ci.yml

     image: connorbutch/gradle-and-java-11:alpha

variables:
  GRADLE_OPTS: "-Dorg.gradle.daemon=false"
  DOCKER_HOST: "tcp://docker:2375"
  DOCKER_DRIVER: "overlay2"

before_script:
  - export GRADLE_USER_HOME=`pwd`/.gradle

services:
  - docker:stable-dind

stages:
  - build
  - docker_build
  - acceptance_test

unit_test:
  stage: build
  script: ./gradlew check
  cache:
    key: "$CI_COMMIT_REF_NAME"
    policy: pull
    paths:
      - build
      - .gradle

build:
  stage: build
  script:
    - ./gradlew clean quarkusBuild
    - ./gradlew clean build -Dquarkus.package.type=native -Dquarkus.native.container-build=true
  cache:
    key: "$CI_COMMIT_REF_NAME"
    policy: push
    paths:
      - build
      - .gradle
  artifacts:
    paths:
      - reading-comprehension-server-quarkus-impl/build/

docker_build:
  stage: docker_build
  script:
    - cd reading-comprehension-server-quarkus-impl
    - docker build -f infrastructure/Dockerfile -t registry.gitlab.com/connorbutch/reading-comprehension:$CI_COMMIT_SHORT_SHA  .
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push registry.gitlab.com/connorbutch/reading-comprehension:$CI_COMMIT_SHORT_SHA

acceptance_test:
  stage: acceptance_test
  only:
    - merge_requests
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - cd reading-comprehension-server-quarkus-impl/infrastructure
    - export IMAGE_TAG=$CI_COMMIT_SHORT_SHA
    - docker-compose up -d amp; ../../wait-for-it-2.sh
    - cd ../..
    - ./gradlew -DBASE_URL='192.168.0.8' acceptanceTest
  artifacts:
    paths:
      - reading-comprehension/reading-comprehension-server-quarkus-impl/build/
  

Файл docker-compose можно найти здесь:
https://gitlab.com/connorbutch/reading-comprehension/-/blob/9-list-all-assessments/reading-comprehension-server-quarkus-impl/infrastructure/docker-compose.yml

Найдите результат одного из неудачных заданий здесь:https://gitlab.com/connorbutch/reading-comprehension/-/jobs/734771859

 #This file is NOT ever intended for use in production.  Docker-compose is a great tool for running
#database with our application for acceptance testing.
version: '3.3'

networks:
  network:
    ipam:
      driver: default
      config:
        - subnet: 192.168.0.0/24

services:
  db:
    image: mysql:5.7.10
    container_name: "db"
    restart: always
    environment:
       MYSQL_DATABASE: "rc"
       MYSQL_USER: "user"
       MYSQL_PASSWORD: "password"
       MYSQL_ROOT_PASSWORD: "password"
       MYSQL_ROOT_HOST: "%"
    networks:
      network:
        ipv4_address: 192.168.0.4
    ports:
      - '3306:3306'
    expose:
      - '3306'
    volumes:
      - db:/var/lib/mysql
  reading-comprehension-ws:
    image: "registry.gitlab.com/connorbutch/reading-comprehension:${IMAGE_TAG}"
    container_name: "reading-comprehension"
    restart: on-failure
    environment:
      WAIT_HOSTS: "db:3306"
      DB_USER: "user"
      DB_PASSWORD: "password"
      DB_JDBC_URL: "jdbc:mysql://192.168.0.4:3306/rc"
    networks:
      network:
        ipv4_address: 192.168.0.8
    ports:
      - 8080:8080
    expose:
      - 8080
volumes:
  db:
  

Есть ли у кого-нибудь идеи о том, как получить доступ к IP контейнера, работающего в docker-compose на сервере gitlab ci? Любые предложения приветствуются.

Спасибо,

Коннор

Комментарии:

1. Возможно, вы можете добавить приемочные тесты в тот же docker-compose?

2. Я вижу этот IP-адрес в вашем сообщении об ошибке: level=info msg="Default bridge (docker0) is assigned with an IP address 172.18.0.0/16 .

3. Спасибо, что указали на этот выход, mdabdullah! Я продолжу и повторю запуск, пытаясь назначить мой статический IP-адрес в подсети. Вы случайно не знаете (это нормально, если вы этого не делаете), всегда ли этот диапазон одинаков для службы, работающей в docker-compose на gitlab-ci? Кроме того, вы знаете, как я мог бы использовать параметр —bip в gitlab ci? Еще раз спасибо

4. Я назначил ту же подсеть, что и в журналах (и получил сообщение журнала, которое, похоже, подтверждает это), и попытался подключиться к этому ip, однако он все еще был недоступен. Я полагаю, что проблема в том, что служба docker-compose полностью выполняется на другом хосте (например, при запуске docker в docker хостом является «docker», но это не работает для docker compose). У вас есть какие-либо идеи о том, как найти этот хост? Спасибо.

Ответ №1:

Это немного сложно, всего несколько дней назад у меня была аналогичная проблема, но с VPN от CI к клиенту 🙂

РЕДАКТИРОВАТЬ: решение для локальных экземпляров gitlab

Создайте пользовательскую сеть для бегунов gitlab:

 docker network create --subnet=172.16.0.0/28 
 --opt com.docker.network.bridge.name=gitlab-runners 
 --opt com.docker.network.bridge.enable_icc=true 
 --opt com.docker.network.bridge.enable_ip_masquerade=true 
 --opt com.docker.network.bridge.host_binding_ipv4=0.0.0.0 
 --opt com.docker.network.driver.mtu=9001 gitlab-runners
  

Подключить новую сеть к gitlab-бегуны

 # /etc/gitlab-runner/config.toml
[[runners]]
....
   [runners.docker]
   ....
   network_mode = "gitlab-runners"
  

Перезапустите бегуны.

И, наконец, gitlab-ci.yml

 start-vpn:
    stage: prepare-deploy
    image: docker:stable
    cache: {}
    variables:
        GIT_STRATEGY: none
    script:
        - >
            docker run -it -d --rm
            --name vpn-branch-$CI_COMMIT_REF_NAME
            --privileged
            --net gitlab-runners
            -e VPNADDR=$VPN_SERVER
            -e VPNUSER=$VPN_USER
            -e VPNPASS=$VPN_PASSWORD
            auchandirect/forticlient || true amp;amp; sleep 2
        - > 
            docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 
            vpn-branch-$CI_COMMIT_REF_NAME > vpn_container_ip
    artifacts:
        paths:
        - vpn_container_ip
  

И на следующем шаге вы можете использовать что-то вроде:

 before_script:
        - ip route add 10.230.163.0/24 via $(cat vpn_container_ip) # prod/dev
        - ip route add 10.230.164.0/24 via $(cat vpn_container_ip) # test
  

РЕДАКТИРОВАТЬ: решение для gitlab.com

Основываясь на ответе на вопрос gitlab, сопоставление портов в DinD немного отличается от nonDinD gitlab-runner, и для открытых портов вы должны использовать имя хоста ‘docker’.

Пример:

 services:
  - docker:stable-dind

variables:
  DOCKER_HOST: "tcp://docker:2375"

stages:
  - test

test env:
  image: tmaier/docker-compose:latest
  stage: test
  script:
    # containous/whoami with exposed port 80:80
    - docker-compose up -d
    - apk --no-cache add curl
    - curl docker:80              # <------- 
    - docker-compose down
  

Комментарии:

1. Большое вам спасибо за факты! Я использую сообщество gitlab, и у меня нет собственного бегуна. Возможно ли вышеуказанное с помощью community runner (я не верю, что могу использовать файл toml там)? К вашему сведению: я поддержал ответ

2. Ах, я никогда не использовал «облачную» версию сообщества, всегда работая с локальным экземпляром. Но я предполагаю, что вы можете достичь того же результата, используя службу DinD, потому что эти контейнеры DinD используют ту же сеть, что и контейнер «master». И спасибо за поддержку 🙂

3. Спасибо за факт продолжения помощи. Как вы сказали, я использую docker в службе docker, и он отлично работает (он позволяет мне запускать docker и docker compose). Я даже могу вызвать службу, когда она запущена в dind, используя хост ‘docker’, однако я не могу найти хост для запуска приложения в docker-compose. Есть идеи о том, как это найти? Я пробовал localhost, docker, а также назначал статический IP-адрес через сеть в docker-compose, но, похоже, ни один из них не работает.

4. После многих неудачных попыток (какая боль :)) найдено решение для dind: проблема с КОНВЕЙЕРОМ заключается в том, как выполняется сопоставление портов в dind. Вы должны использовать hostname docker вместо чего-либо другого

5. Факт благодарности. Как упоминалось ранее, я использовал docker host, и он работал для приложений, работающих в «чистом» docker. Однако я пробовал это решение для приложений, работающих в docker-compose, и, похоже, оно не работает. Позвольте мне еще раз подтвердить, но я совершенно уверен, что имя хоста может отличаться …. (скрестив пальцы, я допустил опечатку или что-то в этом роде)

Ответ №2:

Я использую docker, а не docker-compose, и решение, приведенное выше, не работает для меня / я использую свой собственный образ на основе узла, в котором я устанавливаю docker amp; buildx следующим образом:

 ARG NODE_VER=lts-alpine

FROM node:${NODE_VER}

ARG BUILDX_VERSION=0.5.1
ARG DOCKER_VERSION=20.10.6
ARG BUILDX_ARCH=linux-arm-v7

RUN apk --no-cache add curl

# install docker
RUN curl -SL "https://download.docker.com/linux/static/stable/armhf/docker-${DOCKER_VERSION}.tgz" | 
        tar -xz --strip-components 1 --directory /usr/local/bin/
COPY docker/modprobe.sh /usr/local/bin/modprobe
# replace node entrypoint by docker one /!
COPY docker/docker-entrypoint.sh /usr/local/bin/
ENV DOCKER_TLS_CERTDIR=/certs
RUN mkdir /certs /certs/client amp;amp; chmod 1777 /certs /certs/client

# download buildx
RUN mkdir -p /usr/lib/docker/cli-plugins 
 amp;amp; curl -L 
      --output /usr/lib/docker/cli-plugins/docker-buildx 
      "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.${BUILDX_ARCH}"
RUN chmod a x /usr/lib/docker/cli-plugins/docker-buildx
RUN mkdir -p /etc/docker amp;amp; echo '{"experimental": true}' > /usr/lib/docker/config.json
  

Мой gitlab-ci.yml содержит:

 image: myimageabove

variables:
  DOCKER_DRIVER: overlay2
  PLATFORMS: linux/arm/v7
  IMAGE_NAME: ${CI_PROJECT_NAME}
  TAG: ${CI_COMMIT_BRANCH}-latest
  REGISTRY: registry.gitlab.com
  REGISTRY_ROOT: mygroup
  WEBSOCKETD_VER: 0.4.1
#  DOCKER_GATEWAY_HOST: 172.17.0.1
  DOCKER_GATEWAY_HOST: docker

services:
  - docker:dind

before_script:
  - docker info

build:
  stage: build
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ${REGISTRY}
    - docker buildx create --use
    - docker buildx build --platform $PLATFORMS --tag "${REGISTRY}/${REGISTRY_ROOT}/${IMAGE_NAME}:${TAG}" --push .

test:
  stage: test
  variables:
    WSD_DIR: /tmp/websocketd
    WSD_FILE: /tmp/websocketd/websocketd
  cache:
    key: websocketd
    paths: 
      - ${WSD_DIR}
  before_script:
# download websocketd and put in cache if needed
    - if [ ! -f ${WSD_FILE} ]; then
        mkdir -p ${WSD_DIR};
        curl -o ${WSD_FILE}.zip -L "https://github.com/joewalnes/websocketd/releases/download/v${WEBSOCKETD_VER}/websocketd-${WEBSOCKETD_VER}-linux_arm.zip";
        unzip -o ${WSD_FILE}.zip websocketd -d ${WSD_DIR};
        chmod 755 ${WSD_FILE};
      fi;
    - mkdir /home/pier
    - cp -R ./test/resources/* /home/pier
    # get websocketd from cache
    - cp ${WSD_FILE} /home/pier/Admin/websocketd
    # setup envt variables
    - JWT_KEY=$(cat /home/pier/Services/Secrets/WEBSOCKETD_KEY)
    # - DOCKER_GATEWAY_HOST=$(/sbin/ip route|awk '/default/ { print $3 }')
    # - DOCKER_GATEWAY_HOST=$(hostname)
    - ENVT="-e BASE_URL=/ -e JWT_KEY=$JWT_KEY -e WEBSOCKETD_KEY=$JWT_KEY -e WEBSOCKET_URL=ws://${DOCKER_GATEWAY_HOST:-host.docker.internal}:8088 -e SERVICES_DIR=/home/pier/Services"
    - VOLUMES='-v /tmp:/config -v /home/pier/Services:/services -v /etc/wireguard:/etc/wireguard'

  script:
    # start websocketd
    - /home/pier/start.sh amp;
    # start docker pier admin
    - docker run -p 4000:4000 ${ENVT} ${VOLUMES} ${REGISTRY}/${REGISTRY_ROOT}/${IMAGE_NAME}:${TAG}
    # run postman tests
    - newman run https://api.getpostman.com/collections/${POSTMAN_COLLECTION_UID}?apikey=${POSTMAN_API_KEY}

deploy:
  stage: deploy
  script:
    # just push to docker hub
    - docker login -u "$DOCKERHUB_REGISTRY_USER" -p "$DOCKERHUB_REGISTRY_PASSWORD" ${DOCKERHUB}
    - docker buildx build --platform $PLATFORMS --tag "${DOCKERHUB}/mygroup/${IMAGE}:${TAG}" --push .
  

Когда я запускаю это, задание сборки работает нормально, затем тест «before_script» работает, но при запуске скрипта я получаю следующую трассировку:

 # <= this starts the websocketd server locally on port 8088 =>
$ /home/pier/start.sh amp;
# <= this starts the image I just built which should connect to the above websocketd server =>
$ docker run -p 4000:4000 ${ENVT} ${VOLUMES} ${REGISTRY}/${REGISTRY_ROOT}/${IMAGE_NAME}:${TAG}     
# <= trace of the websocketd server start with url ws://runner-hbghjvzp-project-22314059-concurrent-0:8088/ =>
Tue, 11 May 2021 12:08:13  0000 | INFO   | server     |  | Serving using application   : ./websocket-server.py 
Tue, 11 May 2021 12:08:13  0000 | INFO   | server     |  | Starting WebSocket server   : ws://runner-hbghjvzp-project-22314059-concurrent-0:8088/
# <= trace of the image start saying it tires to conenct to the websocketd server
Websocket connecting to ws://docker:8088 ...
Listen on port 4000
# <= trace with ENOTFOUND on "docker" address =>
websocket connection failed: Error: getaddrinfo ENOTFOUND docker
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:67:26) {
  errno: -3008,
  code: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'docker'
}
/pier/storage/websocket-client.js:52
            throw err;
            ^
Error: getaddrinfo ENOTFOUND docker
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:67:26) {
  errno: -3008,
  code: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'docker'
}
Cleaning up file based variables 00:01
ERROR: Job failed: exit code 1
 
  

Я пробовал другие способы, такие как:

 websocket connection failed: Error: getaddrinfo ENOTFOUND host.docker.internal
websocket connection failed: Error: connect ETIMEDOUT 172.17.0.1:8088 # <= same error when trying $(/sbin/ip route|awk '/default/ { print $3 }') =>
websocket connection failed: Error: getaddrinfo ENOTFOUND runner-meuessxe-project-22314059-concurrent-0  # using $(hostname)
  

Из новой идеи…
Был бы очень признателен за любую помощь по этому вопросу.