krkr
3/1/2015 - 11:02 PM

Get containers info and stats using the Docker Remote API

Get containers info and stats using the Docker Remote API

#!/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)
#!/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
#
# @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"
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
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"]