Як отримати доступ до порту хосту з контейнера docker


278

У мене є контейнер для докера, в якому працюють джинсини. У рамках процесу збирання мені потрібно отримати доступ до веб-сервера, який працює локально на хост-машині. Чи є спосіб веб-сервера хоста (який може бути налаштований для роботи на порту), піддатися дії контейнера jenkins?

РЕДАКТУВАННЯ: Я донецько працюю на докері на машині Linux.

ОНОВЛЕННЯ:

На додаток до відповіді @larsks нижче, щоб отримати IP-адресу хоста 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, чи ти докер запускаєш вдома на Linux?
larsks

@larsks Вибачте, я щойно оновив питання - я запускаю його на самому Linux.
Tri Nguyen

Відповіді:


206

Під час запуску програми 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 впорядковані, і це правило може чи не може робити правильно, залежно від того, які інші правила постають перед ним.

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


1
Дякую. Отримати IP-адресу було все, що мені було потрібно, не змінюючи iptables :)
Tri Nguyen


7
Як щодо Docker для MAC? AFAIK не існує мережі docker0, доступної для "Docker для MAC". У такому випадку як я можу підключитися до хоста з контейнера?
Vijay

17
Якщо ви використовуєте Docker для MAC v 17.06 або вище, просто використовуйте docker.for.mac.localhostзамість localhostабо 127.0.0.1. Ось док .
merito

1
Я використав ім'я хоста свого хоста, а не отримував IP-адресу (команда ім'я хоста на хості)
Marek F

312

Для macOS та Windows

Docker v 18.03 і вище (з 21 березня 2018 року)

Використовуйте свою внутрішню IP-адресу або підключіться до спеціального імені DNS, host.docker.internalяке відповідатиме внутрішній IP-адресі, що використовується хостом.

Підтримка Linux очікує на https://github.com/docker/for-linux/isissue/264

MacOS з більш ранніми версіями Docker

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

Те саме, що вище, але використовувати docker.for.mac.host.internalзамість цього.

Докер для Mac v 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-адресі, що використовується хостом! ... Я перевірив це, і він насправді працює! :)
Kyr

Місце на відповідь для користувачів Mac, як я. Дякую. Одна річ я не розумію - це те, чому ip route show | awk '/default/ {print $3}'дають один IP, а docker.for.mac.localhostінший.
dmmd

8
змінено на host.docker.internal docs.docker.com/docker-for-mac/networking/…
Snowball

1
Рекомендуйте використовувати host.docker.internalв контейнері docker і конфігуруйте 127.0.0.1 host.docker.internalу файлі хостів .
junlin

84

Використовуйте --net="host"у вашій docker runкоманді, тоді localhostваш контейнер докера буде вказувати на вашого хокера докера.


15
Наскільки я розумію, ті, хто використовує Dpcker для Windows / Docker для Mac, не працюватимуть через те, що контейнери працюють у віртуалізованих середовищах: Hyper-V (Windows) / xhyve (Mac)
ninjaboy

6
ЦЕ! Це відповідь!
user3751385

14
Тільки для запису: в межах Docker Componse, network_mode: "host"
jbarros

Це не працює для мене на Docker для Mac версії 19.03.1 на клієнті та сервері. Я б хотів, щоб це працювало, але це не так.
глина

1
Навіть якщо я перебуваю на mac, він працює, коли ви хочете спілкуватися між двома контейнерами докерів, і коли я перетворюю всі програми на зображення докера, мені цього достатньо
bormat

26

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

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

EDIT 2020-04-27: рекомендується використовувати лише в місцевих умовах розвитку.


Тоді як отримати доступ до дженкінів? Здається, переадресація портів не працює, якщо використовувати режим хост-мережі
Джеф Тіан

1
це дуже ризиковане рішення і зовсім не рекомендується. ми НЕ повинні відкривати нашу хост-мережу для контейнерів, якщо прямо не потрібно
Fatemeh Majd,

12

Наразі найпростіший спосіб зробити це на Mac і Windows - це використання хоста host.docker.internal, який вирішує IP-адресу хост-машини. На жаль, він ще не працює на Linux (станом на квітень 2018 року).


Це рішення працювало з версією Docker 19.03.1. Багато інших рішень, наведених тут, не працюють. Це задокументовано на docs.docker.com/docker-for-mac/networking/…
clay

12

Я створив контейнер докер для того, щоб робити саме це https://github.com/qoomon/docker-host

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


Це розумне рішення. Чи знаєте ви що-небудь, використовуючи це з великим трафіком? Можливо, накладні витрати на доступ до всього трафіку через цей контейнер.
bcoughlan

3
Так, це працює дуже приємно, ледь не
накладаючи

11

Ми з'ясували, що більш простим рішенням усього цього мережевого мотлоху є просто використовувати сокет домену для сервісу. Якщо ви все одно намагаєтесь підключитися до хоста, просто встановіть гніздо як гучність, і ви вже на шляху. Для postgresql це було так просто, як:

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

Тоді ми просто встановимо наше підключення до бази даних, щоб використовувати сокет замість мережі. Буквально це просто.


Це чудове рішення. Хороша робота!
платний ботанік

2
FYI, ми зіткнулися з великою проблемою з цим: 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".


7

Для систем Linux ви можете - починаючи з основної версії 20.04- тепер також спілкуватися з хостом через host.docker.internal. Це не працюватиме автоматично , але вам потрібно вказати такий прапор запуску:

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

Побачити


3

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

  1. Підхід 1 із відкритим IP

    Використовуйте IP-адресу загальнодоступної машини для доступу до веб-сервера в контейнері докерів Jenkins.

  2. Підхід 2 з хост-мережею

    Використовуйте "--net хост", щоб додати контейнер докера Jenkins в мережевий стек хоста. Контейнери, які розгорнуті в стеці хостів, мають повний доступ до хост-інтерфейсу. Ви можете отримати доступ до локального веб-сервера в контейнері докера з приватною 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

На жаль, випадковий ідентифікатор не передбачуваний і змінюватиметься кожного разу, коли композиція повинна відтворити мережу (наприклад, при перезавантаженні хоста). Моє рішення для цього - створити приватну мережу у відомій підмережі та налаштувати 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модуль ansible для розгортання мого композиційного файлу зі змінною підміною, а потім використовую модулі 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"підхід usin - це дуже грубий підхід, який не застосовується, коли хочеться мати добре ізольовану мережеву конфігурацію з кількома контейнерами.

Отже, мій підхід полягає в вилученні адреси хостів з боку хоста, а потім передачі її в контейнер з --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.


0

Коли у вас є два зображення докера, "вже" створено, і ви хочете поставити два контейнери для спілкування один з одним.

Для цього ви можете зручно запускати кожен контейнер із власним - ім'ям та використовувати прапор --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 або аналогічно.

Сподіваюся, що ця інформація вам допоможе.


3
Як зауваження, --linkтепер застаріло
Mr.Budris

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 для мосту (якщо ви хочете підтримувати переадресацію порту; якщо не використовувати хост )
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge або альтернативно: використовувати , --net="bridge"якщо ви не використовуєте Докер-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.