Ansible Performance Profiling

From tannerjc wiki
Jump to: navigation, search
  1. pip install --user yappi
  2. python -m yappi -o yappi.out -f pstat $(which ansible-playbook) $EARGS -i $INVENTORY $PLAYBOOK
  3. python -m pstats yappi.out
    • sort cumulative
    • stats 10
$ python -m pstats yappi.out 
Welcome to the profile statistics browser.
yappi.out% sort cumulative
yappi.out% stats 10
Thu Jun 15 15:13:33 2017    yappi.out

         696168 function calls (711009 primitive calls) in 1.493 seconds

   Ordered by: cumulative time
   List reduced from 2744 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    1.820    1.820 /var/cache/ansible/ansible.checkout.clean/bin/ansible-playbook:21(<module>)
        3    0.000    0.000    0.966    0.322 /usr/lib/python2.7/site-packages/pkg_resources/__init__.py:95(_SetuptoolsVersionMixin)
        6    0.000    0.000    0.922    0.154 /usr/lib/python2.7/site-packages/pkg_resources/__init__.py:577(IResourceProvider)
        1    0.000    0.000    0.911    0.911 /usr/lib64/python2.7/site-packages/yaml/__init__.py:87(safe_load)
        1    0.003    0.003    0.911    0.911 /usr/lib64/python2.7/site-packages/yaml/__init__.py:64(load)
        2    0.000    0.000    0.908    0.454 /usr/lib64/python2.7/site-packages/yaml/constructor.py:35(SafeLoader.get_single_data)
        1    0.000    0.000    0.857    0.857 /usr/lib64/python2.7/site-packages/yaml/composer.py:29(SafeLoader.get_single_node)
        1    0.000    0.000    0.857    0.857 /usr/lib64/python2.7/site-packages/yaml/composer.py:50(SafeLoader.compose_document)
   1/4010    0.021    0.021    0.857    0.000 /usr/lib64/python2.7/site-packages/yaml/composer.py:63(SafeLoader.compose_node)
    1/618    0.010    0.010    0.856    0.001 /usr/lib64/python2.7/site-packages/yaml/composer.py:117(SafeLoader.compose_mapping_node)


Example outputs

http://tannerjc.net/notebooks/ansible+hosts+forks+items+performance.html

Benchmarking Scripts

top level script

#!/bin/bash
VERSION=$(ansible --version | head -n1 | awk '{print $2}')

PLAYBOOK=ping_benchmark.yml
#PLAYBOOK=file_benchmark.yml
#PLAYBOOK=site.yml
INVENTORY=inventory.py

EARGS="-vvvv -c ssh"
EARGS="-c ssh"
EARGS="-c local"

FORKSTART=9
FORKSTOP=10
FORKINC=1

HOSTSTART=10
HOSTSTOP=100
HOSTINC=10

ITEMSTART=1
ITEMSTOP=10
ITEMINC=1


for HOSTCOUNT in $(seq $HOSTSTART $HOSTINC $HOSTSTOP); do

    let "FORK_START=HOSTCOUNT-1"

    for FORKCOUNT in $(seq $FORK_START $FORKINC $HOSTCOUNT); do

        for ITEMCOUNT in $(seq $ITEMSTART $ITEMINC $ITEMSTOP); do

            export MAXHOSTS=$HOSTCOUNT
            export MAXITEMS=$ITEMCOUNT
            OUTDIR=pstat_data/$VERSION
            if [ ! -d $OUTDIR ]; then
                mkdir -p $OUTDIR
            fi
            OUTFILE=$OUTDIR/H${HOSTCOUNT}_F${FORKCOUNT}_I${ITEMCOUNT}.pstat

            echo $OUTFILE

            python -m yappi -o $OUTFILE -f pstat $(which ansible-playbook) \
                --forks=$FORKCOUNT $EARGS -i $INVENTORY $PLAYBOOK
        done
    done
done

inventory script

#!/usr/bin/env python

import json
import os
import sys
import uuid
from pprint import pprint

MAXHOSTS = os.environ.get('MAXHOSTS') or 10
if not isinstance(MAXHOSTS, int):
    MAXHOSTS = int(MAXHOSTS)

MAXITEMS = os.environ.get('MAXITEMS') or 10
if not isinstance(MAXITEMS, int):
    MAXITEMS = int(MAXITEMS)

INV = {}
INV['_meta'] = {'hostvars': {}}

groups = ['all']
hosts = ['x' + str(x) for x in range(0, MAXHOSTS)]

for idx, group in enumerate(groups):
    INV[group] = {}
    INV[group]['children'] = []
    INV[group]['vars'] = {}
    INV[group]['hosts'] = [x for x in hosts]

for host in hosts:
    INV['_meta']['hostvars'][host] = {}
    INV['_meta']['hostvars'][host]['ansible_connection'] = 'local'
    INV['_meta']['hostvars'][host]['ansible_ssh_host'] = 'el7host'
    INV['_meta']['hostvars'][host]['ansible_ssh_user'] = 'root'
    INV['_meta']['hostvars'][host]['ansible_ssh_private_key_file'] = '~/.ssh/id_TEST'

    items = [str(x) for x in range(0, MAXITEMS)]
    items = ['/opt/test/%s' % host + x for x in items]
    items = [x + '/' + str(uuid.uuid4()) for x in items]
    INV['_meta']['hostvars'][host]['TESTLIST'] = items

print json.dumps(INV, indent=2)

playbook

---
# from https://gist.github.com/michelleperz/fe3a0eb4eda888221229730e34b28b89
- hosts: all
  gather_facts: no
  tasks:

    - ping:
      with_items: "{{ TESTLIST }}"

summary script

#!/usr/bin/env python

import json
import operator
import os
import pstats
from pprint import pprint

BASEDIR = 'pstat_data'

records = []
for root, dirs, files in os.walk(BASEDIR):
    for x in files:

        VERSION = os.path.basename(root)
        cfile = x.replace('.pstat', '')
        HOSTS = cfile.split('_')[0].replace('H', '')
        FORKS = cfile.split('_')[1].replace('F', '')
        ITEMS = cfile.split('_')[2].replace('I', '')

        filepath = os.path.join(root, x)

        ps = pstats.Stats(filepath)
        sortby = 'cumulative'
        sb = ps.strip_dirs().sort_stats(sortby)
        DURATION = sb.total_tt

        print('{0} {1:>4} {2:>4} {3:>4} {4:>20}'.format(VERSION, HOSTS, FORKS, ITEMS, DURATION))

        record = {
            'version': VERSION,
            'hosts': int(HOSTS),
            'forks': int(FORKS),
            'items': int(ITEMS),
            'time': DURATION
        }
        records.append(record)

records.sort(key=operator.itemgetter('time'))
pprint(records[-10:])

with open('durations.json', 'wb') as f:
    f.write(json.dumps(records))

import epdb; epdb.st()

resources