npm private repository を Chef で作る (Chef Solo 入門を読みました)。

入門Chef Solo - Infrastructure as Code

入門Chef Solo - Infrastructure as Code

今までKnifeとかChefとかVagrantとか色々聞いてたんですけど、便利そうだなーと思いながらも全然手を付けられていませんでした。

丁度先日開催されたLL祭りの時にも話題になってましたね。

Chef Solo入門を読んでみて思いましたが、非常によくまとまっていて、かつそれぞれの位置づけがはっきりと分かる良書でした。

せっかくなので、これを読んでnpm private repositoryを作ってみようと思ったので、作ってみました。
あと、作るまでにやったインストール等の事をまとめていきます。

ちなみに、 npmのprivate repositoryを作るChefのコードはつい10日前くらいにOpsCodeに上がったので、ただprivate repositoryを作りたい人はそれを見たほうが早いかも。ちなみにnpm private repositoryは大体80〜90GB程度の容量が必要になるのでそれ以下の容量の場合は増設を検討してください。

npm registry: Chef Cookbook: npm_registry - Opscode Community

何はともあれ、環境構築

1. Chef
サーバーの状態を記述するツール、状態、というのは例えば nginx が入ってたり、 zsh が入ってたり、といったソフトウェアのインストールされているかどうかやそれらのnginx.conf等の設定ファイルのカスタマイズがそろっている状態のことを指す。

■インストール方法

$ curl -L http://www.opscode.com/chef/install.sh | sudo bash

gemからインストールする方法もあるみたい。

$ gem install chef

2. Vagrant
仮想サーバをCLIで作成、起動、破棄するためのツール。
非常に簡単にイメージを構築できるため、Chefのテスト・確認に使うことが可能。

インストール方法

vagrant はhttp://downloads.vagrantup.com/からインストール可能。

もちろん、これもgemからインストールできます。

$ gem install vagrant

また本書内ではvagrantのイメージを構築するVagrantプラグインとしてsaharaの紹介がされていたが、最近はsaharaよりも vagrant-vbox-snapshot の方がスナップショットに名前を付けられるので良いとのことが書いてあった

vagrant-vbox-snapshot

//install
$ vagrant plugin install vagrant-vbox-snapshot # install
$ vagrant snapshot take init # initという名前でスナップショットを作成
$ vagrant snapshot back # 最新のスナップショットにロールバック
$ vagrant snapshot go init # initを指定してロールバック

3. Knife
Chefのテンプレート作成したり、ローカルで準備したレシピをリモートで実行する際に利用するツール。基本的に直接Chefを実行するというよりもknife経由で実行することが多い。

install

$ gem install knife-solo

4. Berkshelf (入れなくても使えるけど入れておくと便利)

Gemfile風に記述して、bundle execする事で第三者が作ったレシピをローカルに持ってくることができるツール。Berksfileと呼ばれる設定ファイルを書くことでRubyistなら慣れている手順でレシピを持ってくることができる。

install

$ gem install berkshelf

npm private repositoryを Chef で作る

環境を構築したところで手順的に何をどういうステップでやったのか記述していきます。

0. vagrantで仮想マシンを作成する

まずは雛形を作成しましょう。

$ knife solo init npm-repo

次にテスト用の環境を構築しましょう。

Vagrantの設定ファイルであるVagrantfileをプロジェクトの直下に配置しておきます。
vagrant initコマンドを利用するとVagrantfileの雛形を作成してくれます。

$ cd npm-repo
$ vagrant init

Vagrantfile

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos63"
  config.vm.network :forwarded_port, guest: 80, host: 8080
end
$ vagrant box add centos63 https://s3.amazonaws.com/itmat-public/centos-6.3-chef-10.14.2.box
$ vagrant up

これだけで vagrantを使ってCentOS 6.3の環境が簡単に手に入ります。ログインを試す場合は以下の方法で試しましょう。

$ vagrant ssh

あとでも使うのでvagrant sshの設定をssh configに書いておきます。
ここでは、npmというホスト名でログインできるようにしておきます。

$ vagrant ssh-config --host npm >> ~/.ssh/config 
$ ssh npm

1. knifeでテンプレートを作る。

まずはknifeの設定をします。

$ knife configure

いくつか質問されるので適当に回答しましょう。
その後、chefのリモート実行準備用のスクリプトを流しておきましょう。

$ knife solo prepare npm

準備が終わったらいよいよrecipeの作成に入ります。


2. Berkshelfを実行し、npm repositoryに必要なrecipeを持ってくる

Bekrshelfを使う前にOpsCode Communityからクックブックをインポートするための設定をしましょう。
OpsCode CommunityのサイトからSign upを行い、private keyをDLしてください。
DLしたprivate keyは ~/.chef/username.pem のような形式で保存し、最後に ~/.chef/knife.rb のclient_keyの所にそのパスを保存してください。

Berkshelfを使う前にGemfileを作ります。以下の様な要領でGemfileをプロジェクトのルートに作成してください。

Gemfile

# Gemfile
source :rubygems
gem 'berkshelf'

作ったら下記のコマンドを実行します。

$ bundle --path vendor/bundle

これでBerkshelfを使う準備は完了です。
終了したら今度はBerksfileを作成します。
npm repositoryにはcouchdbとnodejsが必要なのでインストールしておきます。
後は一応gitがあると色々と便利なので入れておきます。

Berksfile

site :opscode

cookbook 'couchdb'
cookbook 'nodejs'
cookbook 'git'

作ったら下記のコマンドを実行します。

$ bundle exec berks --path cookbooks

couchdbの設定ファイルを少しだけ編集します。
もっとクールなやり方があるのかもしれませんが、今のところ直接cookbooksを修正する方法しか思いつきませんでした。。

cookbooks/couchdb/attributes/default.rb

#
# Author:: Joshua Timberman <joshua@opscode.com>
# Cookbook Name:: couchdb
# Attributes:: couchdb
#
# Copyright 2010, Opscode, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

default['couch_db']['src_checksum']   = "b54e643f3ca5f046cfd2f329a001efeaae8a3094365fa6c1cb5dcf68c1b25ccd"
default['couch_db']['src_version']    = "1.2.1"
default['couch_db']['src_mirror']     = "http://archive.apache.org/dist/couchdb/#{node['couch_db']['src_version']}/apache-couchdb-#{node['couch_db']['src_version']}.tar.gz"
default['couch_db']['install_erlang'] = true

# Attributes below are used to configure your couchdb instance.
# These defaults were extracted from this url:
#  http://wiki.apache.org/couchdb/Configurationfile_couch.ini
#
# Configuration file is now removed in favor of dynamic
# generation.

default['couch_db']['config']['couchdb']['max_document_size'] = 4294967296 # In bytes (4 GB)
default['couch_db']['config']['couchdb']['max_attachment_chunk_size'] = 4294967296 # In bytes (4 GB)
default['couch_db']['config']['couchdb']['os_process_timeout'] = 5000 # In ms (5 seconds)
default['couch_db']['config']['couchdb']['max_dbs_open'] = 100
default['couch_db']['config']['couchdb']['delayed_commits'] = true
default['couch_db']['config']['couchdb']['batch_save_size'] = 1000
default['couch_db']['config']['couchdb']['batch_save_interval'] = 1000  # In ms (1 second)

default['couch_db']['config']['httpd']['port'] = 5984
default['couch_db']['config']['httpd']['bind_address'] = "127.0.0.1"
default['couch_db']['config']['httpd']['secure_rewrites'] = false #ここにsecure_rewritesを追記。

default['couch_db']['config']['log']['level'] = "info"

default['couch_db']['config']['vhosts']['npm.yourcompany.co.jp'] = "/registry/_design/scratch/_rewrite"

3. npm リポジトリ作成用のレシピを作る

site-cookbooksフォルダ以下にレシピを作成します。

$ knife cookbook create npm-repo -o site-cookbooks

次にsite-cookbooks/npm-repo/recipesの下にあるdefault.rbを以下のように変更します。

site-cookbooks/npm-repo/recipes/default.rb

#
# Cookbook Name:: npm-repo
# Recipe:: default
#
# Copyright 2013, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#

git "#{Chef::Config['file_cache_path']}/npmjs.org" do
  repository "git://github.com/isaacs/npmjs.org.git"
  reference "master"
  action :sync
end

execute "npm install couchapp -g" do
  command "npm install couchapp -g"
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  action :run
end

execute "npm install semver" do
  command "npm install semver"
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  action :run
end

execute "npm install couchapp" do
  command "npm install couchapp"
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  action :run
end

bash "curl put" do
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  code <<-EOH
    curl -X PUT http://localhost:5984/registry
  EOH
end

execute 'push.sh' do
  command './push.sh'
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  environment({'npm_package_config_couch' => 'http://localhost:5984/registry'})
  action :run
end

execute 'load-views.sh' do
  command './load-views.sh'
  cwd "#{Chef::Config[:file_cache_path]}/npmjs.org"
  environment({'npm_package_config_couch' => 'http://localhost:5984/registry'})
  action :run
end

bash 'COPY _design/app' do
  code <<-EOH
    curl http://localhost:5984/registry/_design/scratch -X COPY -H destination:'_design/app'
  EOH
end
execute "setup_couchapp push repository" do
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  command "couchapp push registry/app.js http://localhost:5984/registry"
  action :run
end

execute "setup_couchapp push app.js" do
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  command "couchapp push www/app.js http://localhost:5984/registry"
  action :run
end

bash "curl post" do
  cwd "#{Chef::Config['file_cache_path']}/npmjs.org"
  code <<-EOH
    nohup curl -X POST http://localhost:5984/_replicate -d '{"source":"http://isaacs.iriscouch.com/registry/", "target":"registry" }' -H "Content-Type: application/json" &
  EOH
end

次にnodes/npm.jsonを以下のように変更します。

nodes/npm.json

{
  "run_list": [
    "couchdb",
    "nodejs",
    "git",
    "npm-repo"
  ]
}

4. レシピ実行

以下のコマンドでレシピを実行します。

$ knife solo cook npm

完成、、ですが、npmのリポジトリがクローンされるまでには非常に長い時間がかかるので手放しで終わりにできません。大体1日位放置してその後、サーバーにログインしてnpmコマンドを打ち、思い通りの結果が返ってくれば成功です。

$ ssh npm
$ npm --registry http://localhost:5984/registry/_design/scratch/_rewrite search

まとめ

Chef楽しいです。npm repositoryのような大物を作るのにはやっぱり時間がかかりますが、環境のセットアップする程度であれば全然時間も手間もかからずに作成できますし、3rd partyの作ったレシピが使えるのはやっぱり嬉しい。

Yeomanと併用する話やそれぞれのツールの位置づけを色々まとめたいなと思ったのですが、また後で書きます。