chef-soloでchef資産の有効活用

chefがある環境に慣れるともうchefから離れられませんが、chefサーバと通信できない環境にある場合などはchefを活用することができません。
「嗚呼、chefが使えたら!」と叫びたくなりますね。

そんなときはchef-soloです。
chef-soloを使えばchefサーバに蓄えたノウハウを使うことができます。
ただ、chef-soloを使うためにはいくつかやらなければならないことがありますので、それを含めてchef-soloの使い方を解説したいと思います。

cookbookのexport

chef-soloを使うためには、まずはこれまで作ったcookbookをexportする必要があります。
cookbookのexportはknifeを使って行います。

# create work directory
WORK_DIR=/path/to/workdir
if [ ! -d ${WORK_DIR} ]; then
  mkdir -p ${WORK_DIR}
fi

for i in `knife cookbook list| awk '{ print $1 }'`; do
  echo $i
  knife cookbook download $i -d ${WORK_DIR}
done

cd ${WORK_DIR}
for i in `ls -1 ./`; do
  poi=`echo $i | awk -F- '{ print $NF }'`
  n=`echo $i | sed s/\-${poi}//`
  mv $i $n
done

knife cookbook downloadでcookbookをダウンロードします。
cookbookはcookbook-versionというディレクトリ名でダウンロードされますが、そのままですとchef-soloでは使えないのでversion部分を除去する処理を入れています。

このダウンロードしたcookbookをtarで固めてchef-soloを使うサーバに持っていきましょう。

Roleの代わりとなるjsonファイルを用意する

chefを使う場合、Roleはとても便利なものです。
chef-soloの場合、このRoleを使うことができないので、代わりのjsonを作ります。
例えばこんな感じ。

{
  "ntp":{
    "servers":[
      "ntp01.hoge.com",
      "ntp02.hoge.com"
    ]
  },
  "td_agent":{
    "logfile":"/log/httpd/access",
    "servers":["fluentd-01"],
    "log_tag":"http.access"
  },
  "httpd":{
    "htdocs":"/var/www",
    "logdir":"/log/httpd"
  },
  "run_list": [
    "recipe[ntp]",
    "recipe[chef-td-agent]",
    "recipe[httpd]"
  ]
}

このjsonファイルによって、使うrecipeを設定して、recipeで使うattributeの値を設定します。

chef-solo.rbの用意

最後にchef-soloを動かすための環境設定ファイルを用意します。
chef-solo.rbは以下のような感じ。

file_cache_path "/var/tmp/chef-solo"
cookbook_path "/var/tmp/cookbooks"

これだけ。
chefを動かすためのcacheディレクトリの場所と、上で固めたCookbookを展開する場所を指定しています。

chef-soloの実行

以下のコマンドでchef-soloを実行。

chef-solo -c /path/to/chef-solo.rb -j /path/to/hoge.json

実はchef-soloはchefサーバのインストール時にも使っていたのですが、調べたまんまを意味もわからず書いてサーバをインストールしていましたが、改めて必要に迫られて使ってみると、chefサーバインストール時に書いたjsonの内容も納得。

それでは良いchefライフを!

[chef] knifeでroleで使われているrecipeを検索

chefの運用が始まって、サーバの設定のほとんどをchefでやるようになりましたが、その中で不便なことが出てきました。
recipeを変更してそれをサーバに反映させるときに、「はて?このrecipeはどこで使ってたっけ?」となってしまうのです。
今の環境では能動的にサーバの設定変更をしたいので、chef-clientはdaemonで起動しておらず、手動でchef-clientを実行してサーバに反映させています。
その場合にrecipeを変更した後、どのサーバでchef-clientを実行すればよいか探すことになり、場合によっては反映し忘れということも起こってしまいます。

knifeのsearchでrun_listを検索

そこで、knifeのsearch機能に着目してrun_listを検索することにしました。
使い方はこちらを参照。

Dashboard – Chef – Home – Chef Essentials – Search

knifeのsearchは具体的にはsolrのindexの検索です。
こんな感じで、run_listを検索しました。

$ knife search role "run_list:*MySQL*" -i
10 items found

db-server

stg-db
.
.
.

“-i”は検索結果のidのみを表示するオプションです。
これでrecipe[MySQL]を使っているroleを検索することができました。
ちなみに

$ knife search role "run_list:recipe[MySQL]"

としてもエラーが出てしまい検索できませんでしたので、上記のようなクエリーになったわけですが、多分もっとスタイリッシュなクエリーがあるはずです。それにはもっと勉強が必要なので今日はこれまで。

[chef] chefサーバのバックアップ

一通りchefの構築を済ませたので、この辺でchefサーバのバックアップを仕掛けることにしました。
chefも構築を始めるとノウハウの塊になってきて消失するとかなり痛いです。

node,role,data bagのバックアップ

node,role,data bagのバックアップはドキュメントに書いてあるchef_server_backup.rbを使うことにしました。

これを使えば、node,role,data bagは$cwd/.chef/chef_server_backupというディレクトリにそれぞれのオブジェクトがjson形式でバックアップされます。

chef_server_backup.rbの使い方

使い方ってほどでもないのですが、水増しのため解説します。

まずはchef_server_backup.rbを入手します。

cd /path/to/working_dir
curl -O https://raw.github.com/jtimberman/knife-scripts/master/chef_server_backup.rb

そして、knife execchef_server_backup.rbを実行します。
もちろんknifeが正常に動くことが大前提です。

knife exec /path/to/chef_server_backup.rb

以上!

でも、これではせっかく時間をかけて作ったCookbookがバックアップされません。
Cookbookはこれとは別にバックアップを取る必要があります。

Cookbookのバックアップは自作した

いろしろ探したのですが、Cookbookのバックアップツールは見つかりませんでした。
chef_server_backup.rbと同じような感じでできるのが一番かっこいいのですが、残念ながらそれをするためのRuby言語のスキルが僕には圧倒的に不足しています。

ということで、いろいろ悩んだ結果pythonでknifeを使って、cookbookのリストを取得し、それをknifeコマンドでdownloadするベタな方法にしました。
何で悩んだかというとcookbookのバージョンです。
ご存知の通りCookbookは複数のバージョンを保持することができます。それを全てバックアップを取ろうとするとどうやっても僕のスキルではbashで実装できなかったので、pythonで作りました。

恥をしのんでそのスクリプトを晒します。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import subprocess,sys,os,os.path,shutil
import datetime,locale

import smtplib
from email.MIMEText import MIMEText
from email.Utils import formatdate

backup_dir = '/data/chef-repo/.chef/cookbook'
subject = 'Cookbook Backup ERROR'
mail_from = 'from@domain'
mail_to = 'to@domain'

# 現在時刻を取得する関数
def getnow():
  return datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S")

# メール構築
def create_message(f,to,subject,body):
  message = MIMEText(body)
  message['Subject'] = subject
  message['From'] = f
  message['To'] = to
  message['Date'] = formatdate()
  return message

# メール送信
def sendMail(f, to, message):
  s = smtplib.SMTP()
  s.connect()
  s.sendmail(f, [to], message.as_string())
  s.close()
 
# 処理開始
print '---START {0} ----'.format(getnow())

# 作業用ディレクトリを消す
if os.path.isdir(backup_dir):
  print "Remove {0}".format(backup_dir)
  try:
    shutil.rmtree(backup_dir)
  except OSError:
    error_message = "ERROR: {0} can not delete.".format(backup_dir)
    print error_message
    sendMail(mail_from,mail_to,create_message(mail_from,mail_to,subject,error_message))
    sys.exit(1)

# 作業用ディレクトリの作成
try:
  os.mkdir(backup_dir)
except OSError:
  error_message = "ERROR: {0} can not create.".format(backup_dir)
  print error_message
  sendMail(mail_from,mail_to,create_message(mail_from,mail_to,subject,error_message))
  sys.exit(1)

# cookbookリストを取得する
cmdline = ['/usr/bin/knife', 'cookbook', 'list', '-a']
subproc_args = {
  'stdin':subprocess.PIPE,
  'stdout':subprocess.PIPE,
  'stderr':subprocess.STDOUT,
  'close_fds':True,
}

try:
  p = subprocess.Popen(cmdline, **subproc_args)
except OSError:
  error_message = "Failed to execute command. : {0}".format(cmdline.__str__())
  print error_message
  sendMail(mail_from,mail_to,create_message(mail_from,mail_to,subject,error_message))
  sys.exit(1)

cmd_args = {
  'stdin':subprocess.PIPE,
  'stdout':subprocess.PIPE,
  'stderr':subprocess.STDOUT,
  'close_fds':True,
}

# Cookbookごとにdownload
while True:
  line = p.stdout.readline().split()
  if not line:
    break
  cookbook_name = line.pop(0)
  print cookbook_name
  pp = ""

  # 複数バージョンがある場合はそれらをdownload
  for version in line:
    cmdline = "/usr/bin/knife cookbook download {0} {1} -d {2}".format(cookbook_name, version, backup_dir)
    print "Downloading {0} {1}: ".format(cookbook_name, version),
    try:
      ret = os.system(cmdline)
    except OSError:
      print "ERROR"
      error_message = "Failed to execute command.: {0}".format(cmdline.__str__())
      print error_message
      sendMail(mail_from,mail_to,create_message(mail_from,mail_to,subject,error_message))
      sys.exit(1)

    print "SUCSESS"
    
print '---END {0} ----'.format(getnow())

このスクリプトで/path/to/.chef/cookbookに全てのcookbookをダウンロードして、chef_server_backup.rbで取得したオブジェクトのjsonをまとめてtarで固めてバックアップを取ることにしました。

おしまい。

[chef] data bag活用法

data_bagを使う

chefにはdata_bagというものがあります。
attributeとは何が違うのかなーと思っていましたが、良い使い方を見つけましたのでご紹介させていただきます。

ユーザアカウントは全てdata_bagで管理

ユーザアカウント系は全てdata_bagで管理すると楽かも!と思ってやってみました。
例えばログインアカウント。
サーバの機能や場所ごとにroleを作りますが、そのroleの中でログインアカウントを管理するrecipeを入れるとします。
data_bagを使わないとrecpieもしくはroleのattributeにユーザ情報を格納して、recipeのuser resourceでユーザを作成することになると思います。
僕の管理している環境では鍵認証が基本なので公開鍵の情報も管理をしたいです。公開鍵はユーザに紐づいていてどのサーバでも同じ鍵で入れると便利ですよね。
そこで、data_bagを活用。
data_bagはdata_bagそのものと、data_bag_itemという2階層構造になっているようです。
まず、data_bag['users']というdata_bagを作成します。  
そしてその中にdata_bag_item['login_account']というdata_bag_itemを作ります。
data_bag_item[‘login_account’]は配列でユーザ情報が格納されています。
例えばこんな感じ。

{
  "users":[
    {
      "system": [
        "role1",
        "role2"
      ],
      "key": "ssh-rsa xxx",
      "name": "user01",
      "active": true,
      "uid": 10001
    },
    {
      "system": [
        "role1",
        "role2"
      ],
      "key": "ssh-rsa xxx",
      "name": "user02",
      "active": true,
      "uid": 10002
    }
  ],
  "id": "login_account"
}

ユーザ情報は

  • uid
  • account名
  • 公開鍵
  • ユーザを作るシステム
  • 有効フラグ

という情報からなっています。

ユーザ作成のrecipeはこんな感じ。

# User add or delete
for u in data_bag_item('users','login_account')['users']
act = false

  # 適用システムかどうかを確認する
  for role in node["roles"]
    for system in  u["system"]
      if role == system
        act = true
        break
      end
    end
    if act
      break
    end
  end

  if ! act
    next
  end

 # activeがfalseなら削除
 user_act = u["active"] ? "create":"remove"

 # ホームディレクトリの文字列作成
 home_dir = "/home/" + u["name"]

 # useradd
 user u["name"] do
  uid u["uid"]
  gid u["uid"]
  shell "/bin/bash"
  home home_dir
  action user_act
 end

 # .ssh directory
 ssh_dir = home_dir + "/.ssh"
 # authorized_keys
 authorized_keys = ssh_dir + "/authorized_keys"

 # .ssh directory作成
 if u["active"]
  directory ssh_dir do
   owner u["name"]
   group "ssh_users"
   mode  0700
  end
  
  # authorized_keyの作成
  if u["key"]
   file authorized_keys do
    owner u["name"]
    mode  0600
    content u["key"] + " " + u["name"]
   end
  end
 end
end

こんな感じで、各サーバのユーザアカウントを管理しています。
もう少し大規模になればディレクトリサービスとかを使うのですが、そこまででもない規模はこれでいいかなーと。

これをやっていてchefの残念なところを発見。

 # activeがfalseなら削除
 user_act = u["active"] ? "create":"remove"

この部分はactiveを見て、trueならcreate,falseならremoveというactionのパラメータを指定しています。
理想はrecipeからユーザが消えたら自動で削除してくれるのが良いのですが、どうもそうではないようなので、不要になったユーザはactiveをfalseにして、明示的に削除処理をするようにしています。

login_accountの説明でだいぶ長くなってしまいましたが、このdata_bag["users"]はユーザ管理data_bagとして、login_accountの他に、例えばdata_bag_item['mysql']とかdata_bag_item['basic']とかを登録して、ここを見ればユーザ管理が出来るというように使いたいと思っています。

おしまい。

[chef] clientとnodeの関係

解りづらいclient

chefの要素でclientとnodeがあります。
いろいろ調べる中で、このことを解説しているところがあるのですが、自分は以下のように解釈していました。
Node = 管理対象サーバ
client = Chef Serverとやり取りするインタフェース

この解釈で概ね誤りは無いようなんですが、一つ大きく勘違いをしているところがありました。

Clientの流用は出来ない

どういうことかというと、一つのclient、もっと言えばClientのPrivate Keyを複数のサーバに配布して「Nodeは増えるけど、Clientは増えない」ということは出来なそうな感じです。

chefを使う理由は関するサーバの台数が増えて来て管理が大変なので、一つ例えば「forServer」というclientを作っておいて、そのprivate keyをchef管理下に入るサーバに置いておいて、clientはそれを使ってもらうということにしたかったのです。

chefの管理下に入るには以下の手順が行われます。

  1. 管理対象サーバにchef-clientをセットアップ
  2. chef-serverのclient[chef-validator]のprivate keyをvalidation.pemとして配置。(通常は/etc/chef/validation.pem
  3. 管理対象サーバでchef-clientコマンドを実行
  4. client[chef-validator]によって、chef-serverにnode登録される
  5. 管理対象サーバ用のclientが作られて、管理対象サーバにはclient.pemが配置される(通常は/etc/chef/client.pem

こんな手順でサーバをchef管理下に入れていきますが、出来れば各サーバごとにclientが増えるのは勘弁してほしいところ。
ちなみにあらかじめ作成したclientのprivate keyを配置してclient.rbに以下の行を指定してもダメでした。

client_key   "/etc/chef/client.pem"

どうにかならないものでしょうか。

[chef] attributeの理解

今回はattributeの話。
お約束ですが、この記事の内容は私が一人で分かったつもりになって書いていることなので、誤認やウソが書いてありましたらご指摘してくださると助かります。

attributeとは

Recipeではできるだけ汎用的に書いた方がいいという雰囲気がありますが、それはその通りだと思います。
その汎用性を持たせる機能を担っているのが、providerとこのattributeでは無いでしょうか。
providerはOSなどの環境を吸収してくれて、attributeは変数として使うことができます。

分からなかったのはあちこちでattributeが設定できること

CookbookやRecipeを調べてたり、ドキュメントを読んでいたりすると、あちこちでdefault_attributesやらoverride_attributesを見かけます。
いったいどこでattributeを設定するのが正しいの?と混乱してしまっていました。

その混乱を鎮めたのがドキュメント以下の記述。

Chef > Home > Chef Essentials > Attributes > Precedence

これを見ると設定箇所によってプライオリティがあり、どこで設定するのが適切かということが分かった気がします。

イメージとしては、CookbookのCOOKBOOK/attribute/default.rbではデフォルト値を設定。
roleのdefault_attributesではそのRoleに適した値を設定。
という感じでしょうか。

具体例

具体例を挙げてみます。
シナリオはいつもの通りAWS EC2でchefを使うという想定です。
複数のAWSアカウントを持っていて、横断的にchefを使いたい。
例としてAWSアカウントの各種認証情報をサーバに保管するRecipeを作ります。

template "/path/to/credencial" do
 source "credencial.erb"
 owner "root"
 group "root"
 mode 0644
 variables(
  :cert      => node["ec2"]["cert"],
  :pk        => node["ec2"]["cert"],
  :awsid     => node["ec2"]["awsid"],
  :accesskey => node["s3"]["access_key_id"],
  :secretkey => node["s3"]["secret_key"],
  :region    => node["ec2"]["region"]
 )
end

これは/path/to/credencialというファイルにAPIの証明書やらaccesskey_idなどの情報を記載したファイルのResourceです。
もちろんCOOKBOOK/templates/default/credencial.erbというテンプレートファイルも用意します。
そして、COOKBOOK/attribute/default.rbに以下のように上記resourceで使っているattributeのデフォルト値を設定します。

# s3 key 
default["s3"]["access_key_id"] = ""
default["s3"]["secret_key"] = ""

# ec2 credencial
default["ec2"]["key_location"] = ""
default["ec2"]["cert"] = ""
default["ec2"]["pk"] = ""
default["ec2"]["awsid"] = ""
default["ec2"]["region"] = "ap-northeast-1"

デフォルト値と言ってもそれぞれ、適切な値を入れないと意味がないので、適当に空に設定してみました。

次にRoleを作ります。
account_aというAWS Accountに紐づいたRole[account_a]というroleを作ります。

name "account_a"
description "AWS account_a account servers"
run_list("recipe[Ec2Init]")
default_attributes({
  "s3" => {
    "access_key_id" => "xxxxxxxxxxxxxxxx",
    "secret_key" =>  "xxxxxxxxxxxxxxxxxxx"
  },
  "ec2" => {
    "key_location" => "hoge",
    "cert" => "cert-xxxx.pem",
    "pk" => "pk-xxxx.pem",
    "awsid" => "xxxx"
  }
})

こんな感じで、attributeを便利に使うことができました。

まとめ

attributeが使えるようになって、汎用性がぐっと広がりました。
やってみて分かったんですが、attributeは最終的にnodeに付与されて、node['hoge']['piyo']という感じで指定するんですね。

次回はなにかな?

[Chef] Cookbook,Recipe,Roleの関係

Chef検証再開。

Chefを設計・運用するうえで、どうしても理解しなければいけないのが、CookbookRecipeとRole`の関係。

とりあえず、それを理解するためにこのスライドを見てみた。

Resouceは設定の最小単位

Resouceは各種設定の最小単位と解釈しました。
ファイルを配置する、ソフトウェアをインストールする、ユーザを作る、等など。

これらの設定はOSによってコマンドはまちまちですが、それぞれのコマンドの違いはPlatformやらProviderやらで吸収されて、ResouceではそれらのOSの違いを意識せずに設定を指定できるところが良いところです。

RecipeはResourceのリスト

スライドに

Recipes are lists of Resources

と書いてありました。

僕は、「Recipeは一つの操作をするために必要なこと(Resource)がまとまったもの」と解釈しました。
例えば、apacheをインストールする場合、以下のような設定をする必要があります。

  • apacheのインストール
  • httpd.confの設定
  • 必要に応じて各種設定

それらを一つのRecipeの中に書くことで、apacheのインストールという一つの操作を完了することができます。

[ Cookbook -ne Role ]

分からなかったのが、CookbookとRoleの区別でした。
Roleは単語の意味からWebServerなどの役割を指定して、そのRoleをNodeに適用するとWebServerの機能が構築されてWebServerとして利用できるものと理解しています。

では、Cookbookは?

Cookbookの役割がよくわからなかったので、試しに[Opscode Public Cookbooks](Opscode Public Cookbooks)からApacheのCookbookをダウンロードして中を見てみました。
Cookbook[“apache”]のRecipeのdefaults.rbはapache本体のインストールでした。
そして、defaultではないその他のRecipeはapache modulesのインストールでした。

これで何となくCookbookが分かって来た気がしますが、さらに理解を深めるためcookbook[MySQL]を見てみました。

cookbook[“MySQL”]のRecipesの中にはdefault.rb,client.rb,server.rbがありました。
Recipeのdefault.rbには一行。

include_recipe "mysql::client"

CookbookはRecipeのグループ

これらの調査から、Cookbookはソフトウェアや設定のグルーピングとして使うという理解に至りました。
特定のソフトウェアとそれに付属するソフトウェアや設定などを一つのCookbookにまとめることができそうです。

Roleを書く

ということでまとめという意味でWebServerというRoleがあったと仮定して、そのRoleのファイルを書いてみます。


name "webserver"
run_list "recipe[common]","recipe[apache2]", "recipe[apache2::mod_proxy]","role[product]", "role[datacenter01]"

まだまだ続く

とりあえずここまでなーんとなく分かった気がします。

でも、まだまだ分からないことばかり。
例えば、attributeとか。
さらに調査を続けます。

もし、この記事の内容に理解に誤りがあれば、是非ご指摘ください。お待ちしています。

Chef-Serverを使ってみる(2)

前回は、Recipeの登録と、Roleの登録をしたので、今回はいよいよもう一台サーバを用意して、Recipeの適用をしてみたいと思います。

Nodeの登録

まずはNode登録から。
以下のコマンドでノードを登録します。
ついでにRoleも設定します。

knife node create node01

{
  "chef_environment": "_default",
  "normal": {
  },
  "run_list": [
    "role[front]"
  ],
  "name": "node01"
}

確認してみます。

$ knife node show mode01
Node Name:   node01
Environment: _default
FQDN:        
IP:          
Run List:    role[front]
Roles:       
Recipes:     
Platform:

Node用のClientの作成

Node側の設定の前にNode用のClientを作っておきます。
ちょっと混乱してしまったのですが、Clientとはchef-clientのことで、Chef-Serverとお話しするインタフェースのことです。

  • WebUI用Client
  • knife用Client
  • Node用Client

あたりを用意しておけば良いのかなと思います。
今回はNode用Clientを作ります。
clientの作成はいつものknifeで。

$ export EDITOR=vi
$ knife client create client01 -c /data/chef/.chef/knife.rb
※とくに修正せずに保存
Created client[client02]
-----BEGIN RSA PRIVATE KEY-----
.
.
.
-----END RSA PRIVATE KEY-----

これでClient作成完了。
作成時に出力されたPrivate Keyは大事に保管しておきましょう。

Node側の設定

Chef-ServerにNodeの登録とNode用Clientができたので、ノードのサーバにchef-clientを設定します。
まずはお約束のruby-gemsとruby-develをyumでインストール。

$ sudo yum install -y rubygems.noarch ruby-devel.x86_64

そして、gemでchefをインストール。

$ sudo gem install chef

chef-clientの設定

まずはchef-clientの設定を行います。
knifeを使って設定のひな形を作ります。

$ sudo knife configure client /etc/chef
WARNING: No knife configuration file found
Creating client configuration
Writing client.rb
Writing validation.pem

これで/etc/chefclient.rbvalidation.pemができますので、できたclient.rbを自分の環境に変更します。
僕はChef Configuration Settingsを参照しながら、以下のように修正してみました。

$ sudo vi /etc/chef/client.rb

log_level        :info
log_location     STDOUT
chef_server_url  'http://chef-server:4000'
validation_client_name 'client01'
node_name node01

validation.pemの配置

そして、/etc/chef/validation.pemを配置します。
validation.pemはclient.rbのvalidation_client_nameで設定したclientのPrivate Keyです。
chef-serverでNode用clientのclient01を作成したときに保管したPrivate Keyを/etc/chef/validation.pemというファイル名で配置します。

これで準備完了のはずなので、node01でchef-clientを実行してみます。

$ sudo chef-client
INFO: *** Chef 0.10.8 ***
INFO: Client key /etc/chef/client.pem is not present - registering
INFO: HTTP Request Returned 403 Forbidden: You are not allowed to take this action.
FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
FATAL: Net::HTTPServerException: 403 "Forbidden"

「403 Forbidden」と言われてしまった。
chef-server側のログを見てみます。

$ view /var/log/chef/server.log

merb : chef-server (api) : worker (port 4000) ~ Params: {"format"=>nil, "controller"=>"nodes", "action"=>"show", "id"=>"node01"}
merb : chef-server (api) : worker (port 4000) ~ {:action_time=>0.001098, :after_filters_time=>1.6e-05, :before_filters_time=>7.0e-05, :dispatch_time=>0.008263}
merb : chef-server (api) : worker (port 4000) ~
merb : chef-server (api) : worker (port 4000) ~ Started request handling: Fri Apr 27 00:15:40 +0000 2012
merb : chef-server (api) : worker (port 4000) ~ Params: {"controller"=>"clients", "action"=>"create", "name"=>"node01", "admin"=>false}
merb : chef-server (api) : worker (port 4000) ~ You are not allowed to take this action. - (Merb::ControllerExceptions::Forbidden)

なんかNodeをcreateしようとして”You are not allowed to take this action.“と言われている様子。
すでにNodeは登録しているのにcreateするのはよくわかりませんがとりあえず、Node用Clientでadminをtrueに変更します。

$ knife client edit client01

{
  "json_class": "Chef::ApiClient",
  "_rev": "1-3d9d9fefc931326527ab1bb9d5c43f0a",
  "admin": false,
  "public_key": ….,
  "name": "client01",
  "chef_type": "client"
}

これでもう一度chef-clientを実行すると無事、Recipeが適用されました。

おしまい。

Chef-Serverを使ってみる(1)

今回は、前回構築したchef-serverを使ってみようと思います。

chef-serverにレシピを登録

まずはchef-serverにレシピを登録してみます。
最初にchef-soloでもやったchef-repoを作ります。
今回は/dataにchef-repoを作ります。

$ sudo su -
# mkdir /data/chef
# chown chef:chef /data/chef
# cd /data/chef
# su -c 'git clone git://github.com/opscode/chef-repo.git' chef

次にknifeの設定。

# sudo su - chef
$ bash
$ knife configure
WARNING: No knife configuration file found
Where should I put the config file? [~/.chef/knife.rb] /data/chef/.chef/knife.rb
Please enter the chef server URL: [http://starmp-dev-chef-01:4000] http://localhost:4000
Please enter an existing username or clientname for the API: [www] chef
Please enter the validation clientname: [chef-validator] 
Please enter the location of the validation key: [/etc/chef/validation.pem] 
Please enter the path to a chef repository (or leave blank): /data/chef/chef-repo

そしてCOOKBOOKを作成。

$ knife cookbook create Ec2Base -c /data/chef/.chef/knife.rb
** Creating cookbook Ec2Base
** Creating README for cookbook: Ec2Base
** Creating metadata for cookbook: Ec2Base
$ ls /data/chef/chef-repo/cookbooks/
Ec2Base  README.md

レシピを/data/chef/chef-repo/cookbook/Ec2Base/recipes/initial.rbに登録します。
今回登録するレシピは前回作成したものを使います。

作ったCOOKBOOKをサーバにアップロード

ここからが初体験。
作ったCOOKBOOKをchef-serverにアップロードしてみます。

$ knife cookbook upload -a -o /data/chef/chef-repo/cookbooks -c /data/chef/.chef/knife.rb 
ERROR: Your private key could not be loaded from /data/chef/.chef/chef.pem
Check your configuration file and ensure that your private key is readable

はい、だめー。

サーバとの通信用に鍵交換などをする必要があるっぽいです。
ちょっと調べてみるとknife configure -iで設定するっぽいのでもう一度knifeの設定をやり直します。

$ knife configure -c /data/chef/.chef/knife.rb -i
Overwrite /data/chef/.chef/knife.rb? (Y/N) y
Please enter the chef server URL: [http://server-01:4000] http://localhost:4000
Please enter a clientname for the new client: [www] knife
Please enter the existing admin clientname: [chef-webui] 
Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem] 
Please enter the validation clientname: [chef-validator] 
Please enter the location of the validation key: [/etc/chef/validation.pem]  
Please enter the path to a chef repository (or leave blank): /data/chef/chef-repo
Creating initial API user...
Created client[knife]
Configuration file written to /data/chef/.chef/knife.rb

設定に成功していればknife client listでchef-serverのclient一覧が出るようなのでやってみます。

$ knife client list
  chef-validator
  chef-webui
  cilent-test
  knife

できたー。

それではさっそくCOOKBOOKをアップロードします。

$ knife cookbook upload -a -o /data/chef/chef-repo/cookbooks -c /data/chef/.chef/knife.rb 
Uploading Ec2Base             [0.0.1]
upload complete

できました。

knife cookbook listで確認してみます。

$ knife cookbook list
Ec2Base   0.0.1

登録されています。

Roleの登録

次にこのレシピを使うRoleの設定をしてみます。
今回はknifeを使ってchef-serverのrole設定をしてみます。

$ export EDITOR=vi
$ knife role create web

{
  "json_class": "Chef::Role",
  "name": "web",
  "env_run_lists": {
  },
  "description": "Web Server",
  "chef_type": "role",
  "override_attributes": {
  },
  "default_attributes": {
  },
  "run_list": [
    "recipe[initial]"
    "recipe[Ec2Base::initial]"
  ]
}

登録されているか確認します。
※追記:run_listの書き方が間違っていましたので訂正しました。
書式はrecipe[COOKBOOK::RECIPE]でした。
recipeがdefault.rbの場合はrecipe[COOKBOOK]でOK。

$ knife role show web
chef_type:            role
default_attributes:   
description:          Web Server
env_run_lists:        
json_class:           Chef::Role
name:                 web
override_attributes:  
run_list:             recipe[initial]

今回はここまで。
次回はNodeを登録して、実際にRecipeを反映させてみたいと思います。

chefたのしー!knifeサイコー!

chef-server構築

gemでchefをインストール

今度はchefをS/Cで動かすべく、またイチからchefを構築します。
今度の環境はCentOS6.2 x86_64です。

さっそく

sudo gem install chef

を実行。
しかしエラー。
ruby-develが無いようなのでインストール。

sudo yum install -y ruby-devel.x86_64

そして再び

sudo gem install chef

chef-soloでchef-serverをインストール

chef-serverのインストールは
さくらのVPSにchef-serverをインストール(kikumotoのメモ帳)
を参考にさせていただきました。ありがとうございます!
chef-soloでchef-serverをインストールするのがおもしろい。

まずは作業用のディレクトリを作る。
mkdir ~/tmp_chef

そこにsolo.rbを作成。

vi tmp_chef/solo.rb

file_cache_path "/tmp/chef-solo"
cookbook_path "/tmp/chef-solo/cookbooks"

同じく、chef.jsonも作成。

vi tmp_chef/chef.json

{
  "chef": {
    "server_url": "http://localhost:4000",
    "webui_enabled": true,
    "init_style": "init"
  },
  "run_list": [ "recipe[chef::bootstrap_server]" ]
}

そして、chef-soloコマンドでchef-serverをインストール。

$ sudo su -
# chef-solo -c /home/piyo/tmp_chef/solo.rb -j /home/piyo/tmp_chef/chef.json -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz

うまく行ったでしょうか。
うまく行けばport4040が開いていて、ブラウザで接続できるはずです。

# netstat -an | grep 4000
tcp        0      0 0.0.0.0:4000                0.0.0.0:*                   LISTEN

http://your-server:4040

デフォルトのユーザ/パスワードはadmin/p@ssw0rd1です。

とりあえず、WEB UIにアクセスすることはできました。
が、よーわからんという感じなので、次回は使い方をやりたいと思います。
せっかくWEB UIがあるのですが、できたらCLIで操作したいなと思ったりもしてます。
続く。