AnsibleのPlaybook開発環境をパフォーマンスチューニングしてみた

More than 1 year has passed since last update.

スクラップ・アンド・ビルド方式で Ansible の Playbook を開発する場合に、Bootstrapping / Provisioning / Testing に掛かる時間を短縮するために試したことのメモ

準備

  • Ansible + Vagrant 以外でホストにインストールされているもの

    ./Gemfile
    source 'https://rubygems.org'
    
    gem 'rake'
    gem 'serverspec'
    gem 'highline'
    gem 'test-kitchen'
    gem 'kitchen-ansible'
    gem 'kitchen-vagrant', :github => 'test-kitchen/kitchen-vagrant', :branch => 'master'
    gem 'busser-rspec'
    
    vagrant_plugin_list
    vagrant-share (1.1.3, system)
    vagrant-vbguest (0.10.0)
    
    • dotless-de/vagrant-vbguest
      • VM の VirtualBox Guest Additions のインストール状況を確認して、必要があれば自動的にインストールしてくれるプラグイン
    インストール
       vagrant plugin install vagrant-vbguest
    
  • 実行時間を計測するために用意したもの

    • test-kitchen + kitchen-vagrant + kitchen-ansible で利用するもの
    ./.kitchen.yml
    ---
    driver:
      name: vagrant
      provision: true
      vagrantfiles:
        - ./Vagrantfile.rb
      box: hansode/centos-6.6-x86_64
      vm_hostname: kitchen-vagrant
      customize:
        cpuexecutioncap: 90
        memory: 2048
    
    provisioner:
      name: ansible_playbook
    
    platforms:
      - name: centos6
    
    suites:
      - name: provisioning
        provisioner:
          hosts: kitchen-vagrant
          playbook: provisioning.yml
    
    ./Vagrantfile.rb
    Vagrant.configure('2') do |c|
    
    end
    
    # ここでは特に設定が追加されていない状態
    
    ./provisioning.yml
    ---
    - hosts:        127.0.0.1
      connection:   local
      user:         vagrant
      sudo:         yes
      tasks:
    
      - name: install the 'Development tools'
        yum:  name="@Development tools" state=latest
    
      - name: install the latest version of Apache
        yum: name=httpd state=latest
    
      - name: start service httpd, if not running
        service: name=httpd state=started
    
      - name: enable service httpd, and not touch the running state
        service: name=httpd enabled=yes
    
    • test-kitchen + busser-rspec + Serverspec で利用するもの
      • Serverspec のテストコードはある程度の実行時間が必要となるように適当に作成した
    ./test/integration/provisioning/rspec/でserverspec-initを実行
    serverspec-init
    
    ----------------------------------
    Select OS type:
    
      1) UN*X
      2) Windows
    
    Select number: 1
    
    Select a backend type:
    
      1) SSH
      2) Exec (local)
    
    Select number: 2
    
     + spec/
     + spec/localhost/
     + spec/localhost/sample_spec.rb
     + spec/spec_helper.rb
     + Rakefile
     + .rspec
    -----------------------------------
    
    ./test/integration/provisioning/rspec/Gemfile
    source 'https://rubygems.org'
    
    gem 'serverspec'
    
    ./test/integration/provisioning/rspec/spec/localhost/provisioning_spec.rb
    require 'spec_helper'
    
    enable_service = %w(acpid auditd crond ip6tables iptables kdump
                        mdmonitor netfs network nfslock ntpd ntpdate
                        rpcbind rpcgssd rsyslog sshd sysstat udev-post
                        vboxadd vboxadd-service vboxadd-x11)
    
    unable_service = %w(htcacheclean netconsole nfs postfix rdisc
                        restorecond rpcsvcgssd saslauthd svnserve)
    
    enable_service.each do |service|
      describe service("#{service}") do
        it { should be_enabled.with_level(3) }
      end
    end
    
    unable_service.each do |service|
      describe service("#{service}") do
        it { should_not be_enabled.with_level(3) }
      end
    end
    
    command("rpm -qai |grep Name |grep Relocations |awk '{print $3}'").stdout.each_line do |pkg|
      describe package("#{pkg.chomp}") do
        it { should be_installed }
      end
    end
    
    describe command('ls -al /') do
      its(:stdout) { should match /bin/ }
    end
    
    describe file('/etc/passwd') do
      it { should be_file }
    end
    
    describe file('/var/log/httpd') do
      it { should be_directory }
    end
    
    describe file('/etc/httpd/conf/httpd.conf') do
      its(:content) { should match /^#ServerName www\.example\.com\:80$/ }
    end
    
    describe file('/') do
      it { should be_mounted }
    end
    
    command("awk -F\":\" '{ print $1}' /etc/passwd").stdout.each_line do |user|
      describe user("#{user.chomp}") do
        it { should exist }
      end
    end
    
    command("awk -F\":\" '{ print $1}' /etc/group").stdout.each_line do |group|
      describe group("#{group.chomp}") do
        it { should exist }
      end
    end
    
    describe interface('eth0') do
      it { should exist }
    end
    
    

実行時間の計測方法

test-kitchen を利用して実行時間を計測する

  • VagrantでVMを起動するだけ

    • => kitchen create
    kitchen_create
    kitchen create
    
  • VagrantでVMを起動してAnsibleのPlaybookを実行する

    • => kitchen create => kitchen setup
    kitchen_setup
    kitchen setup
    
  • VagrantでVMを起動してAnsibleのPlaybookを実行してServerspecでテストを実施する

    • => kitchen create => kitchen setup => kitchen verify
    kitchen_test
    kitchen test --destroy=never
    

最初の状態

  • 計測結果

    kitchen_create
    1回目: Kitchen is finished. (3m20.09s)
    2回目: Kitchen is finished. (2m5.09s)
    3回目: Kitchen is finished. (1m57.72s)
    
    kitchen_setup
    1回目: Kitchen is finished. (14m19.28s)
    2回目: Kitchen is finished. (10m6.04s)
    3回目: Kitchen is finished. (10m17.32s)
    
    kitchen_test
    1回目: Kitchen is finished. (12m41.69s)
          => Finished verifying <provisioning-centos6> (1m45.20s).
    2回目: Kitchen is finished. (12m31.62s)
          => Finished verifying <provisioning-centos6> (1m41.59s).
    3回目: Kitchen is finished. (12m15.51s)
          => Finished verifying <provisioning-centos6> (1m45.45s).
    
    # kitchen verify によるテストの部分だけに限れば、 1m40s ~ 1m45s くらい
    
  • 課題 (2015/03/02の時点では)

    1. kitchen_create / kitchen_setup / kitchen_test 共通
      1. vagrant-vbguest プラグインによって、 VirtualBox Guest Additions がアップデート (4.3.22) されている
        1. 依存しているパッケージファイルのアップデート
        2. 旧バージョンをアンインストール
        3. 新バージョンをインストール
    2. kitchen_setup / kitchen_test 共通
      1. kitchen-ansible プラグインによって、 Ansible がインストールされている
        1. yum に epel リポジトリを追加
        2. epel リポジトリのメタデータをキャッシュする
        3. Python などの依存しているパッケージファイルをインストール
        4. Ansible のインストール
      2. test-kitchen によって Chef がインストールされている
        1. オムニバスインストーラーが実行されている
        2. thor がインストールされている
        3. busser がインストールされている
    3. kitchen_test
      1. bundle install が実行されている
        1. ./test/integration/provisioning/rspec/Gemfile によって Serverspec などがインストールされている
    4. kitchen_setup
      1. Ansible が ./provisioning.yml から yum groupinstall -y 'Development tools' を実行している
        1. yum リポジトリからメタデータをダウンロードしている

test-kitchen 専用の Vagrant Box Image を作成する

  • Vagrant Box Image に chef をオムニバスインストールする
    • chef/vagrant-omnibus
      • VM の Chef インストール状況を確認し、必要に応じて自動的にインストールしてくれるプラグイン
インストール
   vagrant plugin install vagrant-omnibus
  • Vagrant Box Image を作成するための Vagrantfile を用意する

    ./vagrant_package/Vagrantfile
    Vagrant.configure('2') do |c|
    
      ## Vagrant Box Base Image
      c.vm.box = "hansode/centos-6.6-x86_64"
    
      ## Plugin
      if Vagrant.has_plugin?("vagrant-omnibus")
        c.omnibus.chef_version = :latest
      end
    
    ## Provision
        c.vm.provision "shell",inline: "yum clean all && yum update -y && yum install -y libyaml"
        c.vm.provision "shell",inline: "rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm"
        c.vm.provision "shell",inline: "yum install -y ansible"
        c.vm.provision "shell",inline: "yum clean all"
    
        c.vm.provision "shell",inline: "sh -c 'BUSSER_ROOT=\"/tmp/busser\" GEM_HOME=\"/tmp/busser/gems\" GEM_PATH=\"/tmp/busser/gems\" GEM_CACHE=\"/tmp/busser/gems/cache\" ; export BUSSER_ROOT GEM_HOME GEM_PATH GEM_CACHE; if ! sudo -E /opt/chef/embedded/bin/gem list busser -i >/dev/null; then sudo -E /opt/chef/embedded/bin/gem install busser serverspec --no-rdoc --no-ri; fi; gem_bindir=`/opt/chef/embedded/bin/ruby -rrubygems -e \"puts Gem.bindir\"`; sudo -E ${gem_bindir}/busser setup; sudo -E /tmp/busser/bin/busser plugin install busser-rspec'"
    
    end
    
  • Vagrant Box Image を作成する

    boxイメージの作成コマンドと登録コマンドの実行
    cd ./vagrant_package
    vagrant up
    vagrant reload
    vagrant halt
    vagrant package
    vagrant box add -f libero18/test-kitchen ./package.box
    rm -f ./package.box
    vagrant destroy -f
    cd ..
    
    # libero18/test-kitchen は任意の名前で登録済みのものは、 vagrant box list で確認可能
    
  • 作成した Vagrant Box Image を test-kitchen から利用する

    ./.kitchen.yml
    ---
    driver:
      name: vagrant
      provision: true
      vagrantfiles:
        - ./Vagrantfile.rb
      box: libero18/test-kitchen
      vm_hostname: kitchen-vagrant
      customize:
        cpuexecutioncap: 90
        memory: 2048
    
    provisioner:
      name: ansible_playbook
    
    platforms:
      - name: centos6
    
    suites:
      - name: provisioning
        provisioner:
          hosts: kitchen-vagrant
          playbook: provisioning.yml
    
  • 計測結果

    kitchen_create
    1回目: Kitchen is finished. (0m57.50s)
    2回目: Kitchen is finished. (0m59.78s)
    3回目: Kitchen is finished. (0m53.06s)
    
    kitchen_setup
    1回目: Kitchen is finished. (2m43.31s)
    2回目: Kitchen is finished. (2m42.10s)
    3回目: Kitchen is finished. (2m58.98s)
    
    kitchen_test
    1回目: Kitchen is finished. (2m59.55s)
          => Finished verifying <provisioning-centos6> (0m25.64s).
    2回目: Kitchen is finished. (3m11.04s)
          => Finished verifying <provisioning-centos6> (0m25.00s).
    3回目: Kitchen is finished. (3m3.56s)
          => Finished verifying <provisioning-centos6> (0m25.15s).
    
    # kitchen verify によるテストの部分だけに限れば、 0m25s ~ 0m26s くらい
    
  • この段階での残課題 (2015/03/02の時点では)

    1. kitchen_create / kitchen_setup / kitchen_test 共通
      1. vagrant-vbguest プラグインによって、 VirtualBox Guest Additions がアップデート (4.3.22) されている
        1. 依存しているパッケージファイルのアップデート
        2. 旧バージョンをアンインストール
        3. 新バージョンをインストール
    2. kitchen_setup / kitchen_test 共通
      1. kitchen-ansible プラグインによって、 Ansible がインストールされている
        1. yum に epel リポジトリを追加
        2. epel リポジトリのメタデータをキャッシュする
        3. Python などの依存しているパッケージファイルをインストール
        4. Ansible のインストール
      2. test-kitchen によって Chef がインストールされている
        1. オムニバスインストーラーが実行されている
        2. thor がインストールされている
        3. busser がインストールされている
    3. kitchen_test
      1. bundle install が実行されている
        1. ./test/integration/provisioning/rspec/Gemfile によって Serverspec がインストールされている
    4. kitchen_setup
      1. Ansible が ./provisioning.yml から yum groupinstall -y 'Development tools' を実行している
        1. yum リポジトリからメタデータをダウンロードしている

パッケージ管理システムのメタデータなどをホストにキャッシュして VM とディレクトリ共有する

  • fgrehm/vagrant-cachier

    インストール
    vagrant plugin install vagrant-cachier
    
  • test-kitchen から作成した VM で vagrant-cachier プラグインを利用するように設定を追加する

    ./Vagrantfile.rb
    Vagrant.configure('2') do |c|
    
      ## Plugin
      if Vagrant.has_plugin?("vagrant-cachier")
        c.cache.scope = :box
      end
    
    end
    
  • 計測結果

    kitchen_create
    1回目: Kitchen is finished. (1m0.70s)
    2回目: Kitchen is finished. (0m58.51s)
    3回目: Kitchen is finished. (0m58.22s)
    
    kitchen_setup
    1回目: Kitchen is finished. (2m24.12s)
    2回目: Kitchen is finished. (2m22.79s)
    3回目: Kitchen is finished. (2m26.45s)
    
    # 数値のみで判断すると誤差と言える程度だが、ネットワークやリポジトリの影響でダウンロード時間にバラツキが出ないことが最大のメリット
    
    kitchen_test
    1回目: Kitchen is finished. (2m49.35s)
          => Finished verifying <provisioning-centos6> (0m25.15s).
    2回目: Kitchen is finished. (2m45.61s)
          => Finished verifying <provisioning-centos6> (0m23.86s).
    3回目: Kitchen is finished. (2m46.16s)
          => Finished verifying <provisioning-centos6> (0m24.27s).
    
    # テスト時間の短縮化について、 vagrant-cachier プラグインの導入による影響はない
    
  • この段階での残課題 (2015/03/02の時点では)

    1. kitchen_create / kitchen_setup / kitchen_test 共通
      1. vagrant-vbguest プラグインによって、 VirtualBox Guest Additions がアップデート (4.3.22) されている
        1. 依存しているパッケージファイルのアップデート
        2. 旧バージョンをアンインストール
        3. 新バージョンをインストール
    2. kitchen_setup / kitchen_test 共通
      1. kitchen-ansible プラグインによって、 Ansible がインストールされている
        1. yum に epel リポジトリを追加
        2. epel リポジトリのメタデータをキャッシュする
        3. Python などの依存しているパッケージファイルをインストール
        4. Ansible のインストール
      2. test-kitchen によって Chef がインストールされている
        1. オムニバスインストーラーが実行されている
        2. thor がインストールされている
        3. busser がインストールされている
    3. kitchen_test
      1. bundle install が実行されている
        1. ./test/integration/provisioning/rspec/Gemfile によって Serverspec がインストールされている
    4. kitchen_setup
      1. Ansible が ./provisioning.yml から yum groupinstall -y 'Development tools' を実行している
        1. yum リポジトリからメタデータをダウンロードしている

その他

上記のやり方では Ansible および Serverspec は、ローカルモードで実行されるため SSH 経由で実行する場合には意味がないので、その場合は以下のようなものを試してみると良さそう

ただし、個人的には test-kitchen/kitchen-vagrant の代わりに test-kitchen/kitchen-ec2test-kitchen/kitchen-digitalocean などの driver を使ってリモートのインスタンスを相手に実行できる仕組みに変えた方が開発環境としては幸せだと思うのでここでの細かい説明などは省略

参考