Как получить доступ к порту хоста из Docker-контейнера

281

У меня есть докер-контейнер с Дженкинсом. Как часть процесса сборки, мне нужно получить доступ к веб-серверу, который работает локально на хост-компьютере. Есть ли способ, с помощью которого хост-веб-сервер (который можно настроить для работы на порте) может быть открыт для контейнера jenkins?

РЕДАКТИРОВАТЬ: я работаю Docker изначально на машине Linux.

ОБНОВИТЬ:

В дополнение к ответу @larsks ниже, чтобы получить IP-адрес Host IP от хост-машины, я делаю следующее:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Три Нгуен
источник
Использование комментария, поскольку это ужасный ответ, но я полагаю, что вы обычно можете получить к нему доступ 172.17.1.78 - если только это не настройка boot2docker.
CashIsClay
@CashIsClay Я попробовал это, и все еще получил эту ошибкуcurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
Вы не указали; Вы используете boot2docker или Docker работает под Linux?
Жаворонки
@larsks извините, я только что обновил вопрос - я запускаю его изначально в Linux.
Три Нгуен

Ответы:

207

При исходном запуске Docker в Linux вы можете получить доступ к службам хоста, используя IP-адрес docker0интерфейса. Изнутри контейнера это будет ваш маршрут по умолчанию.

Например, в моей системе:

$ ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::f4d2:49ff:fedd:28a0/64 scope link 
       valid_lft forever preferred_lft forever

И внутри контейнера:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 

Достаточно легко извлечь этот IP-адрес с помощью простого сценария оболочки:

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip

Возможно, вам придется изменить iptablesправила на вашем хосте, чтобы разрешить соединения из контейнеров Docker. Нечто подобное сделает свое дело:

# iptables -A INPUT -i docker0 -j ACCEPT

Это позволит получить доступ к любым портам на хосте из контейнеров Docker. Обратите внимание, что:

  • Правила iptables упорядочены, и это правило может или не может делать правильные вещи в зависимости от того, какие другие правила стоят перед ним.

  • вы сможете получить доступ только к хост-службам, которые либо (а) прослушивают INADDR_ANY(то есть 0.0.0.0), либо явно прослушивают docker0интерфейс.

larsks
источник
1
Спасибо. Получение IP-адреса было всем, что мне было нужно, без изменения iptables :)
Три Нгуен,
2
Соответствующая ссылка: добавление записей в контейнер / etc / hosts
хронос
7
Как насчет Docker для MAC? AFAIK нет сети docker0, доступной для "Docker для MAC". В таком случае, как я могу подключиться к хосту из контейнера?
Виджей
17
Если вы используете Docker для MAC v 17.06 или выше, просто используйте docker.for.mac.localhostвместо localhostили 127.0.0.1. Вот док .
Merito
1
Я использовал имя хоста своего хоста вместо получения IP-адреса (команда hostname на хосте)
Marek F
313

Для macOS и Windows

Docker v 18.03 и выше (с 21 марта 2018 г.)

Используйте свой внутренний IP-адрес или подключитесь к специальному DNS-имени, host.docker.internalкоторое будет преобразовано во внутренний IP-адрес, используемый хостом.

Ожидается поддержка Linux https://github.com/docker/for-linux/issues/264

MacOS с более ранними версиями Docker

Докер для Mac v 17.12 до 18.02

То же, что и выше, но используйте docker.for.mac.host.internalвместо этого.

Докер для Mac с 17.06 по 17.11

То же, что и выше, но используйте docker.for.mac.localhostвместо этого.

Докер для Mac 17.05 и ниже

Чтобы получить доступ к хост-машине из док-контейнера, вы должны прикрепить псевдоним IP к вашему сетевому интерфейсу. Вы можете связать любой IP-адрес, который хотите, просто убедитесь, что вы не используете его ни с чем другим.

sudo ifconfig lo0 alias 123.123.123.123/24

Затем убедитесь, что ваш сервер прослушивает IP-адрес, упомянутый выше или 0.0.0.0. Если он прослушивает localhost, 127.0.0.1он не примет соединение.

Затем просто укажите свой док-контейнер на этот IP, и вы получите доступ к хост-машине!

Для проверки вы можете запустить что-то вроде curl -X GET 123.123.123.123:3000внутри контейнера.

Псевдоним будет сбрасываться при каждой перезагрузке, поэтому при необходимости создайте сценарий запуска.

Решение и дополнительная документация здесь: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Джанна аннала
источник
Я успешно проверил это. Не нужно выключать брандмауэр
alvaro g
15
Начиная с 17.06, выпуска в июне 2017 года, их рекомендуется подключать к специальному DNS-имени только для Mac, docker.for.mac.localhostкоторое будет преобразовываться во внутренний IP-адрес, используемый хостом! ... Я проверил это, и это действительно работает! :)
Кыр
Найдите ответ для пользователей Mac, таких как я. Спасибо. Однако я не понимаю, зачем ip route show | awk '/default/ {print $3}'давать один IP, а docker.for.mac.localhostдругой.
17
8
изменено на host.docker.internal docs.docker.com/docker-for-mac/networking/…
Снежок
1
Рекомендуется использовать host.docker.internalв контейнере Docker и настроить 127.0.0.1 host.docker.internalв файле hosts .
Junlin
86

Используйте --net="host"в вашей docker runкоманде, тогда localhostв вашем док-контейнере будет указывать на ваш докер хост.

samthebest
источник
15
Насколько я понимаю, он не будет работать для тех, кто использует Dpcker для Windows / Docker для Mac, поскольку контейнеры работают в виртуализированных средах: Hyper-V (Windows) / xhyve (Mac)
ninjaboy
6
ЭТОТ! Это ответ!
user3751385
15
Просто для справки: в Docker Componse, network_mode: "host"
jbarros
Это не работает для меня в Docker для Mac версии 19.03.1 на клиенте и сервере. Хотелось бы, чтобы это работало, но это не так.
глина
1
Даже если я нахожусь на Mac, он работает, когда вам нужна связь между двумя контейнерами докеров, и когда я преобразую все приложения в образ докера, мне этого достаточно
bormat
28

Решение с docker-compose: для доступа к сервису на основе хоста вы можете использовать network_modeпараметр https://docs.docker.com/compose/compose-file/#network_mode

version: '3'
services:
  jenkins:
    network_mode: host

РЕДАКТИРОВАТЬ 2020-04-27: рекомендуется для использования только в местной среде разработки.

Vovan
источник
Тогда как получить доступ к Дженкинс? Кажется, что переадресация портов не работает, если используется режим сети хоста
Джефф Тиан
1
это очень рискованное решение и совсем не рекомендуется. мы НЕ должны открывать нашу хост-сеть для контейнеров, если это явно не нужно
Fatemeh Majd
12

В настоящее время самый простой способ сделать это на Mac и Windows - использовать хост host.docker.internal, который разрешает IP-адрес хост-машины. К сожалению, он не работает на Linux (по состоянию на апрель 2018 года).

Томаш Фейфар
источник
Это решение работало с версией Docker 19.03.1. Многие другие решения, приведенные здесь, не работают. Это задокументировано по адресу docs.docker.com/docker-for-mac/networking/…
глина,
12

Я создал докер-контейнер для выполнения именно этого https://github.com/qoomon/docker-host

Затем вы можете просто использовать имя контейнера dns для доступа к хост-системе, например curl http://dockerhost:9200

qoomon
источник
Это умное решение. Вы знаете что-нибудь, использующее это с большим количеством трафика? Возможно проксирование всего трафика через этот контейнер.
bcoughlan
3
Да, он работает довольно хорошо, почти без издержек, потому что он работает только с loopback-устройством
qoomon
11

Мы обнаружили, что более простым решением для всего этого сетевого мусора является использование доменного сокета для службы. Если вы все равно пытаетесь подключиться к хосту, просто смонтируйте сокет как том, и вы уже в пути. Для postgresql это было так просто:

docker run -v /var/run/postgresql:/var/run/postgresql

Затем мы просто настраиваем соединение с нашей базой данных, чтобы использовать сокет вместо сети. Буквально так просто.

mlissner
источник
Это отличное решение. Хорошая работа!
платный ботаник
2
К вашему сведению, мы столкнулись с большой проблемой: Docker для Mac не поддерживает сокеты как подключенные тома. Это прошло гладко, пока человек Mac не попробовал это. :(
mlissner
Спасибо! Работал как прелесть в Linux!
Марсело Кардосо
7

Я исследовал различные решения и нашел это наименее хакерское решение:

  1. Определите статический IP-адрес для IP-адреса шлюза моста.
  2. Добавьте IP-адрес шлюза в качестве дополнительной записи в extra_hostsдирективе.

Единственным недостатком является то, что если у вас есть несколько сетей или проектов, вы должны убедиться, что их диапазон IP-адресов не конфликтует.

Вот пример Docker Compose:

version: '2.3'

services:
  redis:
    image: "redis"
    extra_hosts:
      - "dockerhost:172.20.0.1"

networks:
  default:
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
        gateway: 172.20.0.1

Затем вы можете получить доступ к портам на хосте из контейнера, используя имя хоста "dockerhost".

bcoughlan
источник
7

Для систем Linux вы можете - начиная с основной версии 20.04- теперь также общаться с хостом через host.docker.internal. Это не будет работать автоматически , но вам нужно указать следующий флаг запуска:

--add-host=host.docker.internal:host-gateway

Видеть

Самуил
источник
3

Вы можете получить доступ к локальному веб-серверу, который работает на вашем хост-компьютере, двумя способами.

  1. Подход 1 с публичным IP

    Используйте общедоступный IP-адрес хост-машины для доступа к веб-серверу в контейнере док-станции Jenkins.

  2. Подход 2 с хост-сетью

    Используйте «--net host», чтобы добавить докер-контейнер Jenkins в сетевой стек хоста. Контейнеры, которые развернуты в стеке хоста, имеют полный доступ к интерфейсу хоста. Вы можете получить доступ к локальному веб-серверу в Docker-контейнере с частным IP-адресом хост-машины.

NETWORK ID          NAME                      DRIVER              SCOPE
b3554ea51ca3        bridge                    bridge              local
2f0d6d6fdd88        host                      host                local
b9c2a4bc23b2        none                      null                local

Запустите контейнер с хост-сетью Eg: docker run --net host -it ubuntuи запустите ifconfigсписок всех доступных сетевых IP-адресов, доступных из док-контейнера.

Например: я запустил сервер nginx на своем локальном хост-компьютере и смог получить доступ к URL-адресам веб-сайта nginx из док-контейнера Ubuntu.

docker run --net host -it ubuntu

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a604f7af5e36        ubuntu              "/bin/bash"         22 seconds ago      Up 20 seconds                           ubuntu_server

Доступ к веб-серверу Nginx (работающему на локальном хосте) из док-контейнера Ubuntu с IP-адресом частной сети.

root@linuxkit-025000000001:/# curl 192.168.x.x -I
HTTP/1.1 200 OK
Server: nginx/1.15.10
Date: Tue, 09 Apr 2019 05:12:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 Mar 2019 14:04:38 GMT
Connection: keep-alive
ETag: "5c9a3176-264"
Accept-Ranges: bytes
Сриниваса Редди
источник
3

Для docker-composeиспользования моста сети для создания частной сети между контейнерами Принятым решения с использованием docker0не работает , потому что интерфейс выхода из контейнеров не , docker0но вместо этого он случайно сгенерированный идентификатора интерфейса, такие как:

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255

К сожалению, этот случайный идентификатор не предсказуем и будет меняться каждый раз, когда compose должен воссоздавать сеть (например, при перезагрузке хоста). Мое решение состоит в том, чтобы создать частную сеть в известной подсети и настроить iptablesдля принятия этого диапазона:

Создать файл сниппета:

version: "3.7"

services:
  mongodb:
    image: mongo:4.2.2
    networks:
    - mynet
    # rest of service config and other services removed for clarity

networks:
  mynet:
    name: mynet
    ipam:
      driver: default
      config:
      - subnet: "192.168.32.0/20"

Вы можете изменить подсеть, если этого требует ваша среда. Я произвольно выбрал 192.168.32.0/20с помощьюdocker network inspect чтобы увидеть, что было создано по умолчанию.

Настройте iptablesна хосте, чтобы разрешить частную подсеть в качестве источника:

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT

Это самое простое из возможных iptablesправил. Вы можете добавить другие ограничения, например, по порту назначения. Не забывайте сохранять свои правила iptables, когда вы счастливы, что они работают.

Преимущество этого подхода в том, что он повторяется и, следовательно, автоматизирован. Я использую анзибль - х templateмодуль для развертывания моего файла с созданием сообщения подстановки переменной , а затем использовать iptablesи shellмодули для настройки и сохраняются правила брандмауэра, соответственно.

Энди Браун
источник
Я видел многократные ответы iptables -A INPUT -i docker0 -j ACCEPT, но это не помогло мне, в то время как iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPTпредложенные здесь решили мою проблему.
Терри Браун
1

Это старый вопрос, на который было дано много ответов, но ни один из них не соответствовал моему контексту. В моем случае контейнеры очень скудны и не содержат никаких сетевых инструментов, необходимых для извлечения IP-адреса хоста из контейнера.

Кроме того, в этом --net="host"подходе используется очень грубый подход, который неприменим, когда требуется иметь хорошо изолированную конфигурацию сети с несколькими контейнерами.

Итак, мой подход заключается в том, чтобы извлечь адрес хоста на стороне хоста, а затем передать его в контейнер с --add-hostпараметром:

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name

или, сохраните IP-адрес хоста в переменной среды и используйте переменную позже:

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name

Затем docker-hostон добавляется в файл хостов контейнера, и вы можете использовать его в строках подключения к базе данных или URL-адресах API.

Passiday
источник
0

Когда у вас есть два уже созданных образа Docker, и вы хотите установить два контейнера для связи друг с другом.

Для этого вы можете удобно запускать каждый контейнер со своим собственным именем - и использовать флаг --link для обеспечения связи между ними. Вы не получаете это во время сборки докера, хотя.

Когда вы находитесь в сценарии, как я, и это ваш

docker build -t "centos7/someApp" someApp/ 

Это ломается, когда вы пытаетесь

curl http://172.17.0.1:localPort/fileIWouldLikeToDownload.tar.gz > dump.tar.gz

и вы застреваете на "curl / wget", не возвращая "route to host".

Причина заключается в безопасности, установленной докером, который по умолчанию запрещает связь между контейнером к хосту или другим контейнерам, работающим на вашем хосте. Должен сказать, что для меня это было довольно неожиданным, вы ожидаете, что экосистема докеров, работающих на локальной машине, просто безупречно может получить доступ друг к другу без особых препятствий.

Объяснение этого подробно описано в следующей документации.

http://www.dedoimedo.com/computers/docker-networking.html

Предлагаются два быстрых обходных пути, которые помогут вам двигаться, понизив безопасность сети.

Самая простая альтернатива - просто выключить брандмауэр или разрешить все. Это означает выполнение необходимой команды, которая может быть systemctl stop firewalld, iptables -F или эквивалентной.

Надеюсь, эта информация поможет вам.

99Sono
источник
3
Так же, как примечание, в --linkнастоящее время устарела
Будрис
0

Для меня (Windows 10, Docker Engine v19.03.8) это была смесь https://stackoverflow.com/a/43541732/7924573 и https://stackoverflow.com/a/50866007/7924573 .

  1. измените host / ip на host.docker.internal,
    например: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. установите для network_mode значение bridge (если вы хотите сохранить переадресацию портов; если не используете хост ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge или альтернативно: используйте, --net="bridge"если вы не используете docker-compose (аналогично https://stackoverflow.com/a/48806927/7924573 )
    Как указывалось в предыдущих ответах: это следует использовать только в локальной среде разработки .
    Для получения дополнительной информации читайте: https://docs.docker.com/compose/compose-file/#network_mode и https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
источник