├── .gitignore ├── package.sh ├── testtools ├── testtools.tar.gz ├── package.sh ├── Makefile ├── Dockerfile ├── memkiller.c └── testrun.sh ├── install.sh ├── zabbix-docker-discover.py ├── zabbix-docker-convert.py ├── ReleaseNotes.md ├── zabbix-docker-info.py ├── userparameter_zabbixdocker.conf ├── zabbix-docker-inspect.py ├── zabbix-docker-stats.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | memkiller 2 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tar zcvf ZabbixDocker.tar.gz *.py *.conf *.xml install.sh 4 | 5 | -------------------------------------------------------------------------------- /testtools/testtools.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rpsedlak/zabbix-docker/HEAD/testtools/testtools.tar.gz -------------------------------------------------------------------------------- /testtools/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tar zcvf testtools.tar.gz Makefile memkiller.c testrun.sh Dockerfile 4 | 5 | -------------------------------------------------------------------------------- /testtools/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: memkiller 3 | 4 | new: clean all 5 | 6 | clean: 7 | echo "Cleaning up" 8 | rm -f memkiller 9 | 10 | memkiller: memkiller.c 11 | echo "Compiling memkiller" 12 | cc memkiller.c -o memkiller 13 | 14 | -------------------------------------------------------------------------------- /testtools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jdeathe/centos-ssh 2 | 3 | MAINTAINER Richard Sedlak 4 | 5 | USER root 6 | 7 | ENV AP /data/app 8 | ENV PATH $PATH:$AP 9 | 10 | RUN yum -y install gcc 11 | 12 | ADD Makefile $AP/ 13 | ADD memkiller.c $AP/ 14 | 15 | WORKDIR $AP 16 | 17 | RUN make clean all 18 | 19 | CMD ["./memkiller"] 20 | 21 | -------------------------------------------------------------------------------- /testtools/memkiller.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SIZE 1024L 5 | 6 | int main ( int argc, char** argv ) 7 | { 8 | char* ptr = NULL; 9 | long size = 0; 10 | 11 | while ( 1 ) 12 | { 13 | #ifdef DEBUG 14 | printf("allocating %ld bytes\n",SIZE); 15 | size += SIZE; 16 | #endif 17 | 18 | ptr = (char*) malloc(SIZE); 19 | 20 | #ifdef DEBUG 21 | printf("%ld allocated thus far\n",size); 22 | #endif 23 | } 24 | 25 | return 0; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /testtools/testrun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # first, build our special docker image 4 | docker build --tag="rpsedlak/memkiller" . 5 | 6 | # next, start some standard stuff 7 | docker run -d --name="httpd-1" httpd 8 | docker run -d --name="httpd-2-pause" httpd 9 | docker run -d --name="httpd-3-kill" httpd 10 | docker run -d --name="redis-1" redis:2.8 11 | docker run -d --name="redis-2-pause" redis:2.8 12 | docker run -d --name="redis-3-kill" redis:2.8 13 | 14 | # next, pause a container 15 | docker pause httpd-2-pause 16 | docker pause redis-2-pause 17 | 18 | # next, kill a container 19 | docker kill httpd-3-kill 20 | docker kill redis-3-kill 21 | 22 | # last, run the memkiller image 23 | docker run -d --memory=4M --name="oom-test" rpsedlak/memkiller 24 | 25 | 26 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # argument processing 4 | if [ -d "/etc/zabbix/zabbix_agentd.d/" ]; 5 | then CONFIG_PATH=/etc/zabbix/zabbix_agentd.d/ 6 | 7 | elif [ -d "/etc/zabbix/zabbix_agentd.conf.d/" ]; 8 | then CONFIG_PATH=/etc/zabbix/zabbix_agentd.conf.d/ 9 | 10 | else 11 | echo "Zabbix Config Path not found - please enter path:" 12 | read -r CONFIG_PATH 13 | 14 | fi 15 | 16 | # Copy the files 17 | cp -f *.py /usr/local/bin 18 | cp -f *.conf $CONFIG_PATH 19 | cp -f *.xml /tmp 20 | 21 | # tell the user some stuff 22 | echo "Python scripts copied to /usr/local/bin" 23 | echo "zabbix-agent configuration files copied to $CONFIG_PATH" 24 | echo "XML Zabbix Templates copied to /tmp" 25 | echo "" 26 | echo "You will need to restart the zabbix-agent and import the XML template on your Zabbix server" 27 | -------------------------------------------------------------------------------- /zabbix-docker-discover.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ################################################################# 4 | # 5 | # zabbix-docker-discover.py 6 | # 7 | # A program that produces LLD information for Zabbix to 8 | # process Docker instances. 9 | # 10 | # Version: 1.0 11 | # 12 | # Author: Richard Sedlak 13 | # 14 | ################################################################# 15 | 16 | import subprocess 17 | import json 18 | 19 | strings = subprocess.Popen("docker ps -a", shell=True, stdout=subprocess.PIPE).stdout.readlines() 20 | 21 | l=list() 22 | for i in range(1,len(strings)): 23 | pstring = strings[i].split() 24 | d=dict() 25 | d["{#ZD_ID}"]=pstring[0] 26 | d["{#ZD_IMAGE}"]=pstring[1] 27 | d["{#ZD_NAME}"]=pstring[-1] 28 | l.append(d) 29 | 30 | s_json=dict() 31 | s_json["data"]=l 32 | 33 | print json.dumps(s_json) 34 | -------------------------------------------------------------------------------- /zabbix-docker-convert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ################################################################# 4 | # 5 | # zabbix-docker-convert-py 6 | # 7 | # A program that converts between K,M,G,T. 8 | # 9 | # Version: 1.0 10 | # 11 | # Author: Richard Sedlak 12 | # 13 | ################################################################# 14 | 15 | import sys 16 | import re 17 | 18 | def B(b): 19 | return int(float(b)) 20 | 21 | def KB(b): 22 | return int(float(b) * 1024) 23 | 24 | def MB(b): 25 | return int(float(b) * 1024 * 1024) 26 | 27 | def GB(b): 28 | return int(float(b) * 1024 * 1024 * 1024) 29 | 30 | def TB(b): 31 | return int(float(b) * 1024 * 1024 * 1024 * 1024) 32 | 33 | options = { 34 | 'k':KB, 35 | 'K':KB, 36 | 'm':MB, 37 | 'M':MB, 38 | 'g':GB, 39 | 'G':GB, 40 | 't':TB, 41 | 'T':TB, 42 | 'b':B, 43 | 'B':B 44 | } 45 | 46 | # 47 | # First read from stdin 48 | # 49 | lines = sys.stdin.readlines()[0] 50 | 51 | pat = re.compile(r'([0-9.]+)([a-zA-Z%])') 52 | mo = pat.match(lines) 53 | if mo: 54 | number, unit = mo.group(1,2) 55 | print options[unit](number) 56 | else: 57 | print '-1' 58 | 59 | -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | ## Release 1.0.4 - 6/10/2017 4 | * Accepted pull request from kr428 - Corrects Ubuntu Docker issue where the command name has changed 5 | 6 | ## Release 1.0.3 - 12/22/2015 7 | * #28 - Fixed 'Running instances showing OOMKilled set to true' 8 | * #27 - Fixed 'ID from 'docker info' only returning a partial value' 9 | * #15 - Fixed 'Requests for data appears to be overloading 'docker' command' 10 | 11 | ## Release 1.0.2 - 12/21/2015 12 | * #24 - Fixed container memory % report for Ubuntu 13 | * #23 - Fixed false "down" report for Ubuntu 14 | 15 | ## Release 1.0.1 - 12/21/2015 16 | * #20 - Changed collection delay for docker version 17 | * #18 - Implemented WARNING trigger for paused containers 18 | * #17 - Implemented AVERAGE trigger for OMMKilled containers 19 | * #16 - Corrected install.sh problem for overridden directories 20 | * #15 - Implemented value file caching on zabbix-agent host to keep docker from becoming over loaded. 21 | * #12 - Changed "Docker Storage Driver Deferred Removal Enabled" from text to unsigned int (Boolean value). 22 | 23 | ## Release 1.0.0 - 12/19/2015 24 | * Feature complete 25 | * Supports docker 'inspect' 26 | * Supports docker 'stats' commands 27 | * Supports docker 'info' 28 | * Supports Zabbix LLD 29 | 30 | -------------------------------------------------------------------------------- /zabbix-docker-info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import os 5 | import time 6 | 7 | errorString="***NOT FOUND***" 8 | 9 | def local_run_command(cmd,file): 10 | cmd = cmd + " | tee > " + file 11 | if os.path.isfile(file) == False: 12 | os.system(cmd) 13 | else: 14 | (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file) 15 | ticks=int(time.time()) 16 | delta=ticks-mtime 17 | if (delta > 60): 18 | os.system(cmd) 19 | 20 | strings = open(file,"r").readlines() 21 | return strings 22 | 23 | def findString(strings,term): 24 | found=-1 25 | ndx=0 26 | maxNdx=len(strings) 27 | while (found==-1) and (ndx=0: 34 | retval=strings[found] 35 | return retval 36 | 37 | def getValue(string): 38 | pos=string.index(":") 39 | return string[pos+2:-1] 40 | 41 | 42 | search_for=sys.argv[1] 43 | 44 | cmd="docker info" 45 | filename="/tmp/zabbix-docker-info.out" 46 | 47 | strings = local_run_command(cmd,filename) 48 | 49 | line=findString(strings,search_for) 50 | 51 | if errorString in line: 52 | print search_for, " ", errorString 53 | else: 54 | print getValue(line) 55 | 56 | -------------------------------------------------------------------------------- /userparameter_zabbixdocker.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Zabbix Docker Monitoring 1.0 3 | # 4 | # Author: Richard Sedlak 5 | # 6 | # Github: git@github.com:rpsedlak/zabbix-docker.git 7 | # 8 | ########################################################## 9 | 10 | UserParameter=docker.version, docker -v 11 | 12 | UserParameter=docker.running.centos, ps -ef | grep 'docker -d' | grep -v grep | wc -l 13 | UserParameter=docker.running.ubuntu, ps -ef | grep 'dockerd' | grep -v grep | wc -l 14 | 15 | UserParameter=docker.containers.running, docker ps -q | wc -l 16 | 17 | UserParameter=docker.discovery, /usr/local/bin/zabbix-docker-discover.py 2> /dev/null 18 | 19 | UserParameter=docker.info[*], /usr/local/bin/zabbix-docker-info.py "$1" 2> /dev/null | grep -v "WARNING" 20 | 21 | UserParameter=docker.info.boolean[*], /usr/local/bin/zabbix-docker-info.py "$1" 2> /dev/null | grep -v "WARNING" | grep -i true | wc -l 22 | 23 | UserParameter=docker.info.convert[*], docker info 2> /dev/null | grep -v "WARNING" | grep "$1" | cut -f2 -d: | cut -c2- | /usr/local/bin/zabbix-docker-convert.py 24 | 25 | UserParameter=docker.container.inspect[*], /usr/local/bin/zabbix-docker-inspect.py $1 $2 2> /dev/null 26 | 27 | UserParameter=docker.container.inspect.boolean[*], /usr/local/bin/zabbix-docker-inspect.py $1 $2 2> /dev/null | grep -i true | wc -l 28 | 29 | UserParameter=docker.container.stats[*], /usr/local/bin/zabbix-docker-stats.py $1 $2 2> /dev/null 30 | -------------------------------------------------------------------------------- /zabbix-docker-inspect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ################################################################# 4 | # 5 | # zabbix-docker-inspect.py 6 | # 7 | # A program that parses the "docker inspect" values for 8 | # reporting data to Zabbix. 9 | # 10 | # Version: 1.0 11 | # 12 | # Author: Richard Sedlak 13 | # 14 | ################################################################# 15 | 16 | import sys 17 | import os 18 | import time 19 | import json 20 | import re 21 | 22 | ################################################################# 23 | # sys.argv[1] - the instanceID of the docker container 24 | # sys.argv[2] - the JSON value of the key to collect 25 | ################################################################# 26 | 27 | def local_run_command(cmd,file): 28 | cmd = cmd + " | tee > " + file 29 | if os.path.isfile(file) == False: 30 | os.system(cmd) 31 | else: 32 | (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file) 33 | ticks=int(time.time()) 34 | delta=ticks-mtime 35 | if (delta > 60): 36 | os.system(cmd) 37 | 38 | strings = open(file,"r").read() 39 | return strings 40 | 41 | cmd="docker inspect " + sys.argv[1] 42 | strings = local_run_command(cmd,"/tmp/zabbix-docker-inspect-"+sys.argv[1]+".out") 43 | 44 | parsed_json = json.loads(strings) 45 | 46 | key_path = sys.argv[2].split('.') 47 | 48 | ptr = parsed_json[0] 49 | 50 | for i in range(0,len(key_path)): 51 | ptr=ptr[key_path[i]] 52 | 53 | # make sure passwords are hidden in zabbix items 54 | if sys.argv[2] == 'Config.Env': 55 | pwd_pattern = re.compile(r'^(?P.*(PASSWORD|PWD).*)=(?P.+)$') 56 | p_out = [] 57 | for i in ptr: 58 | match = pwd_pattern.match(i) 59 | 60 | if match: 61 | p_out.append(match.group('KEY') + "=" + 'x' * len(match.group('VALUE'))) 62 | else: 63 | p_out.append(i) 64 | ptr = p_out 65 | 66 | print ptr 67 | -------------------------------------------------------------------------------- /zabbix-docker-stats.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ################################################################# 4 | # 5 | # zabbix-docker-stats.py 6 | # 7 | # A program that produces information for Zabbix to 8 | # process Docker container statistics. 9 | # 10 | # Version: 1.0 11 | # 12 | # Author: Richard Sedlak 13 | # 14 | ################################################################# 15 | 16 | import sys 17 | import os 18 | import time 19 | import re 20 | 21 | def B(b): 22 | return int(float(b)) 23 | 24 | def KB(b): 25 | return int(float(b) * 1024) 26 | 27 | def MB(b): 28 | return int(float(b) * 1024 * 1024) 29 | 30 | def GB(b): 31 | return int(float(b) * 1024 * 1024 * 1024) 32 | 33 | def TB(b): 34 | return int(float(b) * 1024 * 1024 * 1024 * 1024) 35 | 36 | def PCT(b): 37 | return float(b) 38 | 39 | size_options = { 40 | 'k':KB, 41 | 'K':KB, 42 | 'm':MB, 43 | 'M':MB, 44 | 'g':GB, 45 | 'G':GB, 46 | 't':TB, 47 | 'T':TB, 48 | 'b':B, 49 | 'B':B, 50 | '%':PCT 51 | } 52 | 53 | def recalc(data): 54 | pat = re.compile(r'([0-9.]+)([a-zA-Z%])') 55 | mo = pat.match(data) 56 | if mo: 57 | number, unit = mo.group(1,2) 58 | value = size_options[unit](number) 59 | else: 60 | value = False 61 | return value 62 | 63 | def pcpu(data): 64 | return recalc(data[2]) 65 | 66 | def umem(data): 67 | return recalc(data[3]) 68 | 69 | def lmem(data): 70 | return recalc(data[5]) 71 | 72 | def pmem(data): 73 | return recalc(data[6]) 74 | 75 | def inet(data): 76 | return recalc(data[7]) 77 | 78 | def onet(data): 79 | return recalc(data[9]) 80 | 81 | options = { 82 | 'pcpu':pcpu, 83 | 'umem':umem, 84 | 'lmem':lmem, 85 | 'pmem':pmem, 86 | 'inet':inet, 87 | 'onet':onet 88 | } 89 | 90 | def local_run_command(cmd,file): 91 | cmd = cmd + " | tee > " + file 92 | if os.path.isfile(file) == False: 93 | os.system(cmd) 94 | else: 95 | (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file) 96 | ticks=int(time.time()) 97 | delta=ticks-mtime 98 | if (delta > 60): 99 | os.system(cmd) 100 | 101 | strings = open(file,"r").readlines() 102 | return strings[1].split() 103 | 104 | 105 | container=sys.argv[1] 106 | key=sys.argv[2] 107 | 108 | cmd="docker stats --no-stream=true " + container 109 | strings = local_run_command(cmd,"/tmp/zabbix-docker-stats-"+container+".out") 110 | 111 | print options[key](strings) 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zabbix-docker 1.1.0 2 | This repository contains monitoring code for Zabbix to discover and monitor Docker instances on Linux platforms. 3 | 4 | This module once installed provides monitoring capabilities through Zabbix 2.x and Zabbix 3.x for Docker version 1.7 and later. 5 | 6 | If you experience any software defects related to this module, please notify the author by submitting an issue on [Github](https://github.com/rpsedlak/zabbix-docker/issues). Please make sure to include version specifics and as much configuration information as you can. 7 | 8 | ## 12/3/2020 9 | For a while now, I've had no time to test and maintain this code. That said, I do review and accept pull requests from those that wish to contribute. 10 | 11 | ## Installation Instructions: 12 | * Run package.sh to create the ZabbixDocker.tar.gz file. 13 | * Copy the ZabbixDocker.tar.gz file to necessary servers. 14 | * On the server: tar zxvf ZabbixDocker.tar.gz. It is recommended that this is done in it's own directory. 15 | * Run install.sh. Please note that this assumes that the Zabbix agent files are located at /etc/zabbix/zabbix_agentd.d/. If this is not the case as in an Ubuntu installation then please add the directory as a parameter to install.sh. 16 | * Restart the zabbix-agent process. 17 | * Import the ZabbixDockerTemplate.xml file into Zabbix using the GUI. You can do this from your local computer. 18 | 19 | ## Files: 20 | * userparameter_zabbixdocker.conf - Client-side agent parameter definition 21 | * ZabbixDockerTemplate.xml - File to be imported into Zabbix UI for "Template App Docker" template 22 | * zabbix-docker-discover.py - Python script to provide docker instance discovery. 23 | * zabbix-docker-stats.py - Python helper script to provide information from 'docker stats' 24 | * zabbix-docker-convert.py - Python helper script to convert byte calculations (i.e. GB -> B) 25 | 26 | ## Notes: 27 | * Docker 1.7.1 seems to have an issue where it stops responding after so many commands are issued to it. Several workarounds have been attempted but the long term testing has demonstrated that this is still an issue. This issue wasn't present in Docker versions 1.8.x (and later). 28 | * Approximately half of the discovered keys for a container that are available are disabled by default. You may enable these to your taste and needs. The more data you collect the more storage and processing power you will need. 29 | * The "lifetime" setting for discovered containers is 2 days. You may vary this based on your needs through the Zabbix UI. This value only affects the cleanup of containers that are no longer available. 30 | 31 | ## Testing Information: 32 | * This module was tested using CentOS 6.7, CentOS 7.1, and Ubuntu 14.04 agents and Zabbix server 2.0.16, 2.2.11, and 2.4.7 running on CentOS 6.7. The Docker versions were 1.7.1 and 1.9.1 used for testing. 33 | * 2/11/2018 - This module was tested using CentOS 7.4 and Zabbix server 3.4 running CentOS 7.4. The Docker version was 1.12.6. 34 | 35 | ## Disclaimer: 36 | * This code is provided without warranty and responsibility rests completely on the end user for testing, etc. The author is not responsible for any production issues in any way. 37 | * This code is licensed under [GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). 38 | 39 | ## Note Bene: 40 | ### If you are using this code successfully, please drop me a line at [richard.p.sedlak@gmail.com](mailto:richard.p.sedlak@gmail.com). I'm just curious if anyone is using it successfully. 41 | --------------------------------------------------------------------------------