├── .gitignore
├── .dockerignore
├── client
├── assets
│ ├── logo.png
│ └── kubermetrics.png
├── components
│ ├── Services
│ │ ├── ServicesList.styles.css
│ │ ├── Service.styles.css
│ │ ├── ServicesList.js
│ │ └── Service.js
│ ├── Pods
│ │ ├── PodsList.style.css
│ │ ├── Pod.style.css
│ │ ├── Pod.js
│ │ └── PodsList.js
│ ├── Deployments
│ │ ├── DeploymentList.style.css
│ │ ├── Deployment.style.css
│ │ ├── DeploymentList.js
│ │ └── Deployment.js
│ ├── Node
│ │ ├── currentNode.style.css
│ │ └── CurrentNode.js
│ ├── Header
│ │ ├── Header.style.css
│ │ └── Header.js
│ ├── PrometheusMonitoring
│ │ └── PrometheusAlerts.js
│ ├── GrafanaMonitoring
│ │ ├── ClusterDashboard.js
│ │ └── PodDashboard.js
│ ├── sidebar
│ │ ├── SidebarData.js
│ │ ├── Sidebar.js
│ │ └── Sidebar.css
│ ├── NavRoutes
│ │ └── index.js
│ └── mui-elements.js
├── pages
│ ├── metrics
│ │ └── index.js
│ ├── alerts
│ │ └── index.js
│ └── home
│ │ └── index.js
├── store.js
├── constants
│ └── actionTypes.js
├── reducers
│ ├── servicesListReducer.js
│ ├── podsListReducer.js
│ ├── ingressListReducer.js
│ ├── deploymentListReducer.js
│ ├── index.js
│ ├── nodesListReducer.js
│ └── namespaceListReducer.js
├── src
│ ├── index.js
│ └── App.jsx
├── styles.css
├── Dialog
│ ├── IngressDialog.js
│ ├── ServiceDialog.js
│ ├── DeploymentDialog.js
│ ├── PodsDialog.js
│ └── NodeDialog.js
└── actions
│ └── actions.js
├── Dockerfile
├── skaffold.yaml
├── manifests
├── service.yaml
├── prometheus-service.yaml
├── grafana-datasource-config.yaml
├── clusterRole.yaml
├── kubermetrics-depl.yaml
├── prometheus-deployment.yaml
├── grafana-deployment.yaml
├── prometheus-ingress.yaml
├── config-map.yaml
└── dashboards
│ └── home.json
├── index.html
├── webpack.config.js
├── package.json
├── server
├── server.js
└── controllers
│ └── k8Controller.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
--------------------------------------------------------------------------------
/client/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kubermetrics/HEAD/client/assets/logo.png
--------------------------------------------------------------------------------
/client/assets/kubermetrics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kubermetrics/HEAD/client/assets/kubermetrics.png
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:latest
2 |
3 | ENV CI=true
4 |
5 | WORKDIR /app
6 | COPY package.json ./
7 |
8 | RUN npm install
9 | COPY ./ ./
10 |
11 | CMD ["npm", "run", "dev"]
--------------------------------------------------------------------------------
/skaffold.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: skaffold/v2beta22
2 | kind: Config
3 | metadata:
4 | name: kubermetrics
5 | build:
6 | artifacts:
7 | - image: arajput96/kubermetrics
8 | docker:
9 | dockerfile: Dockerfile
10 | deploy:
11 | kubectl:
12 | manifests:
13 | - manifests/kubermetrics-depl.yaml
14 |
--------------------------------------------------------------------------------
/manifests/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: grafana
5 | namespace: monitoring
6 | annotations:
7 | prometheus.io/scrape: 'true'
8 | prometheus.io/port: '3000'
9 | spec:
10 | selector:
11 | app: grafana
12 | type: NodePort
13 | ports:
14 | - port: 3000
15 | targetPort: 3000
16 | nodePort: 32000
--------------------------------------------------------------------------------
/manifests/prometheus-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: prometheus-service
5 | namespace: monitoring
6 | annotations:
7 | prometheus.io/scrape: 'true'
8 | prometheus.io/port: '9090'
9 |
10 | spec:
11 | selector:
12 | app: prometheus-server
13 | type: NodePort
14 | ports:
15 | - port: 8080
16 | targetPort: 9090
17 | nodePort: 30000
18 |
--------------------------------------------------------------------------------
/client/components/Services/ServicesList.styles.css:
--------------------------------------------------------------------------------
1 | .servicesList {
2 | background-color: #222426;
3 | margin: 10px;
4 | margin-left: 15px;
5 | width: auto;
6 | height: 330px;
7 | border-radius: 10px;
8 | /* max-width: 1500px; */
9 | /* min-width: 1500px; */
10 | border: 1px solid rgb(19, 19, 19);
11 |
12 | display: flex;
13 | flex-direction: column;
14 | -webkit-box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
15 | box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
16 | }
--------------------------------------------------------------------------------
/client/components/Pods/PodsList.style.css:
--------------------------------------------------------------------------------
1 | .podsList {
2 | /* width: 75vw; */
3 | background-color: #222426;
4 | margin: 10px;
5 | margin-left: 15px;
6 | margin-top: 15px;
7 | margin-bottom: 10px;
8 | width: auto;
9 | height: 330px;
10 | border-radius: 10px;
11 | border: 1px solid rgb(19, 19, 19);
12 |
13 |
14 | display: flex;
15 | flex-direction: column;
16 | -webkit-box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
17 | box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
18 | }
--------------------------------------------------------------------------------
/client/components/Deployments/DeploymentList.style.css:
--------------------------------------------------------------------------------
1 | .deploymentList {
2 | /* width: 75vw; */
3 | background-color: #222426;
4 | margin: 10px;
5 | margin-left: 15px;
6 | width: auto;
7 | height: 330px;
8 | border-radius: 10px;
9 | max-width: 1300px;
10 | min-width: 1300px;
11 | border: 1px solid rgb(19, 19, 19);
12 |
13 |
14 | display: flex;
15 | flex-direction: column;
16 | -webkit-box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
17 | box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
18 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Kubermetrics
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/client/components/Services/Service.styles.css:
--------------------------------------------------------------------------------
1 | .serviceContainer {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | filter: drop-shadow(-1px 6px 3px rgba(0, 0, 0, 0.5));
7 | width: 300px;
8 |
9 | }
10 | .serviceContainer:hover {
11 |
12 | filter: drop-shadow(0px 3px 3px rgba(135, 88, 237, 0.276));
13 |
14 | }
15 |
16 |
17 | p {
18 | margin: 2px;
19 | text-align: center;
20 | text-shadow: 2px 2px 4px #000000;
21 | }
22 | .serviceLabel {
23 | font-size: 18px;
24 | margin-top: 5px;
25 | }
--------------------------------------------------------------------------------
/client/pages/metrics/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module index.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Metrics Page Element for React Router
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import ClusterDashboard from '../../components/GrafanaMonitoring/ClusterDashboard';
13 |
14 |
15 | const MetricsPage = (props) => {
16 | return (
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default MetricsPage
--------------------------------------------------------------------------------
/client/pages/alerts/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module index.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Alerts Page for React Router
8 | *
9 | * ************************************
10 | */
11 |
12 | import React from 'react';
13 | import PrometheusAlerts from '../../components/PrometheusMonitoring/PrometheusAlerts';
14 |
15 |
16 |
17 | const AlertsPage = (props) => {
18 | return (
19 |
22 | )
23 | };
24 |
25 |
26 |
27 |
28 | export default AlertsPage;
--------------------------------------------------------------------------------
/manifests/grafana-datasource-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-datasources
5 | namespace: monitoring
6 | data:
7 | prometheus.yaml: |-
8 | {
9 | "apiVersion": 1,
10 | "datasources": [
11 | {
12 | "access":"proxy",
13 | "editable": true,
14 | "name": "prometheus",
15 | "orgId": 1,
16 | "type": "prometheus",
17 | "url": "http://prometheus-service.monitoring.svc:8080",
18 | "version": 1
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/client/store.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module store.js
5 | * @author Team Kubermetrics
6 | * @date
7 | * @description Redux 'single source of truth'
8 | *
9 | * ************************************
10 | */
11 |
12 | import { createStore } from 'redux';
13 | import { composeWithDevTools } from 'redux-devtools-extension';
14 | import reducers from './reducers/index';
15 |
16 | // we are adding composeWithDevTools here to get easy access to the Redux dev tools
17 | const store = createStore(
18 | reducers,
19 | composeWithDevTools()
20 | );
21 |
22 | export default store;
--------------------------------------------------------------------------------
/client/components/Node/currentNode.style.css:
--------------------------------------------------------------------------------
1 | .currentNode {
2 | /* width: 75vw; */
3 | color: white;
4 | background-color: #222426;
5 | margin: 10px;
6 | margin-left: 15px;
7 | width: 600px;
8 | height: 330px;
9 | border-radius: 10px;
10 | border: 1px solid rgb(19, 19, 19);
11 |
12 |
13 |
14 | display: flex;
15 | flex-direction: column;
16 | align-items: left;
17 | overflow: scroll;
18 | -webkit-box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
19 | box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
20 | }
21 |
22 | .nodeListing {
23 | font-size: 20px;
24 | font-family: 'Roboto', sans-serif;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/client/constants/actionTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module actionTypes.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Action Type Constants
8 | *
9 | * ************************************
10 | */
11 |
12 | export const GET_PODS = 'GET_PODS';
13 | export const GET_INGRESS = 'GET_INGRESS';
14 | export const GET_NODES = 'GET_NODES';
15 | export const GET_DEPLOYMENTS = 'GET_DEPLOYMENTS';
16 | export const CHANGE_NODE = 'CHANGE_NODE'
17 | export const GET_SERVICES = 'GET_SERVICES';
18 | export const GET_NAMESPACELIST = 'GET_NAMESPACELIST';
19 | export const CHANGE_NAMESPACE = 'CHANGE_NAMESPACE';
20 |
--------------------------------------------------------------------------------
/client/components/Header/Header.style.css:
--------------------------------------------------------------------------------
1 | .header {
2 | /* width: 75vw; */
3 | background-color: #343840;
4 | margin: 10px;
5 | margin-left: 15px;
6 | margin-top: 20px;
7 | width: auto;
8 | height: 80px;
9 | border-radius: 10px;
10 | color: white;
11 | border: 1px solid rgb(19, 19, 19);
12 |
13 | display: flex;
14 | justify-content: left;
15 | align-items: center;
16 | flex-direction: row;
17 | overflow: scroll;
18 | -webkit-box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
19 | box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.67);
20 | }
21 |
22 | .menuItem {
23 | color: white;
24 | }
25 | .MuiInputBase-input {
26 | color: white;
27 | }
--------------------------------------------------------------------------------
/client/reducers/servicesListReducer.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * ************************************
4 | *
5 | * @module servicesListReducer
6 | * @author team Kubermetrics
7 | * @date
8 | * @description reducer for service list
9 | *
10 | * ************************************
11 | */
12 | import * as types from '../constants/actionTypes';
13 |
14 |
15 | const initialState = {
16 | services: []
17 | }
18 |
19 | const servicesListReducer = (state = initialState, action) => {
20 | switch(action.type) {
21 | case types.GET_SERVICES:
22 | return {...state, services: action.payload}
23 |
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default servicesListReducer;
--------------------------------------------------------------------------------
/client/components/Deployments/Deployment.style.css:
--------------------------------------------------------------------------------
1 | .podContainer {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | filter: drop-shadow(-1px 6px 3px rgba(0, 0, 0, 0.5));
7 | width: 300px;
8 |
9 | }
10 | .podContainer:hover {
11 |
12 | filter:
13 | drop-shadow(5px 0px 0px rgb(143, 92, 254))
14 | drop-shadow(-5px 0px 0px rgb(143, 92, 254))
15 | drop-shadow(0px -5px 0px rgb(143, 92, 254))
16 | drop-shadow(0px 5px 0px rgb(143, 92, 254))
17 |
18 | }
19 |
20 |
21 | p {
22 | margin: 2px;
23 | text-align: center;
24 | text-shadow: 2px 2px 4px #000000;
25 | }
26 | .podLabel {
27 | font-size: 18px;
28 | margin-top: 5px;
29 | }
30 |
--------------------------------------------------------------------------------
/client/reducers/podsListReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module podsListReducer
5 | * @author team Kubermetrics
6 | * @date
7 | * @description reducer for pods list
8 | *
9 | * ************************************
10 | */
11 |
12 | import * as types from '../constants/actionTypes';
13 |
14 | const initialState = {
15 | pods: []
16 | };
17 |
18 | const podsListReducer = (state = initialState, action) => {
19 |
20 | const { type, payload } = action;
21 |
22 | switch (type) {
23 | case types.GET_PODS:
24 | return { ...state, pods: action.payload }
25 |
26 | default:
27 | return state;
28 |
29 | }
30 | }
31 |
32 | export default podsListReducer;
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module index.jsx
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Entry Point to Front End
8 | *
9 | * ************************************
10 | */
11 | import '@babel/polyfill';
12 | import { Provider } from 'react-redux';
13 | import React from 'react';
14 | import ReactDOM from 'react-dom';
15 | import App from './App.jsx'
16 | import store from '../store'
17 | import { BrowserRouter } from 'react-router-dom';
18 |
19 |
20 |
21 | ReactDOM.render(
22 |
23 |
24 |
25 |
26 | ,
27 | document.getElementById('root')
28 | );
29 |
--------------------------------------------------------------------------------
/client/reducers/ingressListReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module ingressList
5 | * @author team Kubermetrics
6 | * @date
7 | * @description reducer for pods list
8 | *
9 | * ************************************
10 | */
11 |
12 | import * as types from '../constants/actionTypes';
13 |
14 | const initialState = {
15 | ingresses: [],
16 | };
17 |
18 | const ingressListReducer = (state = initialState, action) => {
19 |
20 | const { type, payload } = action;
21 |
22 | switch (type) {
23 | case types.GET_INGRESS:
24 | return { ...state, ingresses: action.payload }
25 | default:
26 | return state;
27 |
28 | }
29 | }
30 |
31 | export default ingressListReducer;
--------------------------------------------------------------------------------
/client/components/PrometheusMonitoring/PrometheusAlerts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module PrometheusAlerts.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component prometheus iframe
8 | *
9 | * ************************************
10 | */
11 | import React from 'react'
12 |
13 |
14 | class PrometheusAlerts extends React.Component {
15 | render() {
16 | //suppose user is received from props
17 | const { user } = this.props
18 | return (
19 |
20 |
25 | )
26 | }
27 | }
28 | export default PrometheusAlerts
--------------------------------------------------------------------------------
/client/components/GrafanaMonitoring/ClusterDashboard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module ClusterDashboard.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Create iframe to connect to grafana instance
8 | *
9 | * ************************************
10 | */
11 | import React from 'react'
12 |
13 |
14 | class ClusterDashboard extends React.Component {
15 | render() {
16 | //suppose user is received from props
17 | const { user } = this.props
18 | return (
19 |
20 |
25 | )
26 | }
27 | }
28 | export default ClusterDashboard;
--------------------------------------------------------------------------------
/client/components/Pods/Pod.style.css:
--------------------------------------------------------------------------------
1 | .podContainer {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | filter: drop-shadow(-1px 6px 3px rgba(0, 0, 0, 0.5));
7 | width: 300px;
8 |
9 | }
10 |
11 | p {
12 | margin: 2px;
13 | text-align: center;
14 | text-shadow: 2px 2px 4px #000000;
15 | }
16 | .podLabel {
17 | text-align: center;
18 | color: rgb(255, 255, 255);
19 | font-family: 'Roboto', sans-serif;
20 | font-weight: 400;
21 | color: white;
22 | font-size: 22px;
23 | margin: 15px;
24 | }
25 |
26 | .podLabel2 {
27 | text-align: center;
28 | color: rgb(255, 255, 255);
29 | font-family: 'Roboto', sans-serif;
30 | font-weight: 300;
31 | color: white;
32 | font-size: 18px;
33 | margin: 15px;
34 | }
--------------------------------------------------------------------------------
/manifests/clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: prometheus
5 | rules:
6 | - apiGroups: [""]
7 | resources:
8 | - nodes
9 | - nodes/proxy
10 | - services
11 | - endpoints
12 | - pods
13 | verbs: ["get", "list", "watch"]
14 | - apiGroups:
15 | - extensions
16 | resources:
17 | - ingresses
18 | verbs: ["get", "list", "watch"]
19 | - nonResourceURLs: ["/metrics"]
20 | verbs: ["get"]
21 | ---
22 | apiVersion: rbac.authorization.k8s.io/v1
23 | kind: ClusterRoleBinding
24 | metadata:
25 | name: prometheus
26 | roleRef:
27 | apiGroup: rbac.authorization.k8s.io
28 | kind: ClusterRole
29 | name: prometheus
30 | subjects:
31 | - kind: ServiceAccount
32 | name: default
33 | namespace: monitoring
34 |
--------------------------------------------------------------------------------
/manifests/kubermetrics-depl.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: kubermetrics-depl
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: kubermetrics
10 | template:
11 | metadata:
12 | labels:
13 | app: kubermetrics
14 | spec:
15 | containers:
16 | - name: kubermetrics
17 | image: kubermetrics/kubermetrics:latest
18 | ---
19 | apiVersion: v1
20 | kind: Service
21 | metadata:
22 | name: kubermetrics-srv
23 | labels:
24 | prometheus: cluster-monitoring
25 | k8s-app: kube-state-metrics
26 | spec:
27 | selector:
28 | app: kubermetrics
29 | type: ClusterIP
30 | ports:
31 | - name: kubermetrics
32 | protocol: TCP
33 | port: 3080
34 | targetPort: 3080
--------------------------------------------------------------------------------
/client/reducers/deploymentListReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module deploymentListReducer
5 | * @author team Kubermetrics
6 | * @date
7 | * @description reducer for deployments list
8 | *
9 | * ************************************
10 | */
11 |
12 | import * as types from '../constants/actionTypes';
13 |
14 | const initialState = {
15 | deployments: []
16 | };
17 |
18 | const deploymentListReducer = (state = initialState, action) => {
19 |
20 | const { type, payload } = action;
21 |
22 |
23 | switch (type) {
24 | case types.GET_DEPLOYMENTS:
25 |
26 | return { ...state, deployments: action.payload }
27 |
28 | default:
29 | return state;
30 |
31 | }
32 | }
33 |
34 | export default deploymentListReducer;
--------------------------------------------------------------------------------
/client/components/sidebar/SidebarData.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module SidebarData.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Array containing The Icons & Paths for Sidebar
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import * as FaIcons from 'react-icons/fa';
13 | import * as AiIcons from 'react-icons/ai';
14 |
15 |
16 | export const SidebarData = [
17 | {
18 | title: 'Home',
19 | path: '/',
20 | icon: ,
21 | cName: 'nav-text'
22 | },
23 | {
24 | title: 'Metrics',
25 | path: '/metrics',
26 | icon: ,
27 | cName: 'nav-text'
28 | },
29 | {
30 | title: 'Alerts',
31 | path: '/alerts',
32 | icon: ,
33 | cName: 'nav-text'
34 | },
35 | ];
--------------------------------------------------------------------------------
/client/reducers/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module index.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description simply a place to combine reducers
8 | *
9 | * ************************************
10 | */
11 |
12 | import { combineReducers } from 'redux';
13 | import ingressListReducer from './ingressListReducer';
14 | import podsListReducer from './podsListReducer'
15 | import nodesListReducer from './nodesListReducer'
16 | import deploymentListReducer from './deploymentListReducer';
17 | import servicesListReducer from './servicesListReducer';
18 | import namespaceListReducer from './namespaceListReducer';
19 |
20 | const reducers = combineReducers({
21 | namespaces: namespaceListReducer,
22 | pods: podsListReducer,
23 | ingresses: ingressListReducer,
24 | nodes: nodesListReducer,
25 | deployments: deploymentListReducer,
26 | services: servicesListReducer,
27 | });
28 |
29 | export default reducers;
--------------------------------------------------------------------------------
/client/reducers/nodesListReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module nodesListReducer
5 | * @author team Kubermetrics
6 | * @date
7 | * @description reducer for nodes
8 | *
9 | * ************************************
10 | */
11 |
12 | import * as types from '../constants/actionTypes';
13 |
14 | const initialState = {
15 | nodes: [],
16 | currentNode: {}
17 | };
18 |
19 | const nodesListReducer = (state = initialState, action) => {
20 |
21 | const { type, payload } = action;
22 |
23 |
24 | switch (type) {
25 | case types.GET_NODES:
26 | return { ...state, nodes: action.payload, currentNode: action.payload[0] };
27 |
28 | case types.CHANGE_NODE:
29 | let newCurrent;
30 | state.nodes.forEach((node) => {
31 | if (node.name === payload) newCurrent = node;
32 | });
33 | return { ...state, currentNode: newCurrent}
34 |
35 | default:
36 | return state;
37 |
38 | }
39 | }
40 |
41 | export default nodesListReducer;
--------------------------------------------------------------------------------
/client/reducers/namespaceListReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module namespaceList
5 | * @author team Kubermetrics
6 | * @date
7 | * @description reducer for namespaces
8 | *
9 | * ************************************
10 | */
11 |
12 | import * as types from '../constants/actionTypes';
13 |
14 | const initialState = {
15 | namespaces: [],
16 | currentNamespace: {name: 'default'}
17 | }
18 |
19 | const namespaceListReducer = (state = initialState, action) => {
20 | switch(action.type) {
21 | case types.GET_NAMESPACELIST:
22 | return {...state, namespaces: action.payload, currentNamespace: action.payload[0]};
23 |
24 | case types.CHANGE_NAMESPACE:
25 | let newNamespace;
26 | state.namespaces.forEach((namespace) => {
27 | if (namespace.name === action.payload) newNamespace = namespace;
28 | });
29 | return {...state, currentNamespace: newNamespace}
30 | default:
31 | return state;
32 | }
33 | }
34 |
35 | export default namespaceListReducer;
--------------------------------------------------------------------------------
/client/components/NavRoutes/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import * as actions from '../../actions/actions';
4 | import HomePage from '../../pages/home/index.js'
5 | import MetricsPage from '../../pages/metrics/index.js'
6 | import AlertsPage from '../../pages/alerts/index.js'
7 |
8 | const Home = () => {
9 | return (
10 |
11 | )
12 | }
13 |
14 | const Metrics = () => {
15 | return (
16 |
17 | )
18 | }
19 |
20 | const Alerts = () => {
21 | return (
22 |
23 | )
24 | }
25 |
26 |
27 |
28 | const NavRoutes = [
29 | {
30 | path: '/',
31 | sidebarName: 'Home',
32 | // icon: [material_ui_icon_name],
33 | component: Home,
34 | },
35 | {
36 | path: '/metrics',
37 | sidebarName: 'Metrics',
38 | // icon: [material_ui_icon_name],
39 | component: Metrics,
40 | },
41 | {
42 | path: '/alerts',
43 | sidebarName: 'Alerts',
44 | // icon: [material_ui_icon_name],
45 | component: Alerts,
46 | },
47 | ];
48 |
49 | export default NavRoutes;
--------------------------------------------------------------------------------
/manifests/prometheus-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: prometheus-deployment
5 | namespace: monitoring
6 | labels:
7 | app: prometheus-server
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: prometheus-server
13 | template:
14 | metadata:
15 | labels:
16 | app: prometheus-server
17 | spec:
18 | containers:
19 | - name: prometheus
20 | image: prom/prometheus
21 | args:
22 | - "--config.file=/etc/prometheus/prometheus.yml"
23 | - "--storage.tsdb.path=/prometheus/"
24 | ports:
25 | - containerPort: 9090
26 | volumeMounts:
27 | - name: prometheus-config-volume
28 | mountPath: /etc/prometheus/
29 | - name: prometheus-storage-volume
30 | mountPath: /prometheus/
31 | volumes:
32 | - name: prometheus-config-volume
33 | configMap:
34 | defaultMode: 420
35 | name: prometheus-server-conf
36 |
37 | - name: prometheus-storage-volume
38 | emptyDir: {}
39 |
--------------------------------------------------------------------------------
/client/components/sidebar/Sidebar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module Sidebar.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component for Sidebar & Top Bar
8 | *
9 | * ************************************
10 | */
11 | import React from 'react'
12 | import { Link } from 'react-router-dom';
13 | import { SidebarData } from './SidebarData';
14 | import './Sidebar.css';
15 | import { IconContext } from 'react-icons';
16 | import logo from '../../assets/logo.png';
17 | import text from '../../assets/kubermetrics.png';
18 |
19 |
20 |
21 |
22 | function Sidebar(props) {
23 |
24 | return (
25 | <>
26 |
27 |
28 |
29 |
30 |
31 | {SidebarData.map((item, index) => {
32 | return (
33 |
34 |
35 | {item.icon}
36 | {item.title}
37 |
38 |
39 | );
40 | })};
41 |
42 |
43 |
44 | >
45 | );
46 | };
47 |
48 | export default Sidebar;
--------------------------------------------------------------------------------
/client/components/GrafanaMonitoring/PodDashboard.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * ************************************
4 | *
5 | * @module PodDashboard.js
6 | * @author team Kubermetrics
7 | * @date
8 | * @description Custom Dashboard from Grafana
9 | *
10 | * ************************************
11 | */
12 | import React from 'react';
13 |
14 |
15 | class PodDashboard extends React.Component {
16 | render() {
17 | return (
18 |
19 |
20 |
25 |
30 |
31 |
32 |
37 |
38 |
39 |
40 | )
41 | }
42 | }
43 |
44 |
45 |
46 | export default PodDashboard
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | mode: 'development',
5 | entry: ['@babel/polyfill', './client/src/index.js'],
6 | output: {
7 | path: path.resolve(__dirname, 'build'),
8 | filename: 'bundle.js',
9 | },
10 | module: {
11 | rules: [
12 | {
13 | test: /.(js|jsx)$/,
14 | exclude: /(node_modules)/,
15 | use: {
16 | loader: 'babel-loader',
17 | options: {
18 | presets: ['@babel/preset-env', '@babel/preset-react'],
19 | plugins: []
20 | },
21 | },
22 | },
23 | {
24 | test: /\.css$/,
25 | use: ['style-loader', 'css-loader'],
26 | },
27 | {
28 | test: /\.js$/,
29 | enforce: 'pre',
30 | use: ['source-map-loader'],
31 | },
32 | // {
33 | // test: /\.(jpg|png)$/,
34 | // use: {
35 | // loader: 'url-loader',
36 | // },
37 | // },
38 | {
39 | test: /\.(png|jpe?g|gif)$/i,
40 | use: [
41 | {
42 | loader: 'file-loader',
43 | },
44 | ]
45 | },
46 | ],
47 | },
48 | devServer: {
49 | host: '0.0.0.0',
50 | disableHostCheck: true,
51 | hot: true,
52 | port: 3068,
53 | compress: true,
54 |
55 | publicPath: "/build/",
56 |
57 |
58 | proxy: {
59 | '/': 'http://localhost:3080',
60 | },
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/client/components/sidebar/Sidebar.css:
--------------------------------------------------------------------------------
1 |
2 | .sidebar {
3 | background-color:#111617d0;
4 | color: #7135f0;
5 | top: 0px;
6 | margin: 0px;
7 | width: 100%;
8 | height: 80px;
9 | min-width: 1900px;
10 | display: flex;
11 | justify-content: start;
12 | align-items: center;
13 | /* box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); */
14 | /* border-radius: 10px; */
15 | }
16 |
17 | .menu-bars {
18 | margin-left: 2rem;
19 | font-size: 2rem;
20 | background: none;
21 | }
22 |
23 | .nav-menu {
24 | justify-self: left;
25 | /* background-color: #111617; */
26 | border-radius: 0px 0px 15px 15px;
27 | background: #111617c8;
28 | margin-top: 0px;
29 | margin-left: 0px;
30 |
31 | /* min-width: 100px; */
32 | height: 1170px;
33 | width: 80px;
34 | display: flex;
35 | flex-direction: column;
36 | justify-content: left;
37 | box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
38 | /* left: -100%; */
39 | }
40 |
41 |
42 | .nav-text {
43 | display: flex;
44 | justify-content: center;
45 | align-items: center;
46 | margin-top: 30px;
47 | padding: 4px 16px 4px 4px;
48 | list-style: none;
49 | height: 60px;
50 | color: #7135f0;
51 | }
52 |
53 | .nav-text a {
54 | text-decoration: none;
55 | color: #fff;
56 | font-size: 16px;
57 | font-family: 'Roboto', sans-serif;
58 | width: auto;
59 | height: 100%;
60 | }
61 |
62 | .sideLink:hover {
63 | color: #7135f0;
64 |
65 | }
66 |
67 | .nav-menu-items {
68 | width: 100%;
69 | padding: 5px;
70 | margin-top: 30px;
71 | }
72 |
73 | span {
74 | margin-left: 3px;
75 | }
76 |
77 | h3 {
78 | text-align: center;
79 | align-self: center;
80 | margin-left: 25px;
81 | font-size: 27px;
82 | }
83 |
84 | .icons {
85 |
86 | font-size: 30px;
87 |
88 | }
--------------------------------------------------------------------------------
/manifests/grafana-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: grafana
5 | namespace: monitoring
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: grafana
11 | template:
12 | metadata:
13 | name: grafana
14 | labels:
15 | app: grafana
16 | spec:
17 | containers:
18 | - name: grafana
19 | image: grafana/grafana:latest
20 | # command: ["ADD"]
21 | # args: ["./dashboards/clusterDash.json", "./usr/share/grafana/conf/provisioning/dashboards/"]
22 | ports:
23 | - name: grafana
24 | containerPort: 3000
25 | resources:
26 | limits:
27 | memory: "1Gi"
28 | cpu: "1000m"
29 | requests:
30 | memory: 500M
31 | cpu: "500m"
32 | volumeMounts:
33 | - mountPath: /var/lib/grafana
34 | name: grafana-storage
35 | - mountPath: /etc/grafana/provisioning/datasources
36 | name: grafana-datasources
37 | readOnly: false
38 | # - mountPath: /etc/grafana/provisioning/dashboards
39 | # name: grafana-dashboards
40 | # readOnly: false
41 | env:
42 | - name: GF_SECURITY_ALLOW_EMBEDDING
43 | value: "true"
44 | - name: GF_AUTH_ANONYMOUS_ENABLED
45 | value: "true"
46 | - name: GF_AUTH_ANONYMOUS_ORG_ROLE
47 | value: "Admin"
48 | # - name: GF_AUTH_ANONYMOUS_ENABLED
49 | # value: "true"
50 | volumes:
51 | - name: grafana-storage
52 | emptyDir: {}
53 | - name: grafana-datasources
54 | configMap:
55 | defaultMode: 420
56 | name: grafana-datasources
57 | # - name: grafana-dashboards
58 | # configMap:
59 | # name: grafana-dashboards
60 |
--------------------------------------------------------------------------------
/client/components/mui-elements.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module mui-elements.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description File Containing all custom "styled" Material UI Elements
8 | *
9 | * ************************************
10 | */
11 | import Card from '@material-ui/core/Card';
12 | import { styled } from '@material-ui/core/styles';
13 |
14 | // Utilitize CSS techniques such as clipPath to shape out Pod, Deployment and Service elements from the Card MUI element
15 |
16 | export const PodElement = styled(Card)({
17 | display: 'flex',
18 | width: '150px',
19 | height: '150px',
20 | color: 'white',
21 | backgroundColor: '#3b3d41',
22 | boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
23 | flexDirection: 'column',
24 | padding: '45px',
25 | justifyContent: 'center',
26 | alignItems: 'center',
27 | margin: '10px',
28 | clipPath:
29 | 'circle(50%)',
30 | });
31 |
32 | export const DeploymentElement = styled(Card)({
33 | display: 'flex',
34 | width: '150px',
35 | height: '150px',
36 | color: 'white',
37 | margin: '10px',
38 | backgroundColor: '#3b3d41',
39 | boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
40 | flexDirection: 'column',
41 | padding: '45px',
42 | justifyContent: 'center',
43 | alignItems: 'center',
44 | clipPath:
45 | 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)',
46 | });
47 |
48 | export const ServiceElement = styled(Card)({
49 | display: 'flex',
50 | width: '150px',
51 | height: '150px',
52 | color: 'white',
53 | margin: '10px',
54 | backgroundColor: '#3b3d41',
55 | boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
56 | flexDirection: 'column',
57 | padding: '45px',
58 | justifyContent: 'center',
59 | alignItems: 'center',
60 | clipPath: 'inset(14% 0 15% 0 round 10% 10% 10% 10%)',
61 | });
62 |
--------------------------------------------------------------------------------
/client/components/Services/ServicesList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module ServicesList.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component to contain All Services
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import { connect } from 'react-redux';
13 | import Service from './Service';
14 | import './ServicesList.styles.css';
15 |
16 |
17 | // Map services to props
18 | const mapStateToProps = state => ({
19 | services: state.services.services
20 | })
21 |
22 | const ServiceList = (props) => {
23 |
24 | // Service Array will hold all Service Elements
25 | const servicesArray = [];
26 |
27 | // Iterate over services (passed down from homepage) and create React Component service for each
28 | props.services.forEach((service, i) => {
29 | servicesArray.push(
30 |
31 | )
32 | })
33 |
34 | // Conditional Render accounting for the case in which no services are found.
35 |
36 | if (!servicesArray.length) {
37 |
38 | return (
39 |
40 |
41 |
Services
42 | None Found In Current Namespace
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | // Default render
52 | return (
53 |
54 |
55 |
Services
56 | Total: {servicesArray.length}
57 |
58 |
59 | {servicesArray}
60 |
61 |
62 |
63 | )
64 | }
65 |
66 | export default connect(mapStateToProps, null)(ServiceList);
--------------------------------------------------------------------------------
/client/components/Node/CurrentNode.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module CurrentNode.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Current Node component containing all data on current node.
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import { connect } from 'react-redux';
13 | import './currentNode.style.css'
14 | import NodeDialog from '../../Dialog/NodeDialog';
15 |
16 |
17 |
18 | const mapStateToProps = state => ({
19 | currentNode: state.nodes.currentNode
20 | })
21 |
22 |
23 | const CurrentNode = (props) => {
24 |
25 | // Deconstruct currentNode from props passed down from Home Page
26 | const { currentNode } = props;
27 |
28 | // Ternary statement checking if current node exists - If so we will render more info button
29 | let renderNode = currentNode.name ? More Node Info : undefined
30 |
31 | // Ternary statement checking if current node exists - If so we will render all default information
32 | let defaultInfo = currentNode.name ? (
33 |
34 |
Node Name: {currentNode.name}
35 |
Arch: {currentNode.arch}
36 |
OS: {currentNode.os}
37 |
Kernerl Version: {currentNode.nodeInfo.kernelVersion}
38 |
Kubelet Version: {currentNode.nodeInfo.kubeletVersion}
39 |
Container Runtime: {currentNode.nodeInfo.containerRuntimeVersion}
40 |
41 | ) : undefined
42 |
43 | return (
44 |
45 |
46 |
Current Node
47 |
48 | {defaultInfo}
49 |
50 |
{renderNode}
51 |
52 |
53 | )
54 | }
55 |
56 | export default connect(mapStateToProps, null)(CurrentNode)
--------------------------------------------------------------------------------
/client/src/App.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module App.jsx
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Element Containing all Page Elements
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import HomePage from '../pages/home/index.js'
13 | import MetricsPage from '../pages/metrics/index.js'
14 | import AlertsPage from '../pages/alerts/index.js'
15 | import '../styles.css';
16 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
17 | import Sidebar from '../components/sidebar/Sidebar.js';
18 | import { createTheme, MuiThemeProvider } from '@material-ui/core/styles';
19 | import logo from '../assets/logo.png';
20 | import text from '../assets/kubermetrics.png';
21 |
22 |
23 | // Custom Theme for Material UI
24 | export const darkTheme = createTheme({"palette":{"common":{"black":"#000","white":"rgba(255, 255, 255, 1)"},"background":{"paper":"rgba(26, 30, 33, 1)","default":"rgba(255, 255, 255, 1)"},"primary":{"light":"rgba(45, 53, 55, 1)","main":"rgba(17, 22, 23, 1)","dark":"rgba(13, 15, 16, 1)","contrastText":"#fff"},"secondary":{"light":"rgba(143, 96, 240, 1)","main":"rgba(113, 53, 240, 1)","dark":"rgba(96, 29, 239, 1)","contrastText":"#fff"},"error":{"light":"#e57373","main":"rgba(208, 2, 27, 1)","dark":"#d32f2f","contrastText":"#fff"},"text":{"primary":"rgba(0, 0, 0, 0.87)","secondary":"rgba(255, 255, 255, 0.54)","disabled":"rgba(255, 255, 255, 0.38)","hint":"rgba(255, 255, 255, 0.38)"}}})
25 |
26 | function App() {
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | export default App;
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kubermetrics",
3 | "version": "1.0.0",
4 | "description": "K8s Metrics",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "nodemon ./server/server.js",
8 | "build": "webpack",
9 | "dev": "nodemon server/server.js & webpack serve --hot --open"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/oslabs-beta/kubermetrics.git"
14 | },
15 | "author": "",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/oslabs-beta/kubermetrics/issues"
19 | },
20 | "homepage": "https://github.com/oslabs-beta/kubermetrics#readme",
21 | "devDependencies": {
22 | "@babel/core": "^7.14.6",
23 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
24 | "@babel/plugin-transform-modules-commonjs": "^7.14.5",
25 | "@babel/plugin-transform-runtime": "^7.14.5",
26 | "@babel/preset-env": "^7.14.8",
27 | "@babel/preset-react": "^7.14.5",
28 | "babel-jest": "^27.0.6",
29 | "babel-loader": "^8.2.2",
30 | "concurrently": "^6.2.0",
31 | "cross-env": "^7.0.3",
32 | "css-loader": "^5.2.6",
33 | "file-loader": "^6.2.0",
34 | "html-webpack-plugin": "^5.3.1",
35 | "jest": "^27.1.0",
36 | "mini-css-extract-plugin": "^1.6.0",
37 | "redux-mock-store": "^1.5.4",
38 | "source-map-loader": "^3.0.0",
39 | "style-loader": "^2.0.0",
40 | "supertest": "^6.1.4",
41 | "url-loader": "^4.1.1",
42 | "webpack": "^5.51.1",
43 | "webpack-cli": "^4.6.0",
44 | "webpack-dev-server": "^3.11.2"
45 | },
46 | "dependencies": {
47 | "@babel/polyfill": "^7.12.1",
48 | "@kubernetes/client-node": "^0.15.1",
49 | "@material-ui/core": "^4.12.3",
50 | "@material-ui/icons": "^4.11.2",
51 | "@material-ui/lab": "^4.0.0-alpha.60",
52 | "@material-ui/styles": "^4.11.4",
53 | "axios": "^0.21.4",
54 | "cors": "^2.8.5",
55 | "express": "^4.17.1",
56 | "nodemon": "^2.0.12",
57 | "prom-client": "^13.2.0",
58 | "react": "^17.0.2",
59 | "react-dom": "^17.0.2",
60 | "react-icons": "^4.2.0",
61 | "react-redux": "^7.2.4",
62 | "react-router-dom": "^5.3.0",
63 | "redux": "^4.1.1",
64 | "redux-devtools-extension": "^2.13.9",
65 | "redux-thunk": "^2.3.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/client/components/Deployments/DeploymentList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module DeploymentList.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component that will contain all Deployments
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import { connect } from 'react-redux';
13 | import Deployment from './Deployment';
14 | import './DeploymentList.style.css'
15 |
16 |
17 | // Map deployment array to props
18 |
19 | const mapStateToProps = state => ({
20 | deployments: state.deployments.deployments
21 | })
22 |
23 |
24 | const DeploymentList = (props) => {
25 |
26 | // Array that will contain all Deployment react components
27 | const deploymentsArray = [];
28 |
29 | // Replicas will count each deployments replicas and dispay for end user
30 | let replicas = 0;
31 |
32 | // Iterate over array and create react element for each deployment
33 | props.deployments.forEach((deployment, ind) => {
34 | deploymentsArray.push( )
38 | // Add up replicas
39 | replicas += deployment.replicas;
40 | })
41 |
42 | // Conditional Render to handle case in which no running deployments are found.
43 |
44 | if (!deploymentsArray.length){
45 | return (
46 |
47 |
48 |
Deployments
49 | None Found in Current Namespace
50 |
51 |
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | // Default Render to return Deployment container with all deployment elements
59 |
60 | return (
61 |
62 |
63 |
64 |
Deployments
65 | Total Replicas: {replicas}
66 |
67 |
68 | {deploymentsArray}
69 |
70 |
71 | )
72 | }
73 |
74 | export default connect(mapStateToProps, null)(DeploymentList)
--------------------------------------------------------------------------------
/client/components/Services/Service.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module Service.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component for Services
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import CardContent from '@material-ui/core/CardContent';
13 | import Button from '@material-ui/core/Button';
14 | import './Service.styles.css';
15 | import * as mui from '../mui-elements';
16 | import { connect } from 'react-redux';
17 | import * as actions from '../../actions/actions';
18 | import ServiceDialog from '../../Dialog/ServiceDialog';
19 |
20 | // Deconstruct custom service element imported from mui file
21 | const { ServiceElement } = mui;
22 |
23 | // Map current namespace to props
24 | const mapStateToProps = state => ({
25 | namespace: state.namespaces.currentNamespace
26 | })
27 |
28 | // Map ability to refresh pods to props
29 | const mapDispatchToProps = dispatch => ({
30 | loadServices: async (namespace) => {
31 | let services = await actions.fetchCustomServices(namespace);
32 | dispatch(actions.getServices(services))
33 | }
34 | })
35 |
36 |
37 | const Service = (props) => {
38 |
39 | // Conditional statement accounting for if services are not loaded (passed down from Service List)
40 | if (props.notLoaded) {
41 | return (
42 |
43 |
44 |
45 |
46 |
No Services Found
47 |
props.loadServices(props.namespace)}>Refresh
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 |
56 | // Default Render
57 | return (
58 |
59 |
60 |
61 |
62 |
{props.service.name}
63 |
MORE INFO
64 |
65 |
66 |
67 |
68 | )
69 | };
70 |
71 | export default connect(mapStateToProps, mapDispatchToProps)(Service);
72 |
73 |
--------------------------------------------------------------------------------
/client/components/Pods/Pod.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module Pod.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component for Pods
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import CardContent from '@material-ui/core/CardContent';
13 | import { connect } from 'react-redux';
14 | import * as actions from '../../actions/actions';
15 | import Button from '@material-ui/core/Button';
16 | import './Pod.style.css';
17 | import * as mui from '../mui-elements'
18 | import PodDialog from '../../Dialog/PodsDialog';
19 |
20 | // Deconstruct our Custom Pod element from our mui file
21 |
22 | const { PodElement } = mui;
23 |
24 | // Attach ability to reload pods to each pod component
25 |
26 | const mapDispatchToProps = dispatch => ({
27 | loadPods: async (namespace) => {
28 | let pods = await actions.fetchCustomPods(namespace);
29 | dispatch(actions.getPods(pods));
30 | }
31 | })
32 |
33 | // Map current namespace to props
34 |
35 | const mapStateToProps = state => ({
36 | namespace: state.namespaces.currentNamespace
37 | })
38 |
39 | const Pod = (props) => {
40 |
41 | // Conditional statement checking if props.notLoaded = true (passed down from pod list)
42 | if (props.notLoaded) {
43 | return (
44 |
45 |
46 |
47 |
48 |
No Pods Found
49 |
props.loadPods(props.namespace)}>Refresh
50 |
51 |
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | // Default render for Pod element.
59 |
60 | return (
61 |
62 |
63 |
64 |
65 |
{props.label}
66 |
{props.podName}
67 |
68 |
69 |
70 |
71 |
72 | )
73 | }
74 |
75 |
76 |
77 | export default connect(mapStateToProps, mapDispatchToProps)(Pod);
78 |
--------------------------------------------------------------------------------
/client/components/Pods/PodsList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module PodsList.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component that will contain all Pod Elements
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import { connect } from 'react-redux';
13 | import Pod from './Pod';
14 | import './PodsList.style.css'
15 |
16 |
17 | // Map pods array to state
18 |
19 | const mapStateToProps = state => ({
20 | pods: state.pods.pods
21 | })
22 |
23 |
24 | export const PodList = (props) => {
25 |
26 | // array to hold all react elements to be rendered
27 | const podsArray = [];
28 | // running will count all pods that include "running" or "succeeded" in phase
29 | let running = 0
30 | // total will count total amount of pods.
31 | let total = 0;
32 | props.pods.forEach((pod, ind) => {
33 | podsArray.push( )
50 |
51 | if (pod.allData){
52 | if (pod.allData.status.phase === 'Running' || pod.allData.status.phase === 'Succeeded') running++;
53 | }
54 | total++;
55 | });
56 |
57 | // Conditional render accounting for the case in which no pods are found
58 |
59 | if (!podsArray.length){
60 | return (
61 |
62 |
63 |
Pods
64 | None Found In Current Namespace
65 |
66 |
69 |
70 | )
71 | }
72 |
73 | // default render statement
74 |
75 | return (
76 |
77 |
78 |
Pods
79 | Running: {running} / {total}
80 |
81 |
82 | {podsArray}
83 |
84 |
85 | )
86 | }
87 |
88 | export default connect(mapStateToProps, null)(PodList)
--------------------------------------------------------------------------------
/client/pages/home/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module index.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Home Page Element for React Router
8 | *
9 | * ************************************
10 | */
11 | import { useEffect } from 'react';
12 | import { connect } from 'react-redux';
13 | import React from 'react';
14 | import PodsList from '../../components/Pods/PodsList';
15 | import * as actions from '../../actions/actions';
16 | import DeploymentList from '../../components/Deployments/DeploymentList';
17 | import Header from '../../components/Header/Header';
18 | import ServicesList from '../../components/Services/ServicesList';
19 | import CurrentNode from '../../components/Node/CurrentNode';
20 |
21 |
22 | // Map dispatch to props to handle state change on Load - This will ensure Home Page will populate with all relevant data on load.
23 |
24 | const mapDispatchToProps = dispatch => ({
25 | clickForPods: async () => {
26 | let pods = await actions.fetchPods()
27 | dispatch(actions.getPods(pods))
28 | },
29 | clickForIngresses: async () => {
30 | const ingresses = await actions.fetchIngress()
31 | dispatch(actions.getIngress(ingresses))
32 |
33 | },
34 | clickForNodes: async () => {
35 | let nodes = await actions.fetchNodes();
36 | dispatch(actions.getNodes(nodes))
37 | },
38 | clickForDeployments: async () => {
39 | let deployments = await actions.fetchDeployments();
40 | dispatch(actions.getDeployments(deployments));
41 | },
42 | clickServices: async () => {
43 | let services = await actions.fetchServices();
44 | dispatch(actions.getServices(services));
45 | },
46 | clickNamespaces: async () => {
47 | let namespaces = await actions.fetchNamespaces();
48 | dispatch(actions.getNamespaceList(namespaces));
49 | },
50 | })
51 |
52 |
53 | const HomePage = (props) => {
54 |
55 | // Utilize React useEffect to handle API calls on load
56 | useEffect(() => {
57 | props.clickForPods();
58 | props.clickForDeployments();
59 | props.clickServices();
60 | props.clickForNodes();
61 | props.clickForIngresses();
62 | props.clickNamespaces();
63 | }, [])
64 |
65 | return (
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | )
78 | }
79 |
80 |
81 |
82 |
83 | export default connect(null, mapDispatchToProps)(HomePage);
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const path = require("path");
4 | const client = require('prom-client');
5 | const cors = require('cors');
6 | const axios = require('axios');
7 | const k8Controller = require('./controllers/k8Controller.js');
8 |
9 |
10 | app.use(cors());
11 | app.use(express.json());
12 |
13 | app.use("/build", express.static(path.join(__dirname, "../build")));
14 | app.use(express.static(__dirname + '/client/assets'));
15 |
16 | app.get("/", (req, res) => {
17 | return res.status(200).sendFile(path.join(__dirname, "../index.html"));
18 | });
19 |
20 |
21 |
22 | /*
23 |
24 | All Routes Handling React Router Reload - Ensuring Page will Always Load
25 |
26 | */
27 |
28 | app.get("/metrics", (req, res) => {
29 | return res.redirect('/');
30 | });
31 | app.get("/alerts", (req, res) => {
32 | return res.redirect('/');
33 | });
34 |
35 | /*
36 |
37 | All Routes Handling K8S Cluster Scraping in Default Namespace
38 |
39 | */
40 |
41 | app.get('/namespaceList', k8Controller.getNamespaceList, (req, res) => {
42 | res.status(201).send(res.locals.namespaceList);
43 | });
44 |
45 | app.get('/podList', k8Controller.getPodList, (req, res) => {
46 | res.status(201).send(res.locals.podList);
47 | });
48 |
49 | app.get('/serviceList', k8Controller.getServiceList, (req, res) => {
50 | res.status(201).send(res.locals.serviceList);
51 | });
52 |
53 | app.get('/ingressList', k8Controller.getIngressList, (req, res) => {
54 | res.status(201).send(res.locals.ingressList);
55 | });
56 |
57 | app.get('/deploymentList', k8Controller.getDeploymentList, (req, res) => {
58 | res.status(201).send(res.locals.deploymentList);
59 | });
60 |
61 | app.get('/nodeList', k8Controller.getNodeList, (req, res) => {
62 | res.status(201).send(res.locals.nodeList);
63 | });
64 |
65 | /*
66 |
67 | All Routes Handling K8S Cluster Scraping in Custom Namespace sent from Front End
68 |
69 | */
70 |
71 |
72 | app.post('/customPods', k8Controller.getCustomPodList, (req, res) => {
73 | res.status(201).json(res.locals.customPodList)
74 | });
75 |
76 | app.post('/customServices', k8Controller.getCustomServiceList, (req, res) => {
77 | res.status(201).json(res.locals.serviceList)
78 | });
79 |
80 | app.post('/customIngresses', k8Controller.getCustomIngressList, (req, res) => {
81 | res.status(201).json(res.locals.ingressList)
82 | });
83 |
84 | app.post('/customDeployments', k8Controller.getCustomDeploymentList, (req, res) => {
85 | res.status(201).json(res.locals.deploymentList)
86 | });
87 |
88 |
89 |
90 | /*
91 |
92 | Initialize Server on Port 3080
93 |
94 | */
95 |
96 |
97 | app.listen(3080, async () => {
98 | console.log('listening on port 3080');
99 | });
--------------------------------------------------------------------------------
/client/components/Deployments/Deployment.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module Deployment.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description React Component for Deployments
8 | *
9 | * ************************************
10 | */
11 | import React from 'react';
12 | import CardContent from '@material-ui/core/CardContent';
13 | import Button from '@material-ui/core/Button';
14 | import './Deployment.style.css';
15 | import * as mui from '../mui-elements'
16 | import { connect } from 'react-redux';
17 | import * as actions from '../../actions/actions';
18 | import DeploymentDialog from '../../Dialog/DeploymentDialog';
19 |
20 | // Map dispatch to props to allow ability to refresh Deployments list from within component.
21 |
22 | const mapDispatchToProps = dispatch => ({
23 | loadDeployments: async (namespace) => {
24 | // fetch deployments & format data with current namespace.
25 | let deployments = await actions.fetchCustomDeployments(namespace);
26 | // send array of deployments to reducer.
27 | dispatch(actions.getDeployments(deployments))
28 | }
29 | })
30 |
31 | // Map state to props so we know the currently selected namespace.
32 |
33 | const mapStateToProps = state => ({
34 | namespace: state.namespaces.currentNamespace
35 | })
36 |
37 |
38 |
39 |
40 | const Deployment = (props) => {
41 |
42 | // Deconstruct deployment element from mui file
43 |
44 | const { DeploymentElement } = mui;
45 |
46 | // Conditional render accounting for the case in which no running deployments are found.
47 |
48 | if (props.notLoaded) {
49 | return (
50 |
51 |
52 |
53 |
54 |
No Deployments found
55 |
props.loadDeployments(props.namespace)}>Refresh
56 |
57 |
58 |
59 |
60 | )
61 | }
62 |
63 | // Default Render displaying Deploment name & deployment dialog to handle extra data.
64 |
65 | return (
66 |
67 |
68 |
69 |
70 |
{props.deployment.name}
71 |
72 |
73 |
74 |
75 |
76 | )
77 | }
78 |
79 | // Export component as well as connect it to Redux Reducers & State
80 |
81 | export default connect(mapStateToProps, mapDispatchToProps)(Deployment);
82 |
--------------------------------------------------------------------------------
/client/styles.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500&display=swap');
3 |
4 | body {
5 | /* background-color: #2b2b2b; */
6 | background: rgb(0,0,0);
7 | background: linear-gradient(0deg, rgba(0,0,0,1) 0%, rgba(89,89,89,0.9402135854341737) 0%, rgba(25,25,25,1) 100%);
8 | margin: 0px;
9 | /* background-color: #3b3d41 */
10 |
11 | }
12 |
13 | iframe {
14 | border-radius: 10px;
15 | border: black;
16 | }
17 |
18 |
19 | h2 {
20 | background-color: rgb(85, 85, 85);
21 | }
22 |
23 | h3 {
24 | font-family: 'Roboto', sans-serif;
25 | color: white;
26 | text-shadow: 2px 2px 4px #000000;
27 | }
28 |
29 |
30 |
31 | .graphContainer {
32 | background-color: #222426;
33 | margin-left: 117px;
34 | margin-top: 15px;
35 | margin-bottom: 10px;
36 | width: 1400px;
37 | padding: 15px;
38 | height: auto;
39 | border-radius: 10px;
40 | /* max-width: 1500px;
41 | min-width: 1500px; */
42 | border: 1px solid rgb(19, 19, 19);
43 |
44 |
45 | display: flex;
46 | flex-direction: column;
47 | box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
48 | }
49 |
50 | .mainContent{
51 | display: flex;
52 | flex-direction: row;
53 | /* align-items: center; */
54 | justify-content: center;
55 | }
56 |
57 | .wrapper {
58 | display: flex;
59 | flex-direction: row;
60 | /* background-color: rgb(85, 0, 255); */
61 | }
62 |
63 | .homepageContent {
64 | display: flex;
65 | flex-direction: column;
66 | justify-content: left;
67 | }
68 |
69 | .miniHead {
70 |
71 | background-color: #343840;
72 | }
73 |
74 | .miniHeadList {
75 | background-color: #343840;
76 | display: flex;
77 | justify-content: left;
78 | flex-direction: row;
79 | border-radius: 10px;
80 | margin-bottom: 10px;
81 | }
82 |
83 | .miniHeadText {
84 | text-align: center;
85 | color: white;
86 | font-family: 'Roboto', sans-serif;;
87 | font-size: 20px;
88 | margin: 5px
89 | }
90 |
91 | .overflowBox {
92 | display: flex;
93 | justify-content: left;
94 | flex-direction: row;
95 | overflow: scroll;
96 | }
97 |
98 | .greenText {
99 | text-align: center;
100 | color: rgb(85, 203, 85);
101 | font-family: 'Roboto', sans-serif;;
102 | font-size: 20px;
103 | margin: 5px
104 | }
105 |
106 | .redText {
107 | text-align: center;
108 | color: #c61e64;
109 | font-family: 'Roboto', sans-serif;;
110 | font-size: 20px;
111 | margin: 5px
112 | }
113 |
114 | .homePage{
115 | display: flex;
116 | flex-direction: column;
117 | width: 1920px;
118 | }
119 |
120 | .btn{
121 | height: 15px;
122 | width: 17px;
123 | font-size: 13px;
124 | }
125 |
126 | .MuiTable-root {
127 | min-width: 500px
128 | }
129 |
130 | .ingressButton {
131 | margin-left: 50px;
132 | }
133 |
134 | .nodeInfo {
135 | margin: 20px;
136 | display: flex;
137 | flex-direction: column;
138 | justify-content: center;
139 | align-items: center;
140 | }
141 |
142 |
143 | .metricsPage {
144 | display: flex;
145 | flex-direction: column;
146 | width: 1920px;
147 | height: 100%;
148 | margin-top: 15px;
149 | margin-left: 15px;
150 | border-radius: 10px;
151 | }
152 |
153 |
154 | .alertsPage {
155 | display: flex;
156 | flex-direction: column;
157 | margin-top: 15px;
158 | width: 1920px;
159 | height: 100%;
160 | margin-left: 15px;
161 | }
162 |
163 | .nd {
164 | display: flex;
165 | flex-direction: row;
166 | }
167 |
168 | .logo{
169 | height: 30px;
170 | width: 30px;
171 | margin-left: 30px;
172 | }
173 |
174 | .sidebarLogo{
175 | height: 40px;
176 | width: 30px;
177 | margin-left: 20px;
178 | }
179 |
180 |
181 |
182 | .logoText{
183 | height: 20px;
184 | width: 250px;
185 | margin-left: 20px;
186 | }
--------------------------------------------------------------------------------
/server/controllers/k8Controller.js:
--------------------------------------------------------------------------------
1 | const k8s = require('@kubernetes/client-node');
2 | const kc = new k8s.KubeConfig();
3 | kc.loadFromDefault();
4 |
5 | const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
6 | const k8sApi2 = kc.makeApiClient(k8s.ExtensionsV1beta1Api);
7 | const k8sApi3 = kc.makeApiClient(k8s.AppsV1Api);
8 |
9 | /*
10 |
11 | Initalize K8Controller Middleware to handle All API requests to Kubernetes Cluster
12 |
13 | */
14 |
15 | const k8Controller = {
16 |
17 | /*
18 |
19 | All API Calls for "default" Namespace
20 |
21 | */
22 |
23 | async getNamespaceList (req, res, next) {
24 | try {
25 | const result = await k8sApi.listNamespace();
26 | res.locals.namespaceList = result.body;
27 | return next();
28 | } catch (err) {
29 | console.log('Error in getPodList: ', err);
30 | return next(err);
31 | }
32 | },
33 |
34 | async getPodList (req, res, next) {
35 | try {
36 | const result = await k8sApi.listNamespacedPod('default');
37 | res.locals.podList = result.body;
38 | return next();
39 | } catch (err) {
40 | console.log('Error in getPodList: ', err);
41 | return next(err);
42 | }
43 | },
44 |
45 | async getServiceList (req, res, next) {
46 | try {
47 | const result = await k8sApi.listNamespacedService('default');
48 | res.locals.serviceList = result.body;
49 | return next();
50 | } catch (err) {
51 | console.log('Error in getServiceList: ', err);
52 | return next(err);
53 | }
54 | },
55 |
56 | async getIngressList (req, res, next) {
57 | try {
58 | const result = await k8sApi2.listNamespacedIngress('default');
59 | res.locals.ingressList = result.body;
60 | return next();
61 | } catch (err) {
62 | console.log('Error in getIngressList: ', err);
63 | return next(err);
64 | }
65 | },
66 |
67 | async getDeploymentList (req, res, next) {
68 | try {
69 | const result = await k8sApi3.listNamespacedDeployment('default');
70 | res.locals.deploymentList = result.body;
71 | return next();
72 | } catch (err) {
73 | console.log('Error in getDeploymentList: ', err);
74 | return next(err);
75 | }
76 | },
77 |
78 | async getNodeList (req, res, next) {
79 | try {
80 | const result = await k8sApi.listNode('default');
81 | res.locals.nodeList = result.body;
82 | try {
83 | const result2 = await k8sApi.listComponentStatus();
84 | res.locals.nodeList.nodeProcesses = result2;
85 | return next();
86 | } catch (err) {
87 | console.log('Error in getNodeList/nodeProcesses');
88 | return next(err);
89 | }
90 | } catch (err) {
91 | console.log('Error in getNodeList: ', err);
92 | return next(err);
93 | }
94 | },
95 |
96 |
97 | /*
98 |
99 | All API Calls for "custom" Namespace
100 |
101 | */
102 |
103 |
104 | async getCustomPodList (req, res, next) {
105 | try {
106 | const { namespace } = req.body;
107 | const result = await k8sApi.listNamespacedPod(namespace);
108 | res.locals.customPodList = result.body;
109 | return next();
110 | } catch (err) {
111 | console.log('Error in getPodList: ', err);
112 | return next(err);
113 | }
114 | },
115 |
116 |
117 | async getCustomServiceList (req, res, next) {
118 | try {
119 | const { namespace } = req.body;
120 | const result = await k8sApi.listNamespacedService(namespace);
121 | res.locals.serviceList = result.body;
122 | return next();
123 | } catch (err) {
124 | console.log('Error in getServiceList: ', err);
125 | return next(err);
126 | }
127 | },
128 |
129 | async getCustomIngressList (req, res, next) {
130 | try {
131 | const { namespace } = req.body;
132 | const result = await k8sApi2.listNamespacedIngress(namespace);
133 | res.locals.ingressList = result.body;
134 | return next();
135 | } catch (err) {
136 | console.log('Error in getIngressList: ', err);
137 | return next(err);
138 | }
139 | },
140 |
141 | async getCustomDeploymentList (req, res, next) {
142 | try {
143 | const { namespace } = req.body;
144 | const result = await k8sApi3.listNamespacedDeployment(namespace);
145 | res.locals.deploymentList = result.body;
146 | return next();
147 | } catch (err) {
148 | console.log('Error in getDeploymentList: ', err);
149 | return next(err);
150 | }
151 | },
152 |
153 | }
154 |
155 | module.exports = k8Controller;
--------------------------------------------------------------------------------
/client/Dialog/IngressDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module IngressDialog.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Dialog Box for Ingress Element
8 | *
9 | * ************************************
10 | */
11 | import * as React from 'react';
12 | import Button from '@material-ui/core/Button';
13 | import { } from '@material-ui/core/styles';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import DialogTitle from '@material-ui/core/DialogTitle';
16 | import DialogContent from '@material-ui/core/DialogContent';
17 | import DialogActions from '@material-ui/core/DialogActions';
18 | import Table from '@material-ui/core/Table';
19 | import TableBody from '@material-ui/core/TableBody';
20 | import TableCell from '@material-ui/core/TableCell';
21 | import TableContainer from '@material-ui/core/TableContainer';
22 | import TableHead from '@material-ui/core/TableHead';
23 | import TableRow from '@material-ui/core/TableRow';
24 | import Paper from '@material-ui/core/Paper';
25 |
26 | const customStyle = {
27 | color: 'white',
28 | margin: '5px',
29 | padding: '5px',
30 | }
31 |
32 | const customStyle2 = {
33 | color: '#7135f0',
34 | margin: '5px',
35 | padding: '5px',
36 | }
37 |
38 | export default function IngressDialog(props) {
39 |
40 | // Utilize React Hooks to handle Opening & Closing Dialog Boxes
41 | const [open, setOpen] = React.useState(false);
42 |
43 | const handleClickOpen = () => {
44 | setOpen(true);
45 | };
46 | const handleClose = () => {
47 | setOpen(false);
48 | };
49 |
50 | // Deconstruct Ingress from props - Passed down from Header.js
51 | const { ingress } = props;
52 |
53 | // Initialize Array for dynamic structure of component - Each path in Ingress will have its own row.
54 | const pathsArr = [];
55 |
56 | // Conditional Statement to ensure Ingress exists - this will help avoid errors
57 | // If Ingress exist, we iterate over the paths key within ingress. This is an array which will hold each specific path.
58 |
59 | if (ingress){
60 | ingress.paths.forEach((path, ind) => {
61 | let style = customStyle
62 | pathsArr.push(
63 | <>
64 |
65 |
66 | Path:
67 |
68 | {path.path}
69 |
70 |
71 |
72 | Port:
73 |
74 | {path.servicePort}
75 |
76 |
77 |
78 | Service Name:
79 |
80 | {path.serviceName}
81 |
82 |
83 |
84 | Path Type:
85 |
86 | {path.pathType}
87 |
88 |
89 |
90 |
91 |
92 |
93 | >
94 | )
95 | })
96 | }
97 |
98 | // Return Button that will open dialog box
99 |
100 | return (
101 |
102 |
103 | Ingress
104 |
105 |
111 |
112 | Ingress Information
113 |
114 |
115 |
116 |
117 |
118 |
119 | Key
120 | Value
121 |
122 |
123 |
124 | {pathsArr}
125 |
126 |
127 |
128 |
129 |
130 |
131 | Close
132 |
133 |
134 |
135 |
136 | );
137 | }
--------------------------------------------------------------------------------
/client/components/Header/Header.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module Header.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Main header Component for home page
8 | *
9 | * ************************************
10 | */
11 |
12 | import React from 'react';
13 | import Button from '@material-ui/core/Button';
14 | import { connect } from 'react-redux';
15 | import './Header.style.css'
16 | import { makeStyles } from '@material-ui/core/styles';
17 | import InputLabel from '@material-ui/core/InputLabel';
18 | import MenuItem from '@material-ui/core/MenuItem';
19 | import FormControl from '@material-ui/core/FormControl';
20 | import Select from '@material-ui/core/Select';
21 | import * as actions from '../../actions/actions';
22 | import IngressDialog from '../../Dialog/IngressDialog';
23 |
24 | // Map current state to props
25 |
26 | const mapStateToProps = state => ({
27 | nodes: state.nodes.nodes,
28 | currentNode: state.nodes.currentNode,
29 | namespaces: state.namespaces.namespaces,
30 | currentNamespace: state.namespaces.currentNamespace,
31 | ingresses: state.ingresses.ingresses
32 | });
33 |
34 | // Map dispatch to props - this allows us to update state of deployments, pods, services and ingresses based off namespace
35 | const mapDispatchToProps = dispatch => ({
36 |
37 | fetchNodes: async () => {
38 | let nodes = await actions.fetchNodes();
39 | dispatch(actions.getNodes(nodes));
40 | },
41 |
42 | changeNode: async (node) => {
43 | dispatch(actions.changeNode(node))
44 | },
45 |
46 | changeNamespace: async (namespace) => {
47 |
48 | let newPods = await actions.fetchCustomPods(namespace);
49 | let newServices = await actions.fetchCustomServices(namespace);
50 | let newDeployments = await actions.fetchCustomDeployments(namespace);
51 | dispatch(actions.changeNamespace(namespace));
52 | dispatch(actions.getPods(newPods));
53 | dispatch(actions.getServices(newServices));
54 | dispatch(actions.getDeployments(newDeployments))
55 | }
56 | });
57 |
58 |
59 | const useStyles = makeStyles((theme) => ({
60 | formControl: {
61 | margin: theme.spacing(1),
62 | minWidth: 240,
63 | color: 'white'
64 | },
65 | selectEmpty: {
66 | marginTop: theme.spacing(2),
67 | color: 'white'
68 | },
69 | }));
70 |
71 |
72 | const Header = props => {
73 | const classes = useStyles();
74 |
75 | // handle change and send to dispatch for Node select menu
76 |
77 | const handleChangeNode = (event) => {
78 | props.changeNode(event.target.value);
79 | };
80 |
81 | // handle change and send to dispatch for namespace select menut
82 |
83 | const handleChangeNamespace = (event) => {
84 | props.changeNamespace(event.target.value);
85 | };
86 |
87 | // These arrays will hold all current node and namespace selections
88 | const nodeSelect = [];
89 | const namespaceSelect = [];
90 |
91 | // Push default namespace if none else exist.
92 | if (!props.namespaces.length) {
93 | namespaceSelect.push({props.currentNamespace.name} )
94 | }
95 |
96 | // Push each possible Node into selecion menu
97 | props.nodes.forEach((node, ind) => {
98 | nodeSelect.push({node.name} )
99 | })
100 |
101 | // Push each possible namespace into selection menu
102 | props.namespaces.forEach((namespace, ind) => {
103 | namespaceSelect.push({namespace.name} )
104 | })
105 |
106 |
107 | return (
108 |
109 |
110 | Current Node
111 |
119 | {nodeSelect}
120 |
121 |
122 |
123 |
124 | Current Namespace
125 |
132 | {namespaceSelect}
133 |
134 |
135 |
136 |
137 |
138 | );
139 | }
140 |
141 | export default connect(mapStateToProps, mapDispatchToProps)(Header);
--------------------------------------------------------------------------------
/manifests/prometheus-ingress.yaml:
--------------------------------------------------------------------------------
1 | ## Nginx Ingress
2 | ## Follow https://devopscube.com/setup-ingress-kubernetes-nginx-controller/
3 |
4 | apiVersion: extensions/v1beta1
5 | kind: Ingress
6 | metadata:
7 | name: prometheus-ui
8 | namespace: monitoring
9 | annotations:
10 | kubernetes.io/ingress.class: nginx
11 | spec:
12 | rules:
13 | # Use the host you used in your kubernetes Ingress Configurations
14 | - host: prometheus.example.com
15 | http:
16 | paths:
17 | - backend:
18 | serviceName: prometheus-service
19 | servicePort: 8080
20 | tls:
21 | - hosts:
22 | - prometheus.apps.shaker242.lab
23 | secretName: prometheus-secret
24 | ---
25 | apiVersion: v1
26 | kind: Secret
27 | metadata:
28 | name: prometheus-secret
29 | namespace: monitoring
30 | data:
31 | # USe base64 in the certs
32 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZpVENDQkhHZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCd0RFak1DRUdBMVVFQXhNYWFXNTAKWlhKdFpXUnBZWFJsTG5Ob1lXdGxjakkwTWk1c1lXSXhDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJRXdoVwphWEpuYVc1cFlURVFNQTRHQTFVRUJ4TUhRbkpwYzNSdmR6RXNNQ29HQTFVRUNoTWpVMGhCUzBWU01qUXlJRXhoCllpQkRaWEowYVdacFkyRjBaU0JCZFhSb2IzSnBkSGt4T1RBM0JnTlZCQXNUTUZOSVFVdEZVakkwTWlCTVlXSWcKU1c1MFpYSnRaV1JwWVhSbElFTmxjblJwWm1sallYUmxJRUYxZEdodmNtbDBlVEFlRncweE9URXdNVGN4TmpFMgpNekZhRncweU1URXdNVFl4TmpFMk16RmFNSUdBTVIwd0d3WURWUVFERkJRcUxtRndjSE11YzJoaGEyVnlNalF5CkxteGhZakVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnVENGWnBjbWRwYm1saE1SQXdEZ1lEVlFRSEV3ZEMKY21semRHOTNNUll3RkFZRFZRUUtFdzFUU0VGTFJWSXlORElnVEdGaU1SVXdFd1lEVlFRTEV3eE1ZV0lnVjJWaQpjMmwwWlhNd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURsRm16QVd0U09JcXZNCkpCV3Vuc0VIUmxraXozUmpSK0p1NTV0K0hCUG95YnZwVkJJeXMxZ3prby9INlkxa2Zxa1JCUzZZYVFHM2lYRFcKaDgzNlNWc3pNVUNVS3BtNXlZQXJRNzB4YlpPTXRJcjc1VEcrejFaRGJaeFUzbnh6RXdHdDN3U3c5OVJ0bjhWbgo5dEpTVXI0MHBHUytNemMzcnZOUFZRMjJoYTlhQTdGL2NVcGxtZUpkUnZEVnJ3Q012UklEcndXVEZjZkU3bUtxCjFSUkRxVDhETnlydlJmeUlubytmSkUxTmRuVEVMY0dTYVZlajhZVVFONHY0WFRnLzJncmxIN1pFT1VXNy9oYm8KUXh6NVllejVSam1wOWVPVUpvdVdmWk5FNEJBbGRZeVYxd2NPRXhRTmswck5BOU45ZXBjNWtUVVZQR3pOTWRucgovVXQxOWMweEFnTUJBQUdqZ2dIS01JSUJ4akFKQmdOVkhSTUVBakFBTUJFR0NXQ0dTQUdHK0VJQkFRUUVBd0lHClFEQUxCZ05WSFE4RUJBTUNCYUF3TXdZSllJWklBWWI0UWdFTkJDWVdKRTl3Wlc1VFUwd2dSMlZ1WlhKaGRHVmsKSUZObGNuWmxjaUJEWlhKMGFXWnBZMkYwWlRBZEJnTlZIUTRFRmdRVWRhYy94MTR6dXl3RVZPSi9vTjdQeU82bApDZ2N3Z2RzR0ExVWRJd1NCMHpDQjBJQVVzZFM1WWxuWEpWTk5mRVpkTEQvL2RyNE5mV3FoZ2JTa2diRXdnYTR4CkdUQVhCZ05WQkFNVEVHTmhMbk5vWVd0bGNqSTBNaTVzWVdJeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUkKRXdoV2FYSm5hVzVwWVRFUU1BNEdBMVVFQnhNSFFuSnBjM1J2ZHpFc01Db0dBMVVFQ2hNalUwaEJTMFZTTWpReQpJRXhoWWlCRFpYSjBhV1pwWTJGMFpTQkJkWFJvYjNKcGRIa3hNVEF2QmdOVkJBc1RLRk5JUVV0RlVqSTBNaUJNCllXSWdVbTl2ZENCRFpYSjBhV1pwWTJGMFpTQkJkWFJvYjNKcGRIbUNBUUV3SFFZRFZSMGxCQll3RkFZSUt3WUIKQlFVSEF3RUdDQ3NHQVFVRkNBSUNNRWdHQTFVZEVRUkJNRCtDRFhOb1lXdGxjakkwTWk1c1lXS0NFbUZ3Y0hNdQpjMmhoYTJWeU1qUXlMbXhoWW9JVUtpNWhjSEJ6TG5Ob1lXdGxjakkwTWk1c1lXS0hCTUNvQ3hBd0RRWUpLb1pJCmh2Y05BUUVMQlFBRGdnRUJBRzA3ZHFNdFZYdVQrckduQlN4SkVTNjNSa2pHaWd0c3ZtNTk4NSsrbjZjRW5kSDIKb2hjaGdmRUo5V0UxYUFWSDR4QlJSdVRIUFVJOFcvd3N1OFBxQ1o4NHpRQ2U2elAyeThEcmEwbjFzK2lIeHFwRAorS3BwZS91NkNLVTFEL0VWRU9MakpZd3pRYlFLSUlPL2Y1Q0JVbUpGWjBuZ1VIUEtvUDNyTXordTlBOWFvRkVrCnF3dDBadHFHcWpjMkh3Q09UOTlOVmFsZ29ISXljOElxQXJXdjNSWklraUlyaW9kSUdDMS94MVQ2dHhKcEUyRisKQzZ0Tzk0U0FVSUJwc2VORjNFbGNLNUsyTW44YVAzR3NnNFRHeElPN2Q1eUIvb3YwNGhOV2Q1S2QwWGorL1BvQgpLOU43cFQ1SVU2citLekNoeGlSdmRvZlAzV0VYN1ZkNEtLWG94K0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
33 | tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRGxGbXpBV3RTT0lxdk0KSkJXdW5zRUhSbGtpejNSalIrSnU1NXQrSEJQb3lidnBWQkl5czFnemtvL0g2WTFrZnFrUkJTNllhUUczaVhEVwpoODM2U1Zzek1VQ1VLcG01eVlBclE3MHhiWk9NdElyNzVURyt6MVpEYlp4VTNueHpFd0d0M3dTdzk5UnRuOFZuCjl0SlNVcjQwcEdTK016YzNydk5QVlEyMmhhOWFBN0YvY1VwbG1lSmRSdkRWcndDTXZSSURyd1dURmNmRTdtS3EKMVJSRHFUOEROeXJ2UmZ5SW5vK2ZKRTFOZG5URUxjR1NhVmVqOFlVUU40djRYVGcvMmdybEg3WkVPVVc3L2hibwpReHo1WWV6NVJqbXA5ZU9VSm91V2ZaTkU0QkFsZFl5VjF3Y09FeFFOazByTkE5TjllcGM1a1RVVlBHek5NZG5yCi9VdDE5YzB4QWdNQkFBRUNnZ0VCQU5zOHRjRDBiQnpHZzRFdk8yek0wMUJoKzZYN3daZk4wSjV3bW5kNjZYYkwKc1VEZ1N6WW9PbzNJZ2o5QWZTY2lyQ3YwdUozMVNFWmNpeGRVQ2tTdjlVNnRvTzdyUWdqeUZPM1N1dm5Wc3ZKaQpTZXc5Y0hqNk5jVDczak8rWkgxQVFFZ2tlWG5mQTNZU0JEcTFsSnhpUVZOaHpHUFY0Yzh4Wi9xUkhEbUVBTWR6CmwyaTB6dHJtcWRqSng4aTQxOXpGL1pVektoa2JtcVZVb3JjZ1lNdEt5QVloSENMYms2RFZtQ1FhbDlndEUrNjUKTmFTOEwxUW9yVWNVS0FoSTNKT2Q2TTRwbWRPaExITjZpZ0VwWFdVWGxBZjRITUZicHd5M1oxejNqZzVqTE9ragp6SWNDSVRaai9CYVZvSVc4QzJUb0pieUJKWkN6UDVjUVJTdkJOOGV4aUFFQ2dZRUEvV0Nxb2xVUWtOQkQrSnlPCklXOUJIRVlPS3oxRFZxNWxHRFhoNFMyTStpOU1pck5nUlcvL0NFRGhRUVVMZmtBTDgxMERPQmxsMXRRRUpGK3cKb1V6dWt6U1lkK1hTSnhicTM5YTF1ZGJldTNZU1ljeC8wTEEweGFQOW1sN1l1NXUraUZ4NGhwcnYyL2UrVklZQQpzTWV4WkZSODA3Q3M5YXN5MkdFT1l2aEdKb0VDZ1lFQTUzVm1weFlQbDFOYTVTMElJbEpuYm40dTl0RHpwYm5TCnpsMjBVQ3Q0d0N4STR6YjY1S1o4V1VaYlFzVTVaZ0VqTmxJWURXUisrd3kwVXh2SmNxUG5nS0xuOEdoSzhvOVEKeVJuR2dSYXAxWmNuUEdsbGdCeHQzM0s5TDNWMmJzMXBPcGJKMGlpOVdySWM4MU1wUVFpQjZ1RDRSZ216M0ZWSQpnUk5Ec2ZHS0xyRUNnWUVBbWY5ZXRqc3RUbGJHZVJ2dDVyUlB4bmR0dFNvTysyZ1RXWnVtSmM0aG1RMldYOWFWCjlKNFZTMWJqa1RrWHV5d0NGMis0dlNmeWxaZFd6U1M3bmMyOFV3dnNmekxYZjVxV05tV3hIYnBTdFcwVnp3c1QKeENyVWFDczd2ODlWdXZEMTVMc1BKZ0NWT0FSalVjd0FMM0d2aDJNeVd4ZE9pQ0g5VFRYd0lJYjFYQUVDZ1lBMwp4ZUptZ0xwaERJVHFsRjlSWmVubWhpRnErQTY5OEhrTG9TakI2TGZBRnV1NVZKWkFZcDIwSlcvNE51NE4xbGhWCnpwSmRKOG94Vkc1ZldHTENiUnhyc3RXUTZKQ213a0lGTTJEUjJsUXlVNm53dExUd21la2YzdFlYaVlad1RLNysKbnpjaW5RNkR2RWVkbW54bVgxWnU4cWJndVpYTmtmOVdtdjNFOHg4SkFRS0JnUUNNeDFWNHJIcUpwVXJMdkRVVQo4RzhXVGNrT2VFM2o2anhlcHMwcnExdEd1cE9XWW5saFlNYyt5VkMzMDZUc2dXUmJ5R1Y4YWNaRkF4WS9Ub2N5CmxpcXlUS1NGNUloYXhZQVpRTzVkOU1oTmN0bTRReDNaOUtTekZ5ZG01QlZVL0grMFFmUnRwM29TeFVneXRZNXkKV3ZDTFZ5bmNGZlZpL0VkaTdaZHM2aW82QVE9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==
34 |
--------------------------------------------------------------------------------
/client/Dialog/ServiceDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module ServiceDialog.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Dialog Box for Service Element
8 | *
9 | * ************************************
10 | */
11 | import * as React from 'react';
12 | import Button from '@material-ui/core/Button';
13 | import { } from '@material-ui/core/styles';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import DialogTitle from '@material-ui/core/DialogTitle';
16 | import DialogContent from '@material-ui/core/DialogContent';
17 | import DialogActions from '@material-ui/core/DialogActions';
18 | import Table from '@material-ui/core/Table';
19 | import TableBody from '@material-ui/core/TableBody';
20 | import TableCell from '@material-ui/core/TableCell';
21 | import TableContainer from '@material-ui/core/TableContainer';
22 | import TableHead from '@material-ui/core/TableHead';
23 | import TableRow from '@material-ui/core/TableRow';
24 | import Paper from '@material-ui/core/Paper';
25 |
26 | const customStyle = {
27 | color: 'white',
28 | margin: '5px',
29 | padding: '5px',
30 | }
31 |
32 |
33 | export default function ServiceDialog(props) {
34 |
35 | // Utilize React Hooks to handle opening and closing of dialog box
36 |
37 | const [open, setOpen] = React.useState(false);
38 |
39 | const handleClickOpen = () => {
40 | setOpen(true);
41 | };
42 | const handleClose = () => {
43 | setOpen(false);
44 | };
45 |
46 | // Deconstruct service passed down from Service Element
47 | const { service } = props;
48 |
49 | // Initialize ports Array that will contain all Table Rows & Cells for each port contained in the service
50 | const portsArr = [];
51 |
52 | service.allData.spec.ports.forEach((port, ind) => {
53 | portsArr.push(
54 | <>
55 |
56 |
57 | P{ind + 1} Port Name:
58 |
59 | {port.name}
60 |
61 |
62 |
63 | P{ind + 1} Port:
64 |
65 | {port.port}
66 |
67 |
68 |
69 | P{ind + 1} Protocol:
70 |
71 | {port.protocol}
72 |
73 | >
74 | )
75 | })
76 |
77 | return (
78 |
79 |
80 | More Info
81 |
82 |
88 |
89 | Service Info
90 |
91 |
92 |
93 |
94 |
95 |
96 | Key
97 | Value
98 |
99 |
100 |
101 |
102 |
103 | Service Name:
104 |
105 | {service.name}
106 |
107 |
108 |
109 | Type:
110 |
111 | {service.type}
112 |
113 |
114 |
115 | UID:
116 |
117 | {service.id}
118 |
119 |
120 |
121 | Created:
122 |
123 | {service.created}
124 |
125 |
126 |
127 | Cluster IP:
128 |
129 | {service.allData.spec.clusterIP}
130 |
131 | {portsArr}
132 |
133 |
134 |
135 |
136 |
137 |
138 | Close
139 |
140 |
141 |
142 |
143 | );
144 | }
--------------------------------------------------------------------------------
/client/Dialog/DeploymentDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module DeploymentDialog.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Dialog Box for Deployment Elements
8 | *
9 | * ************************************
10 | */
11 | import * as React from 'react';
12 | import Button from '@material-ui/core/Button';
13 | import { } from '@material-ui/core/styles';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import DialogTitle from '@material-ui/core/DialogTitle';
16 | import DialogContent from '@material-ui/core/DialogContent';
17 | import DialogActions from '@material-ui/core/DialogActions';
18 | import Typography from '@material-ui/core/Typography';
19 | import Table from '@material-ui/core/Table';
20 | import TableBody from '@material-ui/core/TableBody';
21 | import TableCell from '@material-ui/core/TableCell';
22 | import TableContainer from '@material-ui/core/TableContainer';
23 | import TableHead from '@material-ui/core/TableHead';
24 | import TableRow from '@material-ui/core/TableRow';
25 | import Paper from '@material-ui/core/Paper';
26 |
27 | // CSS styles we can apply to elements
28 |
29 | const customStyle = {
30 | color: 'white',
31 | margin: '5px',
32 | padding: '5px',
33 | }
34 |
35 | const customStyle2 = {
36 | color: 'rgb(0, 255, 0)',
37 | margin: '5px',
38 | padding: '5px',
39 | }
40 |
41 |
42 | export default function DeploymentDialog(props) {
43 |
44 | // Utilize React Hooks to handle Opening & Closing Dialog Boxes
45 |
46 | const [open, setOpen] = React.useState(false);
47 |
48 | const handleClickOpen = () => {
49 | setOpen(true);
50 | };
51 | const handleClose = () => {
52 | setOpen(false);
53 | };
54 |
55 | // Deconstruct Deployment from props - This will be passed down from Deployment Element
56 |
57 | const { deployment } = props;
58 |
59 | return (
60 |
61 |
62 | More Info
63 |
64 |
70 |
71 | Deployment Info
72 |
73 |
74 |
75 |
76 |
77 |
78 | Key
79 | Value
80 |
81 |
82 |
83 |
84 |
85 | Deployment Name:
86 |
87 | {deployment.name}
88 |
89 |
90 |
91 | Replicas:
92 |
93 | {deployment.replicas}
94 |
95 |
96 |
97 | UID:
98 |
99 | {deployment.uid}
100 |
101 |
102 |
103 | Created:
104 |
105 | {deployment.created}
106 |
107 |
108 |
109 | Strategy Type:
110 |
111 | {deployment.strategy.type}
112 |
113 |
114 |
115 | DNS Policy:
116 |
117 | {deployment.template.spec.dnsPolicy}
118 |
119 |
120 |
121 | Restart Policy:
122 |
123 | {deployment.template.spec.restartPolicy}
124 |
125 |
126 |
127 | Scheduler Name:
128 |
129 | {deployment.template.spec.schedulerName}
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | Close
138 |
139 |
140 |
141 |
142 | );
143 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # Kubermetrics
3 |
4 | ## **What is Kubermetrics?**
5 |
6 | Kubermetrics is an open-source dev tool that provides Kubernetes cluster monitoring as well as data visualization in a simple and easy to understand user interface. Kubermetrics intergrates both the Prometheus and Grafana Dashboards on one page! Allowing for custominzable dashboards and alerts.
7 |
8 | - Kubermetrics Dockerhub: https://hub.docker.com/r/kubermetrics/kubermetrics
9 | - Kubermetrics Github: https://github.com/oslabs-beta/kubermetrics
10 |
11 | # Home page
12 |
13 | Dashboard displaying all nodes, deployments, pods, services & ingresses by namespace.
14 |
15 |
16 |
17 | # Metrics page
18 |
19 | Our Metrics Page utilizes full Grafana integration for customizable dashboards.
20 |
21 |
22 |
23 | # Alerts page
24 |
25 | Our Alerts Page utilizes full Prometheus integration for access to alerts, graphs, prom-queries and more!
26 |
27 |
28 |
29 |
30 |
31 |
32 | # Kubermetrics Setup
33 |
34 | In this readme we will walk you through the setup process for our app. For this setup you will need the following:
35 |
36 | - Start by cloning our repo down to your local machine.
37 | - This app assumes you have direct access to your K8s Cluster from the local machine in which this app is being installed
38 | - Kubectl - Kubernetes CLI installed
39 | - Ports 3000, 3068, & 9090 open. (This can be changed if necessary)
40 | - For the next steps, please refer to first section if you have prometheus and grafana installed or scroll down for full installation.
41 |
42 |
43 |
44 | # I Already Have Prometheus & Grafana Installed!
45 |
46 | ### Great! Here are a couple of notes on how to deploy our app and how it interacts with Prometheus & Grafana.
47 | - First, to install our app, in your terminal navigate to our "manifests folder"
48 | - From this directory run the following command
49 | - ```kubectl apply -f kubermetrics-depl.yaml```
50 |
51 | ### You've just installed our app on your cluster! Next you will want to open a port to access Kubermetrics from your local host.
52 | - Next, run the following command to access all your current running pods
53 | - ```kubectl get pods```
54 |
55 | 
56 |
57 | - Find your current running Pod for Kubermetrics and run the following command
58 | - ```kubectl port-forward 3068:3068```
59 | - This will allow you to access our dashboard at localhost:3068
60 | - Our app looks for grafana at localhost:3000 & promethues at local host 9090
61 | - Please run the following commands to ensure grafana and prometheus are up and running at these ports!
62 | - ```kubectl port-forward --namespace= 3000:3000```
63 | - ```kubectl port-forward --namespace= 9090:9090```
64 | - Now Navigate to localhost:3068 & enjoy our dashboard with full promethus and grafana integration!
65 |
66 |
67 |
68 | # Prometheus & Grafana Not Currently Installed
69 | ### Don't have prometheus or grafana installed? Don't worry! The process is fast and easy.
70 | - Open a terminal and navigate to the Kubermetrics root directory.
71 | - Run the following command:
72 | ```kubectl create namespace monitoring```
73 | - This will create the K8s namespace monitoring in which we install Prometheus and Grafana.
74 | - Next to install Kubermetrics, Prometheus & Grafana into your cluster, run the following command:
75 | - ``` kubectl apply -f manifests```
76 | ### Great now you have everything you need installed! Now to open up some ports.
77 | - Next Please run the following commands
78 | - ```kubectl get pods```
79 |
80 | 
81 |
82 | - Find your current running Pod for Kubermetrics and run the following command
83 | - ```kubectl port-forward 3068:3068```
84 | - This will allow you to access our dashboard at localhost:3068
85 | - Our app looks for grafana at localhost:3000 & promethues at local host 9090
86 | - Please run the following commands to ensure grafana and prometheus are up and running at these ports!
87 | - ```kubectl port-forward --namespace=monitoring 3000:3000```
88 | - ```kubectl port-forward --namespace=monitoring 9090:9090```
89 | - Now Navigate to localhost:3068 & enjoy our dashboard with full promethus and grafana integration!
90 |
91 |
92 | # Notes
93 |
94 | - Our Included Grafana yaml file includes environment variables that we use in order to load specific settings when using Grafana. Feel free to poke around or change other settings in this file and reapply!
95 | - Current App is in Beta. Many more features are planned to be added!
96 | - Feel free to visit our github page @ https://github.com/oslabs-beta/kubermetrics if you have any issues!
97 |
98 | # About the Team
99 |
100 | ## Ahad Rajput
101 | - LinkedIn: https://www.linkedin.com/in/arajput96/
102 | - Github: https://github.com/arajput96
103 |
104 | ## Dominic DiSalvo
105 |
106 | - LinkedIn: https://www.linkedin.com/in/dominicdisalvo/
107 | - Github: https://github.com/dominicd17
108 |
109 | ## Justin Buckner
110 |
111 | - LinkedIn: https://www.linkedin.com/in/jbuild/
112 | - Github: https://github.com/JWadeOn
--------------------------------------------------------------------------------
/client/Dialog/PodsDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module PodsDialog.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Dialog Box for Pod Element
8 | *
9 | * ************************************
10 | */
11 | import * as React from 'react';
12 | import Button from '@material-ui/core/Button';
13 | import { } from '@material-ui/core/styles';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import DialogTitle from '@material-ui/core/DialogTitle';
16 | import DialogContent from '@material-ui/core/DialogContent';
17 | import DialogActions from '@material-ui/core/DialogActions';
18 | import Table from '@material-ui/core/Table';
19 | import TableBody from '@material-ui/core/TableBody';
20 | import TableCell from '@material-ui/core/TableCell';
21 | import TableContainer from '@material-ui/core/TableContainer';
22 | import TableHead from '@material-ui/core/TableHead';
23 | import TableRow from '@material-ui/core/TableRow';
24 | import Paper from '@material-ui/core/Paper';
25 |
26 | const customStyle = {
27 | color: 'white',
28 | margin: '5px',
29 | padding: '5px',
30 | }
31 |
32 | export default function PodDialog(props) {
33 |
34 | // Utilize React Hooks to handle opening and closing of dialog box
35 | const [open, setOpen] = React.useState(false);
36 |
37 | const handleClickOpen = () => {
38 | setOpen(true);
39 | };
40 | const handleClose = () => {
41 | setOpen(false);
42 | };
43 |
44 | // Deconstruct pod passed down from Pod Element
45 | const { pod } = props;
46 | // Initialize container array to hold all Table Rows for each container
47 | const containerArr = [];
48 |
49 | if (pod.containers){
50 | pod.containers.forEach((container, ind) => {
51 | containerArr.push(
52 |
53 |
54 | Container {ind + 1}:
55 |
56 | {container.image}
57 |
58 | )
59 | })
60 | }
61 |
62 |
63 |
64 | return (
65 |
66 |
67 | More Info
68 |
69 |
75 |
76 | Pod Info
77 |
78 |
79 |
80 |
81 |
82 |
83 | Key
84 | Value
85 |
86 |
87 |
88 |
89 |
90 | Pod Name:
91 |
92 | {pod.podName}
93 |
94 |
95 |
96 | Api Version:
97 |
98 | {pod.apiVersion}
99 |
100 |
101 |
102 | Node Name:
103 |
104 | {pod.nodeName}
105 |
106 |
107 |
108 | Namespace:
109 |
110 | {pod.namespace}
111 |
112 |
113 |
114 | Created:
115 |
116 | {pod.created}
117 |
118 |
119 |
120 | Service Account:
121 |
122 | {pod.serviceAccount}
123 |
124 |
125 |
126 | Service Account Name:
127 |
128 | {pod.serviceAccountName}
129 |
130 |
131 |
132 | Host IP:
133 |
134 | {pod.hostIP}
135 |
136 |
137 |
138 | Pod IP:
139 |
140 | {pod.podIP}
141 |
142 | {containerArr}
143 |
144 |
145 |
146 |
147 |
148 |
149 | Close
150 |
151 |
152 |
153 |
154 | );
155 | }
--------------------------------------------------------------------------------
/manifests/config-map.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: prometheus-server-conf
5 | labels:
6 | name: prometheus-server-conf
7 | namespace: monitoring
8 | data:
9 | prometheus.rules: |-
10 | groups:
11 | - name: devopscube demo alert
12 | rules:
13 | - alert: High Pod Memory
14 | expr: sum(container_memory_usage_bytes) > 1
15 | for: 1m
16 | labels:
17 | severity: slack
18 | annotations:
19 | summary: High Memory Usage
20 | - alert: InstanceDown
21 | expr: up{job="services"} < 1
22 | for: 5m
23 | labels:
24 | severity: page
25 | annotations:
26 | summary: "Instance {{ $labels.instance }} down"
27 | description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
28 | - alert: APIHighRequestLatency
29 | expr: api_http_request_latencies_second{quantiles="0.5"} > 1
30 | for: 10m
31 | annotations:
32 | summary: "High request latency on {{ $labels.instance }}"
33 | description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"
34 | prometheus.yml: |-
35 | global:
36 | scrape_interval: 5s
37 | evaluation_interval: 5s
38 | rule_files:
39 | - /etc/prometheus/prometheus.rules
40 | alerting:
41 | alertmanagers:
42 | - scheme: http
43 | static_configs:
44 | - targets:
45 | - "alertmanager.monitoring.svc:9093"
46 |
47 | scrape_configs:
48 | - job_name: 'node-exporter'
49 | kubernetes_sd_configs:
50 | - role: endpoints
51 | relabel_configs:
52 | - source_labels: [__meta_kubernetes_endpoints_name]
53 | regex: 'node-exporter'
54 | action: keep
55 |
56 | - job_name: 'posts'
57 | static_configs:
58 | - targets: ["docker.for.mac.host.internal:8080"]
59 |
60 | - job_name: 'kubernetes-apiservers'
61 |
62 | kubernetes_sd_configs:
63 | - role: endpoints
64 | scheme: https
65 |
66 | tls_config:
67 | ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
68 | bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
69 |
70 | relabel_configs:
71 | - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
72 | action: keep
73 | regex: default;kubernetes;https
74 |
75 | - job_name: 'kubernetes-nodes'
76 |
77 | scheme: https
78 |
79 | tls_config:
80 | ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
81 | bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
82 |
83 | kubernetes_sd_configs:
84 | - role: node
85 |
86 | relabel_configs:
87 | - action: labelmap
88 | regex: __meta_kubernetes_node_label_(.+)
89 | - target_label: __address__
90 | replacement: kubernetes.default.svc:443
91 | - source_labels: [__meta_kubernetes_node_name]
92 | regex: (.+)
93 | target_label: __metrics_path__
94 | replacement: /api/v1/nodes/${1}/proxy/metrics
95 |
96 | - job_name: 'kubernetes-pods'
97 |
98 | kubernetes_sd_configs:
99 | - role: pod
100 |
101 | relabel_configs:
102 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
103 | action: keep
104 | regex: true
105 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
106 | action: replace
107 | target_label: __metrics_path__
108 | regex: (.+)
109 | - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
110 | action: replace
111 | regex: ([^:]+)(?::\d+)?;(\d+)
112 | replacement: $1:$2
113 | target_label: __address__
114 | - action: labelmap
115 | regex: __meta_kubernetes_pod_label_(.+)
116 | - source_labels: [__meta_kubernetes_namespace]
117 | action: replace
118 | target_label: kubernetes_namespace
119 | - source_labels: [__meta_kubernetes_pod_name]
120 | action: replace
121 | target_label: kubernetes_pod_name
122 |
123 | - job_name: 'kube-state-metrics'
124 | static_configs:
125 | - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']
126 |
127 | - job_name: 'kubernetes-cadvisor'
128 |
129 | scheme: https
130 |
131 | tls_config:
132 | ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
133 | bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
134 |
135 | kubernetes_sd_configs:
136 | - role: node
137 |
138 | relabel_configs:
139 | - action: labelmap
140 | regex: __meta_kubernetes_node_label_(.+)
141 | - target_label: __address__
142 | replacement: kubernetes.default.svc:443
143 | - source_labels: [__meta_kubernetes_node_name]
144 | regex: (.+)
145 | target_label: __metrics_path__
146 | replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
147 |
148 | - job_name: 'kubernetes-service-endpoints'
149 |
150 | kubernetes_sd_configs:
151 | - role: endpoints
152 |
153 | relabel_configs:
154 | - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
155 | action: keep
156 | regex: true
157 | - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
158 | action: replace
159 | target_label: __scheme__
160 | regex: (https?)
161 | - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
162 | action: replace
163 | target_label: __metrics_path__
164 | regex: (.+)
165 | - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
166 | action: replace
167 | target_label: __address__
168 | regex: ([^:]+)(?::\d+)?;(\d+)
169 | replacement: $1:$2
170 | - action: labelmap
171 | regex: __meta_kubernetes_service_label_(.+)
172 | - source_labels: [__meta_kubernetes_namespace]
173 | action: replace
174 | target_label: kubernetes_namespace
175 | - source_labels: [__meta_kubernetes_service_name]
176 | action: replace
177 | target_label: kubernetes_name
178 |
--------------------------------------------------------------------------------
/client/Dialog/NodeDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module NodeDialog.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Dialog Box for Current Node Element
8 | *
9 | * ************************************
10 | */
11 | import * as React from 'react';
12 | import Button from '@material-ui/core/Button';
13 | import { } from '@material-ui/core/styles';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import DialogTitle from '@material-ui/core/DialogTitle';
16 | import DialogContent from '@material-ui/core/DialogContent';
17 | import DialogActions from '@material-ui/core/DialogActions';
18 | import Table from '@material-ui/core/Table';
19 | import TableBody from '@material-ui/core/TableBody';
20 | import TableCell from '@material-ui/core/TableCell';
21 | import TableContainer from '@material-ui/core/TableContainer';
22 | import TableHead from '@material-ui/core/TableHead';
23 | import TableRow from '@material-ui/core/TableRow';
24 | import Paper from '@material-ui/core/Paper';
25 |
26 | const customStyle = {
27 | color: 'white',
28 | margin: '5px',
29 | padding: '5px',
30 | }
31 |
32 |
33 | const customStyle2 = {
34 | color: 'rgb(0, 255, 0)',
35 | margin: '5px',
36 | padding: '5px',
37 | }
38 |
39 | export default function NodeDialog(props) {
40 |
41 | // Utiilize React Hooks to handle Opening and Closing of dialog box
42 | const [open, setOpen] = React.useState(false);
43 |
44 | const handleClickOpen = () => {
45 | setOpen(true);
46 | };
47 | const handleClose = () => {
48 | setOpen(false);
49 | };
50 |
51 | // Deconstruct node from props - Passed down for CurrentNode.js
52 | const { node } = props;
53 |
54 | // Initialize address array to hold all Table Rows & Cells for each address
55 | const addressArr = [];
56 |
57 | // Iterate of addresses and push a row & cell for each one
58 | if (node.addresses){
59 | node.addresses.forEach((address, ind) => {
60 | addressArr.push(
61 |
62 |
63 | {address.type}:
64 |
65 | {address.address}
66 |
67 | )
68 | })
69 | }
70 |
71 |
72 | return (
73 |
74 |
75 | More Info
76 |
77 |
83 |
84 | Current Node Info
85 |
86 |
87 |
88 |
89 |
90 |
91 | Key
92 | Value
93 |
94 |
95 |
96 |
97 |
98 | Node Name:
99 |
100 | {node.name}
101 |
102 |
103 |
104 | Hostname:
105 |
106 | {node.hostname}
107 |
108 |
109 |
110 | Operating System:
111 |
112 | {node.os}
113 |
114 |
115 |
116 | Created:
117 |
118 | {node.created}
119 |
120 |
121 |
122 | Unique ID:
123 |
124 | {node.uid}
125 |
126 |
127 | {addressArr}
128 |
129 |
130 |
131 | Allocatable CPU:
132 |
133 | {node.allocatable.cpu}
134 |
135 |
136 |
137 | Allocatable Ephemeral-Storage:
138 |
139 | {node.allocatable["ephemeral-storage"]}
140 |
141 |
142 |
143 | Allocatable Ephemeral-Storage:
144 |
145 | {node.allocatable["ephemeral-storage"]}
146 |
147 |
148 |
149 | Allocatable memory:
150 |
151 | {node.allocatable.memory}
152 |
153 |
154 |
155 | Allocatable pods:
156 |
157 | {node.allocatable.pods}
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | Close
168 |
169 |
170 |
171 |
172 | );
173 | }
--------------------------------------------------------------------------------
/client/actions/actions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************
3 | *
4 | * @module actions.js
5 | * @author team Kubermetrics
6 | * @date
7 | * @description Action functions
8 | *
9 | * ************************************
10 | */
11 |
12 |
13 | import axios from 'axios';
14 | import * as actionTypes from '../constants/actionTypes';
15 |
16 | /*
17 | All Action Functions That Go To Reducer
18 | */
19 |
20 | export const getNamespaceList = namespaceList => ({
21 | type: actionTypes.GET_NAMESPACELIST,
22 | payload: namespaceList,
23 | });
24 |
25 | export const getPods = podsList => ({
26 | type: actionTypes.GET_PODS,
27 | payload: podsList,
28 | });
29 |
30 | export const getNodes = nodesList => ({
31 | type: actionTypes.GET_NODES,
32 | payload: nodesList
33 | });
34 |
35 | export const getDeployments = deploymentList => ({
36 | type: actionTypes.GET_DEPLOYMENTS,
37 | payload: deploymentList
38 | });
39 |
40 | export const changeNode = node => ({
41 | type: actionTypes.CHANGE_NODE,
42 | payload: node
43 | })
44 | export const getServices = serviceList => ({
45 | type: actionTypes.GET_SERVICES,
46 | payload: serviceList
47 | });
48 |
49 | export const changeNamespace = namespace => ({
50 | type: actionTypes.CHANGE_NAMESPACE,
51 | payload: namespace
52 | })
53 |
54 | export const getIngress = ingressList => ({
55 | type: actionTypes.GET_INGRESS,
56 | payload: ingressList,
57 | });
58 |
59 |
60 | /*
61 | All Functions That Make API Calls To Server To Fetch Pods, Deployments, Services, Ingresses & Nodes,
62 | Once we receive the array from the server, we iterate over it and create object we will then send to reducers via
63 | the action functions.
64 | */
65 |
66 |
67 | export const fetchPods = async (url = '/podList') => {
68 |
69 | let response = await axios.get(url);
70 | let podsList = [];
71 |
72 |
73 |
74 | response.data.items.forEach((item) => {
75 | podsList.push({
76 | allData: item,
77 | apiVersion: response.data.apiVersion,
78 | nodeName: item.spec.nodeName,
79 | label: item.metadata.labels.app,
80 | podName: item.metadata.name,
81 | namespace: item.metadata.namespace,
82 | uid: item.metadata.uid,
83 | created: item.metadata.creationTimestamp,
84 | containers: item.spec.containers,
85 | serviceAccount: item.spec.serviceAccount,
86 | serviceAccountName: item.spec.serviceAccountName,
87 | hostIP: item.status.hostIP,
88 | podIP: item.status.podIP,
89 | phase: item.status.phase
90 | })
91 | })
92 |
93 | return podsList;
94 |
95 | };
96 |
97 |
98 | export const fetchIngress = async (url = '/ingressList') => {
99 |
100 | const response = await axios.get(url);
101 |
102 | const { items } = response.data;
103 | const { metadata, spec } = items[0];
104 |
105 | const ingressList = [{
106 | metadata: {
107 | class: metadata.annotations['kubernetes.io/ingress.class'],
108 | creationTime: metadata.creationTimestamp,
109 | name: metadata.name,
110 | namespace: metadata.namespace,
111 | uid: metadata.uid,
112 | },
113 | host: spec.rules[0].host,
114 | paths: spec.rules[0].http.paths.map((path) => ({
115 | pathType: path.pathType,
116 | serviceName: path.backend.serviceName,
117 | servicePort: path.backend.servicePort,
118 | path: path.path,
119 | })),
120 | fullData: items,
121 | }];
122 |
123 | return ingressList;
124 |
125 | };
126 |
127 |
128 | export const fetchNodes = async (url = '/nodeList') => {
129 |
130 | let response = await axios.get(url);
131 | let nodeList = [];
132 |
133 | response.data.items.forEach((item) => {
134 |
135 | const { metadata, status } = item;
136 | const { labels } = metadata;
137 |
138 |
139 | nodeList.push({
140 |
141 | allData: item,
142 | created: metadata.creationTimestamp,
143 | arch: labels['kubernetes.io/arch'],
144 | os: labels['kubernetes.io/os'],
145 | hostname: labels['kubernetes.io/hostname'],
146 | managedFields: metadata.managedFields,
147 | name: metadata.name,
148 | resourceVersion: metadata.resourceVersion,
149 | uid: metadata.uid,
150 | addresses: status.addresses,
151 | allocatable: status.allocatable,
152 | capacity: status.capacity,
153 | conditions: status.conditions,
154 | daemonEndpoints: status.daemonEndpoints,
155 | images: status.images,
156 | nodeInfo: status.nodeInfo,
157 |
158 | })
159 | });
160 |
161 | return nodeList;
162 |
163 | }
164 |
165 | export const fetchDeployments = async (url = '/deploymentList') => {
166 |
167 | let response = await axios.get(url);
168 | let deploymentList = [];
169 |
170 | response.data.items.forEach((item) => {
171 |
172 | const { metadata, spec, status } = item;
173 |
174 | deploymentList.push({
175 |
176 | allData: item,
177 | created: metadata.creationTimestamp,
178 | managedFields: metadata.managedFields,
179 | name: metadata.name,
180 | namespace: metadata.namespace,
181 | uid: metadata.uid,
182 | replicas: spec.replicas,
183 | selector: spec.matchLabels,
184 | strategy: spec.strategy,
185 | template: spec.template,
186 | availabeReplicas: status.availabeReplicas,
187 | conditions: status.conditions,
188 | readyReplicas: status.readyReplicas
189 |
190 | })
191 | });
192 |
193 | return deploymentList;
194 |
195 | }
196 |
197 | export const fetchServices = async (url = '/serviceList') => {
198 | let response = await axios.get(url);
199 |
200 | let servicesList = [];
201 |
202 | response.data.items.forEach((item) => {
203 |
204 | servicesList.push({
205 | allData: item,
206 | created: item.metadata.creationTimestamp,
207 | name: item.metadata.name,
208 | namespace: item.metadata.namespace,
209 | id: item.metadata.uid,
210 | manager: item.metadata.managedFields.manager,
211 | labels: item.metadata.labels,
212 | selector: item.spec.selector,
213 | type: item.spec.type
214 |
215 | })
216 |
217 | });
218 |
219 | return servicesList;
220 | }
221 |
222 |
223 | export const fetchNamespaces = async (url = '/namespaceList') => {
224 | let response = await axios.get(url);
225 |
226 | let namespaceList = [];
227 |
228 | response.data.items.forEach((item) => {
229 |
230 | namespaceList.push({
231 | allData: item,
232 | name: item.metadata.name
233 | })
234 |
235 | });
236 |
237 | return namespaceList
238 | }
239 |
240 | /*
241 | All Custom Fetch Functions that will make a post request to the server with the current namespace
242 | */
243 |
244 |
245 | export const fetchCustomPods = async (namespace, url = '/customPods') => {
246 | let response = await fetch(url, {
247 | method: 'POST',
248 | headers: { 'Content-Type': 'application/json' },
249 | body: JSON.stringify({namespace: namespace})
250 | })
251 | .then(res => res.json())
252 | .then(data => data);
253 |
254 | let podsList = [];
255 |
256 | response.items.forEach((item) => {
257 | podsList.push({
258 | allData: item,
259 | apiVersion: response.apiVersion,
260 | nodeName: item.spec.nodeName,
261 | label: item.metadata.labels.app,
262 | podName: item.metadata.name,
263 | namespace: item.metadata.namespace,
264 | uid: item.metadata.uid,
265 | created: item.metadata.creationTimestamp,
266 | containters: item.spec.containers,
267 | serviceAccount: item.spec.serviceAccount,
268 | serviceAccountName: item.spec.serviceAccountName,
269 | hostIP: item.status.hostIP,
270 | podIP: item.status.podIP,
271 | phase: item.status.phase
272 | })
273 | })
274 | return podsList;
275 | }
276 |
277 | export const fetchCustomServices = async (namespace, url = '/customServices') => {
278 | let response = await fetch(url, {
279 | method: 'POST',
280 | headers: { 'Content-Type': 'application/json' },
281 | body: JSON.stringify({namespace: namespace})
282 | })
283 | .then(res => res.json())
284 | .then(data => data);
285 |
286 | let servicesList = [];
287 |
288 | response.items.forEach((item) => {
289 |
290 | servicesList.push({
291 | allData: item,
292 | created: item.metadata.creationTimestamp,
293 | name: item.metadata.name,
294 | namespace: item.metadata.namespace,
295 | id: item.metadata.uid,
296 | manager: item.metadata.managedFields.manager,
297 | labels: item.metadata.labels,
298 | selector: item.spec.selector,
299 | type: item.spec.type
300 | })
301 | });
302 | return servicesList;
303 | }
304 |
305 | export const fetchCustomDeployments = async (namespace, url = '/customDeployments') => {
306 | let response = await fetch(url, {
307 | method: 'POST',
308 | headers: { 'Content-Type': 'application/json' },
309 | body: JSON.stringify({namespace: namespace})
310 | })
311 | .then(res => res.json())
312 | .then(data => data);
313 |
314 | let deploymentList = [];
315 |
316 | response.items.forEach((item) => {
317 |
318 | const { metadata, spec, status } = item;
319 |
320 | deploymentList.push({
321 |
322 | allData: item,
323 | created: metadata.creationTimestamp,
324 | managedFields: metadata.managedFields,
325 | name: metadata.name,
326 | namespace: metadata.namespace,
327 | uid: metadata.uid,
328 | replicas: spec.replicas,
329 | selector: spec.matchLabels,
330 | strategy: spec.strategy,
331 | template: spec.template,
332 | availabeReplicas: status.availabeReplicas,
333 | conditions: status.conditions,
334 | readyReplicas: status.readyReplicas
335 |
336 | })
337 | });
338 | return deploymentList;
339 | }
340 |
--------------------------------------------------------------------------------
/manifests/dashboards/home.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": "Prometheus",
7 | "enable": true,
8 | "hide": true,
9 | "iconColor": "rgba(0, 211, 255, 1)",
10 | "name": "Annotations & Alerts",
11 | "type": "dashboard"
12 | }
13 | ]
14 | },
15 | "description": "Monitors Kubernetes cluster using Prometheus. Shows overall cluster CPU / Memory / Filesystem usage as well as individual pod, containers, control plane (as deployed by kops) statistics. Uses cAdvisor metrics only. Tweaked from https://grafana.com/dashboards/3119 to add total pod and container count and the total nodes resource usage.",
16 | "editable": true,
17 | "gnetId": 9873,
18 | "graphTooltip": 0,
19 | "id": 4,
20 | "iteration": 1632167940003,
21 | "links": [],
22 | "panels": [
23 | {
24 | "collapsed": false,
25 | "datasource": null,
26 | "gridPos": {
27 | "h": 1,
28 | "w": 24,
29 | "x": 0,
30 | "y": 0
31 | },
32 | "id": 34,
33 | "panels": [],
34 | "repeat": null,
35 | "title": "Total Cluster Resource Usage",
36 | "type": "row"
37 | },
38 | {
39 | "cacheTimeout": null,
40 | "datasource": "Prometheus",
41 | "fieldConfig": {
42 | "defaults": {
43 | "color": {
44 | "mode": "thresholds"
45 | },
46 | "decimals": 2,
47 | "mappings": [
48 | {
49 | "options": {
50 | "match": "null",
51 | "result": {
52 | "text": "N/A"
53 | }
54 | },
55 | "type": "special"
56 | }
57 | ],
58 | "max": 100,
59 | "min": 0,
60 | "thresholds": {
61 | "mode": "absolute",
62 | "steps": [
63 | {
64 | "color": "rgba(50, 172, 45, 0.97)",
65 | "value": null
66 | },
67 | {
68 | "color": "rgba(237, 129, 40, 0.89)",
69 | "value": 65
70 | },
71 | {
72 | "color": "rgba(245, 54, 54, 0.9)",
73 | "value": 90
74 | }
75 | ]
76 | },
77 | "unit": "percent"
78 | },
79 | "overrides": []
80 | },
81 | "gridPos": {
82 | "h": 5,
83 | "w": 8,
84 | "x": 0,
85 | "y": 1
86 | },
87 | "id": 6,
88 | "interval": null,
89 | "links": [],
90 | "maxDataPoints": 100,
91 | "options": {
92 | "orientation": "horizontal",
93 | "reduceOptions": {
94 | "calcs": [
95 | "lastNotNull"
96 | ],
97 | "fields": "",
98 | "values": false
99 | },
100 | "showThresholdLabels": false,
101 | "showThresholdMarkers": true,
102 | "text": {}
103 | },
104 | "pluginVersion": "8.1.4",
105 | "targets": [
106 | {
107 | "expr": "sum (rate (container_cpu_usage_seconds_total{id=\"/\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) / sum (machine_cpu_cores{kubernetes_io_hostname=~\"^$Node$\"}) * 100",
108 | "format": "time_series",
109 | "interval": "10s",
110 | "intervalFactor": 1,
111 | "legendFormat": "",
112 | "refId": "A",
113 | "step": 10
114 | }
115 | ],
116 | "title": "Cluster CPU Usage ($interval avg)",
117 | "type": "gauge"
118 | },
119 | {
120 | "cacheTimeout": null,
121 | "datasource": "Prometheus",
122 | "fieldConfig": {
123 | "defaults": {
124 | "color": {
125 | "mode": "thresholds"
126 | },
127 | "mappings": [
128 | {
129 | "options": {
130 | "match": "null",
131 | "result": {
132 | "text": "N/A"
133 | }
134 | },
135 | "type": "special"
136 | }
137 | ],
138 | "max": 100,
139 | "min": 0,
140 | "thresholds": {
141 | "mode": "absolute",
142 | "steps": [
143 | {
144 | "color": "rgba(50, 172, 45, 0.97)",
145 | "value": null
146 | },
147 | {
148 | "color": "rgba(237, 129, 40, 0.89)",
149 | "value": 65
150 | },
151 | {
152 | "color": "rgba(245, 54, 54, 0.9)",
153 | "value": 90
154 | }
155 | ]
156 | },
157 | "unit": "percent"
158 | },
159 | "overrides": []
160 | },
161 | "gridPos": {
162 | "h": 5,
163 | "w": 8,
164 | "x": 8,
165 | "y": 1
166 | },
167 | "id": 4,
168 | "interval": null,
169 | "links": [],
170 | "maxDataPoints": 100,
171 | "options": {
172 | "orientation": "horizontal",
173 | "reduceOptions": {
174 | "calcs": [
175 | "lastNotNull"
176 | ],
177 | "fields": "",
178 | "values": false
179 | },
180 | "showThresholdLabels": false,
181 | "showThresholdMarkers": true,
182 | "text": {}
183 | },
184 | "pluginVersion": "8.1.4",
185 | "targets": [
186 | {
187 | "expr": "sum (container_memory_working_set_bytes{id=\"/\",kubernetes_io_hostname=~\"^$Node$\"}) / sum (machine_memory_bytes{kubernetes_io_hostname=~\"^$Node$\"}) * 100",
188 | "format": "time_series",
189 | "interval": "10s",
190 | "intervalFactor": 1,
191 | "refId": "A",
192 | "step": 10
193 | }
194 | ],
195 | "title": "Cluster Memory Usage",
196 | "type": "gauge"
197 | },
198 | {
199 | "cacheTimeout": null,
200 | "datasource": "Prometheus",
201 | "fieldConfig": {
202 | "defaults": {
203 | "color": {
204 | "mode": "thresholds"
205 | },
206 | "decimals": 2,
207 | "mappings": [
208 | {
209 | "options": {
210 | "match": "null",
211 | "result": {
212 | "text": "N/A"
213 | }
214 | },
215 | "type": "special"
216 | }
217 | ],
218 | "max": 100,
219 | "min": 0,
220 | "thresholds": {
221 | "mode": "absolute",
222 | "steps": [
223 | {
224 | "color": "rgba(50, 172, 45, 0.97)",
225 | "value": null
226 | },
227 | {
228 | "color": "rgba(237, 129, 40, 0.89)",
229 | "value": 65
230 | },
231 | {
232 | "color": "rgba(245, 54, 54, 0.9)",
233 | "value": 90
234 | }
235 | ]
236 | },
237 | "unit": "percent"
238 | },
239 | "overrides": []
240 | },
241 | "gridPos": {
242 | "h": 5,
243 | "w": 8,
244 | "x": 16,
245 | "y": 1
246 | },
247 | "id": 7,
248 | "interval": null,
249 | "links": [],
250 | "maxDataPoints": 100,
251 | "options": {
252 | "orientation": "horizontal",
253 | "reduceOptions": {
254 | "calcs": [
255 | "lastNotNull"
256 | ],
257 | "fields": "",
258 | "values": false
259 | },
260 | "showThresholdLabels": false,
261 | "showThresholdMarkers": true,
262 | "text": {}
263 | },
264 | "pluginVersion": "8.1.4",
265 | "targets": [
266 | {
267 | "expr": "sum (container_fs_usage_bytes{device=~\"^/dev/xvda.$\",id=\"/\",kubernetes_io_hostname=~\"^$Node$\"}) / sum (container_fs_limit_bytes{device=~\"^/dev/xvda.$\",id=\"/\",kubernetes_io_hostname=~\"^$Node$\"}) * 100",
268 | "format": "time_series",
269 | "hide": false,
270 | "interval": "10s",
271 | "intervalFactor": 1,
272 | "legendFormat": "",
273 | "metric": "",
274 | "refId": "A",
275 | "step": 10
276 | }
277 | ],
278 | "title": "Cluster Filesystem Usage",
279 | "type": "gauge"
280 | },
281 | {
282 | "cacheTimeout": null,
283 | "datasource": "Prometheus",
284 | "fieldConfig": {
285 | "defaults": {
286 | "color": {
287 | "mode": "thresholds"
288 | },
289 | "decimals": 2,
290 | "mappings": [
291 | {
292 | "options": {
293 | "match": "null",
294 | "result": {
295 | "text": "N/A"
296 | }
297 | },
298 | "type": "special"
299 | }
300 | ],
301 | "thresholds": {
302 | "mode": "absolute",
303 | "steps": [
304 | {
305 | "color": "green",
306 | "value": null
307 | },
308 | {
309 | "color": "red",
310 | "value": 80
311 | }
312 | ]
313 | },
314 | "unit": "bytes"
315 | },
316 | "overrides": []
317 | },
318 | "gridPos": {
319 | "h": 3,
320 | "w": 4,
321 | "x": 0,
322 | "y": 6
323 | },
324 | "id": 9,
325 | "interval": null,
326 | "links": [],
327 | "maxDataPoints": 100,
328 | "options": {
329 | "colorMode": "none",
330 | "graphMode": "none",
331 | "justifyMode": "auto",
332 | "orientation": "horizontal",
333 | "reduceOptions": {
334 | "calcs": [
335 | "lastNotNull"
336 | ],
337 | "fields": "",
338 | "values": false
339 | },
340 | "text": {},
341 | "textMode": "auto"
342 | },
343 | "pluginVersion": "8.1.4",
344 | "targets": [
345 | {
346 | "expr": "sum (container_memory_working_set_bytes{id=\"/\",kubernetes_io_hostname=~\"^$Node$\"})",
347 | "format": "time_series",
348 | "interval": "10s",
349 | "intervalFactor": 1,
350 | "refId": "A",
351 | "step": 10
352 | }
353 | ],
354 | "title": "Used",
355 | "type": "stat"
356 | },
357 | {
358 | "cacheTimeout": null,
359 | "datasource": "Prometheus",
360 | "fieldConfig": {
361 | "defaults": {
362 | "color": {
363 | "mode": "thresholds"
364 | },
365 | "decimals": 2,
366 | "mappings": [
367 | {
368 | "options": {
369 | "match": "null",
370 | "result": {
371 | "text": "N/A"
372 | }
373 | },
374 | "type": "special"
375 | }
376 | ],
377 | "thresholds": {
378 | "mode": "absolute",
379 | "steps": [
380 | {
381 | "color": "green",
382 | "value": null
383 | },
384 | {
385 | "color": "red",
386 | "value": 80
387 | }
388 | ]
389 | },
390 | "unit": "bytes"
391 | },
392 | "overrides": []
393 | },
394 | "gridPos": {
395 | "h": 3,
396 | "w": 4,
397 | "x": 4,
398 | "y": 6
399 | },
400 | "id": 10,
401 | "interval": null,
402 | "links": [],
403 | "maxDataPoints": 100,
404 | "options": {
405 | "colorMode": "none",
406 | "graphMode": "none",
407 | "justifyMode": "auto",
408 | "orientation": "horizontal",
409 | "reduceOptions": {
410 | "calcs": [
411 | "lastNotNull"
412 | ],
413 | "fields": "",
414 | "values": false
415 | },
416 | "text": {},
417 | "textMode": "auto"
418 | },
419 | "pluginVersion": "8.1.4",
420 | "targets": [
421 | {
422 | "expr": "sum (machine_memory_bytes{kubernetes_io_hostname=~\"^$Node$\"})",
423 | "format": "time_series",
424 | "interval": "10s",
425 | "intervalFactor": 1,
426 | "refId": "A",
427 | "step": 10
428 | }
429 | ],
430 | "title": "Total",
431 | "type": "stat"
432 | },
433 | {
434 | "cacheTimeout": null,
435 | "datasource": "Prometheus",
436 | "fieldConfig": {
437 | "defaults": {
438 | "color": {
439 | "mode": "thresholds"
440 | },
441 | "decimals": 2,
442 | "mappings": [
443 | {
444 | "options": {
445 | "match": "null",
446 | "result": {
447 | "text": "N/A"
448 | }
449 | },
450 | "type": "special"
451 | }
452 | ],
453 | "thresholds": {
454 | "mode": "absolute",
455 | "steps": [
456 | {
457 | "color": "green",
458 | "value": null
459 | },
460 | {
461 | "color": "red",
462 | "value": 80
463 | }
464 | ]
465 | },
466 | "unit": "none"
467 | },
468 | "overrides": []
469 | },
470 | "gridPos": {
471 | "h": 3,
472 | "w": 4,
473 | "x": 8,
474 | "y": 6
475 | },
476 | "id": 11,
477 | "interval": null,
478 | "links": [],
479 | "maxDataPoints": 100,
480 | "options": {
481 | "colorMode": "none",
482 | "graphMode": "none",
483 | "justifyMode": "auto",
484 | "orientation": "horizontal",
485 | "reduceOptions": {
486 | "calcs": [
487 | "lastNotNull"
488 | ],
489 | "fields": "",
490 | "values": false
491 | },
492 | "text": {},
493 | "textMode": "auto"
494 | },
495 | "pluginVersion": "8.1.4",
496 | "targets": [
497 | {
498 | "expr": "sum (rate (container_cpu_usage_seconds_total{id=\"/\",kubernetes_io_hostname=~\"^$Node$\"}[$interval]))",
499 | "format": "time_series",
500 | "interval": "10s",
501 | "intervalFactor": 1,
502 | "legendFormat": "",
503 | "refId": "A",
504 | "step": 10
505 | }
506 | ],
507 | "title": "Used",
508 | "type": "stat"
509 | },
510 | {
511 | "cacheTimeout": null,
512 | "datasource": "Prometheus",
513 | "fieldConfig": {
514 | "defaults": {
515 | "color": {
516 | "mode": "thresholds"
517 | },
518 | "decimals": 2,
519 | "mappings": [
520 | {
521 | "options": {
522 | "match": "null",
523 | "result": {
524 | "text": "N/A"
525 | }
526 | },
527 | "type": "special"
528 | }
529 | ],
530 | "thresholds": {
531 | "mode": "absolute",
532 | "steps": [
533 | {
534 | "color": "green",
535 | "value": null
536 | },
537 | {
538 | "color": "red",
539 | "value": 80
540 | }
541 | ]
542 | },
543 | "unit": "none"
544 | },
545 | "overrides": []
546 | },
547 | "gridPos": {
548 | "h": 3,
549 | "w": 4,
550 | "x": 12,
551 | "y": 6
552 | },
553 | "id": 12,
554 | "interval": null,
555 | "links": [],
556 | "maxDataPoints": 100,
557 | "options": {
558 | "colorMode": "none",
559 | "graphMode": "none",
560 | "justifyMode": "auto",
561 | "orientation": "horizontal",
562 | "reduceOptions": {
563 | "calcs": [
564 | "lastNotNull"
565 | ],
566 | "fields": "",
567 | "values": false
568 | },
569 | "text": {},
570 | "textMode": "auto"
571 | },
572 | "pluginVersion": "8.1.4",
573 | "targets": [
574 | {
575 | "expr": "sum (machine_cpu_cores{kubernetes_io_hostname=~\"^$Node$\"})",
576 | "format": "time_series",
577 | "interval": "10s",
578 | "intervalFactor": 1,
579 | "refId": "A",
580 | "step": 10
581 | }
582 | ],
583 | "title": "Total",
584 | "type": "stat"
585 | },
586 | {
587 | "cacheTimeout": null,
588 | "datasource": "Prometheus",
589 | "fieldConfig": {
590 | "defaults": {
591 | "color": {
592 | "mode": "thresholds"
593 | },
594 | "decimals": 2,
595 | "mappings": [
596 | {
597 | "options": {
598 | "match": "null",
599 | "result": {
600 | "text": "N/A"
601 | }
602 | },
603 | "type": "special"
604 | }
605 | ],
606 | "thresholds": {
607 | "mode": "absolute",
608 | "steps": [
609 | {
610 | "color": "green",
611 | "value": null
612 | },
613 | {
614 | "color": "red",
615 | "value": 80
616 | }
617 | ]
618 | },
619 | "unit": "bytes"
620 | },
621 | "overrides": []
622 | },
623 | "gridPos": {
624 | "h": 3,
625 | "w": 4,
626 | "x": 16,
627 | "y": 6
628 | },
629 | "id": 13,
630 | "interval": null,
631 | "links": [],
632 | "maxDataPoints": 100,
633 | "options": {
634 | "colorMode": "none",
635 | "graphMode": "none",
636 | "justifyMode": "auto",
637 | "orientation": "horizontal",
638 | "reduceOptions": {
639 | "calcs": [
640 | "lastNotNull"
641 | ],
642 | "fields": "",
643 | "values": false
644 | },
645 | "text": {},
646 | "textMode": "auto"
647 | },
648 | "pluginVersion": "8.1.4",
649 | "targets": [
650 | {
651 | "expr": "sum (container_fs_usage_bytes{device=~\"^/dev/xvda.$\",id=\"/\",kubernetes_io_hostname=~\"^$Node$\"})",
652 | "format": "time_series",
653 | "hide": false,
654 | "interval": "10s",
655 | "intervalFactor": 1,
656 | "legendFormat": "",
657 | "refId": "A",
658 | "step": 10
659 | }
660 | ],
661 | "title": "Used",
662 | "type": "stat"
663 | },
664 | {
665 | "cacheTimeout": null,
666 | "datasource": "Prometheus",
667 | "fieldConfig": {
668 | "defaults": {
669 | "color": {
670 | "mode": "thresholds"
671 | },
672 | "decimals": 2,
673 | "mappings": [
674 | {
675 | "options": {
676 | "match": "null",
677 | "result": {
678 | "text": "N/A"
679 | }
680 | },
681 | "type": "special"
682 | }
683 | ],
684 | "thresholds": {
685 | "mode": "absolute",
686 | "steps": [
687 | {
688 | "color": "green",
689 | "value": null
690 | },
691 | {
692 | "color": "red",
693 | "value": 80
694 | }
695 | ]
696 | },
697 | "unit": "bytes"
698 | },
699 | "overrides": []
700 | },
701 | "gridPos": {
702 | "h": 3,
703 | "w": 4,
704 | "x": 20,
705 | "y": 6
706 | },
707 | "id": 14,
708 | "interval": null,
709 | "links": [],
710 | "maxDataPoints": 100,
711 | "options": {
712 | "colorMode": "none",
713 | "graphMode": "none",
714 | "justifyMode": "auto",
715 | "orientation": "horizontal",
716 | "reduceOptions": {
717 | "calcs": [
718 | "lastNotNull"
719 | ],
720 | "fields": "",
721 | "values": false
722 | },
723 | "text": {},
724 | "textMode": "auto"
725 | },
726 | "pluginVersion": "8.1.4",
727 | "targets": [
728 | {
729 | "expr": "sum (container_fs_limit_bytes{device=~\"^/dev/xvda.$\",id=\"/\",kubernetes_io_hostname=~\"^$Node$\"})",
730 | "format": "time_series",
731 | "interval": "10s",
732 | "intervalFactor": 1,
733 | "legendFormat": "",
734 | "refId": "A",
735 | "step": 10
736 | }
737 | ],
738 | "title": "Total",
739 | "type": "stat"
740 | },
741 | {
742 | "aliasColors": {},
743 | "bars": false,
744 | "dashLength": 10,
745 | "dashes": false,
746 | "datasource": "Prometheus",
747 | "decimals": 2,
748 | "editable": true,
749 | "error": false,
750 | "fill": 1,
751 | "fillGradient": 0,
752 | "grid": {},
753 | "gridPos": {
754 | "h": 5,
755 | "w": 24,
756 | "x": 0,
757 | "y": 9
758 | },
759 | "height": "200px",
760 | "hiddenSeries": false,
761 | "id": 32,
762 | "legend": {
763 | "alignAsTable": false,
764 | "avg": true,
765 | "current": true,
766 | "max": false,
767 | "min": false,
768 | "rightSide": false,
769 | "show": false,
770 | "sideWidth": 200,
771 | "sort": "current",
772 | "sortDesc": true,
773 | "total": false,
774 | "values": true
775 | },
776 | "lines": true,
777 | "linewidth": 2,
778 | "links": [],
779 | "nullPointMode": "connected",
780 | "options": {
781 | "alertThreshold": true
782 | },
783 | "percentage": false,
784 | "pluginVersion": "8.1.4",
785 | "pointradius": 5,
786 | "points": false,
787 | "renderer": "flot",
788 | "seriesOverrides": [],
789 | "spaceLength": 10,
790 | "stack": false,
791 | "steppedLine": false,
792 | "targets": [
793 | {
794 | "expr": "sum (rate (container_network_receive_bytes_total{kubernetes_io_hostname=~\"^$Node$\"}[$interval]))",
795 | "format": "time_series",
796 | "interval": "10s",
797 | "intervalFactor": 1,
798 | "legendFormat": "Received",
799 | "metric": "network",
800 | "refId": "A",
801 | "step": 10
802 | },
803 | {
804 | "expr": "- sum (rate (container_network_transmit_bytes_total{kubernetes_io_hostname=~\"^$Node$\"}[$interval]))",
805 | "format": "time_series",
806 | "interval": "10s",
807 | "intervalFactor": 1,
808 | "legendFormat": "Sent",
809 | "metric": "network",
810 | "refId": "B",
811 | "step": 10
812 | }
813 | ],
814 | "thresholds": [],
815 | "timeFrom": null,
816 | "timeRegions": [],
817 | "timeShift": null,
818 | "title": "Cluster Network IO",
819 | "tooltip": {
820 | "msResolution": false,
821 | "shared": true,
822 | "sort": 0,
823 | "value_type": "cumulative"
824 | },
825 | "type": "graph",
826 | "xaxis": {
827 | "buckets": null,
828 | "mode": "time",
829 | "name": null,
830 | "show": true,
831 | "values": []
832 | },
833 | "yaxes": [
834 | {
835 | "format": "Bps",
836 | "label": null,
837 | "logBase": 1,
838 | "max": null,
839 | "min": null,
840 | "show": true
841 | },
842 | {
843 | "format": "Bps",
844 | "label": null,
845 | "logBase": 1,
846 | "max": null,
847 | "min": null,
848 | "show": false
849 | }
850 | ],
851 | "yaxis": {
852 | "align": false,
853 | "alignLevel": null
854 | }
855 | },
856 | {
857 | "collapsed": false,
858 | "datasource": null,
859 | "gridPos": {
860 | "h": 1,
861 | "w": 24,
862 | "x": 0,
863 | "y": 14
864 | },
865 | "id": 47,
866 | "panels": [],
867 | "repeat": null,
868 | "title": "Total Node Resource Usage",
869 | "type": "row"
870 | },
871 | {
872 | "cacheTimeout": null,
873 | "datasource": "Prometheus",
874 | "description": "",
875 | "fieldConfig": {
876 | "defaults": {
877 | "color": {
878 | "mode": "thresholds"
879 | },
880 | "decimals": 2,
881 | "mappings": [
882 | {
883 | "options": {
884 | "match": "null",
885 | "result": {
886 | "text": "N/A"
887 | }
888 | },
889 | "type": "special"
890 | }
891 | ],
892 | "thresholds": {
893 | "mode": "absolute",
894 | "steps": [
895 | {
896 | "color": "green",
897 | "value": null
898 | },
899 | {
900 | "color": "red",
901 | "value": 80
902 | }
903 | ]
904 | },
905 | "unit": "bytes"
906 | },
907 | "overrides": []
908 | },
909 | "gridPos": {
910 | "h": 3,
911 | "w": 3,
912 | "x": 0,
913 | "y": 15
914 | },
915 | "id": 61,
916 | "interval": null,
917 | "links": [],
918 | "maxDataPoints": 100,
919 | "options": {
920 | "colorMode": "none",
921 | "graphMode": "none",
922 | "justifyMode": "auto",
923 | "orientation": "horizontal",
924 | "reduceOptions": {
925 | "calcs": [
926 | "lastNotNull"
927 | ],
928 | "fields": "",
929 | "values": false
930 | },
931 | "text": {},
932 | "textMode": "auto"
933 | },
934 | "pluginVersion": "8.1.4",
935 | "targets": [
936 | {
937 | "expr": "sum(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes)",
938 | "format": "time_series",
939 | "intervalFactor": 2,
940 | "legendFormat": "",
941 | "refId": "A",
942 | "step": 60
943 | }
944 | ],
945 | "title": "Used Memory",
946 | "type": "stat"
947 | },
948 | {
949 | "cacheTimeout": null,
950 | "datasource": "Prometheus",
951 | "description": "",
952 | "fieldConfig": {
953 | "defaults": {
954 | "color": {
955 | "mode": "thresholds"
956 | },
957 | "decimals": 2,
958 | "mappings": [
959 | {
960 | "options": {
961 | "match": "null",
962 | "result": {
963 | "text": "N/A"
964 | }
965 | },
966 | "type": "special"
967 | }
968 | ],
969 | "thresholds": {
970 | "mode": "absolute",
971 | "steps": [
972 | {
973 | "color": "green",
974 | "value": null
975 | },
976 | {
977 | "color": "red",
978 | "value": 80
979 | }
980 | ]
981 | },
982 | "unit": "bytes"
983 | },
984 | "overrides": []
985 | },
986 | "gridPos": {
987 | "h": 3,
988 | "w": 3,
989 | "x": 3,
990 | "y": 15
991 | },
992 | "id": 63,
993 | "interval": null,
994 | "links": [],
995 | "maxDataPoints": 100,
996 | "options": {
997 | "colorMode": "none",
998 | "graphMode": "none",
999 | "justifyMode": "auto",
1000 | "orientation": "horizontal",
1001 | "reduceOptions": {
1002 | "calcs": [
1003 | "lastNotNull"
1004 | ],
1005 | "fields": "",
1006 | "values": false
1007 | },
1008 | "text": {},
1009 | "textMode": "auto"
1010 | },
1011 | "pluginVersion": "8.1.4",
1012 | "targets": [
1013 | {
1014 | "expr": "sum(node_memory_MemTotal_bytes)",
1015 | "format": "time_series",
1016 | "intervalFactor": 2,
1017 | "legendFormat": "",
1018 | "refId": "A",
1019 | "step": 60
1020 | }
1021 | ],
1022 | "title": "Total Memory",
1023 | "type": "stat"
1024 | },
1025 | {
1026 | "cacheTimeout": null,
1027 | "datasource": "Prometheus",
1028 | "description": "",
1029 | "fieldConfig": {
1030 | "defaults": {
1031 | "color": {
1032 | "mode": "thresholds"
1033 | },
1034 | "mappings": [
1035 | {
1036 | "options": {
1037 | "match": "null",
1038 | "result": {
1039 | "text": "N/A"
1040 | }
1041 | },
1042 | "type": "special"
1043 | }
1044 | ],
1045 | "thresholds": {
1046 | "mode": "absolute",
1047 | "steps": [
1048 | {
1049 | "color": "green",
1050 | "value": null
1051 | },
1052 | {
1053 | "color": "red",
1054 | "value": 80
1055 | }
1056 | ]
1057 | },
1058 | "unit": "none"
1059 | },
1060 | "overrides": []
1061 | },
1062 | "gridPos": {
1063 | "h": 3,
1064 | "w": 3,
1065 | "x": 6,
1066 | "y": 15
1067 | },
1068 | "id": 57,
1069 | "interval": null,
1070 | "links": [],
1071 | "maxDataPoints": 100,
1072 | "options": {
1073 | "colorMode": "none",
1074 | "graphMode": "none",
1075 | "justifyMode": "auto",
1076 | "orientation": "horizontal",
1077 | "reduceOptions": {
1078 | "calcs": [
1079 | "lastNotNull"
1080 | ],
1081 | "fields": "",
1082 | "values": false
1083 | },
1084 | "text": {},
1085 | "textMode": "auto"
1086 | },
1087 | "pluginVersion": "8.1.4",
1088 | "targets": [
1089 | {
1090 | "expr": "count(node_uname_info)",
1091 | "format": "time_series",
1092 | "hide": false,
1093 | "intervalFactor": 2,
1094 | "refId": "A",
1095 | "step": 60
1096 | }
1097 | ],
1098 | "title": "No. of Nodes",
1099 | "type": "stat"
1100 | },
1101 | {
1102 | "cacheTimeout": null,
1103 | "datasource": "Prometheus",
1104 | "fieldConfig": {
1105 | "defaults": {
1106 | "color": {
1107 | "mode": "thresholds"
1108 | },
1109 | "mappings": [
1110 | {
1111 | "options": {
1112 | "match": "null",
1113 | "result": {
1114 | "text": "N/A"
1115 | }
1116 | },
1117 | "type": "special"
1118 | }
1119 | ],
1120 | "thresholds": {
1121 | "mode": "absolute",
1122 | "steps": [
1123 | {
1124 | "color": "#299c46",
1125 | "value": null
1126 | },
1127 | {
1128 | "color": "rgba(237, 129, 40, 0.89)",
1129 | "value": 1
1130 | },
1131 | {
1132 | "color": "#d44a3a"
1133 | }
1134 | ]
1135 | },
1136 | "unit": "none"
1137 | },
1138 | "overrides": []
1139 | },
1140 | "gridPos": {
1141 | "h": 3,
1142 | "w": 3,
1143 | "x": 9,
1144 | "y": 15
1145 | },
1146 | "id": 73,
1147 | "interval": null,
1148 | "links": [],
1149 | "maxDataPoints": 100,
1150 | "options": {
1151 | "colorMode": "background",
1152 | "graphMode": "none",
1153 | "justifyMode": "auto",
1154 | "orientation": "horizontal",
1155 | "reduceOptions": {
1156 | "calcs": [
1157 | "lastNotNull"
1158 | ],
1159 | "fields": "",
1160 | "values": false
1161 | },
1162 | "text": {},
1163 | "textMode": "auto"
1164 | },
1165 | "pluginVersion": "8.1.4",
1166 | "targets": [
1167 | {
1168 | "expr": "sum(kube_node_status_condition{condition=\"OutOfDisk\", node=~\"$Node\", status=\"true\"})",
1169 | "format": "time_series",
1170 | "intervalFactor": 1,
1171 | "refId": "A"
1172 | }
1173 | ],
1174 | "title": "Nodes Out of Disk",
1175 | "type": "stat"
1176 | },
1177 | {
1178 | "cacheTimeout": null,
1179 | "datasource": "Prometheus",
1180 | "fieldConfig": {
1181 | "defaults": {
1182 | "color": {
1183 | "mode": "thresholds"
1184 | },
1185 | "mappings": [
1186 | {
1187 | "options": {
1188 | "match": "null",
1189 | "result": {
1190 | "text": "N/A"
1191 | }
1192 | },
1193 | "type": "special"
1194 | }
1195 | ],
1196 | "thresholds": {
1197 | "mode": "absolute",
1198 | "steps": [
1199 | {
1200 | "color": "#299c46",
1201 | "value": null
1202 | },
1203 | {
1204 | "color": "rgba(237, 129, 40, 0.89)",
1205 | "value": 1
1206 | },
1207 | {
1208 | "color": "#d44a3a"
1209 | }
1210 | ]
1211 | },
1212 | "unit": "none"
1213 | },
1214 | "overrides": []
1215 | },
1216 | "gridPos": {
1217 | "h": 3,
1218 | "w": 3,
1219 | "x": 12,
1220 | "y": 15
1221 | },
1222 | "id": 75,
1223 | "interval": null,
1224 | "links": [],
1225 | "maxDataPoints": 100,
1226 | "options": {
1227 | "colorMode": "background",
1228 | "graphMode": "none",
1229 | "justifyMode": "auto",
1230 | "orientation": "horizontal",
1231 | "reduceOptions": {
1232 | "calcs": [
1233 | "lastNotNull"
1234 | ],
1235 | "fields": "",
1236 | "values": false
1237 | },
1238 | "text": {},
1239 | "textMode": "auto"
1240 | },
1241 | "pluginVersion": "8.1.4",
1242 | "targets": [
1243 | {
1244 | "expr": "sum(kube_node_spec_unschedulable{node=~\"$Node\"})",
1245 | "format": "time_series",
1246 | "intervalFactor": 1,
1247 | "refId": "A"
1248 | }
1249 | ],
1250 | "title": "Nodes Unavailable",
1251 | "type": "stat"
1252 | },
1253 | {
1254 | "cacheTimeout": null,
1255 | "datasource": "Prometheus",
1256 | "description": "",
1257 | "fieldConfig": {
1258 | "defaults": {
1259 | "color": {
1260 | "mode": "thresholds"
1261 | },
1262 | "mappings": [
1263 | {
1264 | "options": {
1265 | "match": "null",
1266 | "result": {
1267 | "text": "N/A"
1268 | }
1269 | },
1270 | "type": "special"
1271 | }
1272 | ],
1273 | "thresholds": {
1274 | "mode": "absolute",
1275 | "steps": [
1276 | {
1277 | "color": "green",
1278 | "value": null
1279 | },
1280 | {
1281 | "color": "red",
1282 | "value": 80
1283 | }
1284 | ]
1285 | },
1286 | "unit": "none"
1287 | },
1288 | "overrides": []
1289 | },
1290 | "gridPos": {
1291 | "h": 3,
1292 | "w": 3,
1293 | "x": 15,
1294 | "y": 15
1295 | },
1296 | "id": 59,
1297 | "interval": null,
1298 | "links": [],
1299 | "maxDataPoints": 100,
1300 | "options": {
1301 | "colorMode": "none",
1302 | "graphMode": "none",
1303 | "justifyMode": "auto",
1304 | "orientation": "horizontal",
1305 | "reduceOptions": {
1306 | "calcs": [
1307 | "lastNotNull"
1308 | ],
1309 | "fields": "",
1310 | "values": false
1311 | },
1312 | "text": {},
1313 | "textMode": "auto"
1314 | },
1315 | "pluginVersion": "8.1.4",
1316 | "targets": [
1317 | {
1318 | "expr": "count(node_cpu_seconds_total{mode=\"user\"})",
1319 | "format": "time_series",
1320 | "intervalFactor": 2,
1321 | "legendFormat": "",
1322 | "refId": "A",
1323 | "step": 60
1324 | }
1325 | ],
1326 | "title": "Total CPU",
1327 | "type": "stat"
1328 | },
1329 | {
1330 | "cacheTimeout": null,
1331 | "datasource": "Prometheus",
1332 | "description": "",
1333 | "fieldConfig": {
1334 | "defaults": {
1335 | "color": {
1336 | "mode": "thresholds"
1337 | },
1338 | "decimals": 2,
1339 | "mappings": [
1340 | {
1341 | "options": {
1342 | "match": "null",
1343 | "result": {
1344 | "text": "N/A"
1345 | }
1346 | },
1347 | "type": "special"
1348 | }
1349 | ],
1350 | "thresholds": {
1351 | "mode": "absolute",
1352 | "steps": [
1353 | {
1354 | "color": "green",
1355 | "value": null
1356 | },
1357 | {
1358 | "color": "red",
1359 | "value": 80
1360 | }
1361 | ]
1362 | },
1363 | "unit": "bytes"
1364 | },
1365 | "overrides": []
1366 | },
1367 | "gridPos": {
1368 | "h": 3,
1369 | "w": 3,
1370 | "x": 18,
1371 | "y": 15
1372 | },
1373 | "id": 65,
1374 | "interval": null,
1375 | "links": [],
1376 | "maxDataPoints": 100,
1377 | "options": {
1378 | "colorMode": "none",
1379 | "graphMode": "none",
1380 | "justifyMode": "auto",
1381 | "orientation": "horizontal",
1382 | "reduceOptions": {
1383 | "calcs": [
1384 | "lastNotNull"
1385 | ],
1386 | "fields": "",
1387 | "values": false
1388 | },
1389 | "text": {},
1390 | "textMode": "auto"
1391 | },
1392 | "pluginVersion": "8.1.4",
1393 | "targets": [
1394 | {
1395 | "expr": "sum(node_filesystem_size_bytes) - sum(node_filesystem_avail_bytes)",
1396 | "format": "time_series",
1397 | "hide": false,
1398 | "intervalFactor": 2,
1399 | "legendFormat": "",
1400 | "refId": "A",
1401 | "step": 60
1402 | }
1403 | ],
1404 | "title": "Disk Usage",
1405 | "type": "stat"
1406 | },
1407 | {
1408 | "cacheTimeout": null,
1409 | "datasource": "Prometheus",
1410 | "description": "",
1411 | "fieldConfig": {
1412 | "defaults": {
1413 | "color": {
1414 | "mode": "thresholds"
1415 | },
1416 | "decimals": 2,
1417 | "mappings": [
1418 | {
1419 | "options": {
1420 | "match": "null",
1421 | "result": {
1422 | "text": "N/A"
1423 | }
1424 | },
1425 | "type": "special"
1426 | }
1427 | ],
1428 | "thresholds": {
1429 | "mode": "absolute",
1430 | "steps": [
1431 | {
1432 | "color": "green",
1433 | "value": null
1434 | },
1435 | {
1436 | "color": "red",
1437 | "value": 80
1438 | }
1439 | ]
1440 | },
1441 | "unit": "bytes"
1442 | },
1443 | "overrides": []
1444 | },
1445 | "gridPos": {
1446 | "h": 3,
1447 | "w": 3,
1448 | "x": 21,
1449 | "y": 15
1450 | },
1451 | "id": 49,
1452 | "interval": null,
1453 | "links": [],
1454 | "maxDataPoints": 100,
1455 | "options": {
1456 | "colorMode": "none",
1457 | "graphMode": "none",
1458 | "justifyMode": "auto",
1459 | "orientation": "horizontal",
1460 | "reduceOptions": {
1461 | "calcs": [
1462 | "lastNotNull"
1463 | ],
1464 | "fields": "",
1465 | "values": false
1466 | },
1467 | "text": {},
1468 | "textMode": "auto"
1469 | },
1470 | "pluginVersion": "8.1.4",
1471 | "targets": [
1472 | {
1473 | "expr": "sum(node_filesystem_size_bytes)",
1474 | "format": "time_series",
1475 | "hide": false,
1476 | "intervalFactor": 2,
1477 | "legendFormat": "",
1478 | "refId": "A",
1479 | "step": 60
1480 | }
1481 | ],
1482 | "title": "Disk Total",
1483 | "type": "stat"
1484 | },
1485 | {
1486 | "collapsed": false,
1487 | "datasource": null,
1488 | "gridPos": {
1489 | "h": 1,
1490 | "w": 24,
1491 | "x": 0,
1492 | "y": 18
1493 | },
1494 | "id": 39,
1495 | "panels": [],
1496 | "repeat": null,
1497 | "title": "Pods Monitoring",
1498 | "type": "row"
1499 | },
1500 | {
1501 | "aliasColors": {},
1502 | "bars": false,
1503 | "dashLength": 10,
1504 | "dashes": false,
1505 | "datasource": "Prometheus",
1506 | "decimals": 3,
1507 | "editable": true,
1508 | "error": false,
1509 | "fill": 0,
1510 | "fillGradient": 0,
1511 | "grid": {},
1512 | "gridPos": {
1513 | "h": 7,
1514 | "w": 12,
1515 | "x": 0,
1516 | "y": 19
1517 | },
1518 | "height": "",
1519 | "hiddenSeries": false,
1520 | "id": 17,
1521 | "legend": {
1522 | "alignAsTable": true,
1523 | "avg": true,
1524 | "current": true,
1525 | "max": false,
1526 | "min": false,
1527 | "rightSide": true,
1528 | "show": true,
1529 | "sort": "current",
1530 | "sortDesc": true,
1531 | "total": false,
1532 | "values": true
1533 | },
1534 | "lines": true,
1535 | "linewidth": 2,
1536 | "links": [],
1537 | "nullPointMode": "connected",
1538 | "options": {
1539 | "alertThreshold": true
1540 | },
1541 | "percentage": false,
1542 | "pluginVersion": "8.1.4",
1543 | "pointradius": 5,
1544 | "points": false,
1545 | "renderer": "flot",
1546 | "seriesOverrides": [],
1547 | "spaceLength": 10,
1548 | "stack": false,
1549 | "steppedLine": true,
1550 | "targets": [
1551 | {
1552 | "expr": "sum (rate (container_cpu_usage_seconds_total{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (pod_name)",
1553 | "format": "time_series",
1554 | "hide": false,
1555 | "interval": "10s",
1556 | "intervalFactor": 1,
1557 | "legendFormat": "{{ pod_name }}",
1558 | "metric": "container_cpu",
1559 | "refId": "A",
1560 | "step": 10
1561 | }
1562 | ],
1563 | "thresholds": [],
1564 | "timeFrom": null,
1565 | "timeRegions": [],
1566 | "timeShift": null,
1567 | "title": "Pods CPU usage ($interval avg)",
1568 | "tooltip": {
1569 | "msResolution": true,
1570 | "shared": true,
1571 | "sort": 2,
1572 | "value_type": "cumulative"
1573 | },
1574 | "type": "graph",
1575 | "xaxis": {
1576 | "buckets": null,
1577 | "mode": "time",
1578 | "name": null,
1579 | "show": true,
1580 | "values": []
1581 | },
1582 | "yaxes": [
1583 | {
1584 | "format": "none",
1585 | "label": "cores",
1586 | "logBase": 1,
1587 | "max": null,
1588 | "min": null,
1589 | "show": true
1590 | },
1591 | {
1592 | "format": "short",
1593 | "label": null,
1594 | "logBase": 1,
1595 | "max": null,
1596 | "min": null,
1597 | "show": false
1598 | }
1599 | ],
1600 | "yaxis": {
1601 | "align": false,
1602 | "alignLevel": null
1603 | }
1604 | },
1605 | {
1606 | "aliasColors": {},
1607 | "bars": false,
1608 | "dashLength": 10,
1609 | "dashes": false,
1610 | "datasource": "Prometheus",
1611 | "decimals": 2,
1612 | "editable": true,
1613 | "error": false,
1614 | "fill": 0,
1615 | "fillGradient": 0,
1616 | "grid": {},
1617 | "gridPos": {
1618 | "h": 7,
1619 | "w": 12,
1620 | "x": 12,
1621 | "y": 19
1622 | },
1623 | "hiddenSeries": false,
1624 | "id": 25,
1625 | "legend": {
1626 | "alignAsTable": true,
1627 | "avg": true,
1628 | "current": true,
1629 | "max": false,
1630 | "min": false,
1631 | "rightSide": true,
1632 | "show": true,
1633 | "sideWidth": 200,
1634 | "sort": "current",
1635 | "sortDesc": true,
1636 | "total": false,
1637 | "values": true
1638 | },
1639 | "lines": true,
1640 | "linewidth": 2,
1641 | "links": [],
1642 | "nullPointMode": "connected",
1643 | "options": {
1644 | "alertThreshold": true
1645 | },
1646 | "percentage": false,
1647 | "pluginVersion": "8.1.4",
1648 | "pointradius": 5,
1649 | "points": false,
1650 | "renderer": "flot",
1651 | "seriesOverrides": [],
1652 | "spaceLength": 10,
1653 | "stack": false,
1654 | "steppedLine": true,
1655 | "targets": [
1656 | {
1657 | "expr": "sum (container_memory_working_set_bytes{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}) by (pod_name)",
1658 | "format": "time_series",
1659 | "hide": false,
1660 | "interval": "10s",
1661 | "intervalFactor": 1,
1662 | "legendFormat": "{{ pod_name }}",
1663 | "metric": "container_memory_usage:sort_desc",
1664 | "refId": "A",
1665 | "step": 10
1666 | }
1667 | ],
1668 | "thresholds": [],
1669 | "timeFrom": null,
1670 | "timeRegions": [],
1671 | "timeShift": null,
1672 | "title": "Pods memory usage",
1673 | "tooltip": {
1674 | "msResolution": false,
1675 | "shared": true,
1676 | "sort": 2,
1677 | "value_type": "cumulative"
1678 | },
1679 | "type": "graph",
1680 | "xaxis": {
1681 | "buckets": null,
1682 | "mode": "time",
1683 | "name": null,
1684 | "show": true,
1685 | "values": []
1686 | },
1687 | "yaxes": [
1688 | {
1689 | "format": "bytes",
1690 | "label": null,
1691 | "logBase": 1,
1692 | "max": null,
1693 | "min": null,
1694 | "show": true
1695 | },
1696 | {
1697 | "format": "short",
1698 | "label": null,
1699 | "logBase": 1,
1700 | "max": null,
1701 | "min": null,
1702 | "show": false
1703 | }
1704 | ],
1705 | "yaxis": {
1706 | "align": false,
1707 | "alignLevel": null
1708 | }
1709 | },
1710 | {
1711 | "aliasColors": {},
1712 | "bars": false,
1713 | "dashLength": 10,
1714 | "dashes": false,
1715 | "datasource": "Prometheus",
1716 | "decimals": 2,
1717 | "editable": true,
1718 | "error": false,
1719 | "fill": 1,
1720 | "fillGradient": 0,
1721 | "grid": {},
1722 | "gridPos": {
1723 | "h": 7,
1724 | "w": 24,
1725 | "x": 0,
1726 | "y": 26
1727 | },
1728 | "hiddenSeries": false,
1729 | "id": 16,
1730 | "legend": {
1731 | "alignAsTable": true,
1732 | "avg": true,
1733 | "current": true,
1734 | "max": false,
1735 | "min": false,
1736 | "rightSide": true,
1737 | "show": true,
1738 | "sideWidth": 200,
1739 | "sort": "current",
1740 | "sortDesc": true,
1741 | "total": false,
1742 | "values": true
1743 | },
1744 | "lines": true,
1745 | "linewidth": 2,
1746 | "links": [],
1747 | "nullPointMode": "connected",
1748 | "options": {
1749 | "alertThreshold": true
1750 | },
1751 | "percentage": false,
1752 | "pluginVersion": "8.1.4",
1753 | "pointradius": 5,
1754 | "points": false,
1755 | "renderer": "flot",
1756 | "seriesOverrides": [],
1757 | "spaceLength": 10,
1758 | "stack": false,
1759 | "steppedLine": false,
1760 | "targets": [
1761 | {
1762 | "expr": "sum (rate (container_network_receive_bytes_total{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (pod_name)",
1763 | "format": "time_series",
1764 | "interval": "10s",
1765 | "intervalFactor": 1,
1766 | "legendFormat": "-> {{ pod_name }}",
1767 | "metric": "network",
1768 | "refId": "A",
1769 | "step": 10
1770 | },
1771 | {
1772 | "expr": "- sum (rate (container_network_transmit_bytes_total{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (pod_name)",
1773 | "format": "time_series",
1774 | "interval": "10s",
1775 | "intervalFactor": 1,
1776 | "legendFormat": "<- {{ pod_name }}",
1777 | "metric": "network",
1778 | "refId": "B",
1779 | "step": 10
1780 | }
1781 | ],
1782 | "thresholds": [],
1783 | "timeFrom": null,
1784 | "timeRegions": [],
1785 | "timeShift": null,
1786 | "title": "Pods network I/O ($interval avg)",
1787 | "tooltip": {
1788 | "msResolution": false,
1789 | "shared": true,
1790 | "sort": 2,
1791 | "value_type": "cumulative"
1792 | },
1793 | "type": "graph",
1794 | "xaxis": {
1795 | "buckets": null,
1796 | "mode": "time",
1797 | "name": null,
1798 | "show": true,
1799 | "values": []
1800 | },
1801 | "yaxes": [
1802 | {
1803 | "format": "Bps",
1804 | "label": null,
1805 | "logBase": 1,
1806 | "max": null,
1807 | "min": null,
1808 | "show": true
1809 | },
1810 | {
1811 | "format": "short",
1812 | "label": null,
1813 | "logBase": 1,
1814 | "max": null,
1815 | "min": null,
1816 | "show": false
1817 | }
1818 | ],
1819 | "yaxis": {
1820 | "align": false,
1821 | "alignLevel": null
1822 | }
1823 | },
1824 | {
1825 | "collapsed": true,
1826 | "datasource": null,
1827 | "gridPos": {
1828 | "h": 1,
1829 | "w": 24,
1830 | "x": 0,
1831 | "y": 33
1832 | },
1833 | "id": 37,
1834 | "panels": [
1835 | {
1836 | "aliasColors": {},
1837 | "bars": false,
1838 | "dashLength": 10,
1839 | "dashes": false,
1840 | "datasource": "Prometheus",
1841 | "decimals": 3,
1842 | "editable": true,
1843 | "error": false,
1844 | "fill": 0,
1845 | "grid": {},
1846 | "gridPos": {
1847 | "h": 7,
1848 | "w": 12,
1849 | "x": 0,
1850 | "y": 15
1851 | },
1852 | "height": "",
1853 | "id": 24,
1854 | "legend": {
1855 | "alignAsTable": true,
1856 | "avg": true,
1857 | "current": true,
1858 | "hideEmpty": false,
1859 | "hideZero": false,
1860 | "max": false,
1861 | "min": false,
1862 | "rightSide": true,
1863 | "show": true,
1864 | "sideWidth": null,
1865 | "sort": "current",
1866 | "sortDesc": true,
1867 | "total": false,
1868 | "values": true
1869 | },
1870 | "lines": true,
1871 | "linewidth": 2,
1872 | "links": [],
1873 | "nullPointMode": "connected",
1874 | "percentage": false,
1875 | "pointradius": 5,
1876 | "points": false,
1877 | "renderer": "flot",
1878 | "seriesOverrides": [],
1879 | "spaceLength": 10,
1880 | "stack": false,
1881 | "steppedLine": true,
1882 | "targets": [
1883 | {
1884 | "expr": "sum (rate (container_cpu_usage_seconds_total{image!=\"\",name=~\"^k8s_.*\",container_name!=\"POD\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (container_name, pod_name)",
1885 | "format": "time_series",
1886 | "hide": false,
1887 | "interval": "10s",
1888 | "intervalFactor": 1,
1889 | "legendFormat": "pod: {{ pod_name }} | {{ container_name }}",
1890 | "metric": "container_cpu",
1891 | "refId": "A",
1892 | "step": 10
1893 | },
1894 | {
1895 | "expr": "sum (rate (container_cpu_usage_seconds_total{image!=\"\",name!~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, name, image)",
1896 | "format": "time_series",
1897 | "hide": false,
1898 | "interval": "10s",
1899 | "intervalFactor": 1,
1900 | "legendFormat": "docker: {{ kubernetes_io_hostname }} | {{ image }} ({{ name }})",
1901 | "metric": "container_cpu",
1902 | "refId": "B",
1903 | "step": 10
1904 | },
1905 | {
1906 | "expr": "sum (rate (container_cpu_usage_seconds_total{rkt_container_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, rkt_container_name)",
1907 | "format": "time_series",
1908 | "interval": "10s",
1909 | "intervalFactor": 1,
1910 | "legendFormat": "rkt: {{ kubernetes_io_hostname }} | {{ rkt_container_name }}",
1911 | "metric": "container_cpu",
1912 | "refId": "C",
1913 | "step": 10
1914 | }
1915 | ],
1916 | "thresholds": [],
1917 | "timeFrom": null,
1918 | "timeRegions": [],
1919 | "timeShift": null,
1920 | "title": "Containers CPU usage ($interval avg)",
1921 | "tooltip": {
1922 | "msResolution": true,
1923 | "shared": true,
1924 | "sort": 2,
1925 | "value_type": "cumulative"
1926 | },
1927 | "type": "graph",
1928 | "xaxis": {
1929 | "buckets": null,
1930 | "mode": "time",
1931 | "name": null,
1932 | "show": true,
1933 | "values": []
1934 | },
1935 | "yaxes": [
1936 | {
1937 | "format": "none",
1938 | "label": "cores",
1939 | "logBase": 1,
1940 | "max": null,
1941 | "min": null,
1942 | "show": true
1943 | },
1944 | {
1945 | "format": "short",
1946 | "label": null,
1947 | "logBase": 1,
1948 | "max": null,
1949 | "min": null,
1950 | "show": false
1951 | }
1952 | ],
1953 | "yaxis": {
1954 | "align": false,
1955 | "alignLevel": null
1956 | }
1957 | },
1958 | {
1959 | "aliasColors": {},
1960 | "bars": false,
1961 | "dashLength": 10,
1962 | "dashes": false,
1963 | "datasource": "Prometheus",
1964 | "decimals": 2,
1965 | "editable": true,
1966 | "error": false,
1967 | "fill": 0,
1968 | "grid": {},
1969 | "gridPos": {
1970 | "h": 7,
1971 | "w": 12,
1972 | "x": 12,
1973 | "y": 15
1974 | },
1975 | "id": 27,
1976 | "legend": {
1977 | "alignAsTable": true,
1978 | "avg": true,
1979 | "current": true,
1980 | "max": false,
1981 | "min": false,
1982 | "rightSide": true,
1983 | "show": true,
1984 | "sideWidth": 200,
1985 | "sort": "current",
1986 | "sortDesc": true,
1987 | "total": false,
1988 | "values": true
1989 | },
1990 | "lines": true,
1991 | "linewidth": 2,
1992 | "links": [],
1993 | "nullPointMode": "connected",
1994 | "percentage": false,
1995 | "pointradius": 5,
1996 | "points": false,
1997 | "renderer": "flot",
1998 | "seriesOverrides": [],
1999 | "spaceLength": 10,
2000 | "stack": false,
2001 | "steppedLine": true,
2002 | "targets": [
2003 | {
2004 | "expr": "sum (container_memory_working_set_bytes{image!=\"\",name=~\"^k8s_.*\",container_name!=\"POD\",kubernetes_io_hostname=~\"^$Node$\"}) by (container_name, pod_name)",
2005 | "format": "time_series",
2006 | "interval": "10s",
2007 | "intervalFactor": 1,
2008 | "legendFormat": "pod: {{ pod_name }} | {{ container_name }}",
2009 | "metric": "container_memory_usage:sort_desc",
2010 | "refId": "A",
2011 | "step": 10
2012 | },
2013 | {
2014 | "expr": "sum (container_memory_working_set_bytes{image!=\"\",name!~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}) by (kubernetes_io_hostname, name, image)",
2015 | "format": "time_series",
2016 | "interval": "10s",
2017 | "intervalFactor": 1,
2018 | "legendFormat": "docker: {{ kubernetes_io_hostname }} | {{ image }} ({{ name }})",
2019 | "metric": "container_memory_usage:sort_desc",
2020 | "refId": "B",
2021 | "step": 10
2022 | },
2023 | {
2024 | "expr": "sum (container_memory_working_set_bytes{rkt_container_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}) by (kubernetes_io_hostname, rkt_container_name)",
2025 | "format": "time_series",
2026 | "interval": "10s",
2027 | "intervalFactor": 1,
2028 | "legendFormat": "rkt: {{ kubernetes_io_hostname }} | {{ rkt_container_name }}",
2029 | "metric": "container_memory_usage:sort_desc",
2030 | "refId": "C",
2031 | "step": 10
2032 | }
2033 | ],
2034 | "thresholds": [],
2035 | "timeFrom": null,
2036 | "timeRegions": [],
2037 | "timeShift": null,
2038 | "title": "Containers memory usage",
2039 | "tooltip": {
2040 | "msResolution": false,
2041 | "shared": true,
2042 | "sort": 2,
2043 | "value_type": "cumulative"
2044 | },
2045 | "type": "graph",
2046 | "xaxis": {
2047 | "buckets": null,
2048 | "mode": "time",
2049 | "name": null,
2050 | "show": true,
2051 | "values": []
2052 | },
2053 | "yaxes": [
2054 | {
2055 | "format": "bytes",
2056 | "label": null,
2057 | "logBase": 1,
2058 | "max": null,
2059 | "min": null,
2060 | "show": true
2061 | },
2062 | {
2063 | "format": "short",
2064 | "label": null,
2065 | "logBase": 1,
2066 | "max": null,
2067 | "min": null,
2068 | "show": false
2069 | }
2070 | ],
2071 | "yaxis": {
2072 | "align": false,
2073 | "alignLevel": null
2074 | }
2075 | },
2076 | {
2077 | "aliasColors": {},
2078 | "bars": false,
2079 | "dashLength": 10,
2080 | "dashes": false,
2081 | "datasource": "Prometheus",
2082 | "decimals": 2,
2083 | "editable": true,
2084 | "error": false,
2085 | "fill": 1,
2086 | "grid": {},
2087 | "gridPos": {
2088 | "h": 7,
2089 | "w": 24,
2090 | "x": 0,
2091 | "y": 22
2092 | },
2093 | "id": 30,
2094 | "legend": {
2095 | "alignAsTable": true,
2096 | "avg": true,
2097 | "current": true,
2098 | "max": false,
2099 | "min": false,
2100 | "rightSide": true,
2101 | "show": true,
2102 | "sideWidth": 200,
2103 | "sort": "current",
2104 | "sortDesc": true,
2105 | "total": false,
2106 | "values": true
2107 | },
2108 | "lines": true,
2109 | "linewidth": 2,
2110 | "links": [],
2111 | "nullPointMode": "connected",
2112 | "percentage": false,
2113 | "pointradius": 5,
2114 | "points": false,
2115 | "renderer": "flot",
2116 | "seriesOverrides": [],
2117 | "spaceLength": 10,
2118 | "stack": false,
2119 | "steppedLine": false,
2120 | "targets": [
2121 | {
2122 | "expr": "sum (rate (container_network_receive_bytes_total{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (container_name, pod_name)",
2123 | "format": "time_series",
2124 | "hide": false,
2125 | "interval": "10s",
2126 | "intervalFactor": 1,
2127 | "legendFormat": "-> pod: {{ pod_name }} | {{ container_name }}",
2128 | "metric": "network",
2129 | "refId": "B",
2130 | "step": 10
2131 | },
2132 | {
2133 | "expr": "- sum (rate (container_network_transmit_bytes_total{image!=\"\",name=~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (container_name, pod_name)",
2134 | "format": "time_series",
2135 | "hide": false,
2136 | "interval": "10s",
2137 | "intervalFactor": 1,
2138 | "legendFormat": "<- pod: {{ pod_name }} | {{ container_name }}",
2139 | "metric": "network",
2140 | "refId": "D",
2141 | "step": 10
2142 | },
2143 | {
2144 | "expr": "sum (rate (container_network_receive_bytes_total{image!=\"\",name!~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, name, image)",
2145 | "format": "time_series",
2146 | "hide": true,
2147 | "interval": "10s",
2148 | "intervalFactor": 1,
2149 | "legendFormat": "-> docker: {{ kubernetes_io_hostname }} | {{ image }} ({{ name }})",
2150 | "metric": "network",
2151 | "refId": "A",
2152 | "step": 10
2153 | },
2154 | {
2155 | "expr": "- sum (rate (container_network_transmit_bytes_total{image!=\"\",name!~\"^k8s_.*\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, name, image)",
2156 | "format": "time_series",
2157 | "hide": true,
2158 | "interval": "10s",
2159 | "intervalFactor": 1,
2160 | "legendFormat": "<- docker: {{ kubernetes_io_hostname }} | {{ image }} ({{ name }})",
2161 | "metric": "network",
2162 | "refId": "C",
2163 | "step": 10
2164 | },
2165 | {
2166 | "expr": "sum (rate (container_network_transmit_bytes_total{rkt_container_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, rkt_container_name)",
2167 | "format": "time_series",
2168 | "hide": true,
2169 | "interval": "10s",
2170 | "intervalFactor": 1,
2171 | "legendFormat": "-> rkt: {{ kubernetes_io_hostname }} | {{ rkt_container_name }}",
2172 | "metric": "network",
2173 | "refId": "E",
2174 | "step": 10
2175 | },
2176 | {
2177 | "expr": "- sum (rate (container_network_transmit_bytes_total{rkt_container_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (kubernetes_io_hostname, rkt_container_name)",
2178 | "format": "time_series",
2179 | "hide": true,
2180 | "interval": "10s",
2181 | "intervalFactor": 1,
2182 | "legendFormat": "<- rkt: {{ kubernetes_io_hostname }} | {{ rkt_container_name }}",
2183 | "metric": "network",
2184 | "refId": "F",
2185 | "step": 10
2186 | }
2187 | ],
2188 | "thresholds": [],
2189 | "timeFrom": null,
2190 | "timeRegions": [],
2191 | "timeShift": null,
2192 | "title": "Containers network I/O ($interval avg)",
2193 | "tooltip": {
2194 | "msResolution": false,
2195 | "shared": true,
2196 | "sort": 2,
2197 | "value_type": "cumulative"
2198 | },
2199 | "type": "graph",
2200 | "xaxis": {
2201 | "buckets": null,
2202 | "mode": "time",
2203 | "name": null,
2204 | "show": true,
2205 | "values": []
2206 | },
2207 | "yaxes": [
2208 | {
2209 | "format": "Bps",
2210 | "label": null,
2211 | "logBase": 1,
2212 | "max": null,
2213 | "min": null,
2214 | "show": true
2215 | },
2216 | {
2217 | "format": "short",
2218 | "label": null,
2219 | "logBase": 1,
2220 | "max": null,
2221 | "min": null,
2222 | "show": false
2223 | }
2224 | ],
2225 | "yaxis": {
2226 | "align": false,
2227 | "alignLevel": null
2228 | }
2229 | }
2230 | ],
2231 | "repeat": null,
2232 | "title": "Containers Monitoring",
2233 | "type": "row"
2234 | },
2235 | {
2236 | "collapsed": true,
2237 | "datasource": null,
2238 | "gridPos": {
2239 | "h": 1,
2240 | "w": 24,
2241 | "x": 0,
2242 | "y": 34
2243 | },
2244 | "id": 67,
2245 | "panels": [
2246 | {
2247 | "aliasColors": {},
2248 | "bars": true,
2249 | "dashLength": 10,
2250 | "dashes": false,
2251 | "datasource": "Prometheus",
2252 | "decimals": 0,
2253 | "fill": 1,
2254 | "gridPos": {
2255 | "h": 7,
2256 | "w": 12,
2257 | "x": 0,
2258 | "y": 14
2259 | },
2260 | "hideTimeOverride": false,
2261 | "id": 69,
2262 | "legend": {
2263 | "alignAsTable": true,
2264 | "avg": false,
2265 | "current": true,
2266 | "max": false,
2267 | "min": false,
2268 | "rightSide": true,
2269 | "show": true,
2270 | "sideWidth": null,
2271 | "sort": null,
2272 | "sortDesc": null,
2273 | "total": false,
2274 | "values": true
2275 | },
2276 | "lines": false,
2277 | "linewidth": 2,
2278 | "links": [],
2279 | "nullPointMode": "null as zero",
2280 | "percentage": false,
2281 | "pointradius": 5,
2282 | "points": false,
2283 | "renderer": "flot",
2284 | "seriesOverrides": [],
2285 | "spaceLength": 10,
2286 | "stack": false,
2287 | "steppedLine": false,
2288 | "targets": [
2289 | {
2290 | "expr": "count by (namespace) (kube_pod_info{namespace!=\"kube-system\"})",
2291 | "format": "time_series",
2292 | "hide": false,
2293 | "instant": true,
2294 | "intervalFactor": 2,
2295 | "legendFormat": "{{ namespace }}",
2296 | "refId": "A",
2297 | "step": 10
2298 | }
2299 | ],
2300 | "thresholds": [],
2301 | "timeFrom": null,
2302 | "timeRegions": [],
2303 | "timeShift": null,
2304 | "title": "Pod Count",
2305 | "tooltip": {
2306 | "shared": false,
2307 | "sort": 1,
2308 | "value_type": "individual"
2309 | },
2310 | "type": "graph",
2311 | "xaxis": {
2312 | "buckets": null,
2313 | "mode": "series",
2314 | "name": null,
2315 | "show": true,
2316 | "values": [
2317 | "current"
2318 | ]
2319 | },
2320 | "yaxes": [
2321 | {
2322 | "format": "short",
2323 | "label": null,
2324 | "logBase": 1,
2325 | "max": null,
2326 | "min": null,
2327 | "show": true
2328 | },
2329 | {
2330 | "format": "short",
2331 | "label": "",
2332 | "logBase": 1,
2333 | "max": null,
2334 | "min": null,
2335 | "show": false
2336 | }
2337 | ],
2338 | "yaxis": {
2339 | "align": false,
2340 | "alignLevel": null
2341 | }
2342 | },
2343 | {
2344 | "aliasColors": {},
2345 | "bars": true,
2346 | "dashLength": 10,
2347 | "dashes": false,
2348 | "datasource": "Prometheus",
2349 | "decimals": 0,
2350 | "fill": 1,
2351 | "gridPos": {
2352 | "h": 7,
2353 | "w": 12,
2354 | "x": 12,
2355 | "y": 14
2356 | },
2357 | "id": 71,
2358 | "legend": {
2359 | "alignAsTable": true,
2360 | "avg": false,
2361 | "current": true,
2362 | "max": false,
2363 | "min": false,
2364 | "rightSide": true,
2365 | "show": true,
2366 | "sideWidth": null,
2367 | "total": false,
2368 | "values": true
2369 | },
2370 | "lines": false,
2371 | "linewidth": 2,
2372 | "links": [],
2373 | "nullPointMode": "null as zero",
2374 | "percentage": false,
2375 | "pointradius": 5,
2376 | "points": false,
2377 | "renderer": "flot",
2378 | "seriesOverrides": [],
2379 | "spaceLength": 10,
2380 | "stack": false,
2381 | "steppedLine": false,
2382 | "targets": [
2383 | {
2384 | "expr": "count by (namespace) (kube_pod_container_info{namespace!=\"kube-system\"})",
2385 | "format": "time_series",
2386 | "hide": false,
2387 | "intervalFactor": 2,
2388 | "legendFormat": "{{ namespace }}",
2389 | "refId": "A",
2390 | "step": 10
2391 | }
2392 | ],
2393 | "thresholds": [],
2394 | "timeFrom": null,
2395 | "timeRegions": [],
2396 | "timeShift": null,
2397 | "title": "Container Count",
2398 | "tooltip": {
2399 | "shared": false,
2400 | "sort": 0,
2401 | "value_type": "individual"
2402 | },
2403 | "type": "graph",
2404 | "xaxis": {
2405 | "buckets": null,
2406 | "mode": "series",
2407 | "name": null,
2408 | "show": true,
2409 | "values": [
2410 | "current"
2411 | ]
2412 | },
2413 | "yaxes": [
2414 | {
2415 | "format": "short",
2416 | "label": null,
2417 | "logBase": 1,
2418 | "max": null,
2419 | "min": null,
2420 | "show": true
2421 | },
2422 | {
2423 | "decimals": 0,
2424 | "format": "short",
2425 | "label": null,
2426 | "logBase": 1,
2427 | "max": null,
2428 | "min": null,
2429 | "show": true
2430 | }
2431 | ],
2432 | "yaxis": {
2433 | "align": false,
2434 | "alignLevel": null
2435 | }
2436 | }
2437 | ],
2438 | "repeat": null,
2439 | "title": "Total Pod and Container Count",
2440 | "type": "row"
2441 | },
2442 | {
2443 | "collapsed": true,
2444 | "datasource": null,
2445 | "gridPos": {
2446 | "h": 1,
2447 | "w": 24,
2448 | "x": 0,
2449 | "y": 35
2450 | },
2451 | "id": 36,
2452 | "panels": [
2453 | {
2454 | "aliasColors": {},
2455 | "bars": false,
2456 | "dashLength": 10,
2457 | "dashes": false,
2458 | "datasource": "Prometheus",
2459 | "decimals": 3,
2460 | "editable": true,
2461 | "error": false,
2462 | "fill": 0,
2463 | "grid": {},
2464 | "gridPos": {
2465 | "h": 6,
2466 | "w": 24,
2467 | "x": 0,
2468 | "y": 9
2469 | },
2470 | "height": "",
2471 | "id": 23,
2472 | "legend": {
2473 | "alignAsTable": true,
2474 | "avg": true,
2475 | "current": true,
2476 | "max": false,
2477 | "min": false,
2478 | "rightSide": true,
2479 | "show": true,
2480 | "sideWidth": null,
2481 | "sort": "current",
2482 | "sortDesc": true,
2483 | "total": false,
2484 | "values": true
2485 | },
2486 | "lines": true,
2487 | "linewidth": 2,
2488 | "links": [],
2489 | "nullPointMode": "connected",
2490 | "percentage": false,
2491 | "pointradius": 5,
2492 | "points": false,
2493 | "renderer": "flot",
2494 | "seriesOverrides": [],
2495 | "spaceLength": 10,
2496 | "stack": false,
2497 | "steppedLine": true,
2498 | "targets": [
2499 | {
2500 | "expr": "sum (rate (container_cpu_usage_seconds_total{namespace=\"kube-system\",pod_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (name)",
2501 | "format": "time_series",
2502 | "hide": false,
2503 | "interval": "10s",
2504 | "intervalFactor": 1,
2505 | "legendFormat": "{{ name }}",
2506 | "metric": "container_cpu",
2507 | "refId": "A",
2508 | "step": 10
2509 | }
2510 | ],
2511 | "thresholds": [],
2512 | "timeFrom": null,
2513 | "timeRegions": [],
2514 | "timeShift": null,
2515 | "title": "System services CPU usage ($interval avg)",
2516 | "tooltip": {
2517 | "msResolution": true,
2518 | "shared": true,
2519 | "sort": 2,
2520 | "value_type": "cumulative"
2521 | },
2522 | "type": "graph",
2523 | "xaxis": {
2524 | "buckets": null,
2525 | "mode": "time",
2526 | "name": null,
2527 | "show": true,
2528 | "values": []
2529 | },
2530 | "yaxes": [
2531 | {
2532 | "format": "none",
2533 | "label": "cores",
2534 | "logBase": 1,
2535 | "max": null,
2536 | "min": null,
2537 | "show": true
2538 | },
2539 | {
2540 | "format": "short",
2541 | "label": null,
2542 | "logBase": 1,
2543 | "max": null,
2544 | "min": null,
2545 | "show": false
2546 | }
2547 | ],
2548 | "yaxis": {
2549 | "align": false,
2550 | "alignLevel": null
2551 | }
2552 | },
2553 | {
2554 | "aliasColors": {},
2555 | "bars": false,
2556 | "dashLength": 10,
2557 | "dashes": false,
2558 | "datasource": "Prometheus",
2559 | "decimals": 2,
2560 | "editable": true,
2561 | "error": false,
2562 | "fill": 0,
2563 | "grid": {},
2564 | "gridPos": {
2565 | "h": 6,
2566 | "w": 24,
2567 | "x": 0,
2568 | "y": 15
2569 | },
2570 | "id": 26,
2571 | "legend": {
2572 | "alignAsTable": true,
2573 | "avg": true,
2574 | "current": true,
2575 | "max": false,
2576 | "min": false,
2577 | "rightSide": true,
2578 | "show": true,
2579 | "sideWidth": null,
2580 | "sort": "current",
2581 | "sortDesc": true,
2582 | "total": false,
2583 | "values": true
2584 | },
2585 | "lines": true,
2586 | "linewidth": 2,
2587 | "links": [],
2588 | "nullPointMode": "connected",
2589 | "percentage": false,
2590 | "pointradius": 5,
2591 | "points": false,
2592 | "renderer": "flot",
2593 | "seriesOverrides": [],
2594 | "spaceLength": 10,
2595 | "stack": false,
2596 | "steppedLine": true,
2597 | "targets": [
2598 | {
2599 | "expr": "sum (container_memory_working_set_bytes{namespace=\"kube-system\",pod_name!=\"\",kubernetes_io_hostname=~\"^$Node$\"}) by (name)",
2600 | "format": "time_series",
2601 | "interval": "10s",
2602 | "intervalFactor": 1,
2603 | "legendFormat": "{{ name }}",
2604 | "metric": "container_memory_usage:sort_desc",
2605 | "refId": "A",
2606 | "step": 10
2607 | }
2608 | ],
2609 | "thresholds": [],
2610 | "timeFrom": null,
2611 | "timeRegions": [],
2612 | "timeShift": null,
2613 | "title": "System services memory usage",
2614 | "tooltip": {
2615 | "msResolution": false,
2616 | "shared": true,
2617 | "sort": 2,
2618 | "value_type": "cumulative"
2619 | },
2620 | "type": "graph",
2621 | "xaxis": {
2622 | "buckets": null,
2623 | "mode": "time",
2624 | "name": null,
2625 | "show": true,
2626 | "values": []
2627 | },
2628 | "yaxes": [
2629 | {
2630 | "format": "bytes",
2631 | "label": null,
2632 | "logBase": 1,
2633 | "max": null,
2634 | "min": null,
2635 | "show": true
2636 | },
2637 | {
2638 | "format": "short",
2639 | "label": null,
2640 | "logBase": 1,
2641 | "max": null,
2642 | "min": null,
2643 | "show": false
2644 | }
2645 | ],
2646 | "yaxis": {
2647 | "align": false,
2648 | "alignLevel": null
2649 | }
2650 | }
2651 | ],
2652 | "repeat": null,
2653 | "title": "System Services CPU and Memory Usage",
2654 | "type": "row"
2655 | },
2656 | {
2657 | "collapsed": true,
2658 | "datasource": null,
2659 | "gridPos": {
2660 | "h": 1,
2661 | "w": 24,
2662 | "x": 0,
2663 | "y": 36
2664 | },
2665 | "id": 38,
2666 | "panels": [
2667 | {
2668 | "aliasColors": {},
2669 | "bars": false,
2670 | "dashLength": 10,
2671 | "dashes": false,
2672 | "datasource": "Prometheus",
2673 | "decimals": 3,
2674 | "editable": true,
2675 | "error": false,
2676 | "fill": 0,
2677 | "grid": {},
2678 | "gridPos": {
2679 | "h": 9,
2680 | "w": 24,
2681 | "x": 0,
2682 | "y": 18
2683 | },
2684 | "id": 20,
2685 | "legend": {
2686 | "alignAsTable": true,
2687 | "avg": true,
2688 | "current": true,
2689 | "max": false,
2690 | "min": false,
2691 | "rightSide": true,
2692 | "show": true,
2693 | "sort": "current",
2694 | "sortDesc": true,
2695 | "total": false,
2696 | "values": true
2697 | },
2698 | "lines": true,
2699 | "linewidth": 2,
2700 | "links": [],
2701 | "nullPointMode": "connected",
2702 | "percentage": false,
2703 | "pointradius": 5,
2704 | "points": false,
2705 | "renderer": "flot",
2706 | "seriesOverrides": [],
2707 | "spaceLength": 10,
2708 | "stack": false,
2709 | "steppedLine": true,
2710 | "targets": [
2711 | {
2712 | "expr": "sum (rate (container_cpu_usage_seconds_total{id!=\"/\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (id)",
2713 | "format": "time_series",
2714 | "hide": false,
2715 | "interval": "10s",
2716 | "intervalFactor": 1,
2717 | "legendFormat": "{{ id }}",
2718 | "metric": "container_cpu",
2719 | "refId": "A",
2720 | "step": 10
2721 | }
2722 | ],
2723 | "thresholds": [],
2724 | "timeFrom": null,
2725 | "timeRegions": [],
2726 | "timeShift": null,
2727 | "title": "All processes CPU usage ($interval avg)",
2728 | "tooltip": {
2729 | "msResolution": true,
2730 | "shared": true,
2731 | "sort": 2,
2732 | "value_type": "cumulative"
2733 | },
2734 | "type": "graph",
2735 | "xaxis": {
2736 | "buckets": null,
2737 | "mode": "time",
2738 | "name": null,
2739 | "show": true,
2740 | "values": []
2741 | },
2742 | "yaxes": [
2743 | {
2744 | "format": "none",
2745 | "label": "cores",
2746 | "logBase": 1,
2747 | "max": null,
2748 | "min": null,
2749 | "show": true
2750 | },
2751 | {
2752 | "format": "short",
2753 | "label": null,
2754 | "logBase": 1,
2755 | "max": null,
2756 | "min": null,
2757 | "show": false
2758 | }
2759 | ],
2760 | "yaxis": {
2761 | "align": false,
2762 | "alignLevel": null
2763 | }
2764 | },
2765 | {
2766 | "aliasColors": {},
2767 | "bars": false,
2768 | "dashLength": 10,
2769 | "dashes": false,
2770 | "datasource": "Prometheus",
2771 | "decimals": 2,
2772 | "editable": true,
2773 | "error": false,
2774 | "fill": 0,
2775 | "grid": {},
2776 | "gridPos": {
2777 | "h": 8,
2778 | "w": 24,
2779 | "x": 0,
2780 | "y": 27
2781 | },
2782 | "id": 28,
2783 | "legend": {
2784 | "alignAsTable": true,
2785 | "avg": true,
2786 | "current": true,
2787 | "max": false,
2788 | "min": false,
2789 | "rightSide": true,
2790 | "show": true,
2791 | "sideWidth": null,
2792 | "sort": "current",
2793 | "sortDesc": true,
2794 | "total": false,
2795 | "values": true
2796 | },
2797 | "lines": true,
2798 | "linewidth": 2,
2799 | "links": [],
2800 | "nullPointMode": "connected",
2801 | "percentage": false,
2802 | "pointradius": 5,
2803 | "points": false,
2804 | "renderer": "flot",
2805 | "seriesOverrides": [],
2806 | "spaceLength": 10,
2807 | "stack": false,
2808 | "steppedLine": true,
2809 | "targets": [
2810 | {
2811 | "expr": "sum (container_memory_working_set_bytes{id!=\"/\",kubernetes_io_hostname=~\"^$Node$\"}) by (id)",
2812 | "format": "time_series",
2813 | "interval": "10s",
2814 | "intervalFactor": 1,
2815 | "legendFormat": "{{ id }}",
2816 | "metric": "container_memory_usage:sort_desc",
2817 | "refId": "A",
2818 | "step": 10
2819 | }
2820 | ],
2821 | "thresholds": [],
2822 | "timeFrom": null,
2823 | "timeRegions": [],
2824 | "timeShift": null,
2825 | "title": "All processes memory usage",
2826 | "tooltip": {
2827 | "msResolution": false,
2828 | "shared": true,
2829 | "sort": 2,
2830 | "value_type": "cumulative"
2831 | },
2832 | "type": "graph",
2833 | "xaxis": {
2834 | "buckets": null,
2835 | "mode": "time",
2836 | "name": null,
2837 | "show": true,
2838 | "values": []
2839 | },
2840 | "yaxes": [
2841 | {
2842 | "format": "bytes",
2843 | "label": null,
2844 | "logBase": 1,
2845 | "max": null,
2846 | "min": null,
2847 | "show": true
2848 | },
2849 | {
2850 | "format": "short",
2851 | "label": null,
2852 | "logBase": 1,
2853 | "max": null,
2854 | "min": null,
2855 | "show": false
2856 | }
2857 | ],
2858 | "yaxis": {
2859 | "align": false,
2860 | "alignLevel": null
2861 | }
2862 | },
2863 | {
2864 | "aliasColors": {},
2865 | "bars": false,
2866 | "dashLength": 10,
2867 | "dashes": false,
2868 | "datasource": "Prometheus",
2869 | "decimals": 2,
2870 | "editable": true,
2871 | "error": false,
2872 | "fill": 1,
2873 | "grid": {},
2874 | "gridPos": {
2875 | "h": 7,
2876 | "w": 24,
2877 | "x": 0,
2878 | "y": 35
2879 | },
2880 | "id": 29,
2881 | "legend": {
2882 | "alignAsTable": true,
2883 | "avg": true,
2884 | "current": true,
2885 | "max": false,
2886 | "min": false,
2887 | "rightSide": true,
2888 | "show": true,
2889 | "sideWidth": null,
2890 | "sort": "current",
2891 | "sortDesc": true,
2892 | "total": false,
2893 | "values": true
2894 | },
2895 | "lines": true,
2896 | "linewidth": 2,
2897 | "links": [],
2898 | "nullPointMode": "connected",
2899 | "percentage": false,
2900 | "pointradius": 5,
2901 | "points": false,
2902 | "renderer": "flot",
2903 | "seriesOverrides": [],
2904 | "spaceLength": 10,
2905 | "stack": false,
2906 | "steppedLine": false,
2907 | "targets": [
2908 | {
2909 | "expr": "sum (rate (container_network_receive_bytes_total{id!=\"/\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (id)",
2910 | "format": "time_series",
2911 | "interval": "10s",
2912 | "intervalFactor": 1,
2913 | "legendFormat": "-> {{ id }}",
2914 | "metric": "network",
2915 | "refId": "A",
2916 | "step": 10
2917 | },
2918 | {
2919 | "expr": "- sum (rate (container_network_transmit_bytes_total{id!=\"/\",kubernetes_io_hostname=~\"^$Node$\"}[$interval])) by (id)",
2920 | "format": "time_series",
2921 | "interval": "10s",
2922 | "intervalFactor": 1,
2923 | "legendFormat": "<- {{ id }}",
2924 | "metric": "network",
2925 | "refId": "B",
2926 | "step": 10
2927 | }
2928 | ],
2929 | "thresholds": [],
2930 | "timeFrom": null,
2931 | "timeRegions": [],
2932 | "timeShift": null,
2933 | "title": "All processes network I/O ($interval avg)",
2934 | "tooltip": {
2935 | "msResolution": false,
2936 | "shared": true,
2937 | "sort": 2,
2938 | "value_type": "cumulative"
2939 | },
2940 | "type": "graph",
2941 | "xaxis": {
2942 | "buckets": null,
2943 | "mode": "time",
2944 | "name": null,
2945 | "show": true,
2946 | "values": []
2947 | },
2948 | "yaxes": [
2949 | {
2950 | "format": "Bps",
2951 | "label": null,
2952 | "logBase": 1,
2953 | "max": null,
2954 | "min": null,
2955 | "show": true
2956 | },
2957 | {
2958 | "format": "short",
2959 | "label": null,
2960 | "logBase": 1,
2961 | "max": null,
2962 | "min": null,
2963 | "show": false
2964 | }
2965 | ],
2966 | "yaxis": {
2967 | "align": false,
2968 | "alignLevel": null
2969 | }
2970 | }
2971 | ],
2972 | "repeat": null,
2973 | "title": "All Processes CPU and Memory Usage",
2974 | "type": "row"
2975 | }
2976 | ],
2977 | "refresh": "10s",
2978 | "schemaVersion": 30,
2979 | "style": "dark",
2980 | "tags": [
2981 | "kubernetes"
2982 | ],
2983 | "templating": {
2984 | "list": [
2985 | {
2986 | "auto": true,
2987 | "auto_count": 20,
2988 | "auto_min": "2m",
2989 | "current": {
2990 | "selected": false,
2991 | "text": "auto",
2992 | "value": "$__auto_interval_interval"
2993 | },
2994 | "description": null,
2995 | "error": null,
2996 | "hide": 2,
2997 | "label": null,
2998 | "name": "interval",
2999 | "options": [
3000 | {
3001 | "selected": true,
3002 | "text": "auto",
3003 | "value": "$__auto_interval_interval"
3004 | },
3005 | {
3006 | "selected": false,
3007 | "text": "1m",
3008 | "value": "1m"
3009 | },
3010 | {
3011 | "selected": false,
3012 | "text": "10m",
3013 | "value": "10m"
3014 | },
3015 | {
3016 | "selected": false,
3017 | "text": "30m",
3018 | "value": "30m"
3019 | },
3020 | {
3021 | "selected": false,
3022 | "text": "1h",
3023 | "value": "1h"
3024 | },
3025 | {
3026 | "selected": false,
3027 | "text": "6h",
3028 | "value": "6h"
3029 | },
3030 | {
3031 | "selected": false,
3032 | "text": "12h",
3033 | "value": "12h"
3034 | },
3035 | {
3036 | "selected": false,
3037 | "text": "1d",
3038 | "value": "1d"
3039 | },
3040 | {
3041 | "selected": false,
3042 | "text": "7d",
3043 | "value": "7d"
3044 | },
3045 | {
3046 | "selected": false,
3047 | "text": "14d",
3048 | "value": "14d"
3049 | },
3050 | {
3051 | "selected": false,
3052 | "text": "30d",
3053 | "value": "30d"
3054 | }
3055 | ],
3056 | "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
3057 | "refresh": 2,
3058 | "skipUrlSync": false,
3059 | "type": "interval"
3060 | },
3061 | {
3062 | "current": {
3063 | "selected": false,
3064 | "text": "Prometheus",
3065 | "value": "Prometheus"
3066 | },
3067 | "description": null,
3068 | "error": null,
3069 | "hide": 0,
3070 | "includeAll": false,
3071 | "label": null,
3072 | "multi": false,
3073 | "name": "Datasource",
3074 | "options": [],
3075 | "query": "prometheus",
3076 | "refresh": 1,
3077 | "regex": "",
3078 | "skipUrlSync": false,
3079 | "type": "datasource"
3080 | },
3081 | {
3082 | "allValue": ".*",
3083 | "current": {},
3084 | "datasource": "Prometheus",
3085 | "definition": "",
3086 | "description": null,
3087 | "error": {
3088 | "config": {
3089 | "headers": {
3090 | "X-Grafana-Org-Id": 1
3091 | },
3092 | "hideFromInspector": true,
3093 | "method": "GET",
3094 | "retry": 0,
3095 | "url": "api/datasources/proxy/18/api/v1/label/kubernetes_io_hostname/values?start=1632167040&end=1632167940"
3096 | },
3097 | "data": {
3098 | "error": "",
3099 | "message": "",
3100 | "response": ""
3101 | },
3102 | "message": "Query error: 502 ",
3103 | "status": 502,
3104 | "statusText": ""
3105 | },
3106 | "hide": 0,
3107 | "includeAll": true,
3108 | "label": null,
3109 | "multi": false,
3110 | "name": "Node",
3111 | "options": [],
3112 | "query": {
3113 | "query": "label_values(kubernetes_io_hostname)",
3114 | "refId": "Prometheus-Node-Variable-Query"
3115 | },
3116 | "refresh": 1,
3117 | "regex": "",
3118 | "skipUrlSync": false,
3119 | "sort": 0,
3120 | "tagValuesQuery": "",
3121 | "tagsQuery": "",
3122 | "type": "query",
3123 | "useTags": false
3124 | }
3125 | ]
3126 | },
3127 | "time": {
3128 | "from": "now-15m",
3129 | "to": "now"
3130 | },
3131 | "timepicker": {
3132 | "refresh_intervals": [
3133 | "5s",
3134 | "10s",
3135 | "30s",
3136 | "1m",
3137 | "5m",
3138 | "15m",
3139 | "30m",
3140 | "1h",
3141 | "2h",
3142 | "1d"
3143 | ],
3144 | "time_options": [
3145 | "5m",
3146 | "15m",
3147 | "1h",
3148 | "6h",
3149 | "12h",
3150 | "24h",
3151 | "2d",
3152 | "7d",
3153 | "30d"
3154 | ]
3155 | },
3156 | "timezone": "browser",
3157 | "title": "Kubernetes Cluster Monitoring",
3158 | "uid": "U_gA5P2mz",
3159 | "version": 2
3160 | }
--------------------------------------------------------------------------------