Skip to content

Commit 2c58508

Browse files
committed
Merge branch 'develop'
2 parents 255f235 + afec4f5 commit 2c58508

File tree

7 files changed

+414
-0
lines changed

7 files changed

+414
-0
lines changed

.gitignore

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Created by .gitignore support plugin (hsz.mobi)
2+
3+
### JetBrains template
4+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
5+
6+
*.iml
7+
8+
## Directory-based project format:
9+
.idea/
10+
# if you remove the above rule, at least ignore the following:
11+
12+
# User-specific stuff:
13+
#.idea/workspace.xml
14+
#.idea/tasks.xml
15+
#.idea/dictionaries
16+
17+
# Sensitive or high-churn files:
18+
#.idea/dataSources.ids
19+
#.idea/dataSources.xml
20+
#.idea/sqlDataSources.xml
21+
#.idea/dynamic.xml
22+
#.idea/uiDesigner.xml
23+
24+
# Gradle:
25+
#.idea/gradle.xml
26+
#.idea/libraries
27+
28+
# Mongo Explorer plugin:
29+
#.idea/mongoSettings.xml
30+
31+
## File-based project format:
32+
*.ipr
33+
*.iws
34+
35+
## Plugin-specific files:
36+
37+
# IntelliJ
38+
out/
39+
40+
# mpeltonen/sbt-idea plugin
41+
.idea_modules/
42+
43+
# JIRA plugin
44+
atlassian-ide-plugin.xml
45+
46+
# Crashlytics plugin (for Android Studio and IntelliJ)
47+
com_crashlytics_export_strings.xml
48+
crashlytics.properties
49+
crashlytics-build.properties
50+
51+
52+
### Python template
53+
# Byte-compiled / optimized / DLL files
54+
__pycache__/
55+
*.py[cod]
56+
57+
# C extensions
58+
*.so
59+
60+
# Distribution / packaging
61+
.Python
62+
env/
63+
build/
64+
develop-eggs/
65+
dist/
66+
downloads/
67+
eggs/
68+
lib/
69+
lib64/
70+
parts/
71+
sdist/
72+
var/
73+
*.egg-info/
74+
.installed.cfg
75+
*.egg
76+
77+
# PyInstaller
78+
# Usually these files are written by a python script from a template
79+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
80+
*.manifest
81+
*.spec
82+
83+
# Installer logs
84+
pip-log.txt
85+
pip-delete-this-directory.txt
86+
87+
# Unit test / coverage reports
88+
htmlcov/
89+
.tox/
90+
.coverage
91+
.cache
92+
nosetests.xml
93+
coverage.xml
94+
95+
# Translations
96+
*.mo
97+
*.pot
98+
99+
# Django stuff:
100+
*.log
101+
102+
# Sphinx documentation
103+
docs/_build/
104+
105+
# PyBuilder
106+
target/
107+
108+

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Simple Monitoring Tools for Docker
2+
3+
A set of scripts I use on a server with a Docker instance. All monitoring scripts are used with [Zabbix](http://www.zabbix.org).
4+
5+
## containerCount.sh
6+
7+
Very simple shell script to count the number of containers on the docker instance.
8+
9+
### Usage
10+
11+
containerCount.sh (all|running|crashed)
12+
13+
Print the number of containers on the Docker instance.
14+
15+
16+
## containerHelpery.py
17+
18+
A script inspired from [a nice article](http://blog.docker.com/2013/10/gathering-lxc-docker-containers-metrics/) about metric. It enables you to get several metrics from a running container:
19+
20+
- the user or system cpu used by the container
21+
- the memory used by the container
22+
- the ip address of the container
23+
- the container status: running, stopped, crashed or paused
24+
- the container's network activity
25+
26+
### Usage
27+
28+
containerHelper.py [-h] container {cpu,ip,memory,network,status} ...
29+
30+
positional arguments:
31+
container Container name
32+
33+
optional arguments:
34+
-h, --help show this help message and exit
35+
36+
Counters:
37+
Available counters
38+
39+
{cpu,ip,memory,network,status}
40+
cpu Display CPU usage
41+
ip Display IP Address
42+
memory Display memory usage
43+
network Display network usage
44+
status Display the container status
45+
46+
Additional information may be required depending on the counter prameter.
47+
48+
## dockerDDNS.py
49+
50+
A daemon to update a dynamic DNS when Docker starts containers. Designed to be used with bind9. Have a look at [this page](https://www.erianna.com/nsupdate-dynamic-dns-updates-with-bind9) to setup correctly your DNS before using it.
51+
52+
### Usage
53+
54+
dockerDDNS.py [-h] --key KEY [--server SERVER] --domain DOMAIN
55+
[--zone ZONE] [--log-level LOG_LEVEL]
56+
[--log-file LOG_FILE]
57+
58+
optional arguments:
59+
-h, --help show this help message and exit
60+
--key KEY Path to the dynamic dns key
61+
--server SERVER IP/Hostname of the server to update
62+
--domain DOMAIN The domain to be updated
63+
--zone ZONE The zone to be updated (default to the domain)
64+
--log-level LOG_LEVEL
65+
Log level to display
66+
--log-file LOG_FILE Where to put the logs
67+
68+
### Installation
69+
70+
This script is designed to run as a daemon after Docker's startup. You'll find in the `upstart` directory a configuration file to have it launched on boot.
71+

containerCount.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
function countContainers() {
4+
docker ps -q $1 | wc -l
5+
}
6+
7+
function countCrashedContainers() {
8+
docker ps -a | grep -v -F 'Exited (0)' | grep -c -F 'Exited ('
9+
}
10+
11+
TYPE=${1-all}
12+
13+
case $TYPE in
14+
running) COUNT_FUNCTION="countContainers"; shift;;
15+
crashed) COUNT_FUNCTION="countCrashedContainers"; shift;;
16+
all) COUNT_FUNCTION="countContainers -a"; shift;;
17+
esac
18+
19+
$COUNT_FUNCTION

containerHelper.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python3
2+
3+
__author__ = 'Christophe Labouisse'
4+
5+
import argparse
6+
import re
7+
import os
8+
9+
from docker import Client
10+
from docker.utils import kwargs_from_env
11+
12+
13+
def display_cpu(args):
14+
detail = c.inspect_container(args.container)
15+
if bool(detail["State"]["Running"]):
16+
container_id = detail['Id']
17+
cpu_usage = {}
18+
with open('/sys/fs/cgroup/cpuacct/docker/' + container_id + '/cpuacct.stat', 'r') as f:
19+
for line in f:
20+
m = re.search(r"(system|user)\s+(\d+)", line)
21+
if m:
22+
cpu_usage[m.group(1)] = int(m.group(2))
23+
if args.type == "all":
24+
cpu = cpu_usage["system"] + cpu_usage["user"]
25+
else:
26+
cpu = cpu_usage[args.type]
27+
user_ticks = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
28+
print(float(cpu) / user_ticks)
29+
else:
30+
print(0)
31+
32+
33+
def display_ip(args):
34+
detail = c.inspect_container(args.container)
35+
print(detail['NetworkSettings']['IPAddress'])
36+
37+
38+
def display_memory(args):
39+
detail = c.inspect_container(args.container)
40+
if bool(detail["State"]["Running"]):
41+
container_id = detail['Id']
42+
with open('/sys/fs/cgroup/memory/docker/' + container_id + '/memory.stat', 'r') as f:
43+
for line in f:
44+
m = re.search(r"total_rss\s+(\d+)", line)
45+
if m:
46+
print(m.group(1))
47+
return
48+
49+
print(0)
50+
51+
52+
def display_network(args):
53+
detail = c.inspect_container(args.container)
54+
if bool(detail["State"]["Running"]):
55+
ifconfig = c.execute(args.container, "ifconfig eth0")
56+
m = re.search(("RX" if args.direction == "in" else "TX") + r" bytes:(\d+)", str(ifconfig))
57+
if m:
58+
print(m.group(1))
59+
else:
60+
b = c.execute(args.container, "cat /sys/devices/virtual/net/eth0/statistics/"+("rx" if args.direction == "in" else "tx")+"_bytes")
61+
if re.match(r"\s*\d+\s*", b):
62+
print(b)
63+
else:
64+
print(0)
65+
else:
66+
print(0)
67+
68+
69+
def display_status(args):
70+
detail = c.inspect_container(args.container)
71+
state = detail["State"]
72+
if bool(state["Paused"]):
73+
print(1) # Paused
74+
elif bool(state["Running"]):
75+
print(0) # Running
76+
elif int(state["ExitCode"]) == 0:
77+
print(2) # Stopped
78+
else:
79+
print(3) # Crashed
80+
81+
82+
parser = argparse.ArgumentParser()
83+
84+
parser.add_argument("container", help="Container name")
85+
86+
subparsers = parser.add_subparsers(title="Counters", description="Available counters", dest="dataType")
87+
88+
cpu_parser = subparsers.add_parser("cpu", help="Display CPU usage")
89+
cpu_parser.add_argument("type", choices=["system", "user", "all"])
90+
cpu_parser.set_defaults(func=display_cpu)
91+
92+
ip_parser = subparsers.add_parser("ip", help="Display IP Address")
93+
ip_parser.set_defaults(func=display_ip)
94+
95+
memory_parser = subparsers.add_parser("memory", help="Display memory usage")
96+
memory_parser.set_defaults(func=display_memory)
97+
98+
network_parser = subparsers.add_parser("network", help="Display network usage")
99+
network_parser.add_argument("direction", choices=["in", "out"])
100+
network_parser.set_defaults(func=display_network)
101+
102+
status_parser = subparsers.add_parser("status", help="Display the container status")
103+
status_parser.set_defaults(func=display_status)
104+
105+
c = Client(**(kwargs_from_env()))
106+
107+
args = parser.parse_args()
108+
args.func(args)

dockerDDNS.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env python3
2+
3+
__author__ = 'xtof'
4+
5+
import argparse
6+
import re
7+
import logging
8+
from subprocess import Popen, PIPE
9+
from docker import Client
10+
from docker.utils import kwargs_from_env
11+
12+
zone_update_template = """server {0}
13+
zone {1}.
14+
update delete {2}.{3}
15+
update add {2}.{3} 60 A {4}
16+
"""
17+
zone_update_add_alias_template = """update delete {0}.{1}
18+
update add {0}.{1} 600 CNAME {2}.{1}.
19+
"""
20+
21+
parser = argparse.ArgumentParser()
22+
23+
parser.add_argument("--key", required=True, help="Path to the dynamic dns key")
24+
parser.add_argument("--server", help="IP/Hostname of the server to update", default="127.0.0.1")
25+
parser.add_argument("--domain", help="The domain to be updated", required=True)
26+
parser.add_argument("--zone", help="The zone to be updated (default to the domain)")
27+
parser.add_argument("--log-level", help="Log level to display", default="INFO")
28+
parser.add_argument("--log-file", help="Where to put the logs", default="/var/log/docker-ddns.log")
29+
30+
args = parser.parse_args()
31+
32+
logging.basicConfig(level=getattr(logging,args.log_level.upper()),
33+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
34+
filename=(args.log_file if args.log_file != '-' else None))
35+
36+
if args.zone is None:
37+
args.zone = args.domain
38+
39+
logging.info("Starting with arguments %s", args)
40+
41+
c = Client(**(kwargs_from_env()))
42+
43+
# Too bad docker-py does not currently support docker events
44+
p = Popen(['docker', 'events'], stdout=PIPE)
45+
46+
while True:
47+
line = p.stdout.readline()
48+
if line != '':
49+
text_line = line.decode().rstrip()
50+
logging.debug("Read line %s", text_line)
51+
m = re.search(r"\s+([0-9a-f]{64}):.*\s+([a-z]+)\s*$", text_line)
52+
if m:
53+
event = m.group(2)
54+
container_id = m.group(1)
55+
logging.debug("Got event %s for container %s", event, container_id)
56+
57+
if event == "start":
58+
detail = c.inspect_container(container_id)
59+
container_hostname = detail["Config"]["Hostname"]
60+
container_name = detail["Name"].split('/',1)[1]
61+
container_ip = detail["NetworkSettings"]["IPAddress"]
62+
63+
logging.info("Updating %s to ip (%s|%s) -> %s", container_id, container_hostname, container_name, container_ip)
64+
nsupdate = Popen(['nsupdate', '-k', args.key], stdin=PIPE)
65+
nsupdate.stdin.write(bytes(zone_update_template.format(args.server, args.zone, container_hostname, args.domain, container_ip), "UTF-8"))
66+
if container_name != container_hostname:
67+
nsupdate.stdin.write(bytes(zone_update_add_alias_template.format(container_name, args.domain, container_hostname), "UTF-8"))
68+
nsupdate.stdin.write(bytes("send\n", "UTF-8"))
69+
nsupdate.stdin.close()
70+
elif event == "destroy":
71+
logging.info("Destroying %s", container_id)
72+
else:
73+
logging.warning("Couldn't match RE in line %s", text_line)
74+
else:
75+
print("Done return code: ", p.returncode)
76+
break
77+
78+
# 2014-11-28T15:32:04.000000000+01:00 a3d66b00acc9adbdbdbc91cc664d2d94b6a07cc4295c5cf54fcc595e2aa92a43: (from mongo:latest) restart

0 commit comments

Comments
 (0)