平时有时候会突然想写一个小网页,或者部署一个服务到集群里,但添加解析让我很苦恼。我需要手动去 Dnspod 扫码登录,然后点击我的域名,然后点添加,一套下来很繁琐。
能不能让 k8s 的 ingress 有变化的时候,自己添加域名解析啊?这样配合 Traefik Ingress 来自动签发证书,我就能解放很大一部分的双手了。
(实际上我现在使用 Rancher 来管理我的几个集群,如果上面的能实现,那我完全可以图形化的页面里点点点,一套服务就创建好了,想想就很好)
我没找到现成的工具,那就自己写一个吧,大概的需求和思路是这样的:
因为用 Node 比较熟,直接用 Node 的 K8S SDK,直接上代码:
jsconst fs = require('fs');
const http = require('http');
const k8s = require('@kubernetes/client-node');
const DDNS = require('wm-ddns');
// 加载 Kubernetes 群集配置
const kc = new k8s.KubeConfig();
kc.loadFromFile("./config/config.yaml")
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const watch = new k8s.Watch(kc);
const watchIngressStart = () => {
const req = watch.watch('/apis/networking.k8s.io/v1/ingresses', {}, (type, obj) => {
if (type === "ADDED" || type === "MODIFIED") {
console.log(type, obj)
onIngressAddOrEdit(obj)
} else if (type === "DELETED") {
console.log('ingress deleted , 就先不处理了吧。')
} else {
console.log("unknown type", type)
}
}, (err) => {
console.log(err)
}).catch((err) => {
console.log("global error", err);
watchIngressStart();
})
}
const parseHost = (host) => {
if (host.split('.').length == 2) {
return { domain: host, name: "@" };
}
const arr = host.split(".");
const nameArr = [];
while (arr.length > 2) {
nameArr.push(arr.shift())
}
return { domain: arr.join("."), name: nameArr.join(".") };
}
const updateDNS = (host, nodeName) => {
return new Promise((resolve, reject) => {
const { domain, name } = parseHost(host);
const d = new DDNS(domain, {
loginToken: process.env['DNSPOD_TOKEN'],
loginId: process.env['DNSPOD_ID'],
});
d.recordList(0, 300, (err, list) => {
if (err) {
reject(err);
}
const currRecord = list.find(l => l.name == name && ['CNAME', 'A'].includes(l.recordType));
if (currRecord) {
const { id, value } = currRecord
d.updateRecordByName(name, 'CNAME', `${nodeName}.mereith.top`, {}, (err, record) => {
if (err) {
reject(err);
} else {
resolve(record)
}
})
} else {
d.createRecord(name, "CNAME", `${nodeName}.mereith.top`, {}, (err, record) => {
if (err) {
reject(err)
} else {
resolve(record)
}
})
}
});
})
}
const findServiceNode = async (svcName, ns) => {
try {
const { body: service } = await k8sApi.readNamespacedServiceStatus(svcName, ns)
const labelSelector = service.spec.selector;
const { body: podList } = await k8sApi.listNamespacedPod(ns, undefined, undefined, undefined, undefined, labelSelector);
const nodeName = podList.items[0].spec.nodeName;
return nodeName
} catch (err) {
console.log(err)
return null;
}
}
const updateOne = (host, node) => {
console.log("更新 DNS:", host, `${node}.mereith.top`)
updateDNS(host, node).then((res) => {
console.log("更新成功!", host, `${node}.mereith.top`)
}).catch((err) => {
console.log("更新失败!", host, `${node}.mereith.top`, err)
})
}
const onIngressAddOrEdit = async (obj) => {
try {
const rules = obj.spec.rules;
const hosts = rules.map(rule => rule.host)
const ns = obj.metadata.namespace;
const svcNames = rules.map(rule => rule.http.paths[0].backend.service.name)
// 默认就取第一个吧。
const node = await findServiceNode(svcNames[0], ns);
if (node) {
for (const host of hosts) {
if (!host.includes("-speedtest.mereith.com")) {
updateOne(host, node)
}
}
}
} catch (err) {
console.log(err)
}
}
watchIngressStart();
本文作者:mereith
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!