写点什么

Kubenav: 使用手机管理你的 K8S 集群

用户头像
郭旭东
关注
发布于: 2021 年 04 月 13 日
Kubenav: 使用手机管理你的 K8S 集群

背景

相信广大 SRE/运维工程师都和笔者一样,以防出现紧急情况,无论是双休日还是过年过节,笔记本电脑常伴左右。笔者也有过在休假时,业务系统出现大规模故障,蹲在日本街头拿手机远程指挥同事排查问题的经历。那段经历除了给茶余饭后增加一些谈资以外,更多的是在手边没有可以快速接入排查问题的设备时无尽的焦虑。而 Kubenav 的出现很大程度可以缓解这个焦虑。

Kubenav

Kubenav 号称是装在口袋里的 Kubernetes 集群导航,其提供了移动、桌面和 web 端 APP 用来查看和管理 Kubernetes 集群。


该应用使用 Ionic FrameworkCapacitor 开发。应用的前端部分使用 TypeScript 和 React 组件实现功能。后端部分使用 Go mobile 与 Kubernetes API Server 和 Cloud Providers 进行通信,实现了 kubenav 移动端和桌面端实现近 100% 的代码共享。

主要功能

Kubenav 基本上就是一个 Kubernetes Dashboard 的增强版,在其上可以非常方便的查看 Kubernetes 的各种资源,不止移动端,在桌面端和 web 端也十分好用。


  • 支持移动、桌面和 web 端:移动端支持安卓和 IOS,桌面端支持 Windows、Linux 和 Mac,由于几乎 100% 的代码共享,各端具有相同的使用体验。

  • 管理 Kubernetes 资源:可以管理所有的 Kubernetes 资源,包括 CRD 资源。

  • 多集群管理:支持多集群管理,除了通过 kubeconfig 导入集群之外,还提供了 Google GCP、AWS EKS、Azure AKS、DigitalOcean 等集群导入方式。

  • 搜索与筛选:可以全局搜索和筛选 Kubernetes 资源,无需选择固定 Namespace。

  • Logs 日志查看Exec 终端操作Port-Forwarding 等操作点击即可使用。

增强功能

除了 Kubernetes 资源的基本查看和操作外,Kubenav 还提供了插件功能,目前有 Helm、Prometheus、Elasticsearch、Jaeger 四种插件,其中笔者主要体验了 Helm 和 Prometheus 插件的使用。

Helm

Helm 插件


Helm 插件 是 Kubenav 默认开启的一个插件,通过插件页面可以很轻松的查看到 Helm Chart 的全部信息,包括配置状态历史信息Value 值。其中最实用的就是 Value 的展示,可以直观的看到该 Chart 在部署时 --set 的 Value 值,免去了调试时 helm get values 的麻烦。

Prometheus

Prometheus 插件


Prometheus 插件则是需要事先在集群中安装 Prometheus,并在 Kubenav 的 Setting -> General 中手动开启 Prometheus 插件,之后就可以在集群中通过 ConfigMap 来在 Kubenav 中展示各种 Dashboard 了,可以理解为一个简化版的 Grafana,ConfigMap 内容如下:


---apiVersion: v1kind: ConfigMapmetadata:  # Name of the ConfigMap. The name is used as reference in the "kubenav.io/prometheus-dashboards" annotation.  name: nginx-ingress-dashboard  # Dashboards namespace, which is configured in the settings via "Dashboards Namespace" or via the "--plugin.prometheus.dashboards-namespace" command-line flag.  namespace: kubenav  labels:    # Required label, so that kubenav can found the dashboard.    kubenav.io/prometheus-dashboard: "true"data:  # Title of the dashboard.  title: "NGINX Ingress Controller"  # Description of the dashboard.  description: "Dashboard for NGINX Ingress Controller Metrics"  # Array of variables.  variables: |    [      {        "name": "Namespace",        "label": "controller_namespace",        "query": "nginx_ingress_controller_config_hash",        "allowAll": true      },      {        "name": "ControllerClass",        "label": "controller_class",        "query": "nginx_ingress_controller_config_hash{namespace=~\"{{ .Namespace }}\"}",        "allowAll": true      },      {        "name": "Controller",        "label": "controller_pod",        "query": "nginx_ingress_controller_config_hash{namespace=~\"{{ .Namespace }}\",controller_class=~\"{{ .ControllerClass }}\"}",        "allowAll": true      },      {        "name": "Ingress",        "label": "ingress",        "query": "nginx_ingress_controller_requests{namespace=~\"{{ .Namespace }}\",controller_class=~\"{{ .ControllerClass }}\", controller_pod=~\"{{ .Controller }}\"}",        "allowAll": true      }    ]  # Array of charts.  charts: |    [      {        "title": "Controller Request Volume",        "unit": "ops",        "size": {          "xs": "12",          "sm": "12",          "md": "4",          "lg": "4",          "xl": "4"        },        "type": "singlestat",        "queries": [          {            "label": "Request Volume",            "query": "round(sum(irate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m])), 0.001)"          }        ]      },      {        "title": "Controller Connections",        "unit": "",        "size": {          "xs": "12",          "sm": "12",          "md": "4",          "lg": "4",          "xl": "4"        },        "type": "singlestat",        "queries": [          {            "label": "Controller Connections",            "query": "sum(avg_over_time(nginx_ingress_controller_nginx_process_connections{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m]))"          }        ]      },      {        "title": "Controller Success Rate",        "unit": "%",        "size": {          "xs": "12",          "sm": "12",          "md": "4",          "lg": "4",          "xl": "4"        },        "type": "singlestat",        "queries": [          {            "label": "Controller Success Rate",            "query": "sum(rate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\",status!~\"[4-5].*\"}[2m])) / sum(rate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m])) * 100"          }        ]      },      {        "title": "Ingress Request Volume",        "unit": "reqps",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "6",          "xl": "6"        },        "type": "area",        "queries": [          {            "label": "{{ .ingress }}",            "query": "round(sum(irate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\",ingress=~\"{{ .Ingress }}\"}[2m])) by (ingress), 0.001)"          }        ]      },      {        "title": "Ingress Success Rate",        "unit": "%",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "6",          "xl": "6"        },        "type": "area",        "queries": [          {            "label": "{{ .ingress }}",            "query": "sum(rate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\",ingress=~\"{{ .Ingress }}\",status!~\"[4-5].*\"}[2m])) by (ingress) / sum(rate(nginx_ingress_controller_requests{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\",ingress=~\"{{ .Ingress }}\"}[2m])) by (ingress) * 100"          }        ]      },      {        "title": "Network I/O Pressure",        "unit": "MB/s",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "4",          "xl": "4"        },        "type": "area",        "queries": [          {            "label": "Received",            "query": "sum (irate (nginx_ingress_controller_request_size_sum{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m])) / 1024 / 1024"          },          {            "label": "Sent",            "query": "- sum (irate (nginx_ingress_controller_response_size_sum{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m])) / 1024 / 1024"          }        ]      },      {        "title": "Average Memory Usage",        "unit": "MiB",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "4",          "xl": "4"        },        "type": "area",        "queries": [          {            "label": "NGINX",            "query": "avg(nginx_ingress_controller_nginx_process_resident_memory_bytes{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}) / 1024 / 1024"          }        ]      },      {        "title": "Average CPU Usage",        "unit": "Cores",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "4",          "xl": "4"        },        "type": "area",        "queries": [          {            "label": "NGINX",            "query": "sum (rate (nginx_ingress_controller_nginx_process_cpu_seconds_total{controller_pod=~\"{{ .Controller }}\",controller_class=~\"{{ .ControllerClass }}\",namespace=~\"{{ .Namespace }}\"}[2m]))"          }        ]      },      {        "title": "Ingress Certificate Expiry",        "unit": "Days",        "size": {          "xs": "12",          "sm": "12",          "md": "12",          "lg": "12",          "xl": "12"        },        "type": "area",        "queries": [          {            "label": "{{ .host }}",            "query": "(avg(nginx_ingress_controller_ssl_expire_time_seconds{namespace=~\"{{ .Namespace }}\"}) by (host) - time()) / 60 / 60 / 24"          }        ]      }    ]
复制代码


将该 ConfigMap 部署到 kubenav Namespace 中,就可以在 Kubenav 中看到 NGINX Ingress Controller 的 Dashboard 了,官方也提供了一些示例 Dashboard,这些 Dashboard 可以在 kubenav/deploy 中找到。

总结

iPad 端


除了手机端,Kubenav 在 iPad 端有着更好的表现力,十分适合像笔者这样的 iPad 重度使用者。无论是问题排查还是使用的灵活度都有了大大的提升,结合 AWS Console 和阿里云 APP,基本上除写代码以外的大部分工作都可以在移动端完成,妈妈再也不用担心我蹲在日本街头拿手机远程指挥同事排查问题了!

发布于: 2021 年 04 月 13 日阅读数: 24
用户头像

郭旭东

关注

服务可靠无异常,节点稳定不宕机 2018.09.08 加入

柴猫双全的码农

评论

发布
暂无评论
Kubenav: 使用手机管理你的 K8S 集群