Dnsruby試した

RubyでDynamicDNSサーバに対してNsupdateするのに、pNet-DNSというライブラリを使ってきたのですが
Rubyforgeのページを見ると、A direct port of the perl Net::DNS project - this project has been discontinued. PLEASE USE DNSRUBY INSTEAD!って記述があったので、
dnsrubyをインストールして試して見ました。

インストール

rubygemでインストール(fastthreadが使える場合内部で呼び出しているので一緒にインストールした方がいいです)

$ sudo gem install dnsruby
$ sudo gem install fastthread

※最初仕事用のPCにインストールしたとき、なぜか同一バージョンのfastthreadが重複して入ってしまっておりrequire 'dnsruby'したときにエラーが発生しましたが、
その場合は、fastthreadの全削除、再インストールで直りました。

AXFR

とりあえずゾーン転送で全内容表示

サーバの設定としては、

  • Bindのポートはデフォルトではない
  • TSIG鍵による認証
#!/usr/bin/env ruby
# coding: utf-8
# zt.rb

require 'pp'
require 'rubygems'
require 'dnsruby'

# Model find
zt = Dnsruby::ZoneTransfer.new.tap do |ins|
  ins.server = "10.1.1.1"
  ins.port = "10053"
  ins.tsig = "nsupdate.example.com.", "WImSObCU+ClK7Ol8wWSDo6AkeR6kBtGp8CUPSBpKBSc1yA2ODpcye7vryKzIMqBjELzRsHjWJACyfgs+b7qUnA=="
end

%w[nsupdate.example.com int.nsupdate.example.com].each do |zone|
  puts "#{zone} -------------------------------------------------------------"
  result = zt.transfer(zone)
  puts "Record Total: #{result.size}"

  result.each do |r|
    rrset = "#{r.name} #{r.ttl} #{r.klass} #{r.type}"
    rrset = [rrset, r.type != "SOA" ? "#{r.rdata}": r.rdata.map {|x| "#{x}" }.join(' ')].join(' ')
    puts rrset
  end
end

digコマンド風に表示

$ ruby zt.rb
-------- nsupdate.example.com ---------------------------------------------------
Record Total: 9
nsupdate.example.com 10 IN SOA dns.nsupdate.example.com root.nsupdate.example.com 1970010102 20 2 86400 60
nsupdate.example.com 10 IN NS dns.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-001.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-002.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-003.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-004.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-005.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-006.nsupdate.example.com
foo.nsupdate.example.com 3600 IN A 1.2.3.4
-------- int.nsupdate.example.com ---------------------------------------------------
Record Total: 8
int.nsupdate.example.com 10 IN SOA dns.nsupdate.example.com root.int.nsupdate.example.com 1970010100 20 2 86400 60
int.nsupdate.example.com 10 IN NS dns.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-001.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-002.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-003.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-004.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-005.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-006.nsupdate.example.com

Nsupdate

なにかレコードを更新してみます。

#!/usr/bin/env ruby
# coding: utf-8
# nsupdate.rb

require 'pp'
require 'rubygems'
require 'dnsruby'

res = Dnsruby::Resolver.new(:nameservers => ["10.1.1.1"], :port => "10053")
res.tsig = "nsupdate.example.com.", "WImSObCU+ClK7Ol8wWSDo6AkeR6kBtGp8CUPSBpKBSc1yA2ODpcye7vryKzIMqBjELzRsHjWJACyfgs+b7qUnA=="

update = Dnsruby::Update.new("nsupdate.example.com")
update.add("bar.nsupdate.example.com", "A", 3600, "1.2.3.4")
update.delete("foo.nsupdate.example.com")

begin
  reply = res.send_message(update)
rescue => e
  pp e
end

bar.nsupdate.example.comを追加して、foo.nsupdate.example.comを削除しました。
シリアル番号も更新されてますね。

$ ruby nsupdate.rb
$ ruby zt.rb
-------- nsupdate.example.com ---------------------------------------------------
Record Total: 9
nsupdate.example.com 10 IN SOA dns.nsupdate.example.com root.nsupdate.example.com 1970010103 20 2 86400 60
nsupdate.example.com 10 IN NS dns.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-001.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-002.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-003.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-004.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-005.nsupdate.example.com
nsupdate.example.com 10 IN NS dns-006.nsupdate.example.com
bar.nsupdate.example.com 3600 IN A 1.2.3.4
-------- int.nsupdate.example.com ---------------------------------------------------
Record Total: 8
int.nsupdate.example.com 10 IN SOA dns.nsupdate.example.com root.int.nsupdate.example.com 1970010100 20 2 86400 60
int.nsupdate.example.com 10 IN NS dns.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-001.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-002.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-003.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-004.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-005.nsupdate.example.com
int.nsupdate.example.com 10 IN NS dns-006.nsupdate.example.com

Symfony1.4をgithubから取得し、プロジェクトにサブモジュールで取り込む

イメージ的には、svn externals
svnじゃなくてgitの方がうざい隠しディレクトリがなくていいですね

手順

Symfonyプロジェクトのトップディレクトリで下記のコマンドを実行するだけ

$ cd <sfproject>
$ mkdir -p lib/vendor
$ git submodule add git://github.com/vjousse/symfony-1.4.git lib/vendor/symfony
Cloning into lib/vendor/symfony...
remote: Counting objects: 5247, done.
remote: Compressing objects: 100% (2611/2611), done.
remote: Total 5247 (delta 2383), reused 5037 (delta 2226)
Receiving objects: 100% (5247/5247), 4.12 MiB | 308 KiB/s, done.
Resolving deltas: 100% (2383/2383), done.
$ git ci -m 'import symfony'
[master (root-commit) 688aee2] import symfony
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 lib/vendor/symfony
$ git st
# On branch master
nothing to commit (working directory clean)
# 確認
$ php lib/vendor/symfony/data/bin/symfony -V
symfony version 1.4.12-DEV (<sfproject>/lib/vendor/symfony/lib)
# タスク一覧
$ php lib/vendor/symfony/data/bin/symfony -vT
Usage:
  symfony [options] task_name [arguments]

Options:
  --help                   -H  Display this help message.
  --quiet                  -q  Do not log messages to standard output.
  --trace                  -t  Turn on invoke/execute tracing, enable full backtrace.
  --version                -V  Display the program version.
  --color                      Forces ANSI color output.
  --xml                        To output help as XML
[...]

memo storeconfigs

設定
Using Stored Configuration - Puppet - Puppet Labs

Exported Resourceを使うのであれば必須
Exporting and Collecting Resources — Documentation — Puppet Labs

とりあえず、Rails必須、DB必須、Queuing任意(ActiveMQ)
記述があっても動かない



ただ、Exported Resources Overrideは使えるっぽい

入れたもの

rpm
gem
Puppet 0.25 and later

Rails version 2.2.2 or greater.

設定

#<my.cnf>innodb-4G-heavy.cnfをコピー
$ sudo /usr/bin/mysqladmin -u root password 'rootpassword'
$ mysql -u root -p
mysql> create database puppet;
mysql> grant all privileges on puppet.* to puppet@localhost identified by 'password';
[puppetmasterd]
storeconfigs = true
dbadapter = mysql
dbuser = puppet
dbpassword = password
dbserver = localhost
dbsocket = /var/lib/mysql/mysql.sock

PuppetをPassengerで動かして大規模サイトに対応させる

大規模サイトでPuppetを運用する場合、複数のノードに同時にmanifestを適用すると思います。しかし、同時接続数5台を超えたあたりからクライアントのリクエストを捌けなくなり適用時にエラーが発生することがよくあります。その場合、同時接続数を減らしたり接続時間をずらしたりして対応していると思います。ただ、そんなのやってられねーって人に、Passengerを使ったパフォーマンスの改善を紹介します。

前提

  • puppet環境は構築されているものとして説明
  • puppet v0.25.x
  • CentOS v5.4
    • yum EPEL repository
  • Ruby v1.8.5
Passengerってなに?

上記で説明されている通りApache等でRubyアプリケーションを動かすための動作環境です。インストールと設定が楽で、RailsにおいてはほとんどApacheの設定だけで動作環境の設定が済んでしまいます。

Rackって?

一言で言うと、RubyアプリケーションのためのWebサーバへのインタフェースらしい

注意事項

上記でも書かれているように、何も考えずrackとpassengerの最新版をインストールしても動作しません。

Ruby v1.8.5(CentOS5系のデフォルトバージョン)では、

Remove rack 1.2.1 first and then install rack 1.1.0. It seems that rack
1.1.0 and passenger 2.2.9 have been the magic combination (at least for me).

  • Gary

って、言ってるのでおとなしくrack v1.1.0とpassenger v2.2.9をインストールしましょう。
Railsアプリケーションを既に動かしている場合は、別のサーバを用意するかした方がいいです。
Ruby v1.8.7系では最新版でも問題なく動作するかもしれません。(未検証)

手順

必要なソフトウエアのインストール
$ sudo yum install httpd httpd-devel ruby-devel rubygems mod_ssl
$ sudo gem rack -v 1.1.0
$ sudo gem Passenger -v 2.2.9
Passenger-Apacheモジュールのビルドとインストール

対話形式なので、指示に従って進めます。

$ sudo passenger-install-apache2-module

途中で必要なパッケージがインストールされていなければ、下記のように親切に指示されますので指示に従ってインストールした後、再実行しましょう。

Installation instructions for required software
* To install Curl development headers with SSL support:
  Please run yum install curl-devel as root.
* To install OpenSSL development headers:
  Please run yum install openssl-devel as root.

Rackの設定

Rack用のディレクトリと設定ファイルを作成します。Puppet v0.25系からはconfig.ruのテンプレートが用意されているのでそれをコピーして使うだけでいいです。

$ mkdir -p /usr/share/puppet/rack/puppetmasterd
$ mkdir /usr/share/puppet/rack/puppetmasterd/public /usr/share/puppet/rack/puppetmasterd/tmp
$ cp /usr/share/puppet/ext/rack/files/config.ru /usr/share/puppet/rack/puppetmasterd/
$ chown puppet /usr/share/puppet/rack/puppetmasterd/config.ru

Apacheの設定

インストール時に指示されたように、passenger-Apacheモジュールの設定を組み込みます。

$ sudo vim /etc/httpd/conf.d/passenger.conf
# passenger.conf
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9
PassengerRuby /usr/bin/ruby

puppetmasterを動かすためのVirtualHostの設定を行います。
SSL証明書ファイルの名前はssl配下のディレクトリに作成されているサーバ用証明書のファイル名に置き換えてください。

$ sudo vim /etc/httpd/conf.d/puppetmaster.conf
# puppetmaster.conf
# Performance
#PassengerPoolIdleTime 300  # default: 300
PassengerUseGlobalQueue on
PassengerMaxPoolSize 60

Listen 8140
<VirtualHost *:8140>

    SSLEngine on
    SSLCipherSuite SSLv2:-LOW:-EXPORT:RC4+RSA
    SSLCertificateFile      /var/lib/puppet/ssl/certs/puppet-001.example.com.pem
    SSLCertificateKeyFile   /var/lib/puppet/ssl/private_keys/puppet-001.example.com.pem
    SSLCertificateChainFile /var/lib/puppet/ssl/certs/ca.pem
    SSLCACertificateFile    /var/lib/puppet/ssl/certs/ca.pem
    # CRL checking should be enabled; if you have problems with Apache complaining about the CRL, disable the next line
    SSLCARevocationFile     /var/lib/puppet/ssl/ca/ca_crl.pem
    SSLVerifyClient optional
    SSLVerifyDepth  1
    SSLOptions +StdEnvVars

    # The following client headers allow the same configuration to work with Pound.
    RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
    RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
    RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e

    PassengerHighPerformance on # DO NOT USE Global config!!(disable mod_rewrite)
    RackAutoDetect On
    RackBaseURI /

    DocumentRoot /usr/share/puppet/rack/puppetmasterd/public/
    CustomLog /puppet/logs/puppet_access_log combined
    ErrorLog /puppet/logs/puppet_error_log

    <Directory /usr/share/puppet/rack/puppetmasterd/>
        Options None
        AllowOverride None
        Order Allow,Deny
        Allow from all
    </Directory>
</VirtualHost>

後はApacheを起動するだけです。

動作確認

クライアントから、puppetd等でアクセスするとpassengerはpuppetmasterのインスタンスを起動し通常通りpuppetmasterにアクセスできるようになります。

$ sudo /usr/sbin/puppetd --verbose --no-daemonize --onetime --noop

サーバで下記のコマンドを実行すると、passengerがpuppetmasterを起動していることを確認できます。

$ sudo passenger-status 
----------- General information -----------
max      = 15
count    = 1
active   = 1
inactive = 0
Waiting on global queue: 0

----------- Domains -----------
/usr/share/puppet/rack/puppetmasterd: 
  PID: 8897    Sessions: 1    Processed: 1       Uptime: 12s

Tips

通常の/etc/init.d/puppetmasterdスクリプトを使用した方法では、/etc/sysconfig/puppetmasterの内容をデーモン実行時のコマンドライン引数として読み込めたのですが、この方法でも同じことが可能です。

config.ruに記述することで実現できます。

# config.ru
# a config.ru, for use with every rack-compatible webserver.
# SSL needs to be handled outside this, though.

# if puppet is not in your RUBYLIB:
# $:.unshift('/opt/puppet/lib')

$0 = "puppetmasterd"
require 'puppet'

# if you want debugging:
# ARGV << "--debug"

ARGV << "--rack"
ARGV << "--confdir=/puppet/etc"
ARGV << "--logdest=/var/log/puppet/puppetmaster.log"

require 'puppet/application/puppetmasterd'
# we're usually running inside a Rack::Builder.new {} block,
# therefore we need to call run *here*.
run Puppet::Application[:puppetmasterd].run

上記の通り、ARGV変数に追加したいオプションを記述するだけです。

終わりに

Passengerを使用することにより、悩みどころだった大規模サイトでのpuppet運用がこれで可能になります。
''passenger-status''や''sudo passenger-memory-stats''を使うことによりモニタリングが可能ですし、
Passengerのチューニング可能でApacheの設定に記述するだけなので、手軽に運用できると思います。

Puppetサーバを集約する(複数環境対応)

はじめに

サーバ管理にpuppetを使うと、設定の管理等の点において楽できます。
一台のpuppetで複数のサービス環境に対応させる方法がわかったのでその設定方法

前提

下記についてはわかっているものとして説明

  • 既に基本的なpuppetの概念や設定がわかっていること
    • 特にpuppet.conf等の設定ファイルの記述

複数環境に対応できるのは、manifestとmodulepathのみのようです。そのためpuppet.conf等の設定ファイルはサーバに一つしか配置できません。またこの機能、本来は(production, development, test)環境に応じたmanifestを運用することを想定しているらしいです。
この機能は、個々のサービスごとのmanifestを一つのサーバで運用する方法に使うのに有効に活用できるので、下記では、そのような構成で運用する設定を載せます。

ソフトウェア

手順

サーバ(puppetmasterd)の設定
  • 複数環境に対応するためのpuppet.confを用意する
# puppet.conf
[main]
# This is the default environment for all clients
environment=infra    # 1.) Defaultのenvironmentを定義(何も定義しないとproductionが割り当てられる)

server = puppet

pluginsync = true
pluginsource = puppet://$server/plugins

diff_args = -u

[puppetmasterd]
# specify allowed environments
environments=infra,blog,iphone,board      # 2.)個々のmanifest,moduleに環境名を定義

# 3.) 個々の環境ごとに使用するmanifestとmoduleを設定
# configure environments
[infra]
   modulepath = /puppet/infra/current/idc/modules:/puppet/infra/current/ec2/modules
   manifest = /puppet/infra/current/manifests/site.pp

[blog]
   modulepath = /puppet/blog/current/modules:/puppet/infra/current/idc/modules:/puppet/infra/current/ec2/modules
   manifest = /puppet/blog/current/manifests/site.pp

[iphone]
   modulepath = /puppet/iphone/current/modules:/puppet/infra/current/idc/modules:/puppet/infra/current/ec2/modules
   manifest = /puppet/iphone/current/manifests/site.pp

[board]
   modulepath = /puppet/board/current/modules:/puppet/infra/current/idc/modules:/puppet/infra/current/ec2/modules
   manifest = /puppet/board/current/manifests/site.pp

上記の通りなんですが、2.)のようにまず、puppetmasterdセクションに個々のmanifest,moduleに対して何でもいいので環境名を定義します。
3.)でその名前を付けた個々の環境で使用するmanifestとmoduleのmodulepathを定義します。
1.)は、オプションで、後で説明するClient側のpuppet.confに適用する環境が未設定の場合に適用するdefaultを定義します。これはmainセクションに定義します。

クライアント(puppetd)の設定
  • puppet.confにサーバで定義した環境名を定義する

サーバのpuppet.confで定義した環境名のうち、適用したいmanifestが配置されている環境を一つ記述します
ちなみに、サーバでdefaultの環境(mainセクションのenvironmentで定義した環境)が設定されている場合、クライアントに適用されるDefaultのmanifestはその環境のmanifestとなります。逆にサーバでdefaultの環境が定義されていない場合、エラーが発生します。

# puppet.conf
[puppetd]
    environment = blog

おわりに

複数のmanifestを管理していて別々のサーバと立てている場合、これで一つのサーバに集約することができると思います。しかし、puppetで管理するノードが多い場合、パフォーマンスの悪化という別の問題が発生するため、その場合passengerを併用して動かすといいでしょう。

カッとなってUbuntu/DebianでRubygems実行コマンドのパスを通した

はじめに

MacOSXCentOSを普段使用している場合、Rubygemsの実行コマンドのパスを意識することはないですが、Ubuntu/Debianの場合そこにパスがとおっていないため、コマンドを実行するのが少々面倒です。

毎回rubygemsの実行コマンドがどこにあるか確認しなくて済むように、シェル実行時にパスを通してしまいます。
.bash_profileや.profileに追加します

手順

1. Linux DistributionとGem environmentを調べる

まずLinux Distributionは、下記のコマンドで調査できます。(例は、Ubuntuで実行した場合)

$ lsb_release -si
=> Ubuntu

これで、特定のディストリビューションの場合のみRubygemsの実行コマンドのパスを設定することができるようになりました


次にGem environmentを調べます。下記のコマンドで確認できます。

$ gem environment
RubyGems Environment:
  - RUBYGEMS VERSION: 1.3.7
  - RUBY VERSION: 1.8.7 (2010-06-23 patchlevel 299) [x86_64-linux]
  - INSTALLATION DIRECTORY: /var/lib/gems/1.8
  - RUBY EXECUTABLE: /usr/bin/ruby1.8
  - EXECUTABLE DIRECTORY: /var/lib/gems/1.8/bin
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-linux
  - GEM PATHS:
     - /var/lib/gems/1.8
     - /home/satsv/.gem/ruby/1.8
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :benchmark => false
     - :backtrace => false
     - :bulk_threshold => 1000
  - REMOTE SOURCES:
     - http://rubygems.org/

上記のEXECUTABLE DIRECTORY変数が取得したいパスです。あとはgrepなりsedコマンドなりを駆使して実行コマンドのパスを取得できるのですが、もっと簡単に取得する方法があります。

$ ruby -e 'require "rubygems"; puts Gem::bindir'
=> /var/lib/gems/1.8/bin

Rubyのスクリプトとして実行すれば余計な文字列もないのでそのまま使用することができます。

2. .bash_profileまたは、.profileに追記

最終的に下記のように記述すれば、シェルにログインした時にRubygemsの実行コマンドのパスが環境変数PATHに追加されると思います。
最初にwhichコマンドを実行しているのは、MacOSXではlsb_releaseがないを利用して判定から除外します。

[...]
# Gem executable directory
which lsb_release >/dev/null 2>&1 && lsb_release -si | grep -E 'Ubuntu|Debian' >/dev/null 2>/dev/null && {
  export GEM_BIN=$(ruby -e 'require "rubygems"; puts Gem::bindir')
  export PATH=$GEM_BIN:$PATH
}


Ubuntuで、毎回cap shellを実行してcapがないよって言われてたので、かっとなって調べてみました。
これで、安心して''cap shell''とか実行できますね。