Как получить доступ к порту хоста из 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 года).
Я исследовал различные решения и нашел это наименее хакерское решение:
- Определите статический IP-адрес для IP-адреса шлюза моста.
- Добавьте 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.
- измените host / ip на host.docker.internal,
например: LOGGER_URL = " http://host.docker.internal:8085/log" - установите 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 с публичным IP
Используйте общедоступный IP-адрес хост-машины для доступа к веб-серверу в контейнере док-станции Jenkins.
Подход 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
- У меня есть фиксированный локальный сетевой адрес на адаптере WiFi LAN:
использовать
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
Вы можете проверить порт своего локального компьютера, на котором работает ваш док-контейнер, с помощью следующей команды на терминале.
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 или эквивалентной.
Надеюсь, эта информация поможет вам.