MENU

利用Django编写Kubernetes中服务可用性查询

June 9, 2020 • Linux运维工作

由于工作中,经常会出现测试在群里发一个报错的url,发到群里面造成AOE范围伤害波及到运维,所以自己根据需求,整理了一个可 根据 服务名进行匹配,以及url 进行匹配对应服务的东西,便于排查问题;

因为Kubernetes本身各方面还是较为完善,所以在Master上开启一个 kubectl proxy 模式可以通过这个模式,直接通过http请求访问到对应日志、配置文件相关之类文件;

nohup kubectl proxy --address='0.0.0.0' --port=8080 --accept-hosts='^*$' &

以及后面通过命令行,快速生成字典:

kubectl get ing | grep -v NAME |awk 'BEGIN{print "{"}{print "\""$2"\"" ":" "\""$1"\""","}END{print "}"}'

注意删除最后一个逗号!

界面使用的示例:

在这里插入图片描述

然后发一下代码:Django部分就是很简单的配置,前端页面也是最简单的html + css

调用代码kubetools.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''通过输入server,取出所需的所有pod'''

import os

os.environ['DJANGO_SETTINGS_MODULE'] = 'books.settings'  # 配置系统变量
import django

import urllib3
import datetime
import traceback
import socket
from kubernetes import client
from kubernetes.client.rest import ApiException

django.setup()


class FilterBundle():
    def __init__(self, token, apiserver, namespace, server, scope=0):
        self.token = token
        self.apiserver = apiserver
        self.namespace = namespace
        self.server = server
        self.scope = scope

    def initSetting(self):
        urllib3.disable_warnings()
        configuration = client.Configuration()
        configuration.host = self.apiserver
        configuration.verify_ssl = False
        configuration.api_key = {'authorization': 'Bearer ' + self.token}
        client.Configuration.set_default(configuration)
        return client

    def telnet(self, ip, port):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.settimeout(2)
            s.connect((ip, int(port)))
            s.shutdown(2)
            return True
        except:
            return False

    def hostStatus(self, host):
        try:
            api_instance = self.initSetting().CoreV1Api()
            api_response = api_instance.list_node()
            for h in api_response.items:
                svc_ip = h.status.addresses[0].address
                if host in svc_ip:
                    host_status = h.status.conditions[3].status
                    return host_status
        except ApiException as e:
            traceback.print_exc(e)

    def filterSvc(self):
        try:
            api_instance = self.initSetting().CoreV1Api()
            api_response = api_instance.list_namespaced_service(self.namespace)
            svc_list = api_response.items
            for s in svc_list:
                # svc name
                svc_name = s.metadata.name
                if self.server in svc_name:
                    # print(s)
                    svc_type = s.spec.type
                    svc_ip = s.spec.cluster_ip
                    svc_port = s.spec.ports[0].port
                    svc_message = "http://x.x.x.x:8080" + s.metadata.self_link
                    if self.namespace == 'default':
                        svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip,
                                      'svc_port': svc_port,
                                      'socket_status': "阿里云暂不支持检测", 'svc_message': "暂不支持阿里云"}
                    else:
                        svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip,
                                      'svc_port': svc_port,
                                      'socket_status': self.telnet(svc_ip, svc_port), 'svc_message': svc_message}
                    yield svc_bundle
        except ApiException as e:
            traceback.print_exc(e)

    def readSvc(self):
        try:
            api_instance = self.initSetting().CoreV1Api()
            api_response = api_instance.read_namespaced_service(name=self.server, namepace=self.namespace)
            svc_list = api_response.items
            # for s in svc_list:
            #     # svc name
            svc_name = svc_list.metadata.name
            if self.server in svc_name:
                # print(s)
                svc_type = svc_list.spec.type
                svc_ip = svc_list.spec.cluster_ip
                svc_port = svc_list.spec.ports[0].port
                svc_message = "http://x.x.x.x:8080" + s.metadata.self_link
                if self.namespace == 'default':
                    svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip,
                                  'svc_port': svc_port,
                                  'socket_status': "阿里云暂不支持检测", 'svc_message': "暂不支持阿里云"}
                else:
                    svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip,
                                  'svc_port': svc_port,
                                  'socket_status': self.telnet(svc_ip, svc_port), 'svc_message': svc_message}
                yield svc_bundle
        except ApiException as e:
            traceback.print_exc(e)

    def filterPod(self):
        try:
            api_instance = self.initSetting().CoreV1Api()
            api_response = api_instance.list_namespaced_pod(self.namespace)
            pod_list = api_response.items
            for p in pod_list:
                # pod名字
                pod_name = p.metadata.name
                # print(self.server)
                if self.server in pod_name or self.server[:-6] in pod_name:
                    # print(i)
                    # 镜像版本
                    image_version = p.status.container_statuses[0].image.split(':')[1]
                    # pod状态
                    pod_status = p.status.phase
                    pod_time = p.status.start_time + datetime.timedelta(hours=8)
                    # pod所在宿主机
                    pod_host = p.status.host_ip
                    pod_ip = p.status.pod_ip
                    log_url = "http://x.x.x.x:8080/api/v1/namespaces/{}/pods/{}/log".format(self.namespace,
                                                                                                 pod_name)
                    if self.namespace == 'default':
                        pod_bundle = {'pod_name': pod_name, 'status': pod_status, 'pod_ip': pod_ip,
                                      'version': image_version,
                                      'starttime': str(pod_time).split('+')[0], 'host': pod_host,
                                      'host_status': "阿里云暂不支持检测", "log_url": "暂不支持阿里云"}
                    else:
                        pod_bundle = {'pod_name': pod_name, 'status': pod_status, 'pod_ip': pod_ip,
                                      'version': image_version,
                                      'starttime': str(pod_time).split('+')[0], 'host': pod_host,
                                      'host_status': self.hostStatus(pod_host), "log_url": log_url}
                    yield pod_bundle
        except ApiException as e:
            traceback.print_exc(e)

    def filterIng(self):
        try:
            api_instance = self.initSetting().ExtensionsV1beta1Api()
            api_response = api_instance.list_namespaced_ingress(namespace=self.namespace)
            ing_list = api_response.items
            for i in ing_list:
                # pod名字
                ing_http = i.spec.rules[0].http.paths
                for s in ing_http:
                    ing_svc = s.backend.service_name
                    # print(self.server)
                    if self.server in ing_svc:
                        ing_name = i.metadata.name
                        ing_domain = i.spec.rules[0].host
                        ing_port = s.backend.service_port
                        ing_path = "http://" + ing_domain + s.path
                        ing_msg = "http://192.168.1.100:8080" + i.metadata.self_link
                        if self.namespace == 'default':
                            ing_msg = "暂不支持阿里云"
                        else:
                            ing_msg = "http://192.168.1.100:8080" + i.metadata.self_link
                        ing_bundle = {"ing_name": ing_name, "ing_domain": ing_domain, "ing_svc": ing_svc,
                                      "ing_port": ing_port, "ing_path": ing_path, "ing_msg": ing_msg}
                        yield ing_bundle
        except ApiException as e:
            traceback.print_exc(e)

    def filterPath(self, ingress_name, ingress_path):
        try:
            api_instance = self.initSetting().ExtensionsV1beta1Api()
            api_response = api_instance.read_namespaced_ingress(name=ingress_name, namespace=self.namespace)
            ing_http = api_response.spec.rules[0].http.paths
            for s in ing_http:
                ing_path = s.path
                if ingress_path == ing_path:
                    ing_svc = s.backend.service_name
                    ing_name = api_response.metadata.name
                    ing_domain = api_response.spec.rules[0].host
                    ing_port = s.backend.service_port
                    ing_path = "http://" + ing_domain + s.path
                    ing_msg = "http://x.x.x.x:8080" + api_response.metadata.self_link
                    ing_bundle = {"ing_name": ing_name, "ing_domain": ing_domain, "ing_svc": ing_svc,
                                  "ing_port": ing_port, "ing_path": ing_path, "ing_msg": ing_msg}
                    self.server = ing_svc
                    yield ing_bundle
        except ApiException as e:
            traceback.print_exc(e)


class ToolsBundle():
    def __init__(self, serverName='', urlPath='', envName='sit'):
        if envName == 'release':
            self.apiserver = 'https://x.x.x.x:6443'
            self.Token = 'token,自行通过命令行拿'
            self.envName = 'default'
        else:
            self.Token = 'token,自行通过命令行拿'
            self.apiserver = 'https://x.x.x.x:6443'
            self.envName = envName
        self.serverName = serverName
        self.urlPath = urlPath

        self.tools = FilterBundle(token=self.Token, apiserver=self.apiserver, server=self.serverName,
                                  namespace=self.envName)

    def pod(self):
        pod_bundle = self.tools.filterPod()
        return pod_bundle

    def svc(self):
        svc_bundle = self.tools.filterSvc()
        return svc_bundle

    def ing(self):
        ing_bundle = self.tools.filterIng()
        return ing_bundle

    def ingpath(self):
        dev_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下}
        sit_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下}
        release_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下}
        if self.envName == 'sit':
            ing_name = sit_ingdata.get(self.urlPath.split('/')[2])
            ing_path = "/" + self.urlPath.split('/')[3]
        elif self.envName == 'default':
            ing_name = release_ingdata.get(self.urlPath.split('/')[2])
            ing_path = "/" + self.urlPath.split('/')[3]
        else:
            ing_name = dev_ingdata.get(self.urlPath.split('/')[2])
            ing_path = "/" + self.urlPath.split('/')[3]

        ingpath_bundle = self.tools.filterPath(ingress_name=ing_name, ingress_path=ing_path)
        return ingpath_bundle

    def read(self):
        return self.tools.readSvc()

views.py

from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
import logman.kubetools as tools


def index(request):
    if request.GET.get('svc') and request.GET.get('env'):
        keyword = request.GET['svc']
        if '_' in keyword:
            keyword = keyword.replace('_', '-')
        env = request.GET['env']
        all = tools.ToolsBundle(serverName=keyword, envName=env)
        svc_data = all.svc()
        pod_data = all.pod()
        ing_data = all.ing()
        return render(request, "svc.html", {"PodBundle": pod_data, "SvcBundle": svc_data, "IngBundle": ing_data})
    elif request.GET.get('urlpath') and request.GET.get('env'):
        urlpath = request.GET['urlpath']
        env = request.GET['env']
        all = tools.ToolsBundle(urlPath=urlpath, envName=env)
        dataing = all.ingpath()
        ing_data = dataing
        for s in ing_data:
            server = s.get('ing_svc')
            all2 = tools.ToolsBundle(serverName=server, envName=env)
            svc_data = all2.svc()
            pod_data = all2.pod()
            ing_data = all2.ing()
            return render(request, "svc.html", {"PodBundle": pod_data, "SvcBundle": svc_data, "IngBundle": ing_data})
    else:
        return render(request, 'svc.html')

前端页面 svc.html:

<!--注释-->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Welcome</title>
    <style type="text/css">
        thead {
            color: green
        }

        strong {
            color: red;
            font-size: 20px;
        }
         td div {
            color: blue;
        }
    </style>
</head>

<body>
<div class=" body_div">
    <a href="/logman/"><h1>服务可用性查询</h1></a>
    <hr/>
    <form action="/logman/" method="get">
        <p>服务名或URL 条件二选一!</p>
        <p>示例:stocking-cloud-grpc-stuff-return-server 或 http://order.chtwebapi.lingcb.net/retail_order/</p>
        <p>
            <label>服务:<input type="text" size="40px" name="svc" value=""
                             \></label>
        </p>
        <p>
            <label>URL:<input type="text" size="200px" name="urlpath" value=""
                              \></label>
        </p>
        <p>
            环境:
            <select name="env">
                <option name="sit" value="sit">sit</option>
                <option name="dev" value="dev">dev</option>
                <option name="release" value="release">release</option>
            </select>
            <input type="submit" value="提交" \>
        </p>
    </form>
</div>
<hr/>
<br>
<h3>Service</h3>
<table border="1">
    <thead>
    <tr>
        <th>SvcName</th>
        <th>SvcType</th>
        <th>SvcIP</th>
        <th><div>SvcPort</div></th>
        <th>Socket_Status</th>
        <th>message</th>
    </tr>
    </thead>
    <tbody>
    {% for line in SvcBundle %}
    <tr>
        <td>{{ line.svc_name }}</td>
        <td>{{ line.svc_type }}</td>
        <td>{{ line.svc_ip }}</td>
        <td><div>{{ line.svc_port }}</div></td>
        <td><strong>{{ line.socket_status }}</strong></td>
        <td>{{ line.svc_message }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>
<hr/>
<br>
<h3>Pod</h3>
<table border="1">
    <thead>
    <tr>
        <th>PodName</th>
        <th>PodStatus</th>
        <th>PodIP</th>
        <th>PodVersion</th>
        <th>启动时间</th>
        <th>Host</th>
        <th>HostStatus</th>
        <th>LogURL</th>
    </tr>
    </thead>
    <tbody>
    {% for line in PodBundle %}
    <tr>
        <td>{{ line.pod_name }}</td>
        <td><strong>{{ line.status }}</strong></td>
        <td>{{ line.pod_ip }}</td>
        <td>{{ line.version }}</td>
        <td>{{ line.starttime }}</td>
        <td>{{ line.host }}</td>
        <td><strong>{{ line.host_status }}</strong></td>
        <td>{{ line.log_url }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>
<hr/>
<br>
<h3>Ingress</h3>
<table border="1">
    <thead>
    <tr>
        <th>IngName</th>
        <th>IngDomain</th>
        <th>IngSvc</th>
        <th>IngPort</th>
        <th>IngPath</th>
        <th>IngMsg</th>
    </tr>
    </thead>
    <tbody>
    {% for line in IngBundle %}
    <tr>
        <td>{{ line.ing_name }}</td>
        <td>{{ line.ing_domain }}</td>
        <td>{{ line.ing_svc }}</td>
        <td><div>{{ line.ing_port }}</div></td>
        <td>{{ line.ing_path }}</td>
        <td>{{ line.ing_msg }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>

项目地址:https://github.com/J-jingwei/kubernetes-log/tree/master/Django

Last Modified: August 24, 2020