TL;DR
Bonjour à tous, J'essaie d'appeler des microservices nodejs backend à partir d'un frontend nodejs écrit en Express, par le biais de Consul Interface DNS mais j'ai des erreurs.
J'utilise le nodejs dns api pour définir le DNS de l'application du nœud unique afin d'effectuer les appels dns.resolve() ultérieurs à l'interface DNS locale de Consul.
Cible
J'aimerais pouvoir faire une demande http à mon service dorsal sans avoir à câbler ses IP et ses ports dans mon code client. Je ne veux pas non plus écrire un code personnalisé pour interroger l'API HTTP de Consul afin d'obtenir le couple ip:port de mon service chaque fois que j'ai besoin de l'appeler.
Problema
Le problème est que lorsque j'utilise axios (similaire à demande ) pour effectuer un appel HTTP vers le service dorsal, j'obtiens toujours une erreur car il ne peut pas résoudre l'adresse. Il semble qu'Axios n'utilise pas le DNS que j'ai précédemment configuré :
dns.setServers(['127.0.0.1:8600']);
Mise à jour_1
En fixant le dns de la machine virtuelle (/etc/resolv.conf) à localhost et en utilisant l'option de ligne de commande -recursor pour consul avec le dns par défaut, tout fonctionne ! Je voudrais quand même comprendre ce que je fais de mal en configurant le dns uniquement sur mon application nodejs.
Configuration
- 1 nœud FE avec un processus nodejs exécutant un simple serveur web avec Expressjs. Dans la route app.get('/') il fait un appel REST POST à un service backend appelé be01 à travers consul et axios (comme une requête).
- 2 BE node avec un processus nodejs exécutant un simple serveur web avec Expressjs exposant une api REST.
- 1 nœud Consul avec Consul fonctionnant en tant que serveur.
- Chaque nœud a son propre agent consul en cours d'exécution et joint au cluster.
- Les ports TCP sont correctement ouverts
En exécutant les membres du consul sur le nœud FE, j'obtiens :
agent-server 10.0.1.7:8301 alive server 1.0.1 2 dc1 <all>
agent-be-01 10.0.1.5:8301 alive client 1.0.1 2 dc1 <default>
agent-be-02 10.0.1.6:8301 alive client 1.0.1 2 dc1 <default>
agent-fe 10.0.1.4:8301 alive client 1.0.1 2 dc1 <default>
Si je cours dig @localhost -p 8600 be01.service.consul SRV sur le nœud FE, j'obtiens correctement ce résultat ( comme dans les documents du Consul ):
root@NGINXLB:~/node8080$ dig @localhost -p 8600 be01.service.consul SRV
; <<>> DiG 9.9.5-3ubuntu0.16-Ubuntu <<>> @localhost -p 8600 be01.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43367
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 5
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;be01.service.consul. IN SRV
;; ANSWER SECTION:
be01.service.consul. 0 IN SRV 1 1 8081 agent-be-02.node.dc1.consul.
be01.service.consul. 0 IN SRV 1 1 8080 agent-be-01.node.dc1.consul.
;; ADDITIONAL SECTION:
agent-be-02.node.dc1.consul. 0 IN A 10.0.1.6
agent-be-02.node.dc1.consul. 0 IN TXT "consul-network-segment="
agent-be-01.node.dc1.consul. 0 IN A 10.0.1.5
agent-be-01.node.dc1.consul. 0 IN TXT "consul-network-segment="
;; Query time: 2 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Fri Dec 01 10:09:00 UTC 2017
;; MSG SIZE rcvd: 246
Il s'agit du code de service BE :
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var jsonParser = bodyParser.json()
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.post('/api/getUserTiles', jsonParser, function (req, res) {
console.log("> API request for '/api/getUserTiles'");
if (!req.body) { return res.sendStatus(400) }
else {
var user = req.body.user;
console.log("User: " + user);
res.json({
"authorizedTiles": [
{"tileID": "profile"}
,{"tileID": "search"}
,{"tileID": "test"}
],
"email": "max@google.com",
"userName":"Max",
"has_error": false,
"error_message": ""
});
}
});
var server = app.listen(8080, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
L'appeler depuis le FE en utilisant son ip:port avec curl fonctionne sans problème :
root@NGINXLB:~/node8080$ curl -d="user=Max" -X POST http://10.0.1.5:8080/api/getUserTiles
{"authorizedTiles":[{"tileID":"profile"},{"tileID":"search"},{"tileID":"test"}],"email":"max@google.com","userName":"Max","has_error":false,"error_message":""}
Le service web sur le nœud FE, en simplifiant, est en quelque sorte ceci :
var axios = require('axios');
var dns = require('dns');
var consul = require('consul')();
dns.setServers(['127.0.0.1:8600']);
//console.log(dns.getServers());
dns.resolveSrv("be01.service.consul", function(err, records){
console.log("\nDNS SRV query");
if(err){
console.log(err);
}else{
console.log(records);
}
});
consul.health.service({
"service": "be01",
"dc": "dc1",
"passing": true
}, function(err, result){
console.log("\nConsul.health.service query:");
if(err){console.log(err); throw err;}
else if(result.length > 0){
for(var i=0; i<result.length; i++){
console.log(result[i].Node.Address + ":" + result[i].Service.Port);
}
}
});
axios.post("http://be01.service.consul/api/getUserTiles", {'user':'Max'})
.then(function(response){
if (response.data.has_error) {
console.log("\nRESPONSE HAS ERROR!");
}else {
console.log("\nSUCCESS!");
}
})
.catch(function(err) {
console.log("\nERROR CALLING THE BE SERVICE");
});
En l'exécutant, j'obtiens le résultat suivant :
root@NGINXLB:~/node8080$ node app.js
DNS SRV query
[ { name: 'agent-be-01.node.dc1.consul',
port: 8080,
priority: 1,
weight: 1 },
{ name: 'agent-be-02.node.dc1.consul',
port: 8081,
priority: 1,
weight: 1 } ]
Consul.health.service query:
10.0.1.5:8080
10.0.1.6:8081
ERROR CALLING THE BE SERVICE
{ Error: getaddrinfo ENOTFOUND be01.service.consul be01.service.consul:80
at errnoException (dns.js:50:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:92:26)
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'be01.service.consul',
host: 'be01.service.consul',
port: 80,
...
...
Conclusion
Comme vous pouvez le constater, le client nodejs appelle le service be mais ne parvient pas à résoudre 'be01.service.consul'. De plus, il utilise le port 80 au lieu de 8080 ou 8081 comme le prévoit l'interface DNS de Consul. Que me manque-t-il ?