search
LoginSignup
2

More than 5 years have passed since last update.

posted at

updated at

Puppetが遅いときはRubyバージョンアップ ?【実測編】

Puppet Advent Calendar 2015の12日目です。
Puppetの性能比較です。9日目に投稿した、OSのRubyと違うバージョンでPuppetを動かす方法でRubyを変えた場合の性能比較。

リソース定義が増えると遅くなる

マニフェストのリソース定義が多すぎると、実行に時間がかかります。なんとなくCPUネックな気がしますが、サーバリソースに余裕がないとかなり遅い場合があります。設定差分チェック、設定変更に時間がかかると使いづらいのでその対策です。

マスター側の対策

マスター/エージェント構成の場合、Puppet3の場合Configuring a Puppet Master Server with Passenger and Apacheを試す、もしくはPuppet4にしてpuppetserver(高速らしい)に乗り換えるなどです。今回こっちは対象外です。

エージェント側の対策

Puppet4のマスター側が速くなったなら、エージェント側も速くなったのか?ということでベンチマークやりました。結果はPuppet4だから速いではなく、エージェント側のRubyをバージョンアップすると速かったとなりました。特に元がRuby1.8.7の場合、2.1.7にすることで実行時間が1/3程度の大幅改善です。(※マニフェスト構成、実行環境によっては違う結果になる可能性もあります。おそらく、インストール処理が大半の場合は短縮しないです)

ベンチマーク

ベンチマーク用マニフェスト

ベンチマークとして、/tmpに10000ファイル作るマニフェストをpuppet applyで適用するテストをしました。ループは使わず、べた書きでリソース定義数は10000です。

ベンチマーク対象

ベンチマーク対象として、次のようなバージョンの組み合わせのDockerイメージをビルドして、ベンチマークやりました。

イメージ OS Puppet Ruby
1 CentOS6 (Puppet 3.8.4, Ruby 1.8.7) CentOS6 3.8.4(rpm) 1.8.7(rpm)
2 CentOS6 (Puppet 3.8.4, Ruby 2.1.7) CentOS6 3.8.4(gem) 2.1.7(rbenv)
3 CentOS6 (Puppet 4.3.1, Ruby 2.1.7) CentOS6 4.3.1(rpm) 2.1.7(puppet-agent)
4 CentOS7 (Puppet 3.8.4, Ruby 2.0.0) CentOS7 3.8.4(rpm) 2.0.0(rpm)
5 CentOS7 (Puppet 3.8.4, Ruby 2.1.7) CentOS7 3.8.4(gem) 2.1.7(rbenv)
6 CentOS7 (Puppet 4.3.1, Ruby 2.1.7) CentOS7 4.3.1(rpm) 2.1.7(puppet-agent)

補足ですが、
1,4はPuppet3でのrpm最新版の組み合わせです。
3,6はPuppet4のrpm最新版でRubyはpuppet-agentに同梱されているものです。(※Puppet4のrpmで入れるpuppet-agentは、OS側のRubyに依存しない)
2,5はRuby2.1.7をrbenvを使ってバイナリからビルドして、puppetはRuby Gemsで入れています。rpmで入れたPuppetだと、rbenvのRubyを使ってくれないので、rbenvの下のgemパッケージとしてpuppetを入れないとだめでした。最初気づかなくて、Ruby変えても全然速くならないとがっかりしたのは過去のこと。
※最新版は2015/12/5時点。また、Ruby2.2.xはまだ、推奨ではないみたいな記載があったためRuby2.1.7です。

ベンチマーク環境

MacにDocker ToolboxでDocker入れた環境でベンチマークしました。
Mac,docker-machine,dockerと環境表記が難しいので、後の詳細のdockerコンテナのfacterの結果を参考にしてください。

ベンチマーク方法

ベンチマークはDockerコンテナを立てて、1回目のpuppet applyでファイル作成(以降1回目)、2回目のpuppet applyで環境変更なしの場合(以降2回目)、コンテナ停止、コンテナ削除をそれぞれのイメージでやりました。1回目、2回目の実行時間はtimeコマンドのrealの結果です(実行時間は、puppet applyのみの計測)。
なお、ホスト側のバックグランド処理の影響を極力なくすため、各イメージで14回やって、上位2つと下位2つを除いた10回分の平均値をとりました。

ベンチマーク結果

説明が長くなってしまいましたが、ベンチマーク結果です。

puppetベンチ.png

横軸が実行時間(秒)で、1st applyが1回目(ファイル作成あり)、2nd applyが2回目(ファイル作成なし)です。結果的には、Rubyのバージョンが新しいほど速いようです。特に今回の結果ではRuby1.8.7はかなり遅い結果でした。
Puppetのバージョンについては、Puppet3.8.4の方が今回わずかに速いですが、この辺はやり方によるかもです。Puppet3.8.4の方は、Ruby2.1.7を実行環境でビルドしているのでその辺の最適化がされている?とか、Puppet4の方がマスター側が高速であれば、トータルではPuppet4の方が速い可能性もあります。その辺は、今回未検証です。あと、OSバージョンについてはほぼ差はないようです。

まとめ

Puppetが遅いと思ったら、Rubyのバージョンアップを検討した方がよいかも。特にRuby1.8系を使っている場合です。

余談

今回は単純リソースを大量につくるだけだったので、moduleが多かったり、Hieraを使ったりすると傾向が違うかもしれません。ちなみに、1ファイルだけ作るマニフェストだと逆にRuby1.8.7の方が速かったりします、この辺はRuby1.9以降の実行時オーバヘッドに絡んでいる???
あと、まとめてから気づきましたが、性能比較としては全部rbenv + Gemsで比較した方がよかったかも。。。

参考

以降、ベンチマークに使ったものとか

詳細を書くと結構大変なので、参考程度にひたすら貼っただけです。

ベンチマーク用マニフェスト作成スクリプト

/tmpの下にファイルをたくさん作る定義をつくる。

makepp.rb
#! /usr/bin/evn ruby

def file(no)
  index = "%05d"%no
  content = "0123456789\\n" * 10
  ret = <<EOS
file { '/tmp/benchmark_file_#{index}.txt':
  ensure  => 'file',
  content => "#{content}",
  owner   => 'root',
  group   => 'root',
  mode    => '666',
}

EOS
  return ret
end

testcase = [1, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
testcase.each do |test|
  output = "file_%05d.pp"%test
  File.open(output, 'w') do |io|
    test.times do |no|
      io.puts file(no+1)
    end
  end
end

ベンチマーク環境

facterの抜粋

ホスト依存

facter
memoryfree => 1.03 GB
memorysize => 1.96 GB
processors => {"models"=>["Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz", "Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz", "Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz", "Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz"], "physicalcount"=>1, "count"=>4}

コンテナ側

イメージ名

上記のDockerイメージは、今回は次のようなイメージ名でビルドしました。

イメージ イメージ名
1 CentOS6 (Puppet 3.8.4, Ruby 1.8.7) local/centos6-puppet
2 CentOS6 (Puppet 3.8.4, Ruby 2.1.7) local/centos6-puppet-ruby-2-1-7
3 CentOS6 (Puppet 4.3.1, Ruby 2.1.7) local/centos6-puppet-puppet-4-3-1
4 CentOS7 (Puppet 3.8.4, Ruby 2.0.0) local/centos7-puppet
5 CentOS7 (Puppet 3.8.4, Ruby 2.1.7) local/centos7-puppet-ruby-2-1-7
6 CentOS7 (Puppet 4.3.1, Ruby 2.1.7) local/centos7-puppet-puppet-4-3-1

各コンテナ関連

※facterの表示形式が違うのは、バージョンが違うためです。

local/centos6-puppet

facter
facterversion => 2.4.4
os => {"release"=>{"full"=>"6.7", "minor"=>"7", "major"=>"6"}, "name"=>"CentOS", "family"=>"RedHat"}
osfamily => RedHat
puppetversion => 3.8.4
rubyplatform => x86_64-linux
rubysitedir => /usr/lib/ruby/site_ruby/1.8
rubyversion => 1.8.7

local/centos6-puppet-ruby-2-1-7

facter
facterversion => 2.4.4
os => {"name"=>"CentOS", "family"=>"RedHat", "release"=>{"major"=>"6", "minor"=>"7", "full"=>"6.7"}}
puppetversion => 3.8.4
rubyplatform => x86_64-linux
rubysitedir => /opt/rbenv/versions/2.1.7/lib/ruby/site_ruby/2.1.0
rubyversion => 2.1.7

local/centos6-puppet-puppet-4-3-1

facter
facterversion => 3.1.3
os => {
  architecture => "x86_64",
  family => "RedHat",
  hardware => "x86_64",
  name => "CentOS",
  release => {
    full => "6.7",
    major => "6",
    minor => "7"
  },
  selinux => {
    enabled => false
  }
}
ruby => {
  platform => "x86_64-linux",
  sitedir => "/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.1.0",
  version => "2.1.7"
}

local/centos7-puppet

facter
facterversion => 2.4.4
os => {"name"=>"CentOS", "family"=>"RedHat", "release"=>{"major"=>"7", "minor"=>"1", "full"=>"7.1.1503"}}
puppetversion => 3.8.4
rubyplatform => x86_64-linux
rubysitedir => /usr/local/share/ruby/site_ruby/
rubyversion => 2.0.0

local/centos7-puppet-ruby-2-1-7

facter
facterversion => 2.4.4
os => {"name"=>"CentOS", "family"=>"RedHat", "release"=>{"major"=>"7", "minor"=>"1", "full"=>"7.1.1503"}}
puppetversion => 3.8.4
rubyplatform => x86_64-linux
rubysitedir => /opt/rbenv/versions/2.1.7/lib/ruby/site_ruby/2.1.0
rubyversion => 2.1.7

local/centos7-puppet-puppet-4-3-1

facterversion => 3.1.3
os => {
  architecture => "x86_64",
  family => "RedHat",
  hardware => "x86_64",
  name => "CentOS",
  release => {
    full => "7.1.1503",
    major => "7",
    minor => "1"
  },
  selinux => {
    enabled => false
  }
}
ruby => {
  platform => "x86_64-linux",
  sitedir => "/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.1.0",
  version => "2.1.7"
}

Dockerfile

各イメージビルドしたときに使ったDockerfile

local/centos6-puppet

Dockerfile
FROM centos:6
MAINTAINER kijibato

# puppet install
RUN rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm
RUN yum clean all && \
    yum install -y puppet && \
    yum install -y puppet-server

local/centos6-puppet-ruby-2-1-7

evalの実行があやしいかも?

Dockerfile
FROM centos:6
MAINTAINER kijibato

# ----- init
RUN yum clean all

# ----- ruby(2.1.7) install
RUN yum install -y git
RUN yum install -y tar
RUN yum install -y gcc make openssl-devel libffi-devel readline-devel

RUN cd /opt; git clone git://github.com/sstephenson/rbenv.git
RUN mkdir /opt/rbenv/plugins
RUN cd /opt/rbenv/plugins; git clone git://github.com/sstephenson/ruby-build.git

ENV RBENV_ROOT="/opt/rbenv"
ENV PATH="${RBENV_ROOT}/bin:${PATH}"
RUN eval "$(rbenv init -)" && \
    rbenv install 2.1.7 && \
    rbenv global 2.1.7

# ----- puppet(3.8.4) install from Gems
RUN eval "$(rbenv init -)" && \
    rbenv exec gem install puppet -v 3.8.4

local/centos6-puppet-puppet-4-3-1

Dockerfile
FROM centos:6
MAINTAINER kijibato

# puppet install
RUN rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm
RUN yum clean all && \
    yum install -y puppet-agent && \
    yum install -y puppetserver

local/centos7-puppet

Dockerfile
FROM centos:7
MAINTAINER kijibato

# puppet install
RUN rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm
RUN yum clean all && \
    yum install -y puppet && \
    yum install -y puppet-server

local/centos7-puppet-ruby-2-1-7

evalの実行があやしいかも?

Docker
FROM centos:7
MAINTAINER kijibato

# ----- init
RUN yum clean all

# ----- ruby(2.1.7) install
RUN yum install -y git
RUN yum install -y tar
RUN yum install -y gcc make openssl-devel libffi-devel readline-devel

RUN cd /opt; git clone git://github.com/sstephenson/rbenv.git
RUN mkdir /opt/rbenv/plugins
RUN cd /opt/rbenv/plugins; git clone git://github.com/sstephenson/ruby-build.git

ENV RBENV_ROOT="/opt/rbenv"
ENV PATH="${RBENV_ROOT}/bin:${PATH}"
RUN eval "$(rbenv init -)" && \
    rbenv install 2.1.7 && \
    rbenv global 2.1.7

# ----- puppet(3.8.4) install from Gems
RUN eval "$(rbenv init -)" && \
    rbenv exec gem install puppet -v 3.8.4

local/centos7-puppet-puppet-4-3-1

Dockerfile
FROM centos:7
MAINTAINER kijibato

# puppet install
RUN rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm
RUN yum clean all && \
    yum install -y puppet-agent && \
    yum install -y puppetserver

ベンチマークスクリプト

上記のDockerfileでビルドした後に実行しました。
全部流すと結構長いです。もし、使う人がいましたら、利用は自己責任でお願いします。ただ、evalがちょっとあやしいかも?

benchmark.rb
#! /usr/bin/evn ruby
require 'open3'
require 'pp'
require 'yaml'

test_n = 14
ignore_faster = 2
ignore_later = 2
work = File.expand_path(File.dirname(__FILE__))

ppfile = '/share/file_10000.pp'

images = [ 
           'local/centos6-puppet',
           'local/centos6-puppet-ruby-2-1-7',
           'local/centos6-puppet-puppet-4-3-1',
           'local/centos7-puppet',
           'local/centos7-puppet-ruby-2-1-7',
           'local/centos7-puppet-puppet-4-3-1'
]
results = {}
images.each do |image|
  results[image] = []
end

def get_time(lines)
  real = 0
  user = 0 
  sys = 0  
  lines.each_line do |line|
    if /^real (\d+\.\d+)$/ =~ line
      real = $1.to_f
    end
    if /^user (\d+\.\d+)$/ =~ line
      user = $1.to_f
    end
    if /^sys (\d+\.\d+)$/ =~ line
      sys = $1.to_f
    end
  end
  return real, user, sys
end

test_n.times do |no|
  images.each do |image|
    result = {}
    puts "----- #{image} "+"-"*25
    name = image.gsub(/[\/\-]/, '_')
    puts `docker run -d -v #{work}:/share --name #{name} #{image} /sbin/init`
    case image
    when 'local/centos6-puppet', 'local/centos7-puppet' then
      puppet = 'puppet'
    when 'local/centos6-puppet-ruby-2-1-7','local/centos7-puppet-ruby-2-1-7' then
      puts `docker exec #{name} /bin/bash -c 'eval "$(rbenv init -)"'`
      puppet = 'rbenv exec puppet'
    when 'local/centos6-puppet-puppet-4-3-1', 'local/centos7-puppet-puppet-4-3-1' then
      puppet = '/opt/puppetlabs/bin/puppet'
    end
    puts '--- 1st apply'
    out, err, status = Open3.capture3("docker exec #{name} /bin/bash -c 'time -p #{puppet} apply #{ppfile}'")
    puts out
    puts err
    real, user, sys = get_time(err)
    result['1st'] = {'real' => real, 'user' => user, 'sys' => sys}
    puts '--- ls'
    puts `docker exec #{name} ls -l /tmp`
    puts '--- 2nd apply'
    out, err, status = Open3.capture3("docker exec #{name} /bin/bash -c 'time -p #{puppet} apply #{ppfile}'")
    puts out
    puts err
    real, user, sys = get_time(err)
    result['2nd'] = {'real' => real, 'user' => user, 'sys' => sys}
    puts `docker stop #{name}`
    puts `docker rm #{name}`

    results[image].push(result)
  end
end

puts YAML.dump(results)

results.each do |image, result|
  puts image + ':'
  sort_ret = result.sort{ |a, b| a["1st"]["real"] <=> b["1st"]["real"] }
  sort_ret.shift(ignore_faster)
  sort_ret.pop(ignore_later)
  sum = 0
  sort_ret.each do |time|
    sum += time['1st']['real']
  end
  ave = sum / sort_ret.size
  puts '+'*10 + "1st(#{sort_ret.size}): #{ave}"

  sort_ret = result.sort{ |a, b| a["2nd"]["real"] <=> b["2nd"]["real"] }
  sort_ret.shift(ignore_faster)
  sort_ret.pop(ignore_later)
  sum = 0
  sort_ret.each do |time|
    sum += time['2nd']['real']
  end
  ave = sum / sort_ret.size
  puts '+'*10 + "2nd(#{sort_ret.size}): #{ave}"
end

おわり。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2