1
votes

L'application Node.js "ancrée" ne répond pas à l'hôte

J'essaye de "Docker" une application Node.js assez grande et compliquée, qui est à peu près structurée comme ceci:

Nebula [répertoire racine du projet; exécute l'application Node.js]

| __CosmosD3 [répertoire qui contient tous les fichiers HTML et JS]

| __Nebula-Pipeline [répertoire qui contient la plupart des fichiers Python qui effectuent tous les calculs lourds ]

Notre processus d'installation standard pour ce projet est plutôt compliqué en raison de toutes les pièces interconnectées qui doivent toutes fonctionner parfaitement ensemble pour permettre à nos fichiers JS frontaux de communiquer avec nos scripts Python back-end. Pour les systèmes Linux / Unix, voici le processus approximatif (avec des commandes exécutées depuis le répertoire Nebula):

  1. Installez Java. (À peu près n'importe quelle version devrait faire l'affaire; nous avons un seul fichier Java qui doit être exécuté pour une interaction particulière dans le front-end)
  2. Installez Python 2.7 et pip
  3. Exécutez pip install --upgrade pip pour vous assurer que le dernier pip est utilisé
  4. Installez Node.js, npm et zmq avec sudo apt-get install libzmq-dev npm nodejs-legacy
  5. Exécutez npm install pour installer nos dépendances de package Node.js
  6. Exécutez pip install -e ./Nebula-Pipeline pour installer toutes les dépendances Python
  7. Priez pour que tout se passe bien et exécutez l'application avec npm start . Le port par défaut est 8081 , l'application doit donc être accessible via localhost: 8081 .

Un projet très similaire à celui-ci a déjà été «ancré». En modifiant le Dockerfile pour que l'autre projet corresponde plus étroitement aux étapes ci-dessus, voici le Dockerfile que j'ai créé:

/*Import packages required in package.json   */
/*Add these packages from the ../node_modules path*/
var express = require('express');//A lightweight nodejs web framework
var path = require('path');//Ability to join filepaths to filenames.
var favicon = require('serve-favicon');//Set prefered icon in browser URL bar. Unused?
var logger = require('morgan');//HTTP request logger. Unused?
var cookieParser = require('cookie-parser');//Stores cookies in req.cookies
var bodyParser = require('body-parser');//Middleware parser for incoming request bodies, 

/* REST API routes */
var routes = require('./routes/index');//Points to /routes/index.js.  Currently, index.js points to CosmosD3/CosmosD3.html

/* Connect to the databases */
//var mongo = require('mongodb');
//var monk = require('monk');
//var db = monk('localhost:27017/nodetest');
//var datasets = monk('localhost:27017/datasets');

/* The HTTP request handler */

var app = express();//Creates app from express class. (Baseline famework for an app. No web functionality).
var debug = require('debug')('Nebula:server');//Require the debug module. Pass it scoping 'Nebula:server'
var http = require('http').Server(app);//Create an http server on top of app.

/* The Socket.io WebSocket module */
var io = require('socket.io')(http);//Create an io/websocket on top of http object.

/* Our custom Nebula module handles the WebSocket synchronization */
var nebula = require('./nebula')(io);//Creates nebula layer on top of io.

/* Set the port we want to run on */
var port = process.env.PORT || 8080;
//app.set('port', port);
var host = '0.0.0.0';
app.listen(port, host);
console.log(`Running on http://${host}:${port}`);

/* view engine setup, currently not used */
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

/* Expose everything in public/ through our web server */
app.use(express.static(path.join(__dirname, 'public')));
app.use("/cosmos", express.static(path.join(__dirname, 'CosmosD3')));

// Make our db accessible to our router
//app.use(function(req, res, next){
//    req.db = db;
//    req.datasets = datasets;
//    next();
//});

/* Initiate the REST API */
app.use('/', routes);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = (typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port);

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = http.address();
  var bind = (typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port);
  debug('Listening on ' + bind);
}

/**
 * Listen on provided port, on all network interfaces.
 */
http.listen(port);
http.on('error', onError);
http.on('listening', onListening);

Quand j'exécute ce Dockerfile avec docker build -t nebulaserver , il semble que l'image soit créée avec succès. Lorsque j'exécute l'image avec docker run -p 8081: 8081 nebulaserver , j'obtiens l'impression suivante qui semble indiquer que tout fonctionne réellement correctement. (Notez que l'impression "PORT: 8081" est une confirmation du port utilisé par l'application Node.js.)

Michelles-MacBook-Pro-8$ docker run -p 8081:8081 nebulaserver

> Nebula@0.0.1 start /www
> node app.js

PORT:
8081

Cependant, lorsque j'essaye par la suite de me connecter à localhost: 8081 , je n'obtiens aucune réponse. De plus, je m'attends à voir des impressions supplémentaires de mon serveur Node.js lorsqu'il reçoit une demande pour l'une des pages HTML. Je ne vois aucune de ces impressions non plus. C'est comme si la redirection de port ne fonctionnait pas correctement. D'après tout ce que j'ai lu, je devrais faire les choses correctement, mais je n'ai jamais utilisé Docker auparavant, alors il me manque peut-être quelque chose?

MODIFIER: Voici mon fichier app.js, au cas où cela aiderait à comprendre ce qui se passe ...

FROM ubuntu:16.04

RUN mkdir /www
WORKDIR /www
RUN apt-get update

RUN apt-get install -y libzmq-dev npm nodejs-legacy python python-pip
RUN apt-get update && \
    apt-get install -y openjdk-8-jdk && \
    apt-get install -y ant && \
    apt-get clean;

# Fix certificate issues, found as of 
# https://bugs.launchpad.net/ubuntu/+source/ca-certificates-java/+bug/983302
RUN apt-get update && \
    apt-get install ca-certificates-java && \
    apt-get clean && \
    update-ca-certificates -f;

# Setup JAVA_HOME, this is useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME

##COPY ./lib/ /opt/lib

RUN pip install --upgrade pip
COPY ./Nebula /www
RUN ls -a /www
RUN npm install

RUN pip install -e /www/Nebula-Pipeline

EXPOSE 8081

CMD ["npm", "start"]


6 commentaires

Je n'ai pas mon ordinateur pour faire des tests, mais je pense que j'ai eu le même problème. Si tel est le cas, le problème est lié au CMD ["npm", "start"] si je me souviens bien, il démarrera votre instance mais l'arrêtera ensuite, cela ne la maintiendra pas en vie. Une solution était d'utiliser un superviseur ou d'utiliser le Entrypoint dans votre DockerFile, je vais essayer de creuser et de revenir. J'espère qu'au moins cela pourra vous donner de nouvelles idées à tester.


Essayez de lire que blog.trifork.com/2014/03/11/… Je pense que c'est peut-être votre solution. Je voudrais cmd un superviseur dans votre dockerFile, avec un "nodeamon = true" et avec votre commande pour exécuter votre npm


Cela ressemble à un symptôme courant où le processus à l'intérieur du conteneur est configuré pour écouter uniquement sur 127.0.0.1, qui est une valeur par défaut courante pour les serveurs de type développement. Il doit écouter sur 0.0.0.0 («toutes les interfaces entrantes») pour être accessible depuis l'extérieur du conteneur.


@DavidMaze Je ne pense pas que mon conteneur écoute sur le port 127.0.0.1 (localhost) ... Tout ce que j'ai lu semble indiquer que tout s'exécute dans un conteneur fonctionne sur 0.0.0.0 et que la redirection de port que je spécifie quand Je l'exécute ( -p 8081: 8081 ) devrait être suffisant pour que mon hôte et mon conteneur communiquent correctement ... Savez-vous comment je peux vérifier sur quelle URL mon application s'exécute dans le conteneur juste pour etre sur? (Je ne sais pas trop comment je ferais cela car je n'ai pas d'accès direct au conteneur ...)


@Niradnik J'essaie de me débrouiller sans superviseur ... Il ne semble pas que j'en ai vraiment besoin car je veux juste qu'une instance de mon seul conteneur fonctionne. (De plus, j'essaie de garder les choses aussi simples que possible ...) D'après certains exemples que j'ai examinés, il ne semble pas que j'ai vraiment besoin d'un superviseur pour faire parler mon conteneur avec mon hôte. Ou ai-je un malentendu sur la façon dont je devrais configurer mon application avec Docker?


Non, vous n'avez pas besoin d'un superviseur, c'était juste une autre solution au non-démon, mais il semble que cela n'a pas résolu votre problème.


3 Réponses :


0
votes

Je pense que votre problème est dû au démon, vous devez l'éteindre sinon le processus sera arrêté immédiatement après son démarrage. Donc, dans votre cmd, vous devez ajouter quelque chose comme "-g", "démon désactivé;"

Ou utilisez un superviseur pour faire fonctionner votre pid


7 commentaires

Essayez autrement CMD ["npm", "start", "--no-daemon"]


J'ai essayé ces deux choses ... ajouter "--no-daemon" à CMD ne semblait rien faire. L'installation de nodemon et son utilisation à la place via CMD ["nodemon", "start"] ont démarré avec succès mon application et produit la sortie suivante, mais je n'ai toujours pas pu me connecter à l'application en utilisant localhost: 8081 Michelles-MacBook-Pro-8: Nebula michelledowling $ docker run -p 8081: 8081 nebulaserver [nodemon] 1.18.10 [nodemon] pour redémarrer à tout moment, entrez rs` [nodemon] en regardant: . [nodemon] démarrage du node app.js start PORT: 8081`


De plus, pour autant que je sache, l'autre application ne dérange pas le démon ... Et d'autres exemples d'applications simplistes Dockerized Node.js ne semblent pas non plus devoir le faire


J'ai transposé à partir d'un problème nginx, donc peut-être que les symptômes sont les mêmes mais les causes sont différentes.


Le point est que deamon exécutera votre commande alors qu'elle est exécutée, arrêtez-la car il n'est pas conscient qu'il devrait garder ce pid up (du moins c'est ce qui se passait avec nginx donc je devais dire pas de démon) sans démon, il garde le tâche au premier plan pour que docker puisse la maintenir. J'ai une très faible compréhension de cela, je peux dire des choses fausses mais le fait est que Deamon a tué mon processus après son début


PS avez-vous essayé le CMD ["npm", "start", "-g", "daemon off;"] c'était ce que j'ai utilisé pour nginx mais encore une fois pas npm ..


J'ai essayé ça ... Pour l'instant, j'obtiens toujours l'erreur EADDRINUSE . Je suppose que vous avez peut-être raison en ce sens que je ne dois pas utiliser le démon pour que mon application réponde correctement à mon hôte. Mais même pour aller aussi loin, je dois d'abord déterminer d'où vient cette erreur étrange, puis déterminer pourquoi ma redirection de port ne fonctionne tout simplement pas correctement (étant donné que le test "plus simple" que j'utilise ne fonctionne pas non plus) .



2
votes

J'ai déjà eu ce problème et (comme mentionné par @davidmaze dans son commentaire ) J'ai dû changer l'hôte sur lequel mon application écoutait. Dans mon cas, c'était que j'utilisais le framework Express , qui (malgré la documentation sous-entendant le contraire) n'écoutait que pour localhost ( 127.0.0.1 ), et j'en avais besoin pour écouter 0.0.0.0 . Voir lignes 58- 66 (qui constituent le deuxième bloc de code dans Créez la section de l'application Node.js de la exemple d'ancrage d'une application Web Node.js ) pour savoir où ils indiquent explicitement à express d'écouter 0.0.0.0 .


10 commentaires

J'ai essayé cela et j'ai eu Erreur: écoutez EADDRINUSE 0.0.0.0:8081 , ce qui n'a aucun sens pour moi puisque je n'utilise que cette seule application. J'obtiens la même erreur en essayant d'exécuter mon application sur le port 80 à la place.


Lorsque vous exécutez votre conteneur "normalement" lorsque vous ne pouvez pas y accéder de l'extérieur, avez-vous essayé de l'appeler de l'intérieur. Avec la commande docker exec? Essayez et si vous pouvez l'atteindre de l'intérieur sur le 127.0.0.1, le problème est probablement ce qu'il est dit ici.


Je ne sais pas exactement ce que vous entendez par "appelez-le de l'intérieur" ... Je suppose que non? Je ne devrais vraiment rien faire avec ce port autre que d'écouter les requêtes HTTP entrantes pour afficher la page Web. C'est ainsi que l'application fonctionne en dehors de Docker, au moins


Faire un docker exec - il bash puis lancez votre appel ou directement docker exec - il où je pense que la commande sera quelque chose comme un curl si vous en avez un installé ou un wget de 127.0.0.1:8081/xxxxxx


Vous créez un conteneur à partir d'images Ubuntu, donc votre conteneur est fondamentalement un "ordinateur" sur le système d'exploitation Ubuntu, vous pouvez simplement entrer et vérifier si cela fonctionne comme vous le feriez sur une installation locale


désolé je n'ai pas vu un espace a été ajouté c'est -it et non - ça


J'ai donc fait quelques recherches en utilisant une application beaucoup plus simple que j'ai créée exactement comme spécifié à partir de nodejs.org/en/docs/guides/nodejs-docker-webapp . Je rencontre des problèmes similaires là-bas où je ne peux pas accéder à l'application depuis ma machine hôte, même si je peux aller dans le conteneur et faire curl -i localhost: 8080 ou curl -i 0.0. 0.0: 8080 et voir l'impression que je suis censé voir. Mon conteneur fonctionne toujours correctement et tout, selon ce que je vois de docker ps depuis ma machine hôte. Y a-t-il quelque chose qui me manque pour que la redirection de port fonctionne correctement?


Peut-être vaut-il également la peine de mentionner que j'utilise une ancienne version de Docker car mon ordinateur exécute Mac OS X Yosemite (10.10.5), qui est trop ancien pour la dernière version de Docker. (Mon environnement de développement est fragile, j'ai donc hésité à laisser mon système d'exploitation se mettre à jour de peur qu'il ne casse les choses horriblement) De plus, le problème de redirection de port que j'ai même avec le test simple n'explique pas pourquoi mon application plus compliquée lève l'erreur EADDRINUSE ... Je continuerai à fouiller avec cela pour voir si je peux au moins résoudre ce problème


D'accord, donc, après avoir fouillé un peu plus, je ne sais pas pourquoi mon application lance l'erreur EADDRINUSE ... J'ai modifié le message d'origine pour contenir mon fichier app.js pour plus d'informations / contexte ... Je suppose que l'utilisation de app.listen (0.0.0.0, 8080) n'est peut-être pas suffisante pour que mon application écoute sur cette adresse / port. Dois-je apporter d'autres modifications pour me débarrasser de cette erreur?


Ok, je pense que j'ai compris le problème EADDRINUSE ... J'avais besoin que http écoute sur l'hôte et le port spécifiés, pas sur l'application. Je ne parviens toujours pas à accéder à l'application sur ma machine hôte



3
votes

J'ai enfin compris !! Après une tonne de fouilles, j'ai découvert que l'adresse IP par défaut pour les applications Docker est http://192.168.99.100 par opposition à http: // localhost . J'ai eu la réponse d'ici:

https: //forums.docker .com / t / docker-running-host-but-not-accessible / 44082/13


1 commentaires

De plus, pour quiconque essaie de résoudre des problèmes similaires, en fonction de ce que j'ai vu / lu d'autre depuis que j'ai trouvé cette réponse, cela ressemble à la raison pour laquelle je ne peux pas me connecter via localhost peut-être parce que j'utilise l'ancien Docker Toolbox (car mon système d'exploitation est trop ancien pour prendre en charge les nouvelles versions de Docker). Un moyen de voir à quelle adresse IP se connecter est docker-machine ip default , où default est le nom du démon par lequel vous vous connectez à vos conteneurs. (Vous obtenez default "prêt à l'emploi" avec Docker, mais il semble que vous puissiez également définir d'autres démons si vous le souhaitez.)