Контейнерный сервер Node недоступен с помощью server.listen (порт, '127.0.0.1')

Я установил простой сервер Node в Docker.

Dockerfile

FROM node:latest
RUN apt-get -y update
ADD example.js .
EXPOSE 1337   
CMD node example.js

example.js

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n'+new Date);
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Теперь создайте образ

$ docker build -t node_server .

Теперь запустите в контейнере

$ docker run -p 1337:1337 -d node_server  
$ 5909e87302ab7520884060437e19ef543ffafc568419c04630abffe6ff731f70

Убедитесь, что контейнер запущен и порты сопоставлены:

$ docker ps  

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
5909e87302ab        node_server         "/bin/sh -c 'node exa"   7 seconds ago       Up 6 seconds        0.0.0.0:1337->1337/tcp   grave_goldberg

Теперь подключимся к контейнеру и проверим, что внутри работает сервер:

$ docker exec -it 5909e87302ab7520884060437e19ef543ffafc568419c04630abffe6ff731f70 /bin/bash 

И в командной строке контейнера введите:

root@5909e87302ab:/# curl http://localhost:1337
Hello World
Mon Feb 15 2016 16:28:38 GMT+0000 (UTC)

Выглядит хорошо, правда?

Проблема

Когда я выполняю ту же команду curl на хосте (или перехожу в браузере по адресу http: // localhost: 1337), я ничего не вижу.

Есть идеи, почему сопоставление портов между контейнером и хостом не работает?

То, что я уже пробовал:

  • Запуск с флагом --expose 1337

person Assaf Shomer    schedule 15.02.2016    source источник
comment
Вы не бежите с --expose. Вы создаете образ с помощью директивы EXPOSE, затем вы запускаете с --publish (или -p). Смотрите мой ответ ниже.   -  person VonC    schedule 15.02.2016


Ответы (2)


Ваши порты отображаются правильно, но ваш сервер прослушивает соединения на 127.0.0.1 внутри вашего контейнера:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n'+new Date);
}).listen(1337, '127.0.0.1');

Вам нужно запустить свой сервер следующим образом:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n'+new Date);
}).listen(1337, '0.0.0.0');

Обратите внимание на 0.0.0.0 вместо 127.0.0.1.

person Chris McKinnel    schedule 15.02.2016
comment
Прослушивание 0.0.0.0 выглядит очень странно, не сразу понятно, что это значит. Однако, похоже, это значение по умолчанию, поэтому вы можете просто опустить этот аргумент в функции listen(), что, как мне кажется, легче понять nodejs.org/api/ - person Davos; 11.11.2017
comment
Прослушивание 0.0.0.0 означает прослушивание на всех интерфейсах. Часто это излишне, иногда это вызывает беспокойство по поводу безопасности, но в целом безвредно. Это имеет смысл внутри контейнера Docker. - person Jim Stewart; 22.03.2018
comment
Большое спасибо, это решение решает проблему с любым сервером, работающим в докере, например java или node.js express server. Интерфейс ДОЛЖЕН быть в любом случае: 0.0.0.0, где порт - это тот, который экспортируется с опцией -P $PORT:$PORT/tcp, поэтому обычно сервер проверяет export HOST=0.0.0.0 во время выполнения. - person loretoparisi; 04.04.2018
comment
Большое спасибо за это - мне потребовались бесконечные часы, чтобы решить эту проблему! - person readikus; 13.04.2020
comment
Большое спасибо. У меня была такая же проблема, но с netty, я не мог получить к ней доступ извне контейнера. Есть ли лучший способ понять, какие границы контейнера сетевого интерфейса, чтобы не использовать 0.0.0.0? - person matio; 22.11.2020
comment
Это спасло мне день! Я только что понял, что, очевидно, нет возможности опубликовать порт интерфейса localhost контейнера. Правило --publish [ip: hostPort: containerPort | ip :: containerPort | hostPort: containerPort | containerPort] - person dom free; 22.07.2021

Добавление EXPOSE 1337 в файл докера

EXPOSE является обязательным, если вы хотите "раскрыть "этот порт в другие контейнеры.

Как отмечает BMitch:

Expose не требуется для публикации порта или для подключения контейнера к контейнеру через общую сеть докеров.
Это метаданные для публикации всех портов с помощью -P и проверки образа / контейнера.

So:

Запуск с флагом --expose 1337

Не совсем так: вам нужно запустить докер it с -p 1337:1337

Вам потребуется либо:

  • создать образ с директивой EXPOSE в нем (используется -P)
  • или запустите его с портом, опубликованным на хосте -p 1337:1337

Тест curl http://localhost:1337 был проведен из контейнера (не требуется EXPOSE или публикация).
Если вы хотите, чтобы он работал с хоста Linux, вам потребуется EXPOSE+-P или -p 1337:1337.
Либо .

Объявление только его открытого доступа хорошо для документирования намерения, но само по себе ничего не делает.

Например:

https://i.stack.imgur.com/wmKgd.png

На этом рисунке 8080 EXPOSE'd, опубликованный на хосте Linux 8888.
И если этот хост Linux не является фактическим хостом, этот же порт необходимо быстро перенаправить на фактический хост. См. «Как получить доступ к tomcat, работающему в контейнере докеров, из браузера?».

Если localhost не работает с хоста Linux, попробуйте его IP-адрес:

CID=$(docker run -p 1337:1337 -d node_server)
CIP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${CID})
curl http://${CIP}:1337

Или, как упоминалось выше, заставьте ваш сервер прослушивать соединения, поступающие с любого IP-адреса: 0.0.0.0 который является широковещательным адресом или нулевым сеть.

person VonC    schedule 15.02.2016
comment
Спасибо за комментарий, но, как я уже упоминал выше, я уже пробовал добавить EXPOSE 1337, и он тоже не работает. - person Assaf Shomer; 15.02.2016
comment
@AssafShomer вам нужно и то, и другое, в этом весь смысл моего ответа - person VonC; 15.02.2016
comment
@AssafShomer Я отредактировал ответ, чтобы прояснить этот момент. - person VonC; 15.02.2016
comment
Спасибо, конечно, с обоими пробовал. А именно: собрать с EXPOSE 1337 и запустить с -p 1337: 1337, по-прежнему ничего. Я изменил вопрос, чтобы отразить это. - person Assaf Shomer; 15.02.2016
comment
@AssafShomer Вы работаете непосредственно в Linux или используете docker через виртуальную машину boot2docker в Windows или Mac? - person VonC; 15.02.2016
comment
Непосредственно в Linux, без boot2docker, без Windows и без виртуальной машины. Голый металл :) (почти) - person Assaf Shomer; 15.02.2016
comment
@AssafShomer Вы пробовали использовать IP контейнера для тестирования? (Я отредактировал ответ) - person VonC; 15.02.2016
comment
Что тогда делает переключатель docker run --expose? В документах указано This exposes port ... of the container without publishing the port to the host system’s interfaces, что означает, что он доступен на интерфейсах контейнера, если вы знаете этот адрес docs.docker.com/engine/reference/commandline/run/ Кажется полезным, если вы не хотите, чтобы ваш хост открывал эти порты в ваши контейнеры. - person Davos; 10.11.2017
comment
@Davos Что тогда делает docker run --expose switch? : он предоставляет во время выполнения (чтобы другие контейнеры могли видеть) порт, который изначально не был EXPOSE'd в образе Dockerfile. - person VonC; 10.11.2017
comment
Expose не требуется для публикации порта или для подключения контейнера к контейнеру через общую сеть докеров. Это метаданные для публикации всех портов с -P и проверки образа / контейнера. Я никогда не видел такого воздействия на время выполнения. - person BMitch; 11.11.2017
comment
EXPOSE в Dockerfile совершенно не нужен. Он служит исключительно в качестве документации. Показывать намерение - хорошая идея, но она также вводит в заблуждение, потому что ничего не делает. С таким же успехом это может быть комментарий. - person Jim Stewart; 22.03.2018
comment
@jim Я согласен. Не стесняйтесь редактировать этот ответ, чтобы сделать его более ясным. - person VonC; 22.03.2018