Skip to content

Instantly share code, notes, and snippets.

@gorenje
Last active May 25, 2021 13:38
Show Gist options
  • Save gorenje/dff508489c3c8a460433ad709f14b7db to your computer and use it in GitHub Desktop.
Save gorenje/dff508489c3c8a460433ad709f14b7db to your computer and use it in GitHub Desktop.
Retrieving Allocated Resources from a Kubernetes Node

Used to retrieve the allocatable resources of a Kubernetes cluster.

Assumes that this is being executed within the K8s cluster.

Tested using python 2.7 and requires the installation of two pip libraries:

pip install pint
pip install kubernetes

It provides the same values as kubectl describe nodes, for example:

Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                660m (34%)   500m (25%)
  memory             622Mi (11%)  938Mi (16%)
  ephemeral-storage  0 (0%)       0 (0%)
import kubernetes as k8s
from pint import UnitRegistry
from collections import defaultdict
__all__ = ["compute_allocated_resources"]
def compute_allocated_resources():
ureg = UnitRegistry()
ureg.load_definitions('kubernetes_units.txt')
Q_ = ureg.Quantity
data = {}
# doing this computation within a k8s cluster
k8s.config.load_incluster_config()
core_v1 = k8s.client.CoreV1Api()
for node in core_v1.list_node().items:
stats = {}
node_name = node.metadata.name
allocatable = node.status.allocatable
max_pods = int(int(allocatable["pods"]) * 1.5)
field_selector = ("status.phase!=Succeeded,status.phase!=Failed," +
"spec.nodeName=" + node_name)
stats["cpu_alloc"] = Q_(allocatable["cpu"])
stats["mem_alloc"] = Q_(allocatable["memory"])
pods = core_v1.list_pod_for_all_namespaces(limit=max_pods,
field_selector=field_selector).items
# compute the allocated resources
cpureqs,cpulmts,memreqs,memlmts = [], [], [], []
for pod in pods:
for container in pod.spec.containers:
res = container.resources
reqs = defaultdict(lambda: 0, res.requests or {})
lmts = defaultdict(lambda: 0, res.limits or {})
cpureqs.append(Q_(reqs["cpu"]))
memreqs.append(Q_(reqs["memory"]))
cpulmts.append(Q_(lmts["cpu"]))
memlmts.append(Q_(lmts["memory"]))
stats["cpu_req"] = sum(cpureqs)
stats["cpu_lmt"] = sum(cpulmts)
stats["cpu_req_per"] = (stats["cpu_req"] / stats["cpu_alloc"] * 100)
stats["cpu_lmt_per"] = (stats["cpu_lmt"] / stats["cpu_alloc"] * 100)
stats["mem_req"] = sum(memreqs)
stats["mem_lmt"] = sum(memlmts)
stats["mem_req_per"] = (stats["mem_req"] / stats["mem_alloc"] * 100)
stats["mem_lmt_per"] = (stats["mem_lmt"] / stats["mem_alloc"] * 100)
data[node_name] = stats
return data
# memory units
kmemunits = 1 = [kmemunits]
Ki = 1024 * kmemunits
Mi = Ki^2
Gi = Ki^3
Ti = Ki^4
Pi = Ki^5
Ei = Ki^6
# cpu units
kcpuunits = 1 = [kcpuunits]
m = 1/1000 * kcpuunits
k = 1000 * kcpuunits
M = k^2
G = k^3
T = k^4
P = k^5
E = k^6
@gorenje
Copy link
Author

gorenje commented Jun 17, 2020

Is it necessary to standardize the units before calculating the percentage? i.e.

Q_('2Mi') / Q_('2Gi')
<Quantity(1.0, 'Mi / Gi')>

to

 Q_('2Mi') / Q_('2Gi').to('Mi')
<Quantity(0.0009765625, 'dimensionless')>

The percent computation are indeed unit based however pint takes care of remembering the units. So, yes, before using the percent values (i.e. cpu_req_per, cpu_lmt_per, etc) you would need to do a .to('dimensionless'), e.g., stats["cpu_lmt_per"].to('dimensionless').

However, it seems that pint does this automagically for you:

>>> (Q_('2Mi') / Q_('2Gi')).to('dimensionless')
<Quantity(0.0009765625, 'dimensionless')>
>>> (Q_('2Mi') / Q_('2Gi')) > 1
False
>>> (Q_('2Mi') / Q_('2Gi')) > 0.00008
True
>>> (Q_('2Mi') / Q_('2Gi')) > 0.0008
True
>>> (Q_('2Mi') / Q_('2Gi')) > 0.008
False

So the short answer is: you don't have to explicitly convert percent values, pint takes care of this for you.

@shaowei-su
Copy link

Good point, thanks for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment