Source code for duct.sources.docker
"""
.. module:: docker
:platform: Any
:synopsis: A source module for Docker container metrics
.. moduleauthor:: Colin Alston <colin.alston@gmail.com>
"""
from zope.interface import implementer
from duct.interfaces import IDuctSource
from duct.objects import Source
from duct.utils import HTTPRequest, PersistentCache
from duct.aggregators import Counter64
[docs]
@implementer(IDuctSource)
class ContainerStats(Source):
"""Returns stats for Docker containers on this host
:param url: Docker stats URL
:type url: str.
**Metrics:**
:(service name).(container name).mem_limit: Maximum memory for container
:(service name).(container name).mem_used: Memory used by container
:(service name).(container name).cpu: Percentage of system CPU in use
:(service name).(container name).io_read: IO reads per second
:(service name).(container name).io_write: IO writes per second
:(service name).(container name).io_sync: IO synchronous op/s
:(service name).(container name).io_async: IO asynchronous op/s
:(service name).(container name).io_total: Total IOPS
Note. If a MARATHON_APP_ID environment variable exists on the container
then `container name` will be used instead of that.
"""
def __init__(self, *a, **kw):
Source.__init__(self, *a, **kw)
self.url = self.config.get('url', 'unix:/var/run/docker.sock')
self.cache = PersistentCache(location='/tmp/dockerstats.cache')
async def _get_stats_from_node(self):
if self.url.startswith('unix:'):
sock = self.url
pref = ''
else:
sock = None
pref = self.url
containers = await HTTPRequest().getJson(
f'{pref}/containers/json', socket=sock)
allStats = {}
for container in containers:
name = container.get('Names', [None])[0].lstrip('/')
stats = await HTTPRequest().getJson(
f'{pref}/containers/{name}/stats?stream=false',
socket=sock
)
detail = await HTTPRequest().getJson(
f'{pref}/containers/{name}/json', socket=sock)
env = detail['Config']['Env']
if env:
for var in env:
if var.startswith('MARATHON_APP_ID='):
name = var.split('=', 1)[-1].lstrip('/')
allStats[name] = {
'mem_limit': stats['memory_stats']['limit'],
'mem_used': stats['memory_stats']['usage']
}
io_stats = stats['blkio_stats']['io_service_bytes_recursive']
for item in io_stats:
allStats[name]['io_' + item['op'].lower()] = item['value']
sysCpu = stats['cpu_stats']['system_cpu_usage']
dockCpu = stats['cpu_stats']['cpu_usage']['total_usage']
if self.cache.contains(name):
_lastTime, lastStats = self.cache.get(name)
sysDelta = sysCpu - lastStats[0]
dockDelta = dockCpu - lastStats[1]
if sysDelta > 0:
allStats[name]['cpu'] = int((dockDelta / sysDelta) * 100)
self.cache.set(name, [sysCpu, dockCpu])
return allStats
[docs]
async def get(self):
stats = await self._get_stats_from_node()
events = []
for name, container in stats.items():
for pref, val in container.items():
if pref.startswith('io_'):
events.append(self.createEvent(
'ok', '', val,
prefix=f'{name}.{pref}',
aggregation=Counter64
))
else:
events.append(self.createEvent(
'ok', '', val, prefix=f'{name}.{pref}'))
return events