Compare commits

...
Author SHA1 Message Date
ElevenNotes f60f0ad124 0.1.5, remove container restart on kill 2023-12-19 11:44:50 +01:00
ElevenNotes e7202481b3 0.1.4 2023-12-05 08:58:34 +01:00
ElevenNotes ccdf29b7ae add webhook 2023-12-05 08:57:00 +01:00
ElevenNotes 69f14549c6 rediss://localhost:6379/0 2023-12-04 15:16:53 +01:00
ElevenNotes 6ab10f6b2a sanitize container name 2023-12-04 14:43:43 +01:00
ElevenNotes 35fee8e82b status:null for poll 2023-12-04 14:42:57 +01:00
ElevenNotes 8686ba58c0 log for container event only if traefik label 2023-12-04 14:28:23 +01:00
ElevenNotes 437a3e92d3 event?.status 2023-12-04 14:09:20 +01:00
ElevenNotes 4796cc1aa1 update 0.1.3 2023-12-04 14:03:26 +01:00
5 changed files with 75 additions and 36 deletions
+1 -2
View File
@@ -1,2 +1 @@
maintain/
/build
maintain/
+17 -5
View File
@@ -1,5 +1,5 @@
# Alpine :: Traefik Labels
![size](https://img.shields.io/docker/image-size/11notes/traefik-labels/0.1.2?color=0eb305) ![version](https://img.shields.io/docker/v/11notes/traefik-labels?color=eb7a09) ![pulls](https://img.shields.io/docker/pulls/11notes/traefik-labels?color=2b75d6) ![activity](https://img.shields.io/github/commit-activity/m/11notes/docker-traefik-labels?color=c91cb8) ![commit-last](https://img.shields.io/github/last-commit/11notes/docker-traefik-labels?color=c91cb8)
![size](https://img.shields.io/docker/image-size/11notes/traefik-labels/0.1.5?color=0eb305) ![version](https://img.shields.io/docker/v/11notes/traefik-labels?color=eb7a09) ![pulls](https://img.shields.io/docker/pulls/11notes/traefik-labels?color=2b75d6) ![activity](https://img.shields.io/github/commit-activity/m/11notes/docker-traefik-labels?color=c91cb8) ![commit-last](https://img.shields.io/github/last-commit/11notes/docker-traefik-labels?color=c91cb8)
Run Traefik Labels based on Alpine Linux. Small, lightweight, secure and fast 🏔️
@@ -7,8 +7,9 @@ What can I do with this? Simply put: It will export any traefik labels on a cont
In order to use this image, you need to setup Traefik with a Redis provider and then point this image via REDIS_URL to the same Redis instance. Each entry will have an expire timer set in Redis, so that if a container is removed by a server crashing, Redis will automatically remove stale entries as well. Entries are refreshed every 60 seconds or on all docker container events (create, run, kill, stop, restart, ...).
This image provides the ability to call a webhook for each container for each event or poll after the data was updates in Redis.
## Run
This will export all labels from all containers to the Redis instance specified in LABELS_REDIS_URL from the same host this container is running on.
```shell
docker run --name traefik-labels \
-v /run/docker.sock:/run/docker.sock \
@@ -16,6 +17,15 @@ docker run --name traefik-labels \
-d 11notes/traefik-labels:[tag]
```
```shell
docker run --name traefik-labels \
-v /run/docker.sock:/run/docker.sock \
-e LABELS_REDIS_URL="rediss://foo:bar@10.127.198.254:6379/0" \
-e LABELS_WEBHOOK="https://domain.com/traefik/labels" \
-e LABELS_WEBHOOK_AUTH_BASIC="foo:bar" \
-d 11notes/traefik-labels:[tag]
```
This is a demo webserver that will start on :8080, all the traefik labels will be exportet to the Redis instance. They follow the exact same [syntax](https://doc.traefik.io/traefik/routing/providers/kv/) as for normal Redis and Traefik, just as labels.
```shell
docker run --name demo \
@@ -39,9 +49,11 @@ docker run --name demo \
## Environment
| Parameter | Value | Default |
| --- | --- | --- |
| `LABELS_REDIS_URL` | the redis URL to connect, use rediss:// for SSL | redis:://localhost:6379/0 |
| `LABELS_INTERVAL` | in what interval container information is pulled | 300 |
| `LABELS_TIMEOUT` | how many seconds after an interval the keys should stay till they expire | 30 |
| `LABELS_REDIS_URL` | the redis URL to connect, use rediss:// for SSL | rediss://localhost:6379/0 |
| `LABELS_INTERVAL` | in what interval container information is pulled in seconds | 300 |
| `LABELS_TIMEOUT` | how many seconds after an interval the keys should stay till they expire in seconds | 30 |
| `LABELS_WEBHOOK` | URL to call on each event or poll for each container | |
| `LABELS_WEBHOOK_AUTH_BASIC` | Basic authentication to use in the form of "username:password" for the webhook | |
## Parent image
* [11notes/node:stable](https://github.com/11notes/docker-node)
-1
View File
@@ -1,6 +1,5 @@
# :: Header
FROM 11notes/node:stable
ENV APP_VERSION=0.1.1
ENV APP_ROOT=/labels
# :: Run
+54 -26
View File
@@ -8,7 +8,7 @@ class Labels{
#poll = false;
constructor(){
this.#docker = new Docker({socketPath: '/run/docker.sock'});
this.#docker = new Docker({socketPath:'/run/docker.sock'});
}
#log(message, error){
@@ -20,6 +20,10 @@ class Labels{
}
async watch(){
if('' !== process.env.LABELS_WEBHOOK){
this.#log(`using webhook ${process.env.LABELS_WEBHOOK}`);
}
this.#redis = await redis.createClient({
url:process.env.LABELS_REDIS_URL,
pingInterval:30000,
@@ -30,7 +34,7 @@ class Labels{
this.#redis.connect();
this.#redis.on('ready', ()=>{
this.#log('connected to Redis');
this.#log('successfully connected to redis database');
(async() => {
await this.dockerPoll();
})();
@@ -48,13 +52,16 @@ class Labels{
dockerEvents(){
this.#docker.getEvents({}, (error, data) => {
data.on('data', async(chunk) => {
const event = JSON.parse(chunk.toString('utf8'));
if(/Container/i.test(event?.Type) && /start|stop|restart|kill|die|destroy/i.test(event?.status)){
this.#log(`new docker event for container ${event.id}`);
await this.dockerInspect(event.id, event.status);
}
});
if(error){
this.#log(error, ERROR);
}else{
data.on('data', async(chunk) => {
const event = JSON.parse(chunk.toString('utf8'));
if(/Container/i.test(event?.Type) && /^(start|stop|restart|kill|die|destroy)$/i.test(event?.status)){
await this.dockerInspect(event.id, event.status);
}
});
}
});
}
@@ -62,16 +69,16 @@ class Labels{
if(!this.#poll){
try{
this.#poll = true;
this.#log(`poll start (interval:${process.env.LABELS_INTERVAL})}`);
this.#docker.listContainers((error, containers) => {
containers.forEach(async(container) => {
await this.dockerInspect(container.Id, 'start');
});
if(!error){
containers.forEach(async(container) => {
await this.dockerInspect(container.Id);
});
}
});
}catch(e){
this.#log(e, ERROR);
}finally{
this.#log('poll end');
this.#poll = false;
}
}
@@ -81,23 +88,44 @@ class Labels{
return(new Promise((resolve, reject) => {
const container = this.#docker.getContainer(id);
container.inspect(async(error, data) => {
if(error){
reject(error);
}
for(const label in data?.Config?.Labels){
if(/traefik\//i.test(label)){
switch(true){
case /start|restart/i.test(status):
await this.#redis.set(label, data?.Config?.Labels[label], {EX:parseInt(process.env.LABELS_INTERVAL) + parseInt(process.env.LABELS_TIMEOUT)});
break;
if(!error){
let update = false;
const webHook = {event:status, labels:{}};
const headers = {'Content-Type':'application/json'};
case /stop|kill|die|destroy/i.test(status):
if('' !== process.env.LABELS_WEBHOOK_AUTH_BASIC){
headers['Authorization'] = 'Basic ' + Buffer.from(process.env.LABELS_WEBHOOK_AUTH_BASIC).toString('base64')
}
if(/start|restart/i.test(status)){
update = true;
}
this.#log(`inspect container {${(data?.Name || data?.id).replace(/^\//i, '')}}${(
(null === status) ? '' : ` event[${status}]`
)}`);
for(const label in data?.Config?.Labels){
if(/traefik\//i.test(label)){
webHook.labels[label] = data?.Config?.Labels[label];
if(update){
await this.#redis.set(label, data?.Config?.Labels[label], {EX:parseInt(process.env.LABELS_INTERVAL) + parseInt(process.env.LABELS_TIMEOUT)});
}else{
await this.#redis.del(label);
break;
}
}
}
if('' !== process.env.LABELS_WEBHOOK){
try{
await fetch(process.env.LABELS_WEBHOOK, {method:(
(update) ? 'PUT' : 'DELETE'
), body:JSON.stringify(webHook), headers:headers});
}catch(e){
this.#log(e, ERROR);
}
}
resolve(true);
}
resolve(true);
});
}));
}
+3 -2
View File
@@ -3,14 +3,15 @@ process.once('SIGINT', () => process.exit(0));
const { fork } = require('node:child_process');
const child = fork(`${__dirname}/app.js`, [], {
env:{
LABELS_REDIS_URL:process.env?.LABELS_REDIS_URL || 'redis:://localhost:6379/0',
LABELS_REDIS_URL:process.env?.LABELS_REDIS_URL || 'rediss://localhost:6379/0',
LABELS_INTERVAL:parseInt(process.env?.LABELS_INTERVAL || 300),
LABELS_TIMEOUT:parseInt(process.env?.LABELS_TIMEOUT|| 30),
LABELS_WEBHOOK:process.env?.LABELS_WEBHOOK || '',
LABELS_WEBHOOK_AUTH_BASIC:process.env?.LABELS_WEBHOOK_AUTH_BASIC || '',
}
});
child.on('error', (error) =>{
console.error(error);
process.exit(1);
});
child.on('close', (code) =>{
console.warn(`child process closed with exit code ${code}`);