惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
Recorded Future
Recorded Future
人人都是产品经理
人人都是产品经理
S
SegmentFault 最新的问题
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
量子位
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tor Project blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
T
Threatpost
T
Tenable Blog
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
C
Cisco Blogs
H
Heimdal Security Blog
Attack and Defense Labs
Attack and Defense Labs
A
About on SuperTechFans
Last Week in AI
Last Week in AI
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
I
Intezer
V
V2EX
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
V
Vulnerabilities – Threatpost
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
PCI Perspectives
PCI Perspectives
P
Privacy International News Feed
D
Docker

Mereith's Blog

记一次代理转发问题的排查与解决 PVE DataCenter Manager 端口号跳转错误修复 k8s + jenkins + gitlab 触发器 【转载】没有编程生产力这样的东西 【后续】居然被 ddos 了 居然被 ddos 了 Tagger - 让版本标签管理更简单 openclash 开启后端口转发失效 nextjs 启动时执行代码 买了太多VPS不知道干什么,干脆做个网站 juhost Level-1 测评 VMISS CN - Hong Kong VMISS JP - Tokyo - BGP 测评 VMISS CN - Hong Kong - BGP HomeLab 的终点是 最近在做的事情 因为选择艰难症,自己写了一套开源博客系统 手撸一个nodejs分布式爬虫,还要可视化 HomeLab 分享 我的个人工作流——开源项目推荐 动手写一个超简单的编译器 safari 插件开发 Safari Extensions Preferences 按钮无响应 设置移动端软键盘回车按钮文案 nginx proxy manager 非标准端口反代 Host 不对 使用 ingress 注解给 traefik ingress 添加中间件 使用流水线功能为文章添加固定结尾 为 VanBlog 添加一个小挂件 基于React/umi/egg自建博客系统 如何正确停止 NodeJS 子进程 k8s pod 替换策略 让威联通、pve 共享 ups 实现断电关机 Ubuntu 配置 samba 混合云部署k3s集群(基于wireguard) pve 无法启动 grub lvmid not found error antd Password 关闭自动补全 code-server node not found pve no quorum 错误 || 无法登录 pve Cannot Initiate CMAP Service pve 节点删不干净 PVE 指针(鼠标)漂移 pve 重启网络 Proxmox VE 直通显卡 ProxmoxVE (PVE) 7.0 换源升级 用 SOCAT 端口转发 git 无法 pull 仓库refusing to merge unrelated histories 用 vuepress 搭建私人知识库 js 防抖节流中的 this 与箭头函数 Dockerfile 给 Ubuntu 换源 docker 内访问宿主机 docker 删除无用镜像 css 实现展开收起动画 nginx 301 问题 Node.js 流式编程 关于我重写了三次博客项目这件事 css flex 布局超出隐藏 css 隐藏滚动条 css 文字超出隐藏并显示省略号 css 填充 svg 颜色 css 黑白滤镜 nginx 反代丢端口 docker-compose 部署 matomo 分析系统 基于 strapi 开发应用 strapi 关闭 Content Security Policy nginx 反代 rewite url ssh 通过跳板机访问目标机器 Cent os 7 安装 zsh 5.6 以上版本 win11 任务栏不合并 bash 数组操作 vim 修改文件格式为 unix k8s 集群 control-plane-endpoint 主机名 git https 保存密码 Bash 判断命令存在 & 重定向 python 导出项目依赖 cent os 7 安装 gcc 版本 9 System limit for number of file watchers reached cent os 7 安装 git 2.x css 下划线中间向两边滑动效果 linux 挂载 SMB/CIFS linux 清除 ACL windows 命令行安装 inf 驱动 css 文字不可选择 iperf3 网络测速工具 cent os 换源 k8s 1.21.3 从 docker 迁移到 containerd 解决Nginx安装错误:No package nginx available 问题 从pve的硬盘里导出数据 Errors were encountered while processing adguardhome配合clash pve中使用LXC容器安装OMV LXC直通硬盘 nginx反代模版 docker常用开启容器的命令 PVE的LXC容器中安装Docker ProxmoxVE系统分区相关 基于clash搭建旁路网关 Portainer添加节点 linux声卡设置及录音音 OpenWrt用VLAN PVE换源及去掉订阅提示和改端口 adguardhome踩坑
监控 k8s ingress 自动添加域名 DNS 解析
wanglu@mereith.com (mereith) · 2023-04-25 · via Mereith's Blog

请注意,本文编写于 1138 天前,最后修改于 1138 天前,其中某些信息可能已经过时。

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

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

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

思路

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

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

实现

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

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();