Skip to content

Instantly share code, notes, and snippets.

@thbkrkr
Last active June 8, 2022 21:07
Show Gist options
  • Save thbkrkr/5d3c11a53669e18e1f46 to your computer and use it in GitHub Desktop.
Save thbkrkr/5d3c11a53669e18e1f46 to your computer and use it in GitHub Desktop.
Get containers info and stats using the Docker Remote API
#!/bin/bash -eu
#
# @description Get containers info and stats using the Docker Remote API
# @deps curl, jq
# @usage docker-stats.sh host-42 101.0.0.42
DOCKER_HOSTNAME=$1
DOCKER_IP=$2
DOCKER_PORT=2376
CERT_DIR=~/.docker/machines/.client
VERBOSE=1
[[ "$(which curl 2>/dev/null)" == "" ]] && echo "error: install curl (http://curl.haxx.se/download.html)" && exit 1
[[ "$(which jq 2>/dev/null)" == "" ]] && echo "error: install jq (http://stedolan.github.io/jq/download/)" && exit 1
api() {
local endpoint=$1
local options=$2
curl -sk $options \
https://$DOCKER_IP:$DOCKER_PORT/$endpoint \
--cert $CERT_DIR/cert.pem \
--key $CERT_DIR/key.pem \
--cacert $CERT_DIR/ca.pem
}
PREFIX=/tmp/docker-container-$(date +%s)
w_container_json() {
local id=$1
log "GET containers/$id/json"
api containers/$id/json "" > $PREFIX-$id-json
name=$(r "$(cat $PREFIX-$id-json)" ".Name" | sed -e 's|"||' -e 's|/||')
echo "$name" > $PREFIX-$id-name
log "SET name $name"
}
w_container_stats() {
local id=$1
log "GET /containers/$containerId/stats"
api containers/$id/stats "-m 2" | tail -1 > $PREFIX-$id-stats
}
wf() {
local id=$1
local kind=$2
cat $PREFIX-$id-$kind >> JSON
}
w() {
echo "$1" >> JSON
}
r() {
local json="$1"
local jqexpr="$2"
echo "$json" | jq -r "$jqexpr"
}
log() {
[[ $VERBOSE -eq 1 ]] && echo " [dstats] $1" || nothing=todo
}
################################
> JSON
log "GET /containers/json"
containers="$(api containers/json "")"
total_containers=$(r "$containers" '. | length')
w '{
"'$DOCKER_HOSTNAME'": {
"total_containers": '$total_containers',
"containers": ['
s=""; nb_proc=0
while read containerId; do
w_container_json $containerId &
sleep 0.0$RANDOM
w_container_stats $containerId &
nb_proc=$(($nb_proc+2))
if [ "$nb_proc" -ge $((total_containers*2)) ]; then
wait
nb_proc=0
fi
s=","
done < <(r "$containers" '.[] | .Id')
wait
log "Write JSON"
s=""
while read containerId; do
name=$(cat $PREFIX-$containerId-name)
w $s' {
"'$name'": {
"info": '
wf $containerId json
w ',
"stats": '
wf $containerId stats
w '
}
}'
s=","
done < <(r "$containers" '.[] | .Id')
w ' ]
}
}'
rm -f $PREFIX*
cat JSON | jq . >/dev/null 2>&1 \
&& log "Successful" || log "Error"
#!/bin/bash -eu
#
# @description Get containers info and stats using the Docker Remote API
# @deps jq, curl or nc
# @usage docker-stats.sh host-42 101.0.0.42
#
if [ -S /var/run/docker.sock ]; then
API=api_socket
else
API=api_http
fi
case $API in
api_http)
DOCKER_HOSTNAME=${1:-$(ls -1 /ops/machine/machines | head -1)} ;;
esac
api_http() {
declare endpoint=$1 options=${2:-""}
DOCKER_IP=$(docker-machine ip $DOCKER_HOSTNAME 2> /dev/null)
DOCKER_PORT=2376
CERT_DIR=$MACHINE_STORAGE_PATH/machines/$DOCKER_HOSTNAME
curl -sk $options \
--cert $CERT_DIR/cert.pem --key $CERT_DIR/key.pem --cacert $CERT_DIR/ca.pem \
https://$DOCKER_IP:$DOCKER_PORT$endpoint
}
api_socket() {
declare endpoint=$1 options=${2:-""}
echo -ne "GET $endpoint HTTP/1.1\r\n\r\n" \
| nc -U $options /var/run/docker.sock \
| grep '^[{|\[]'
}
stats() {
(
while read container; do
local container_id=$(jq -r .Id <<< $container)
echo '{
"container": '$container',
"stats": '$($API "/containers/$container_id/stats?stream=false" "-q 2")'
}' &
done < <($API /containers/json | jq -c '.[]' | sed "s|/||")
wait
) \
| jq '{
name:.container.Names[0],
image:.container.Image,
mem_usage:.stats.memory_stats.usage,
mem_limit:.stats.memory_stats.limit,
cpu_usage:.stats.precpu_stats.cpu_usage.total_usage,
cpu_usage:.stats.precpu_stats.system_cpu_usage,
rx_bytes:.stats.networks.eth0.rx_bytes,
tx_bytes:.stats.networks.eth0.tx_bytes
}' \
| jq -s .
}
stats
#!/bin/bash -eu
metrics() {
declare cid=$1
echo '{'
container_metric $cid memory memory.stat
echo ","
container_metric $cid cpuacct cpuacct.stat
echo ","
container_metric $cid blkio blkio.io_serviced
echo ","
container_metric $cid blkio blkio.io_service_bytes
echo ","
container_metric $cid blkio blkio.io_service_queued
echo ","
container_metric $cid blkio blkio.sectors
echo '}'
}
container_metric() {
declare cid=$1 metric_type=$2 metric_filename=$3
local cgroup_path=/sys/fs/cgroup
local metric_file=$cgroup_path/$metric_type/docker/$cid/$metric_filename
local comma=
echo $metric_file
if [[ -f $metric_file ]]; then
while read metric; do
tpl="$comma\"$metric_filename.\1\":\"\2\""
sed -r \
"s|([A-Za-z_]*) ([0-9]*)|$tpl|" \
<<< $metric
comma=','
done < <(cat $metric_file)
fi
}
while read cid; do
metrics $cid
done < <(docker ps --no-trunc -q)
FROM alpine:3.7
RUN apk --no-cache add bash jq netcat-openbsd bc
COPY docker-stats.sh /usr/local/bin/docker-stats.sh
ENTRYPOINT ["docker-stats.sh"]
build:
docker build -t krkr/docker-stats .
run:
@docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
krkr/docker-stats
test:
./docker-stats-v$v.sh
@kbroughton
Copy link

another option (requires curl > 7.4 ) if not exposing the http docker api is to change api() method to use
curl --unix-socket /var/run/docker.sock $options

It is possible for container/stats to return an empty string so i made this change
w_container_stats() {
local id=$1

log "GET /containers/$containerId/stats"
api containers/$id/stats "-m 2" | tail -1 > $PREFIX-$id-stats
if [[ ! -s $PREFIX-$id-stats ]];then
    echo "{}" > $PREFIX-$id-stats
fi

}

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