First stable version

This commit is contained in:
Juan Ferrer 2020-01-28 14:57:24 +01:00
parent 17acb5582e
commit 4fea7fa1dc
9 changed files with 128 additions and 55 deletions

View File

@ -19,6 +19,6 @@ WORKDIR /docker-discover
COPY package.json package-lock.json ./
RUN npm install --only=prod
COPY index.js config.json rproxy.handlebars ./
COPY index.js config.yml rproxy.handlebars ./
CMD ["pm2-runtime", "index.js"]

View File

@ -1,18 +0,0 @@
{
"docker": {
"socketPath": "/var/run/docker.sock"
},
"rproxies": [
"rproxy1.local",
"rproxy2.local"
],
"sshAuth": {
"username": "root",
"privateKey": "/root/.ssh/id_rsa"
},
"proxyConf": "/etc/haproxy/haproxy.cfg",
"reloadCmd": "service haproxy reload",
"delay": 4,
"debug": false,
"events": ["service", "node"]
}

14
config.yml Normal file
View File

@ -0,0 +1,14 @@
delay: 4
debug: true
events: [service, node]
docker:
socketPath: /var/run/docker.sock
rproxy:
hosts:
- rproxy1.local
- rproxy2.local
auth:
username: root,
privateKey: /root/.ssh/id_rsa
confPath: /etc/haproxy/haproxy.cfg
reloadCmd: service haproxy reload

View File

@ -1,15 +1,15 @@
version: '3.7'
services:
discover:
main:
image: registry.verdnatura.es/docker-discover
build: .
environment:
- NODE_ENV=production
NODE_ENV: production
volumes:
- /var/run/docker.sock:/var/run/docker.sock
configs:
- source: config
target: /docker-discover/config.json
target: /docker-discover/config.yml
- source: template
target: /docker-discover/rproxy.handlebars
- source: ssh

View File

@ -1,30 +1,60 @@
require('require-yaml');
let Docker = require('dockerode');
let handlebars = require('handlebars');
let ssh = require('node-ssh');
let fs = require('fs');
let conf = require('./config.json');
let conf = require('./config.yml');
let docker;
let template;
let isProduction = process.env.NODE_ENV === 'production';
async function updateProxy() {
console.log('Updating proxy configuration.');
let data;
console.log('Updating reverse proxy configuration.');
let info;
if (process.env.NODE_ENV != 'production') {
data = require('./test.json');
if (!isProduction) {
info = require('./test.json');
} else {
data = {
info = {
services: await docker.listServices(),
nodes: await docker.listNodes()
};
}
let services = [];
for (let serviceInfo of info.services) {
let ports = serviceInfo.Endpoint.Ports;
if (!Array.isArray(ports) || !ports.length) continue;
let name = serviceInfo.Spec.Name;
let match = name.match(/(.+)_main$/);
if (match) name = match[1];
let service = {
name,
port: ports[0].PublishedPort,
nodes: []
};
services.push(service);
for (let node of info.nodes) {
let address = node.ManagerStatus
? node.ManagerStatus.Addr.split(':')[0]
: node.Status.Addr;
service.nodes.push({
name: node.Description.Hostname,
endpoint: `${address}:${service.port}`
});
}
}
let tmpConf = `/tmp/rproxy.${new Date().getTime()}`;
let configString = template(data);
let configString = template({info, services});
fs.writeFileSync(tmpConf, configString);
if (conf.debug || process.env.NODE_ENV != 'production') {
if (conf.debug || !isProduction) {
let delimiter = '#'.repeat(80);
console.log(delimiter);
console.log(configString);
@ -33,15 +63,18 @@ async function updateProxy() {
let files = {
local: tmpConf,
remote: conf.proxyConf
remote: conf.rproxy.confPath
};
for (let host of conf.rproxies) {
for (let host of conf.rproxy.hosts) {
console.log(`Updating host: ${host}`);
if (!isProduction) continue;
let sshClient = new ssh();
await sshClient.connect(Object.assign({host}, conf.sshAuth));
await sshClient.connect(Object.assign({host}, conf.rproxy.auth));
await sshClient.putFiles([files]);
//await ssh.exec(conf.reloadCmd);
if (conf.rproxy.reloadCmd)
await ssh.exec(conf.rproxy.reloadCmd);
await sshClient.dispose();
}
@ -53,7 +86,7 @@ async function updateProxy() {
let timeoutId;
docker = new Docker(conf.docker);
template = handlebars.compile(fs.readFileSync('rproxy.handlebars', 'utf8'));
updateProxy();
await updateProxy();
console.log('Listenig for events.')
docker.getEvents({}, (err, stream) => {
@ -68,9 +101,9 @@ async function updateProxy() {
console.log(`Event: ${event.Type}: ${event.Action}`);
if (timeoutId) return;
timeoutId = setTimeout(() => {
timeoutId = setTimeout(async () => {
timeoutId = null;
updateProxy();
await updateProxy();
}, conf.delay * 1000);
})
});

36
package-lock.json generated
View File

@ -1,4 +1,5 @@
{
"name": "docker-discover",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
@ -20,6 +21,14 @@
"indent-string": "^4.0.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@ -137,6 +146,11 @@
"once": "^1.4.0"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@ -168,6 +182,15 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsonparse": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
@ -252,6 +275,14 @@
"util-deprecate": "^1.0.1"
}
},
"require-yaml": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
"integrity": "sha1-LhsY2RPDuqcqWk03O28Tjd0sMr0=",
"requires": {
"js-yaml": "^3.13.1"
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
@ -298,6 +329,11 @@
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"ssh2": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.7.tgz",

View File

@ -5,11 +5,11 @@
"repository": {
"type": "git",
"url": "https://gitea.verdnatura.es/verdnatura/docker-discover"
},
},
"dependencies": {
"dockerode": "^3.0.2",
"handlebars": "^4.7.2",
"node-ssh": "^7.0.0"
"node-ssh": "^7.0.0",
"require-yaml": "0.0.1"
}
}

View File

@ -2,10 +2,8 @@
# Auto-generated backends
{{#each services}}
{{#if Endpoint.Ports.[0]}}
backend {{Spec.Name}}
{{#each ../nodes}}
server {{Description.Hostname}}({{Status.Addr}}):{{../Endpoint.Ports.[0].PublishedPort}} {{Status.Addr}}:{{../Endpoint.Ports.[0].PublishedPort}} check
backend {{name}}
{{#each nodes}}
server {{name}}({{endpoint}}) {{endpoint}} check
{{/each}}
{{/if}}
{{/each}}

View File

@ -1,33 +1,43 @@
{
"services": [
{
"Spec": {"Name": "service1"},
"Spec": {"Name": "foo"},
"Endpoint": {
"Ports": [
{"PublishedPort": 10001}
]
}
}, {
"Spec": {"Name": "service2"},
"Endpoint": {
"Ports": []
}
}, {
"Spec": {"Name": "service3"},
"Spec": {"Name": "bar_main"},
"Endpoint": {
"Ports": [
{"PublishedPort": 10003}
]
}
}, {
"Spec": {"Name": "bar_foo"},
"Endpoint": {
"Ports": [
{"PublishedPort": 10003}
]
}
}, {
"Spec": {"Name": "baz"},
"Endpoint": {}
}
],
"nodes": [
{
"Description": {"Hostname": "node1"},
"Status": {"Addr": "10.0.0.1"}
"Description": {"Hostname": "fooNode"},
"ManagerStatus": {"Addr": "10.0.0.1:2377"},
"Status": {"Addr": "0.0.0.0"}
}, {
"Description": {"Hostname": "node2"},
"Description": {"Hostname": "barNode"},
"ManagerStatus": {"Addr": "10.0.0.2:2377"},
"Status": {"Addr": "10.0.0.2"}
}, {
"Description": {"Hostname": "bazNode"},
"Status": {"Addr": "10.0.0.3"}
}
]
}