Watching & Caching
Watch for real-time changes to Kubernetes resources, or use informers for higher-level caching.
Prerequisites
Install the client: npm install @kubernetes/client-node. These examples require a valid kubeconfig (default: ~/.kube/config) or in-cluster service account. Run with Node.js 18+.
Watch Resources
examples/typescript/watch/watch-example.ts
import * as k8s from '@kubernetes/client-node';
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const watch = new k8s.Watch(kc);
const req = await watch.watch(
'/api/v1/namespaces',
// optional query parameters can go here.
{
allowWatchBookmarks: true,
},
// callback is called for each received object.
(type, apiObj, watchObj) => {
if (type === 'ADDED') {
console.log('new object:');
} else if (type === 'MODIFIED') {
console.log('changed object:');
} else if (type === 'DELETED') {
console.log('deleted object:');
} else if (type === 'BOOKMARK') {
console.log(`bookmark: ${watchObj.metadata.resourceVersion}`);
} else {
console.log('unknown type: ' + type);
}
console.log(apiObj);
},
// done callback is called if the watch terminates either normally or with an error
(err) => {
if (err) {
console.error(err);
} else {
console.log('watch finished normally');
}
},
);
// watch returns a request object which you can use to abort the watch.
setTimeout(() => {
req.abort();
}, 10 * 1000);
Related: KubeConfig · Watch
Informer
Informers provide a higher-level abstraction over watches with automatic reconnection and a local cache.
examples/typescript/informer/informer.ts
import * as k8s from '@kubernetes/client-node';
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const namespace = 'default';
const listFn = () => k8sApi.listNamespacedPod({ namespace });
const informer = k8s.makeInformer(kc, `/api/v1/namespaces/${namespace}/pods`, listFn);
informer.on('add', (obj: k8s.V1Pod) => {
console.log(`Added: ${obj.metadata!.name}`);
});
informer.on('update', (obj: k8s.V1Pod) => {
console.log(`Updated: ${obj.metadata!.name}`);
});
informer.on('delete', (obj: k8s.V1Pod) => {
console.log(`Deleted: ${obj.metadata!.name}`);
});
informer.on('error', (err: k8s.V1Pod) => {
console.error(err);
// Restart informer after 5sec
setTimeout(() => {
informer.start();
}, 5000);
});
informer.start();
Related: KubeConfig · makeInformer · CoreV1Api · Pod operations
Informer with Label Selector
examples/typescript/informer/informer-with-label-selector.ts
import * as k8s from '@kubernetes/client-node';
import { setTimeout as delay } from 'node:timers/promises';
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const namespace = 'default';
const APP_LABEL_SELECTOR = 'app=foo';
const listFn = () =>
k8sApi.listNamespacedPod({
namespace,
labelSelector: APP_LABEL_SELECTOR,
});
const createPod = async (name: string, app: string) => {
const appPodContainer = {
name: 'nginx',
image: 'nginx:latest',
} as k8s.V1Container;
const appPod = {
metadata: {
name,
labels: {
app,
},
},
spec: {
containers: [appPodContainer],
},
} as k8s.V1Pod;
try {
await k8sApi.createNamespacedPod({ namespace, body: appPod });
console.log('create', name);
} catch (e) {
console.error(e);
}
};
const deletePod = async (podName: string, podNamespace: string) => {
await k8sApi.deleteNamespacedPod({ name: podName, namespace: podNamespace });
console.log('delete', podName);
};
const informer = k8s.makeInformer(kc, `/api/v1/namespaces/${namespace}/pods`, listFn, APP_LABEL_SELECTOR);
informer.on('add', (obj: k8s.V1Pod) => {
console.log(`Added: ${obj.metadata!.name}`);
});
informer.on('update', (obj: k8s.V1Pod) => {
console.log(`Updated: ${obj.metadata!.name}`);
});
informer.on('delete', (obj: k8s.V1Pod) => {
console.log(`Deleted: ${obj.metadata!.name}`);
});
informer.on('error', (err: k8s.V1Pod) => {
console.error(err);
// Restart informer after 5sec
setTimeout(() => {
informer.start();
}, 5000);
});
await informer.start();
setTimeout(async () => {
await createPod('server-foo', 'foo');
await delay(5000);
await createPod('server-bar', 'bar');
await delay(5000);
await deletePod('server-foo', namespace);
await deletePod('server-bar', namespace);
}, 5000);
Related: KubeConfig · makeInformer · CoreV1Api · Pod operations
Cache
examples/cache-example.js
import * as k8s from '@kubernetes/client-node';
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const namespace = 'default';
const path = '/api/v1/pods';
const watch = new k8s.Watch(kc);
const listFn = () => k8sApi.listPodForAllNamespaces();
const cache = new k8s.ListWatch(path, watch, listFn);
const looper = () => {
const list = cache.list(namespace);
if (list) {
let names = [];
for (let i = 0; i < list.length; i++) {
names.push(list[i].metadata.name);
}
console.log(names.join(','));
}
setTimeout(looper, 2000);
};
looper();
Related: KubeConfig · Watch · ListWatch · CoreV1Api · Pod operations