Контейнерний сервер Node недоступний за допомогою server.listen (порт, '127.0.0.1')


80

Я створив простий сервер Node в Docker.

Докерфайл

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прапором

Ви не "бігаєте з --expose". Ви створюєте образ за EXPOSEдирективою, а потім запускаєте за допомогою --publish(або -p). Дивіться мою відповідь нижче.
VonC

Відповіді:


120

Ваші порти виставляються правильно, але ваш сервер прослуховує підключення 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.


3
Прослуховування на 0.0.0.0 виглядає дуже дивно, не відразу очевидно, що це означає. Здається, це за замовчуванням, тому ви можете просто опустити цей аргумент із listen()функції, яку, на мою думку, легше зрозуміти nodejs.org/api/…
Давос,

5
Прослуховування на 0.0.0.0 означає "прослуховування на всіх інтерфейсах". Це часто надмірно, іноді проблема безпеки, але, як правило, нешкідлива. Це має сенс всередині контейнера Docker.
Джим Стюарт,

1
Велике спасибі, це рішення вирішує проблему з будь-яким сервером, що працює в докері, наприклад, java або експрес-сервер node.js. Інтерфейс ПОВИНЕН бути у будь-якому випадку:, 0.0.0.0де порт експортується з -P $PORT:$PORT/tcpопцією, тому зазвичай сервер перевіряє наявність export HOST=0.0.0.0під час виконання.
loretoparisi

1
Щиро дякую за це - мені знадобилися нескінченні години, щоб це вирішити!
readikus

Дуже дякую. У мене була та ж проблема, але з мережею, через яку я не міг отримати до неї доступ ззовні контейнера. Чи є кращий спосіб зрозуміти, які межі контейнера мережевого інтерфейсу, щоб не використовувати 0.0.0.0?
matio

35

Додавання EXPOSE 1337 до файлу докера

EXPOSE є обов'язковий якщо ви хочете "піддати" цей порт іншим контейнерам.

Як коментує BMitch :

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

Так:

Біг з --expose 1337прапором

Не зовсім так: вам потрібно докер запустити його з-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це трансляційна адреса або нульова мережа .


Дякую за коментар, але, як я вже згадував вище, я вже намагався додати EXPOSE 1337, і це теж не працює
Асаф Шомер

@AssafShomer вам потрібні обидва, це суть моєї відповіді
VonC

@AssafShomer Я відредагував відповідь, щоб зробити це зрозумілішим.
VonC

2
Дякую, звичайно, я спробував це з обома. А саме: будувати за допомогою EXPOSE 1337 і запускати за допомогою -p 1337: 1337, все одно нічого. Я змінив питання, щоб це відобразити.
Асаф Шомер

2
EXPOSEв Dockerfileабсолютно непотрібна. Він служить суто документацією. Це гарна ідея - вказати на намір, але це також вводить в оману, оскільки це взагалі нічого не робить. Це також може бути коментар.
Джим Стюарт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.