И така, имам Nginx, работещ в контейнер на докер, имам mysql, работещ на localhost, искам да се свържа с MySql от моя Nginx. MySql работи на localhost и не разкрива порт за външния свят, така че е свързан с localhost, а не с ip адреса на машината.
Има ли някакъв начин да се свържа с този MySql или с друга програма на localhost от този контейнер на docker?
Този въпрос се различава от "Как да получа IP адреса на хоста на докера от вътрешността на контейнера на докера" поради факта, че IP адресът на хоста на докера може да бъде публичен IP адрес или частен IP адрес в мрежата, който може да е или да не е достъпен от вътрешността на контейнера на докера (имам предвид публичен IP адрес, ако е хостван в AWS или нещо подобно). Дори да имате IP адреса на хоста на докера, това не означава, че можете да се свържете с хоста на докера от контейнера, като се има предвид този IP адрес, тъй като вашата мрежа на докера може да е покриваща, хост, мост, macvlan, никаква и т.н., което ограничава достижимостта на този IP адрес.
Обработка: Ако използвате Docker-for-mac или Docker-for-Windows 18.03+, просто се свържете с вашата услуга mysql, като използвате хоста host.docker.internal
.
От версия Docker 18.09.3 това не работи на Docker-for-Linux. На 8 март 2019 г. беше подадена поправка, която се надяваме да бъде включена в базата с код. Дотогава обходният вариант е да се използва контейнер, както е описано в отговора на qoomon.
Използвайте --network="host"
в командата docker run
, след което 127.0.0.1
в контейнера на докера ще сочи към вашия хост на докера.
Забележка: Този режим работи само в Docker за Linux според документацията.
Docker предлага различни режими на работа в мрежа при стартиране на контейнери. В зависимост от избрания режим ще се свържете с базата данни MySQL, която работи на хоста на докера, по различен начин.
Docker създава мост с име docker0
по подразбиране. Както хостът на docker, така и контейнерите на docker имат IP адрес в този мост.
Ако на хоста на Docker въведете 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
И така, тук моят докер хост има 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 адресът на докер хоста 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 с мрежови настройки, зададени на host
. Такъв контейнер ще споделя мрежовия стек с хоста на докера и от гледна точка на контейнера localhost
(или 127.0.0.1
) ще се отнася за хоста на докера.
Имайте предвид, че всеки порт, отворен в контейнера на докера, ще бъде отворен и на хоста на докера. И това без да е необходима опцията -p
или -P
docker run
.
Конфигурация на IP адреса на моя докер хост:
[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
Както виждате, и хостът, и контейнерът на докера използват един и същ мрежов интерфейс и имат един и същ IP адрес.
За да получите достъп до MySQL, работеща на хоста на докера, от контейнери в режим мост, трябва да се уверите, че услугата MySQL слуша за връзки на IP адреса 172.17.42.1
.
За да направите това, уверете се, че във файла за конфигуриране на MySQL (my.cnf) имате или bind-address = 172.17.42.1
, или bind-address = 0.0.0.0
.
Ако трябва да зададете променлива на средата с IP адреса на шлюза, можете да изпълните следния код в контейнер :
export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print $2}')
след това в приложението си използвайте променливата на средата DOCKER_HOST_IP
, за да отворите връзка с MySQL.
Забележка: ако използвате bind-address = 0.0.0.0
, вашият MySQL сървър ще слуша за връзки на всички мрежови интерфейси. Това означава, че вашият MySQL сървър може да бъде достигнат от интернет ; не забравяйте да настроите съответно правилата на защитната стена.
Забележка 2: ако използвате bind-address = 172.17.42.1
, вашият MySQL сървър няма да слуша за връзки, осъществени към 127.0.0.1
. Процесите, работещи на хоста на докера, които искат да се свържат с MySQL, ще трябва да използват IP адреса 172.17.42.1
.
За да получите достъп до MySQL, работеща на хоста на докера, от контейнери в режим хост, можете да запазите 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 сокет.
Това работи за мен на NGINX/PHP-FPM стек, без да се докосва никакъв код или мрежа, където приложението просто очаква да може да се свърже с localhost
.
Монтирайте mysqld.sock
от хоста в контейнера.
Намерете местоположението на файла mysql.sock на хоста, на който работи mysql:
netstat -ln | awk '/mysql(.*)?\.sock/ { print $9 }'
Монтирайте този файл на мястото, където се очаква в докера:
docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock
Възможни местоположения на mysqld.sock:
/tmp/mysqld.sock
/var/run/mysqld/mysqld.sock
/var/lib/mysql/mysql.sock
/Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP
Не съм съгласен с отговора на Thomasleveil.
Ако накарате mysql да се свърже с 172.17.42.1, това ще попречи на други програми, използващи базата данни на хоста, да я достигнат. Това ще работи само ако всички потребители на базата данни са докеризирани.
Ако направите mysql bind към 0.0.0.0, ще отворите db за външния свят, което не само е много лошо, но и противоречи на това, което авторът на първоначалния въпрос иска да направи. Той изрично казва: "MySql работи на localhost и не разкрива порт за външния свят, затова е свързана на localhost".
В отговор на коментара на ivant
"Защо да не се обвърже mysql и с docker0?"
Това не е възможно. В документацията на mysql/mariadb изрично се казва, че не е възможно да се свърже към няколко интерфейса. Можете да се свържете само с 0, 1 или с всички интерфейси.
Като заключение, НЕ открих никакъв начин да достигна до базата данни (само localhost) на хоста от контейнер docker. Това определено изглежда като много много често срещан модел, но не знам как да го направя.