Loading

SpringBoot 调用 K8s metrics-server

起因

官方 API io.kubernetes.client-java无法调用 metrics-server API

解决

ApiClient 内部会创建一个 OkHttpClient 用于发送 http 请求,该 clinet 包含授权参数等信息

K8sConfig

public class K8sConfig {
    public static ApiClient getApiClient() {
        ApiClient client = null;
        try {
            File file = new File("E:\\k3s.yaml");
            client = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(file))).build();
            client.setDebugging(true);
            Configuration.setDefaultApiClient(client);
        } catch (IOException e) {
            log.error("K8s ApiClient 初始化失败!");
        }
        return client;
    }
}

MetricsService

@Service
public class MetricsService {

    private ApiClient apiClient;

    private static final String BASE_URL="/apis/metrics.k8s.io/v1beta1";

    private static final String URL_NODES = "/nodes";

    private static final String URL_PODS = "/pods";

    private static final String URL_NAMESPACES = "/namespaces/";

    public MetricsService() {
        this.apiClient = K8sConfig.getApiClient();
    }

    /**
     * 获取集群节点的资源统计信息
     */
    public MetricsNodeList getNodeMetrics() throws IOException, ApiException {
        String path = BASE_URL + URL_NODES;
        Call call = buildCall(path, "GET");
        ApiResponse<MetricsNodeList> r = apiClient.execute(call, MetricsNodeList.class);
        return r.getData();
    }

    /**
     * 获取所有pods的资源统计信息
     */
    public MetricsPodList getPodMetrics() throws IOException, ApiException {
        String path = BASE_URL + URL_PODS;
        Call call = buildCall(path, "GET");
        ApiResponse<MetricsPodList> r = apiClient.execute(call, MetricsPodList.class);
        return r.getData();
    }

    /**
     * 获取namespace下pods的资源统计信息
     */
    public MetricsPodList getPodsMetrics(String namespace) throws IOException, ApiException {
        String path = BASE_URL + URL_NAMESPACES + namespace + URL_PODS;
        Call call = buildCall(path, "GET");
        ApiResponse<MetricsPodList> r = apiClient.execute(call, MetricsPodList.class);
        return r.getData();
    }

    /**
     * 构建Call
     */
    private Call buildCall(String path, String method) throws ApiException {
        List<Pair> localVarQueryParams = new ArrayList<>();
        List<Pair> localVarCollectionQueryParams = new ArrayList<>();
        Map<String, String> localVarHeaderParams = new HashMap<>();
        localVarHeaderParams.put("Accept", apiClient.selectHeaderAccept(new String[]{"*/*"}));
        localVarHeaderParams.put("Content-Type", apiClient.selectHeaderContentType(new String[]{}));
        Map<String, String> localVarCookieParams = new HashMap<>();
        Map<String, Object> localVarFormParams = new HashMap<>();
        String[] localVarAuthNames = new String[]{"BearerToken"};
        return apiClient.buildCall(path, method, localVarQueryParams, localVarCollectionQueryParams, null, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, null);
    }
}

Models

@Data
public class MetricsNodeList {
    private String kind;
    private String apiVersion;
    private V1ListMeta metadata;
    private List<MetricsNode> items;

    @Data
    public static class MetricsNode{
        private V1ObjectMeta metadata;
        private DateTime timestamp;
        private String window;
        private MetricsUsage usage;
    }
}
@Data
public class MetricsPodList {
    private String kind;
    private String apiVersion;
    private V1ListMeta metadata;
    private List<MetricsPod> items;

    @Data
    public static class MetricsPod{
        private V1ObjectMeta metadata;
        private DateTime timestamp;
        private String window;
        private List<MetricsPodContainer> containers;
    }

    @Data
    public static class MetricsPodContainer{
        private String name;
        private MetricsUsage usage;
    }
}
public class MetricsUsage {

    private static Map<String,Double> UNIT_CPU = new HashMap<>();
    private static Map<String,Double> UNIT_MEMORY = new HashMap<>();

    static {
        UNIT_CPU.put("n", Math.pow(10, -6));
        UNIT_CPU.put("u", Math.pow(10, -3));
        UNIT_CPU.put("m", Math.pow(10, 0));
        UNIT_CPU.put("", Math.pow(10, 3));
        UNIT_CPU.put("k", Math.pow(10, 6));
        UNIT_CPU.put("M", Math.pow(10, 9));
        UNIT_CPU.put("G", Math.pow(10, 3*4));
        UNIT_CPU.put("T", Math.pow(10, 3*5));
        UNIT_CPU.put("P", Math.pow(10, 3*6));
        UNIT_CPU.put("E", Math.pow(10, 3*7));

        UNIT_MEMORY.put("Ki", Math.pow(1024, -1));
        UNIT_MEMORY.put("Mi", Math.pow(1024, 0));
        UNIT_MEMORY.put("Gi", Math.pow(1024, 1));
        UNIT_MEMORY.put("Ti", Math.pow(1024, 2));
        UNIT_MEMORY.put("Pi", Math.pow(1024, 3));
        UNIT_MEMORY.put("Ei", Math.pow(1024, 4));
    }

    private String cpu;

    private String memory;

    public String getCpu() {
        return commomUnifiedUnit(cpu,1,UNIT_CPU) + "m";
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getMemory() {
        return commomUnifiedUnit(memory, 2, UNIT_MEMORY) + "Mi";
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    /**
     * 获取的cpu和memory是带单位的字符串,加入转换逻辑,cpu统一单位为m,memory统一单位为Mi,如果不需要转换单位可以去掉相关逻辑
     */
    private static String commomUnifiedUnit(String unitValStr, int unitLength, Map<String, Double> unitMap) {
        Long r = 0L;
        if(unitValStr == null) {
            return "0";
        }
        String unit = unitValStr.substring(unitValStr.length() - unitLength, unitValStr.length());
        String val = unitValStr.substring(0, unitValStr.length() - unitLength);
        if(unit.matches("\\d")) {
            unit = "";
            val = unitValStr;
        }
        Double factor = unitMap.get(unit);
        if(factor == null) {
            throw new IllegalArgumentException("无法解析单位:" + unitValStr);
        }
        r = Math.round(Integer.parseInt(val) * factor);
        return r + "";
    }
}

参考:https://blog.csdn.net/suo082407128/article/details/106804811

posted @ 2021-06-24 17:42  LB477  阅读(776)  评论(0编辑  收藏  举报