LoginSignup
2
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-12-11

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

おわり。

2
2
0

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
  3. You can use dark theme
What you can do with signing up
2
2