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

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

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

ОБНОВИТЬ:

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

ip addr show docker0 | grep -Po 'inet \K[\d.]+'

19 ответов

Решение

При естественном запуске 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 интерфейс.

Для 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, Если он слушает на локальном хосте 127.0.0.1 он не примет соединение.

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

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

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

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

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

Мой поиск в Google привел меня сюда, и, покопавшись в комментариях, я обнаружил, что это дубликат изнутри контейнера Docker, как мне подключиться к локальному хосту машины?. Я проголосовал за то, чтобы закрыть это как дубликат, но поскольку люди (включая меня!) Часто прокручивают ответы вниз, а не внимательно читают комментарии, вот короткий ответ.

TL;DR:

Заменять http://127.0.0.1 или же http://localhost с участием http://host.docker.internal. (Источник )

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

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

Увидеть

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

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

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

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

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

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

  1. Определите статический IP-адрес для IP-адреса шлюза моста.
  2. Добавьте IP-адрес шлюза в качестве дополнительной записи в файл 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".

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

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

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

За 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 модули для настройки и сохранения правил брандмауэра соответственно.

Это старый вопрос, на него было много ответов, но ни один из них не подходит достаточно хорошо для моего контекста. В моем случае контейнеры очень компактны и не содержат каких-либо сетевых инструментов, необходимых для извлечения 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 добавляется в файл hosts контейнера, и вы можете использовать его в строках подключения к базе данных или URL-адресах API.

Для меня (Windows 10, Docker Engine v19.03.8) это было сочетание /questions/46010012/kak-poluchit-dostup-k-portu-hosta-iz-docker-kontejnera/46010025#46010025 и /questions/46010012/kak-poluchit-dostup-k-portu-hosta-iz-docker-kontejnera/46010037#46010037.

  1. измените host / ip на host.docker.internal,
    например: LOGGER_URL = " http://host.docker.internal:8085/log"
  2. установите network_mode для моста (если вы хотите сохранить переадресацию портов; если не использовать host):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge или альтернативно: используйте --net="bridge"если вы не используете docker-compose (аналогично /questions/46010012/kak-poluchit-dostup-k-portu-hosta-iz-docker-kontejnera/46010058#46010058)
    Как указывалось в предыдущих ответах: это следует использовать только в локальной среде разработки.
    Для получения дополнительной информации читайте: https://docs.docker.com/compose/compose-file/ и https://docs.docker.com/docker-for-windows/networking/

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

  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-адреса, которые доступны из контейнера Docker.

Например: я запустил сервер 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

Почти за 7 лет задавался вопрос, то ли докер поменялся, то ли никто так не пробовал. Поэтому я включу свой собственный ответ.

Я нашел, что все ответы используют сложные методы. Сегодня мне это нужно, и я нашел 2 очень простых способа:

  • использовать ipconfigили же ifconfigна вашем хосте и запишите все IP-адреса. Как минимум два из них могут использоваться контейнером.

    • У меня есть фиксированный локальный сетевой адрес на адаптере WiFi LAN: 192.168.1.101. Это должно быть 10.0.1.101. результат будет меняться в зависимости от вашего роутера
    • Я использую WSL в Windows, и у него есть собственный vEthernetадрес: 172.19.192.1
  • использовать host.docker.internal. Большинство ответов имеют ту или иную форму в зависимости от ОС. Название предполагает, что теперь оно используется докером во всем мире.

Третий вариант — использовать WAN-адрес машины или, другими словами, IP-адрес, предоставленный поставщиком услуг. Однако это может не сработать, если IP-адрес не является статическим и требует настройки маршрутизации и брандмауэра.


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

Самый простой вариант, который сработал для меня, заключался в том, что я использовал IP-адрес моей машины в локальной сети (назначенный маршрутизатором).

Вы можете найти это, используя ifconfig команда

например

      ifconfig

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        options=400<CHANNEL_IO>
        ether f0:18:98:08:74:d4 
        inet 192.168.178.63 netmask 0xffffff00 broadcast 192.168.178.255
        media: autoselect
        status: active

а затем использовал inet-адрес. Это помогло мне подключить любые порты на моей машине.

Мой Dockerfile для запуска Jenkins:

      FROM jenkins/jenkins:2.333-jdk11
USER root

USER jenkins

https://github.com/dylanops/докер-дженкинс

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

docker port <container_id/container_name>

Если вы выполнили сопоставление портов, как показано ниже, при запуске контейнера докеров вы получите, как показано ниже.

ex: docker run -p 8080:3000 --name <container_name> <docker_image_name>

docker port <container_id/container_name> даст вам результат, как показано ниже.

3000/tcp -> 0.0.0.0:8080

Если у вас есть веб-приложение, которое контейнеризуется с помощью Docker, вы можете получить ответ в браузере, как показано ниже.

http://<your_web_app_url/ or localhost>:8080

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

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

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

docker build -t "centos7/someApp" someApp/ 

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

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

и вы застреваете на "curl / wget", не возвращая "маршрут к хосту".

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

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

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

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

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

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

Другие вопросы по тегам