Как получить IP-адрес узла докера из контейнера докера
Как видно из названия. Мне нужно иметь возможность получить IP-адрес хоста докера и сопоставления портов от хоста к контейнеру, и делать это внутри контейнера.
28 ответов
/sbin/ip route|awk '/default/ { print $3 }'
Как заметил @MichaelNeale, нет смысла использовать этот метод в Dockerfile
(кроме случаев, когда нам нужен этот IP только во время сборки), потому что этот IP будет жестко задан во время сборки.
На Docker для Mac, начиная с версии 18.03, вы можете использовать host.docker.internal
в качестве IP-адреса хоста.
Обратите внимание, как и в документации: "Это для целей разработки и не будет работать в производственной среде за пределами Docker для Mac". Это связано с тем, что в Docker для Mac "вы не можете видеть интерфейс docker0 на хосте. Этот интерфейс фактически находится внутри виртуальной машины".
Это обновление от docker.for.mac.localhost
, доступно с версии 17.06, и docker.for.mac.host.internal
, доступно с версии 17.12, которая также может работать.
Например, на моем хосте установлены переменные окружения:
MONGO_SERVER=host.docker.internal
В моем docker-compose.yml
файл, у меня есть это:
version: '3'
services:
api:
build: ./api
volumes:
- ./api:/usr/src/app:ro
ports:
- "8000"
environment:
- MONGO_SERVER
command: /usr/local/bin/gunicorn -c /usr/src/app/gunicorn_config.py -w 1 -b :8000 wsgi
Обновление: в Docker для Mac, начиная с версии 18.03, вы можете использовать host.docker.internal в качестве IP- адреса хоста. Смотрите ответ Алланберри. Для предыдущих версий Docker для Mac все еще может быть полезен следующий ответ:
На Docker для Mac docker0
Мост не существует, поэтому другие ответы здесь могут не работать. Однако весь исходящий трафик направляется через ваш родительский хост, поэтому до тех пор, пока вы пытаетесь подключиться к IP-адресу, который он распознает как сам по себе (а док-контейнер не считает себя таким), вы должны иметь возможность подключиться. Например, если вы запускаете это с родительского компьютера, запустите:
ipconfig getifaddr en0
Это должно показать вам IP вашего Mac в его текущей сети, и ваш док-контейнер также должен иметь возможность подключиться к этому адресу. Это, конечно, неприятно, если этот IP-адрес когда-либо изменится, но вы можете добавить свой петлевой IP-адрес к вашему Mac, который контейнер не считает самим, выполнив что-то вроде этого на родительской машине:
sudo ifconfig lo0 alias 192.168.46.49
Затем вы можете проверить соединение из контейнера Docker с помощью telnet. В моем случае я хотел подключиться к удаленному серверу xdebug:
telnet 192.168.46.49 9000
Теперь, когда трафик поступает на ваш Mac с адресом 192.168.46.49 (и весь трафик, выходящий из вашего контейнера, проходит через ваш Mac), ваш Mac будет считать, что IP сам по себе. Когда вы закончите использовать этот IP-адрес, вы можете удалить псевдоним петли следующим образом:
sudo ifconfig lo0 -alias 192.168.46.49
Одна вещь, о которой следует быть осторожным, заключается в том, что док-контейнер не будет отправлять трафик на родительский хост, если он думает, что адресат трафика сам по себе. Поэтому проверьте интерфейс обратной связи внутри контейнера, если у вас возникли проблемы:
sudo ip addr show lo
В моем случае это показало inet 127.0.0.1/8
Это означает, что я не мог использовать какие-либо IP-адреса в 127.*
спектр. Вот почему я использовал 192.168.*
в приведенном выше примере. Убедитесь, что используемый вами IP-адрес не конфликтует с чем-то в вашей сети.
AFAIK, в случае Docker для Linux (стандартный дистрибутив) IP-адрес хоста всегда будет 172.17.0.1
.
Самый простой способ получить его - через ifconfig
(интерфейс docker0) с хоста:
ifconfig
Изнутри докера следующая команда из докера: ip -4 route show default | cut -d" " -f3
Вы можете быстро запустить его в докере с помощью следующей командной строки:
# 1. Run an ubuntu docker
# 2. Updates dependencies (quietly)
# 3. Install ip package (quietly)
# 4. Shows (nicely) the ip of the host
# 5. Removes the docker (thanks to `--rm` arg)
docker run -it --rm ubuntu:19.10 bash -c "apt-get update && apt-get install iproute2 -y && ip -4 route show default | cut -d' ' -f3"
Я надеюсь, что это помогает.
Для тех, кто запускает Docker в AWS, метаданные экземпляра для хоста по-прежнему доступны внутри контейнера.
curl http://169.254.169.254/latest/meta-data/local-ipv4
Например:
$ docker run alpine /bin/sh -c "apk update ; apk add curl ; curl -s http://169.254.169.254/latest/meta-data/local-ipv4 ; echo"
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
v3.3.1-119-gb247c0a [http://dl-cdn.alpinelinux.org/alpine/v3.3/main]
v3.3.1-59-g48b0368 [http://dl-cdn.alpinelinux.org/alpine/v3.3/community]
OK: 5855 distinct packages available
(1/4) Installing openssl (1.0.2g-r0)
(2/4) Installing ca-certificates (20160104-r2)
(3/4) Installing libssh2 (1.6.0-r1)
(4/4) Installing curl (7.47.0-r0)
Executing busybox-1.24.1-r7.trigger
Executing ca-certificates-20160104-r2.trigger
OK: 7 MiB in 15 packages
172.31.27.238
$ ifconfig eth0 | grep -oP 'inet addr:\K\S+'
172.31.27.238
docker network inspect bridge -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
Его можно получить с помощью docker network inspect.
Единственный способ - передать информацию о хосте в качестве среды при создании контейнера.
run --env <key>=<value>
--add-host
может быть более чистым решением (но без части порта, только хост может быть обработан с этим решением). Итак, в вашем docker run
команда, сделайте что-то вроде:
docker run --add-host dockerhost:`/sbin/ip route|awk '/default/ { print $3}'` [my container]
(С /questions/24868618/vstavte-ip-roditelskogo-hosta-docker-v-fajl-hosta-kontejnera/24868625#24868625)
Стандартная передовая практика для большинства приложений, которые хотят делать это автоматически: вы этого не делаете. Вместо этого пользователь, запускающий контейнер, вводит внешнее имя хоста / IP-адрес в качестве конфигурации, например, в виде переменной среды или файла конфигурации. Разрешение пользователю вводить это дает вам наиболее портативный дизайн.
Почему это было так сложно? Поскольку контейнеры по своей природе изолируют приложение от среды хоста. Сеть по умолчанию привязана к пространству имен только для этого контейнера, а детали хоста защищены от процессов, выполняемых внутри контейнера, которым нельзя полностью доверять.
Существуют разные варианты в зависимости от вашей конкретной ситуации:
Если ваш контейнер работает с сетью хоста, вы можете посмотреть таблицу маршрутизации на хосте напрямую, чтобы увидеть маршрут по умолчанию. Из этого вопроса для меня работает следующее, например:
ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'
Пример, показывающий это с сетью хоста в контейнере, выглядит так:
docker run --rm --net host busybox /bin/sh -c \
"ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'"
Для некоторых версий Docker Desktop они внедрили запись DNS во встроенную виртуальную машину:
getent hosts host.docker.internal | awk '{print $1}'
Если вы работаете в облачной среде, вы можете проверить службу метаданных у поставщика облачных услуг, например, AWS:
curl http://169.254.169.254/latest/meta-data/local-ipv4
Если вам нужен внешний / интернет-адрес, вы можете запросить удаленную службу, например:
curl ifconfig.co
Каждый из них имеет ограничения и работает только в определенных сценариях. Наиболее переносимым вариантом по-прежнему является запуск вашего контейнера с IP-адресом, введенным в качестве конфигурации, например, здесь вариант, запускающий более раннийip
на хосте и вводим ее как переменную среды:
export HOST_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
docker run --rm -e HOST_IP busybox printenv HOST_IP
TL; DR для Mac и Windows
docker run -it --rm alpine nslookup host.docker.internal
... печатает IP-адрес хоста...
nslookup: can't resolve '(null)': Name does not resolve
Name: host.docker.internal
Address 1: 192.168.65.2
Детали
На Mac и Windows вы можете использовать специальное DNS-имяhost.docker.internal
.
У хоста меняющийся IP-адрес (или его нет, если у вас нет доступа к сети). Начиная с 18 марта, мы рекомендуем подключаться к специальному DNS-имени host.docker.internal, которое разрешается во внутренний IP-адрес, используемый хостом. Это сделано для целей разработки и не будет работать в производственной среде за пределами Docker Desktop для Mac.
Если вы хотите по-настоящему IP
адрес (не мост IP
) на Windows
и у вас есть докер 18.03
(или более поздний) сделать следующее:
Запустите bash на контейнере с хоста, где имя изображения nginx
(работает на Alpine Linux distribution
):
docker run -it nginx /bin/ash
Затем запустите внутри контейнера
/ # nslookup host.docker.internal
Name: host.docker.internal
Address 1: 192.168.65.2
192.168.65.2
это IP -адрес хоста, а не IP -адрес моста, как в spinus
принятый ответ.
Я использую здесь host.docker.internal:
Хост имеет изменяющийся IP -адрес (или не имеет, если у вас нет доступа к сети). Начиная с 18.03, мы рекомендуем подключаться к специальному DNS-имени host.docker.internal, который разрешает внутренний IP -адрес, используемый хостом. Это для целей разработки и не будет работать в производственной среде за пределами Docker для Windows.
В Linux вы можете запустить
HOST_IP=`hostname -I | awk '{print $1}'`
В macOS ваш хост-компьютер не является хостом Docker. Docker установит свою ОС в VirtualBox.
HOST_IP=`docker run busybox ping -c 1 docker.for.mac.localhost | awk 'FNR==2 {print $4}' | sed s'/.$//'`
У меня Ubuntu 16.03. Для меня
docker run --add-host dockerhost:`/sbin/ip route|awk '/default/ { print $3}'` [image]
это НЕ работает (неправильно внутрибрюшинно генерировала)
Мое рабочее решение заключалось в следующем:
docker run --add-host dockerhost:`docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' bridge` [image]
Docker для Mac Я хочу подключиться из контейнера к службе на хосте
Хост имеет изменяющийся IP-адрес (или не имеет, если у вас нет доступа к сети). Начиная с 18.03, мы рекомендуем подключаться к специальному DNS-имени host.docker.internal, который разрешает внутренний IP-адрес, используемый хостом.
Шлюз также доступен как gateway.docker.internal. https://docs.docker.com/docker-for-mac/networking/
Мое решение:
docker run --net= хост
затем в контейнере докера:
имя хоста -I | awk '{print $1}'
Если вы активировали удаленный API докера (через -H
tcp://0.0.0.0:4243
например) и знать имя хоста или IP-адрес хост-машины, это можно сделать с помощью большого количества bash.
Внутри пользователя моего контейнера bashrc
:
export hostIP=$(ip r | awk '/default/{print $3}')
export containerID=$(awk -F/ '/docker/{print $NF;exit;}' /proc/self/cgroup)
export proxyPort=$(
curl -s http://$hostIP:4243/containers/$containerID/json |
node -pe 'JSON.parse(require("fs").readFileSync("/dev/stdin").toString()).NetworkSettings.Ports["DESIRED_PORT/tcp"][0].HostPort'
)
Вторая строка захватывает идентификатор контейнера из вашего местного /proc/self/cgroup
файл.
Третья строка выводится на хост-машину (если вы используете 4243 в качестве порта докера), а затем использует узел для анализа возвращенного JSON для DESIRED_PORT
,
Вот еще один вариант для тех, кто работает в Docker в AWS. Эта опция позволяет избежать использования apk для добавления пакета curl и экономит драгоценные 7 МБ пространства. Используйте встроенный wget (часть монолитного двоичного файла BusyBox):
wget -q -O - http://169.254.169.254/latest/meta-data/local-ipv4
Вот как я это делаю. В этом случае он добавляет запись hosts в /etc/hosts в образе докера, указывающую taurus-host на IP-адрес моей локальной машины:
TAURUS_HOST=`ipconfig getifaddr en0`
docker run -it --rm -e MY_ENVIRONMENT='local' --add-host "taurus-host:${TAURUS_HOST}" ...
Затем из контейнера Docker сценарий может использовать имя хоста taurus-host, чтобы выйти на мою локальную машину, на которой размещен контейнер Docker.
Это минималистичная реализация в Node.js того, кто запускает хост на экземплярах AWS EC2, с использованием вышеупомянутого экземпляра метаданных EC2.
const cp = require('child_process');
const ec2 = function (callback) {
const URL = 'http://169.254.169.254/latest/meta-data/local-ipv4';
// we make it silent and timeout to 1 sec
const args = [URL, '-s', '--max-time', '1'];
const opts = {};
cp.execFile('curl', args, opts, (error, stdout) => {
if (error) return callback(new Error('ec2 ip error'));
else return callback(null, stdout);
})
.on('error', (error) => callback(new Error('ec2 ip error')));
}//ec2
и используется как
ec2(function(err, ip) {
if(err) console.log(err)
else console.log(ip);
})
Итак... если вы запускаете свои контейнеры с помощью сервера Rancher, контейнеры Rancher v1.6 (не уверен, есть ли это в версии 2.0) имеют доступ к http://rancher-metadata/, который содержит много полезной информации.
Внутри контейнера IP-адрес можно найти здесь:curl http://rancher-metadata/latest/self/host/agent_ip
Подробнее см.:https://rancher.com/docs/rancher/v1.6/en/rancher-services/metadata-service/.
Решение, которое я использую, основано на "сервере", который возвращает внешний адрес хоста Docker, когда он получает запрос http.
На "сервере":
1) Запустите jwilder/nginx-прокси
# docker run -d -p <external server port>:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
2) Запустить ipify контейнер
# docker run -e VIRTUAL_HOST=<external server name/address> --detach --name ipify osixia/ipify-api:0.1.0
Теперь, когда контейнер отправляет запрос http на сервер, например
# curl http://<external server name/address>:<external server port>
IP-адрес хоста Docker возвращается через ipify через заголовок http "X-Forwarded-For"
Пример (сервер ipify имеет имя "ipify.example.com" и работает на порту 80, хост докера имеет IP 10.20.30.40):
# docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
# docker run -e VIRTUAL_HOST=ipify.example.com --detach --name ipify osixia/ipify-api:0.1.0
Внутри контейнера теперь можно звонить:
# curl http://ipify.example.com
10.20.30.40
На Ubuntu, hostname
Команду можно использовать со следующими параметрами:
-i
,--ip-address
адреса для имени хоста-I
,--all-ip-addresses
все адреса для хоста
Например:
$ hostname -i
172.17.0.2
Для присвоения переменной можно использовать следующую однострочную строку:
IP=$(hostname -i)
Если вы запускаете контейнер Windows в кластере Service Fabric, IP-адрес хоста доступен через переменную среды Fabric_NodeIPOrFQDN
, Переменные среды Service Fabric
Может быть, созданный мною контейнер также полезен https://github.com/qoomon/docker-host
Вы можете просто использовать имя контейнера dns для доступа к хост-системе, например, curl http://dockerhost:9200/, поэтому вам не нужно беспокоиться о каком-либо IP-адресе.
Другой подход основан на
traceroute
и у меня он работает на хосте Linux, например, в контейнере на основе Alpine:
traceroute -n 8.8.8.8 -m 4 -w 1 | awk '$1~/\d/&&$2!~/^172\./{print$2}' | head -1
Это займет некоторое время, но перечислит IP-адрес первого прыжка, который не начинается с 172. Если нет успешного ответа, попробуйте увеличить лимит на тестируемых прыжках, используя
-m 4
аргумент.
С помощью https://docs.docker.com/machine/install-machine/
а) $ docker-machine ip
б) Получить IP-адрес одной или нескольких машин.
$ docker-machine ip host_name
$ docker-machine ip host_name1 host_name2