Как да се свържа с локалния хост на машината от вътрешността на контейнер Docker?
И така, имам 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.
TLDR
Използвайте
--network="host"
в командатаdocker run
, след което127.0.0.1
в контейнера на докера ще сочи към вашия хост на докера.Забележка: Този режим работи само в Docker за Linux според документацията.
Бележка за мрежовите режими на контейнера Docker
Docker предлага различни режими на работа в мрежа при стартиране на контейнери. В зависимост от избрания режим ще се свържете с базата данни MySQL, която работи на хоста на докера, по различен начин.
docker run --network="bridge" (по подразбиране)
Docker създава мост с име
docker0
по подразбиране. Както хостът на docker, така и контейнерите на docker имат IP адрес в този мост.Ако на хоста на Docker въведете
sudo ip addr show docker0
, ще получите изходна информация, изглеждаща по следния начин:И така, тук моят докер хост има IP адрес
172.17.42.1
на мрежовия интерфейсdocker0
.Сега стартирайте нов контейнер и му направете шел:
docker run --rm -it ubuntu:trusty bash
и в контейнера въведетеip addr show eth0
, за да откриете как е настроен основният му мрежов интерфейс:Тук моят контейнер има IP адрес
172.17.1.192
. Сега погледнете таблицата за маршрутизация:Така че IP адресът на докер хоста
172.17.42.1
е зададен като маршрут по подразбиране и е достъпен от вашия контейнер.docker run --network="host"
Алтернативно можете да стартирате контейнер на docker с мрежови настройки, зададени на
host
. Такъв контейнер ще споделя мрежовия стек с хоста на докера и от гледна точка на контейнераlocalhost
(или127.0.0.1
) ще се отнася за хоста на докера.Имайте предвид, че всеки порт, отворен в контейнера на докера, ще бъде отворен и на хоста на докера. И това без да е необходима опцията
-p
или-P
docker run
.Конфигурация на IP адреса на моя докер хост:
и от докер контейнер в режим хост:
Както виждате, и хостът, и контейнерът на докера използват един и същ мрежов интерфейс и имат един и същ IP адрес.
Свързване към MySQL от контейнери
режим на мост
За да получите достъп до MySQL, работеща на хоста на докера, от контейнери в режим мост, трябва да се уверите, че услугата MySQL слуша за връзки на IP адреса
172.17.42.1
.За да направите това, уверете се, че във файла за конфигуриране на MySQL (my.cnf) имате или
bind-address = 172.17.42.1
, илиbind-address = 0.0.0.0
.Ако трябва да зададете променлива на средата с IP адреса на шлюза, можете да изпълните следния код в контейнер :
след това в приложението си използвайте променливата на средата
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
.host mode
За да получите достъп до MySQL, работеща на хоста на докера, от контейнери в режим хост, можете да запазите
bind-address = 127.0.0.1
в конфигурацията на MySQL и всичко, което трябва да направите, е да се свържете към127.0.0.1
от вашите контейнери:бележка: Използвайте
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:
Не съм съгласен с отговора на Thomasleveil.
Ако накарате mysql да се свърже с 172.17.42.1, това ще попречи на други програми, използващи базата данни на хоста, да я достигнат. Това ще работи само ако всички потребители на базата данни са докеризирани.
Ако направите mysql bind към 0.0.0.0, ще отворите db за външния свят, което не само е много лошо, но и противоречи на това, което авторът на първоначалния въпрос иска да направи. Той изрично казва: "MySql работи на localhost и не разкрива порт за външния свят, затова е свързана на localhost".
В отговор на коментара на ivant
Това не е възможно. В документацията на mysql/mariadb изрично се казва, че не е възможно да се свърже към няколко интерфейса. Можете да се свържете само с 0, 1 или с всички интерфейси.
Като заключение, НЕ открих никакъв начин да достигна до базата данни (само localhost) на хоста от контейнер docker. Това определено изглежда като много много често срещан модел, но не знам как да го направя.