Изнутри контейнера Docker, как подключиться к localhost машины?

Итак, у меня есть Nginx, работающий внутри контейнера docker, у меня есть mysql, работающий на localhost, я хочу подключиться к MySql из моего Nginx. MySql работает на localhost и не подвергает порт внешнему миру, поэтому его привязка к localhost, не привязанная к ip-адресу машины.

есть ли способ подключиться к этому MySql или любой другой программе на localhost из этого контейнера docker?

15 ответов


Edit: если вы используете Докер-для-mac или Docker-for-Windows 18.03+, просто подключитесь к своей службе mysql, используя хост host.docker.internal.

начиная с Docker 18.04, это не работает на Docker-for-Linux.


TLDR

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


обратите внимание на docker контейнерные сетевые режимы

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

docker run --network= "мост" (по умолчанию)

Docker создает мост с именем docker0 по умолчанию. И хост docker, и контейнеры docker имеют IP-адрес на этом мосту.

на Докер хост, типа sudo ip addr show docker0 у вас будет выход, похожий на:

[vagrant@docker:~] $ sudo ip addr show docker0
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::5484:7aff:fefe:9799/64 scope link
       valid_lft forever preferred_lft forever

Итак, здесь мой хост docker имеет IP-адрес 172.17.42.1 на docker0 сетевой интерфейс.

теперь запустите новый контейнер и получите оболочку на нем:docker run --rm -it ubuntu:trusty bash и в пределах контейнерного типа ip addr show eth0 чтобы узнать, как настроен его основной сетевой интерфейс:

root@e77f6a1b3740:/# ip addr show eth0
863: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.192/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::6432:13ff:fef0:f1e3/64 scope link
       valid_lft forever preferred_lft forever

здесь мой контейнер имеет IP-адрес 172.17.1.192. Теперь посмотрите на таблицу маршрутизации:

root@e77f6a1b3740:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.42.1     0.0.0.0         UG    0      0        0 eth0
172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

Итак, IP Адрес хоста docker 172.17.42.1 устанавливается в качестве маршрута по умолчанию и доступен из контейнера.

root@e77f6a1b3740:/# ping 172.17.42.1
PING 172.17.42.1 (172.17.42.1) 56(84) bytes of data.
64 bytes from 172.17.42.1: icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.201 ms
64 bytes from 172.17.42.1: icmp_seq=3 ttl=64 time=0.116 ms

docker run --network= "host"

в качестве альтернативы вы можете запустить контейнер docker с настройки сети установлено значение host. Такой контейнер будет делиться сетевым стеком с хостом docker и с точки зрения контейнера, localhost (или 127.0.0.1) будет ссылаться на хост docker.

имейте в виду, что любой порт открыт в ваш контейнер docker будет открыт на хосте docker. И это не требуя -p или -P docker run опции.

конфигурация IP на моем хосте docker:

[vagrant@docker:~] $ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

и из контейнера докера в хоста режим:

[vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

как вы можете видеть, как хост docker и контейнер docker имеют один и тот же сетевой интерфейс и как таковой имеют один и тот же IP-адрес.


подключение к MySQL из контейнеров

режим моста

для доступа к MySQL, работающему на хосте docker из контейнеров в режим моста, вам нужно убедиться, что служба MySQL прослушивает соединения на 172.17.42.1 IP-адрес.

чтобы сделать это, убедитесь, что у вас есть либо bind-address = 172.17.42.1 или bind-address = 0.0.0.0 в вашем конфигурационном файле MySQL (my.cnf).

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

export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print }')

тогда в вашем приложении, используйте DOCKER_HOST_IP переменная окружения, чтобы открыть соединение с MySQL.

Примечание: если вы используете bind-address = 0.0.0.0 ваш сервер MySQL будет прослушивать соединения на всех сетевых интерфейсах. Это означает, что ваш сервер MySQL может быть достигнут из интернета ; убедитесь, что настроили правила брандмауэра соответственно.

примечание 2: если вы используете bind-address = 172.17.42.1 ваш сервер MySQL не будет слушать для подключения к 127.0.0.1. Процессы, запущенные на хосте docker, которые хотели бы подключиться к MySQL, должны были бы использовать 172.17.42.1 IP-адрес.

режим хоста

для доступа к MySQL, работающему на хосте docker из контейнеров в режим хоста, вы можете сохранить bind-address = 127.0.0.1 в вашей конфигурации MySQL и все, что вам нужно сделать, это подключиться к 127.0.0.1 из ваших контейнеров:

[vagrant@docker:~] $ docker run --rm -it --network=host mysql mysql -h 127.0.0.1 -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36
Server version: 5.5.41-0ubuntu0.14.04.1 (Ubuntu)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Примечание: используйте mysql -h 127.0.0.1 и не mysql -h localhost; в противном случае клиент MySQL попытался бы подключиться с помощью сокета unix.


для 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-v 18.02

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

Докер для Mac v 17.06 - V 17.11

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

Docker для Mac 17.05 и ниже

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

sudo ifconfig lo0 alias 123.123.123.123/24

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

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

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

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

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


Я делаю Хак, подобный вышеуказанным сообщениям, чтобы получить локальный IP-адрес для сопоставления с псевдонимом (DNS) в контейнере. Основная проблема заключается в том, чтобы получить динамически с помощью простого скрипта, который работает как в Linux и OSX IP-адрес хоста. Я сделал этот скрипт, который работает в обеих средах (даже в Linux с "$LANG" != "en_*" настроить):

ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print  }' | cut -f2 -d: | head -n1

Итак, используя Docker Compose, полная конфигурация будет:

скрипт запуска (docker-run.sh):

export DOCKERHOST=$(ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print  }' | cut -f2 -d: | head -n1)
docker-compose -f docker-compose.yml up

докер-сочинять.в формате YML:

myapp:
  build: .
  ports:
    - "80:80"
  extra_hosts:
    - "dockerhost:$DOCKERHOST"

затем измените http://localhost до http://dockerhost в коде.

для более руководство о том, как настроить DOCKERHOST скрипт, взгляните на этот пост с объяснением как это работает.


это сработало для меня в стеке nginx / PHP-FPM, не касаясь кода или сети, где приложение просто ожидает возможности подключения к localhost

Mount mysqld.sock от хоста к внутри контейнера.

найдите местоположение mysql.файл носка на хосте с mysql:
netstat -ln | awk '/mysql(.*)?\.sock/ { print }'

установите этот файл туда, где он ожидается в докере:
docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock

возможные местоположения mysqld.носок:

/tmp/mysqld.sock
/var/run/mysqld/mysqld.sock 
/var/lib/mysql/mysql.sock
/Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP

решение для Windows 10

Docker Community Edition 17.06.0-ce-win18 2017-06-28 (стабильный)

вы можете использовать DNS-имя хоста docker.for.win.localhost, для разрешения на внутренний IP. (Предупреждение некоторые источники упомянуты windows но он должен быть!--2-->)

обзор
Мне нужно было сделать что-то подобное, то есть подключиться из моего контейнера Docker к моему localhost, на котором был запущен Azure Storage Emulator и CosmosDB Emulator.

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

это также работает для подключения из моего контейнера Docker в SQL Server и IIS, оба работают локально на моем хосте с настройки порта по умолчанию.


для тех, кто в Windows, предполагая, что вы используете сетевой драйвер моста, вы захотите специально привязать MySQL к ip-адресу сетевого интерфейса hyper-V.

Это делается через файл конфигурации под обычно скрытым C:\ProgramData\MySQL папка.

привязка к 0.0.0.0 не будет работать. Необходимый адрес также отображается в конфигурации docker, и в моем случае это был 10.0.75.1.


Edit: я закончил прототипирование концепции на GitHub. Проверьте: https://github.com/sivabudh/system-in-a-box


во-первых, мой ответ ориентирован на 2 группы людей: те, кто использует Mac, и те, кто использует Linux.

на хоста сетевой режим не работает на Mac. Вы должны использовать псевдоним IP, см.:https://stackoverflow.com/a/43541681/2713729

что такое хост сетевой режим? Смотри:https://docs.docker.com/engine/reference/run/#/network-settings

во-вторых, для тех из вас, кто использует Linux (мой прямой опыт был с Ubuntu 14.04 LTS, и я скоро обновлюсь до 16.04 LTS в производстве),да, вы можете заставить службу работать внутри контейнера Docker подключиться к localhost службы, работающие на хосте Docker (например. ваш ноутбук.)

как?

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

docker run --network="host" -id <Docker image ID>

когда вы делаете ifconfig (вам понадобится apt-get install net-tools контейнер для ifconfig для вызова) внутри вашего контейнера вы увидите, что сетевые интерфейсы такие же, как и на хосте Docker (например. ваш ноутбук.)

важно отметить, что я пользователь Mac, но я запускаю Ubuntu под Parallels, поэтому использование Mac не является недостатком. ;-)

и вот как вы подключаете контейнер NGINX к MySQL, работающему на localhost.


решение для Linux (ядро >=3.6).

Ok, ваш сервер localhost имеет интерфейс docker по умолчанию docker0 С ip-адресом 172.17.0.1. Ваш контейнер запущен с сетевыми настройками по умолчанию --net= "мост".

  1. включить route_localnet для интерфейса docker0:
    $ sysctl -w net.ipv4.conf.docker0.route_localnet=1
  2. добавить правила в iptables:
    $ iptables -t nat -I PREROUTING -i docker0 -d 172.17.0.1 -p tcp --dport 3306 -j DNAT --to 127.0.0.1:3306
    $ iptables -t filter -I INPUT -i docker0 -d 127.0.0.1 -p tcp --dport 3306 -j ACCEPT
  3. создать пользователя mysql с доступом из "%", что означает-от любого, за исключением localhost:
    CREATE USER 'user'@'%' IDENTIFIED BY 'password';
  4. измените в своем скрипте адрес mysql-сервера на 172.17.0.1


От документации к ядру:

route_localnet - BOOLEAN: не рассматривайте адреса замыкания на себя в качестве марсианского источника или назначения при маршрутизации. Это позволяет использовать 127/8 для локальной маршрутизации (по умолчанию Ложные).


вот мое решение : это работает для моего случая

  • установите локальный сервер mysql в открытый доступ по комментарию #bind-address = 127.0.0.1 в /etc/mysql / mysql.conf.d

  • перезапустить сервер mysql sudo /etc/init.d/mysql restart

  • выполните следующую команду, чтобы открыть корневой доступ пользователя к любому хосту mysql -uroot -proot GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; FLUSH PRIVILEGES;

  • создать сценарий sh : run_docker.sh

    #!bin/bash

    HOSTIP=`ip -4 addr show scope global dev eth0 | grep inet | awk '{print $2}' | cut -d / -f 1`


      docker run -it -d --name web-app \
                  --add-host=local:${HOSTIP} \
                  -p 8080:8080 \
                  -e DATABASE_HOST=${HOSTIP} \
                  -e DATABASE_PORT=3306 \
                  -e DATABASE_NAME=demo \
                  -e DATABASE_USER=root \
                  -e DATABASE_PASSWORD=root \
                  sopheamak/springboot_docker_mysql

  
  • запуск с докер-композитор

    version: '2.1'
    
    

    services:
    tomcatwar: extra_hosts: - "local:10.1.2.232" image: sopheamak/springboot_docker_mysql
    ports: - 8080:8080 environment: - DATABASE_HOST=local - DATABASE_USER=root - DATABASE_PASSWORD=root - DATABASE_NAME=demo - DATABASE_PORT=3306


Я не согласен с ответом от Thomasleveil.

привязка mysql к 172.17.42.1 предотвратит другие программы, использующие базу данных на хосте, чтобы достичь ее. Это будет работать, только если все пользователи базы данных dockerized.

привязка mysql к 0.0.0.0 откроет БД для внешнего мира, что не только очень плохо, но и противоречит тому, что хочет сделать автор исходного вопроса. Он явно говорит: "MySql работает на localhost, а не подвергая порт внешнему миру, поэтому его привязывают к localhost"

чтобы ответить на комментарий от ivant

" Почему бы не привязать mysql к docker0?"

Это невозможно. Документация mysql/mariadb явно говорит, что невозможно привязать к нескольким интерфейсам. Можно выполнить привязку только к 0, 1 или ко всем интерфейсам.

В заключение, я не нашел никакого способа добраться до базы данных (только localhost) на хосте из контейнер докера. Это определенно кажется очень распространенной моделью, но я не знаю, как это сделать.


простейшее решение для Mac OSX

просто используйте IP-адрес вашего Mac. На Mac запустите это, чтобы получить IP-адрес и использовать его из контейнера:

$ ifconfig | grep 'inet 192'| awk '{ print }'

пока сервер, работающий локально на вашем Mac или в другом контейнере docker, прослушивает 0.0.0.0, контейнер docker сможет связаться с этим адресом.

Если вы просто хотите получить доступ к другому контейнеру docker, который прослушивает 0.0.0.0, вы можете использовать 172.17.0.1


группы и пространства имен играют важную роль в экосистеме контейнеров.

пространство имен обеспечивает уровень изоляции. Каждый контейнер выполняется в отдельном пространстве имен, и его доступ ограничен этим пространством имен. Группы управляют использованием ресурсов каждого контейнера, а пространство имен - тем, что процесс может видеть и получать доступ к соответствующему ресурсу.

вот базовые понимания подход решения можно следуй,

Использовать Сетевое Пространство Имен

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

$ docker run -it alpine ifconfig

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

$ docker run -it --net=host alpine ifconfig

если процесс прослушивает порты, они будут слушать на интерфейс хоста и сопоставлен с контейнером.

использовать пространство имен PID Изменение пространства имен Pid позволяет контейнеру взаимодействовать с другим процессом за пределами его обычной области.

этот контейнер будет работать в собственном пространстве имен.

$ docker run -it alpine ps aux

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

$ docker run -it --pid=host alpine ps aux

Совместное Использование Пространства Имен

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

первый контейнер-сервер nginx. Это создаст новое пространство имен network и process. Этот контейнер будет привязан к порту 80 вновь созданного сетевого интерфейса.

$ docker run -d --name http nginx:alpine

еще один контейнер может теперь повторно используйте это пространство имен,

$ docker run --net=container:http mohan08p/curl curl -s localhost

кроме того, этот контейнер может видеть интерфейс с процессами в общем контейнере.

$ docker run --pid=container:http alpine ps aux

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


вы можете получить ip-адрес хоста, используя alpine image

docker run --rm alpine ip route | awk 'NR==1 {print }'

Это было бы более последовательно, поскольку вы всегда используете alpine для запуска команды.

подобно ответу Мариано, вы можете использовать ту же команду для установки переменной среды

DOCKER_HOST=$(docker run --rm alpine ip route | awk 'NR==1 {print }') docker-compose up

Для Компьютера Windows :-

выполните следующую команду, чтобы выставить порт docker случайным образом во время сборки

$docker run -d --name MyWebServer -P mediawiki

enter image description here

enter image description here

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

localhost:32768 

вы можете увидеть страницу mediawiki


можно использовать сайту ngrok чтобы создать безопасный туннель к вашей машине localhost, а затем подвергнуть этот туннель контейнеру docker.

сайту ngrok свободно использовать как в 05/22/2017.

действия:

1) перейти к сайту ngrok

2) скачать клиент ngrok и следуйте инструкциям по установке

3) ЗАРЕГИСТРИРОВАТЬСЯ для учетной записи, и они обеспечат аутентификацию знак. Регистрация требуется, потому что ngrok дает вам туннель tcp-порта только после регистрации. Для регистрации не требуется никаких затрат или кредитной карты.

4) в вашем терминале do ngrok tcp 3306. 3306 - это порт, который mysql работает на моем локальном, вы можете сделать это с любым другим портом.

5) Вы получите адрес из шага 4 например: tcp://0.tcp.ngrok.io:10117. Это туннельное соединение с вашей локальной машиной. 0.tcp.ngrok.io отображается на ваш localhost и порт 10117 is сопоставлено с вашим локальным портом 3306. Теперь вы можете получить доступ к порту localhost 3306 из любого места, используя этот адрес, включая любой контейнер docker, работающий на этом компьютере. В контейнере docker(где бы он ни находился), предполагая, что у вас уже установлен клиент mysql, сделайте следующее:

mysql --host 0.tcp.ngrok.io --port 10117 -u root

вы сможете войти в свой root учетная запись вашей локальной машины изнутри контейнера docker!

я написал об этом решении подробнее подробности здесь