Puppet on AWS + α
自己紹介
菅原 元気
● クックパッド勤務
● インフラエンジニア
● @sgwr_dts
● id:winebarrel
宣伝
OSS公開しています!
● Bitbucket
○ https://bitbucket.org/winebarrel
● GitHub
○ https://github.com/cookpad
○ elasticfox-ec2tag、R53 Fox、IAM Fox
● RubyGems.org
○ http://rubygems.org/profiles/winebarrel
宣伝
最近ddbcliというものを作りました!
https://bitbucket.org/winebarrel/ddbcli
Please try it out!
クックパッドについて
レシピサイトです!
以下略)
本題
Puppetについて
Puppetについて
クックパッドではPuppetを使っています
Puppetとは
● システム構成管理ツール
○ http://puppetlabs.com/puppet/puppet-open-source/
● 最近はChefばかりが流行ってます
● OpsWorksもChefなのでオワコン感Up!
Puppetについて
Puppet Labs曰く
"Opscodeがここまでのスケール
性に達したことは印象的です
が,当社ではすでに3~4年前
から,このようなスケールレベル
に達しています。"
Facebook,Web層管理にChefを導入
http://www.infoq.com/jp/news/2013/03/facebook-chef
リポジトリ構成
リポジトリ構成
設定ファイル等はほとんどGHEで管理
リポジトリ構成
言語の比率はこんな感じ…
リポジトリ構成
infra/
├── bin
├── capvars
├── config
│ └── deploy
├── ec2-init
│ ├── bin
│ ├── repos
│ │ └── centos6
│ │ └── rpmbuild
│ │ └── RPMS -> ../../../../rpm/RPMS
│ └── scripts
├── fluentd-proxy
├── haproxy
├── maintenance
├── nagios
├── page-cache
├── page-cache-misc
├── puppet
│ ├── bin
│ ├── lib
│ ├── manifests
│ ├── modules
│ ├── roles
│ └── types
├── rpm
│ ├── RPMS
│ │ ├── noarch
│ │ └── x86_64
│ ├── SPECS
│ └── SRPMS
├── rproxy
└── search-cache
リポジトリ構成
infra/
├── bin
├── capvars
├── config
│ └── deploy
├── ec2-init
│ ├── bin
│ ├── repos
│ │ └── centos6
│ │ └── rpmbuild
│ │ └── RPMS -> ../../../../rpm/RPMS
│ └── scripts
├── fluentd-proxy
├── haproxy
├── maintenance
├── nagios
├── page-cache
├── page-cache-misc
├── puppet
│ ├── bin
│ ├── lib
│ ├── manifests
│ ├── modules
│ ├── roles
│ └── types
├── rpm
│ ├── RPMS
│ │ ├── noarch
│ │ └── x86_64
│ ├── SPECS
│ └── SRPMS
├── rproxy
└── search-cache
リポジトリ構成
puppet/
├── bin
├── lib
├── manifests
│ └── site.pp
├── modules
│ ├...
│ ├── nginx
│ │ └── manifests
│ │ └── init.pp
│ ...
├── roles
│ ├── dns_internal
│ │ ├── files
│ │ │ └── etc
│ │ │ └── pdns
│ │ │ ├── ...
│ │ │ ├── named.ca
│ │ │ └── named.conf
│ │ ├── manifests
│ │ │ └── init.pp
│ │ └── templates
│ │ ├── ...
│ │ └── usr
│ │ └── local
│ │ └── sbin
│ │ ├── update-dns-rev
│ ... ...
└── types
├── gem.pp
├── sources.pp
├── template.pp
...
サーバ・
プロビジョニング
サーバ・プロビジョニング
プロビジョニングの流れ
1. Base AMIからインスタンスを起動
2. インスタンスにログインしてec2-initを実行
3. puppetを実行
サーバ・プロビジョニング
ElasticfoxからBase AMIを起動
サーバ・プロビジョニング
Base AMI
● CentOS 6.2
● 最小限のパッケージをインストール
● 最低限のネットワーク設定、不要なサービス等
の無効化、不要グループの削除、ipv6の無効
化、公開鍵の取得の設定…etc
サーバ・プロビジョニング
Base AMI
● エフェメラルディスクを自動アタッチ
● /tmp、swapはエフェメラルディスクを使用
サーバ・プロビジョニング
インスタンスにログインしてec2-initを実行
ec2-init: 初期処理を行うスクリプト
● ネットワーク設定(DNS、route…)
● yumの設定
● Rubyのインストール(1.8/1.9/2.0/ree)
● Puppetのインストール
● TerminateProtectionの有効化
● ...etc
サーバ・プロビジョニング
ec2-initの仕組み
サーバ・プロビジョニング
Puppet
● Puppet 2.7
○ Ruby 1.9/2.0対応を追加
サーバ・プロビジョニング
Puppetの仕組み
Puppet+AWS
Puppet+AWS
External Node Classifiers
http://docs.puppetlabs.com/guides/external_nodes.html
ノード(サーバ)の設定を
動的に定義する仕組み
Puppet+AWS
External Node Classifiers
Puppet+AWS
メタ情報はタグで管理
とあるDB
サーバのタ
グ
Puppet+AWS
Puppetで利用するタグ
● Role
○ そのサーバに適用するロール・モジュール
複数設定可能
● Params[xxx]
○ Puppetに渡す任意の変数
● PuppetApplied
○ Puppet適用日時
Puppet
Puppet
マニフェスト
modules/
├─ maatkit
│ └── manifests
│ └── init.pp
├─ mysql55
...
roles/
├─ db_server
│ ├── files/usr/bin
│ │ └── summary-mysqldump..
│ ├── manifests
│ │ └── init.pp
│ └── templates
│ └── etc
│ └── my.cnf
├── app_server
├── git_server
...
class db_server {
include mysql55
include mysql55_server
include maatkit
include innotop
user_and_home { 'yamada':
uid => '10001',
authorized_keys => '...'',
}
template { '/etc/my.cnf':
owner => 'root', mode => '644',
}
source { '/usr/bin/summary-mysqldumpslow':
onwer => 'root', mode => '700',
}
cron { 'summary-slow-log':
command => '/usr/bin/summary-mysqldumpslow',
user => 'root', minute => '*/5',
}
Puppet
テンプレート
# my.cnf
[client]
port = <%= port %>
socket = /var/lib/mysql/mysql.sock
default-character-set = utf8
# The MySQL server
[mysqld]
max_connect_errors = 999999999
port = <%= port %>
socket = /var/lib/mysql/mysql.sock
log-error = /var/lib/mysql/mysqld.err
pid-file = /var/lib/mysql/mysqld.pid
<% if role =~ /slave/ %>
replicate-ignore-table
<% end %>
...
Puppet
設計・運用方針
● すべてのサーバをPuppetで管理する。
例外は作らない
設計・運用方針
● RoleとModuleで管理
○ Role: サーバの役割
(app_server、git_server...)
○ Module: ミドルウエアやツール群の集まり
(nginx、mysql_server、zlib...)
Puppet
設計・運用方針
● Roleはbase+個別ロールで管理
○ base: すべてのサーバに適用されるロール
○ 個別ロール: サーバ毎の設定が定義されたロール
Puppet
Puppet
設計・運用方針
● 継承は使わない(Puppetの鬼門!)
● その代わりModuleをこまか〜く定義
Puppet
設計・運用方針
● Puppetの適用はまめに行う(放置しない)
● サーバのrefresh/restart/reloadは
なるべく定義しない
○ オンラインでpuppetを適用できるようにする
Puppet
設計・運用方針
● パッケージのインストールは
必ずバージョンを指定
● メジャーバージョンはModuleを分けて管理
(mysql_5_5_server、mysql_5_6_server..)
package { 'zlib':
ensure => '1.2.3',
}
Puppet
設計・運用方針
● Puppet自身の管理をしない
● Rubyの管理をしない
Puppet
設計・運用方針
どーしてもPuppetで管理できないときは…
● manifestにコメントを書く
● puppet実行時にメッセージを出す
shell> sudo puppet-apply
info: Caching catalog for my-server-001.vpc.ap-northeast-1.compute.internal
info: Applying configuration version '1368608841'
...
notice: /Stage[main]/Ruby_notify/Exec[ruby19_notify]/returns: Please update ruby-1.9.3 (run
'yum clean all; yum update ruby-1.9.3.p392-3ckpd')
...
template { '/etc/my.cnf':
owner => '...', mode => '...,
}
# XXXを手動でインストールすること!
...
Puppet
その他Tips的なこと
● Custom Typeの活用
○ http://docs.puppetlabs.com/guides/custom_types.html
define template($mode, $owner, $group = $owner, $source = $name) {
file { "$name":
ensure => 'present',
mode => "$mode", owner => "$owner", group => "$group",
content => template("${module_name}$source"),
}
}
...
template { 'my.cnf':
owner => '...',
mode => '...',
}
Puppet
その他Tips的なこと
● Parameterized Classesの活用
○ http://docs.puppetlabs.com/guides/parameterized_classes.html
class mysql_server($include_my_cnf = true) {
if $include_my_cnf {
source { "/etc/my.cnf":
owner => '...', mode => '...',
}
}
...
class db {
# include mysql_server
class { 'mysql_server':
include_my_cnf => false,
}
Puppet
その他Tips的なこと
● Custom Functionsの活用
○ http://docs.puppetlabs.com/guides/custom_functions.html
newfunction(:mysql_server_id, :type => :rvalue) do |args|
ip_addr = lookupvar('ipaddress')
ip_addr = IPAddr.new(ip_addr)
# IPアドレスは下位16ビットを整数値として使う
ip_addr = (ip_addr.to_i & 0xFFFF)
server_id = '%02d%03d%05d' % [
0, # 0〜41の連番
392, # 国コード(http://ja.wikipedia.org/wiki/ISO_3166-1_numeric)
ip_addr,
]
# 先頭の0は削除
server_id.sub!(/A0+/, '')
return server_id
end
# my.cnf
server-id = <%= scope.function_mysql_server_id %>
Puppet
その他Tips的なこと
● Puppetの適用はログインしてコマンド実行する
か、capistranoを使用
○ capistranoはAWSからサーバ情報を取得
shell> cap puppet noop ROLES=any_role
triggering load callbacks
executing `puppet'
triggering start callbacks for `noop'
executing `multistage:ensure'
executing `noop'
executing "sudo -p 'sudo password: ' /usr/sbin/puppet-noop"
servers: server-001..004, db-001..003
Password:
[server-001] info: Caching catalog for server-001
[server-003] info: Caching catalog for server-003
[server-001] info: Applying configuration version '1368614218'
Puppet
その他Tips的なこと
● クライアントのコマンドをスクリプト化
● pupept-noop、puppet-apply
○ apply終了後に日時タグをつける
○ restart/reloadなど危険な処理に色付け
puppet agent --no-daemonize -l console -o -v --noop --test
Puppet
その他Tips的なこと
● execは基本的にCustom Typeでラップ
○ onlyif・unlessで多重実行防止
● daemonizeは未使用(使ってる人いる?)
Puppet
その他Tips的なこと
● タグAPI情報は内部でキャッシュ
(DescribeTagsは叩きすぎるとエラーが)
● タグにメタ情報を詰め込みすぎて
そろそろ足りなくなりそう…
Puppet
ドキュメント
● Type Reference
○ http://docs.puppetlabs.com/references/latest/type.html
● Style Guide
○ http://docs.puppetlabs.com/guides/style_guide.html
● Best practices
○ http://docs.puppetlabs.com/guides/best_practices.html
● Wiki
○ http://projects.puppetlabs.com/projects/puppet/wiki/
● Puppet Forge
○ https://forge.puppetlabs.com/
Puppet以外
Puppet以外
デプロイの粒度が異なるものは
Puppetで管理しない
● httpd、varnish、haproxy、nagios…etc
○ 設定ファイルの配置・configtest・reload
○ サービスイン・サービスアウト
○ キャッシュのクリア
○ デプロイのロック・アンロック
○ ...etc
Puppet以外
デプロイの粒度が異なるものは
Puppetで管理しない
● GHE+capistranoで管理・運用
shell> cap haproxy deploy configtest ROLES=foo-balancer
Puppet以外
Puppet以外の仕組み
Puppet以外
Tips的なもの
● Multistage Extension
○ https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension
shell> cap haproxy deploy configtest ROLES=foo-balancer
config/
├── deploy
│ ├── app.rb
│ ├── autoscale.rb
│ ├── cache.rb
│ ├── dns.rb
│ ├── ec2-init.rb
│ ├── haproxy.rb
│ ├── help.rb
│ ├── nagios.rb
│ ...
└── deploy.rb
Puppet以外
Tips的なもの
● bracecomp
○ https://rubygems.org/gems/bracecomp
shell> cap haproxy deploy configtest HOSTS="balancer-{001..010}"
Puppet以外
Tips的なもの
● ERB: デプロイ時にERBを展開
task :make_haproxy_cfg do
run %!ruby -rerb -e 'puts ERB.new(ARGF.read, nil, "-").result' #{current_release}/haproxy.cfg.erb > #
{current_release}/haproxy.cfg!
end
end
namespace :deploy do
task :finalize_update do
make_haproxy_cfg
diff_previous
end
...
# haproxy.cfg.erb
...
<%- hosts = {'db-001' => 100, ... } -%
listen db
bind :3306
mode tcp
<%- hosts.each do |host, weight| -%>
server <%= host %> <%= host %>:3306 check port 3306 inter 5s fall 3 weight <%= weight %>
<%- end -%>
...
そんな感じで運用してます…
お約束的なアレ
インフラエンジニアを募集中!
『クックパッド インフラエンジニア』で検索!
http://info.cookpad.com/jobs/position/server-system-engineer
以上です!

Puppet on AWS