编辑
2023-04-25
DevOps
00
请注意,本文编写于 372 天前,最后修改于 372 天前,其中某些信息可能已经过时。

目录

思路
实现

平时有时候会突然想写一个小网页,或者部署一个服务到集群里,但添加解析让我很苦恼。我需要手动去 Dnspod 扫码登录,然后点击我的域名,然后点添加,一套下来很繁琐。

能不能让 k8s 的 ingress 有变化的时候,自己添加域名解析啊?这样配合 Traefik Ingress 来自动签发证书,我就能解放很大一部分的双手了。

(实际上我现在使用 Rancher 来管理我的几个集群,如果上面的能实现,那我完全可以图形化的页面里点点点,一套服务就创建好了,想想就很好)

思路

我没找到现成的工具,那就自己写一个吧,大概的需求和思路是这样的:

  1. 监控 K8S ingress 的变化
  2. 找到对应 ingress 对应的 service
  3. 找到对一个 service 的 pod,看一下 pod 在哪个节点上
  4. 给对应的节点和 ingress 对应的域名增加一条解析

实现

因为用 Node 比较熟,直接用 Node 的 K8S SDK,直接上代码:

js
const 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();
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:mereith

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!