つい先々週の話なんですが、Webアプリのテスト環境を構築するためにVagrantとChefを使ってみました。
きっかけはQiitaのこの記事、
事前準備から基本的な使い方・バグについての言及まで含まれた良記事で、これを見ながら手を動かすだけで、Chefを用いたサーバー構築自動化についてはざっくりと理解することができると思います。
今回、Vagrant+Chef+Berkshelfに入門した私が一所懸命かんばってMySQLのインストールと設定をやってみたので、その過程を書き留めておきます。
※ この記事の前半では、上記の記事の内容を大いに参考にしています。前半はChefなどのインストールが続きます。
Chefのセットアップなどはすっかり終わっていてだけ見たい方はこの記事の後半まで飛んでください。
事前準備
まずはインストール
- VirtualBox:VirtualBox 5.0.6
Download VirtualBox - Vagrant:Vagrant 1.7.4
Download Vagrant - Chef:Chef Development Kit Version: 0.9.0
Download Chef
必要な Vagrant プラグインをインストールしておく
$ vagrant plugin install vagrant-omnibus $ vagrant plugin install vagrant-chef-zero
- vagrant-omnibus:ゲスト OS に Chef をインストールするために必要なプラグイン
- vagrant-chef-zero:Vagrant から Chef Zero Server を起動するためのプラグイン
プレーンな環境を構築
と、ここまでほぼコピペなんですよね。
なんせ変えるところがない。
引き続きOSのインストールに移ります。
今回はCentOS 6.5 (64bit)を使用します。
公式のVagrant Boxカタログにアクセスして"CentOS"で検索を書けると、puphpet/centos65-x64というBoxが見つかるので、これを使います。
$ mkdir test_centos65 $ cd test_centos65 $ vagrant init puphpet/centos65-x64 --minimal
--minimal
オプションをつけることで説明用のコメントが一切無い、すっきりとしたVagrantfileが生成されます。
Vagrantfile
Vagrant.configure(2) do |config| config.vm.box = "puphpet/centos65-x64" end
こんな感じのファイルが生成されているはずです。
余談ですが、Boxを探すときはVagrant公式でもおすすめされているようにAtlas by HashiCorpから優先的に探したほうがいいようです。
"Vagrant Box"などで検索するとVagrantbox.esが先にヒットするんですが、こちらは個人でホスティングしているBoxへのリンクが貼られているだけなので、久しぶりにvagrant up
してみたらBoxがリンク切れしていて立ち上がらない... みたいなことになりかねません。
DropboxのPublicディレクトリのURLを貼ってるものもあるみたいですし、信頼性は低いのかもしれません。
ちゃんとAtlas by HashiCorpを使いましょうね。
Vagrantfile 追加設定
さて、最初にVagrantfileをエディタで開いて最小限の設定をしてしまいましょう。
Vagrantfile
Vagrant.configure(2) do |config| config.vm.box = "puphpet/centos65-x64" config.vm.provider "virtualbox" do |vm| # "vm.gui = true"でGUIを起動する # "vagrant up"が終わらないなどの場合はGUIを起動して様子を見てみる vm.gui = false # 32bitマシンからでもエラーなく起動できるようにゲストOSの種類を明示的に指定 vm.customize ["modifyvm", :id, "--ostype", "RedHat_64"] # メモリ割り当ては1024MB # vagrant upの最中にメモリ不足でコケるようだったらメモリをもっと割り当ててみる vm.customize ["modifyvm", :id, "--memory", "1024"] end # ゲストOSにアクセスするときのプライベートIPを指定 config.vm.network :private_network, ip: "192.168.33.10" end
追記して保存したら、仮想マシンが起動できるか確かめてみます。
$ vagrant up
最初の1回はBoxファイルのダウンロードが入るので少し時間がかかるかも知れません。のんびり待ちましょう。
立ち上がったらゲストOSにSSHで接続してみます。
$ vagrant ssh
Windowsをお使いの方は多分コマンドプロンプトから直接SSHできないので、適当なSSHクライアントで192.168.33.10
にアクセスしてみてください。
ユーザーはvagrant
、パスワードもvagrant
です。
問題なく接続できたら、ゲストOSからログアウトします。
[vagrant@localhost ~]$ exit
さて、はやく次に進みたいところですが、その前に、先ほどVagrantfileに追記した内容について一つずつ追って見ていきましょうか。
vm.gui = false
これはvagrant up
したときにゲストOSのGUIのウィンドウを立ち上げない設定です。
とはいえ、デフォルトでvm.gui = false
なのでこの記述は無くても影響ないです。
なぜわざわざ記述を追加するかというと、vagrant up
してみたけどいつまで経っても完了しないといった場合のためです。
そんなときにはvm.gui = true
に書き換えてGUIの方を見守ってみてください。
ゲストOS側でVagrantに通知されない致命的なエラーが起こって止まっている場合があります。GUIを立ち上げていればGUIの方のダイアログでエラー通知が見れるかもしれません。
vm.customize ["modifyvm", :id, "--ostype", "RedHat_64"]
ゲストOSがRedHatの64bit OSであることを明記しています。
この記述も省略可能ですが、32bitマシン上に64bitのゲストOSをマウントしようとするとエラーが起こる場合があるらしいので念のためこの記述を追加しておきます。
"RedHat_64"
の部分は立ち上げようとしているゲストOSの種類によって変える必要があります。
主だったところでは、
ゲストOSの種類 | ostype ID |
---|---|
Ubuntu 32bit | Ubuntu |
Ubuntu 64bit | Ubuntu_64 |
CentOS 32bit | RedHat |
CentOS 64bit | RedHat_64 |
などの指定ができるようです。
v.customize ["modifyvm", :id, "--memory", "1024"]
ゲストOSに割り当てるメモリの量をMB
単位で指定しています。
例によってこの記述も省略可で、省略された場合にはホストマシンのメモリサイズを見ながらいい感じの量が割り当てられます。
とはいえ、もしゲストOSに割り当てられるメモリが極端に少なかったら、MySQLインストールの最中にゲストOSがハングする → vagrant up
がいつまでも終わらない、といった問題を引き起こすおそれがあるので、念のため指定しておきます。
ホストマシンの方にメモリ資源が潤沢にある場合は、ゲストOSへのメモリ割り当てをさらに増やしてみてもいいかもしれません。
ゲストOSにChefをインストール
Vagrantfileにさらに追記します。
Vagrantfile
Vagrant.configure(2) do |config| config.vm.box = "puphpet/centos65-x64" # ... 中略 ... config.omnibus.chef_version = :latest # ... 中略 ... end
事前準備でインストールしたvagrant-omnibusプラグインを使ってゲストOSにChefの最新版をインストールします。
ゲストOSを立ち上げたままprovisionersを実行して追記した設定を反映させてみましょう。
$ vagrant provision
Chefリポジトリを作成
chef generate repo {REPO_NAME}
で空のリポジトリを作ります。
$ chef generate repo chef-repo $ tree . . ├── Vagrantfile └── chef-repo ├── LICENSE ├── README.md ├── chefignore ├── cookbooks │ ├── README.md │ └── example │ ├── attributes │ │ └── default.rb │ ├── metadata.rb │ └── recipes │ └── default.rb ├── data_bags │ ├── README.md │ └── example │ └── example_item.json ├── environments │ ├── README.md │ └── example.json └── roles ├── README.md └── example.json 9 directories, 14 files
さて、これから表題のとおりMySQLのインストールと設定を行うオリジナルのcookbookを書いていくわけなのですが。オリジナルのcookbookは./chef-repo/cookbooks
ディレクトリではなく、./chef-repo/site-cookbooks
ディレクトリの中に作っていくのが慣習のようです。
それを踏まえて、knife cookbook create {COOKBOOK_NAME} -o {COOKBOOKS_DIR}
で空のcookbookを作りましょう。
$ pushd chef-repo/; knife cookbook create setup_mysql -o site-cookbooks; popd; $ tree . . ├── Vagrantfile └── chef-repo ├── LICENSE ├── README.md ├── chefignore ├── cookbooks │ ├── README.md │ └── example │ ├── attributes │ │ └── default.rb │ ├── metadata.rb │ └── recipes │ └── default.rb ├── data_bags │ ├── README.md │ └── example │ └── example_item.json ├── environments │ ├── README.md │ └── example.json ├── roles │ ├── README.md │ └── example.json └── site-cookbooks └── setup_mysql ├── CHANGELOG.md ├── README.md ├── attributes ├── definitions ├── files │ └── default ├── libraries ├── metadata.rb ├── providers ├── recipes │ └── default.rb ├── resources └── templates └── default 21 directories, 18 files
site-cookbooks
ディレクトリの中にsetup_mysql
という名前のcookbookができてますね!
ここから先は主に./chef-repo/site-cookbooks
ディレクトリの中をゴリゴリといじっていきますよ。
Berksfileを作成
これからいよいよオリジナルのcookbookを書いていきます。
が、cookbookの依存関係を管理するのにはBerkshelfというツールを使うので、Berkshelfについて軽く知っておきましょう。
Berkshelfは、RubyGemsにおけるBundlerのような、Node.jsにおけるnpmのような、PHPにおけるComposerのようなツールです。
"cookbookを正しく動かすのに必要なcookbook"を順々にたどっていって、それら全てをダウンロード → ./chef-repo/cookbooks
ディレクトリに配置してくれます。
「いちいちzipやtar.gzをダウンロードしてきて解答して使うなんてカッタルイことやってらんない」という貴方のためのツールです!
Berkshelfの仕組みの中で、自分が使いたいcookbookをあらかじめ書いておく設定ファイルがBerksfileです。
さっそく作りましょう。
$ vi chef-repo/Berksfile
中身はこんな感じで、
./chef-repo/Berksfile
source "https://supermarket.chef.io" cookbook 'setup_mysql', path: './site-cookbooks/setup_mysql'
cookbookの名前とバージョンを記述してからberks vendor {COOKBOOKS_DIR}
すると、cookbookを自動でダウンロードして{COOKBOOKS_DIR}
で指定したディレクトリに配置してくれます。
どこからダウンロードするかというと、1行目で指定しているとおりChef Supermarketからですね。
4行目のようにpath:
を指定した場合は、Supermarketから落としてくるのではなく、ローカルのcookbookを複製して使ってくれます。
では、Berksfileに記述したcookbookをインストールします。
$ pushd chef-repo/; berks vendor cookbooks; popd; Resolving cookbook dependencies... Fetching 'setup_mysql' from source at site-cookbooks/setup_mysql Using setup_mysql (0.1.0) from source at site-cookbooks/setup_mysql Vendoring setup_mysql (0.1.0) to cookbooks/setup_mysql
setup-mysql cookbookの中身が空っぽなので、site-cookbooksからcookbooksにディレクトリがコピーされただけで終わりました。
が、これからcookbookにいろいろと記述して、再びberks vendor cookbooks
するときには必要なものがドバッとインストールされるので便利です。
VagrantとChef の連携
あとはChefをVagrantから実行させるようにしないといけませんね。 rolesファイルとVagrantfileに手を加えます。
$ vi chef-repo/roles/databese.json
./chef-repo/roles/databese.json
{ "name": "database", "chef_type": "role", "json_class": "Chef::Role", "default_attributes": { }, "override_attributes": { }, "run_list": [ "recipe[setup_mysql]" ] }
Vagrantfile に下記を追加します
./Vagrantfile
Vagrant.configure(2) do |config| config.vm.box = "puphpet/centos65-x64" # ... 中略 ... config.vm.provision "chef_zero" do |chef| chef.cookbooks_path = "./chef-repo/cookbooks" chef.roles_path = "chef-repo/roles" chef.add_role "database" end # ... 中略 ... end
さて、諸々の設定が済んだのでvagrant provision
してみてください。
database.jsonが読み込まれた後にrun_listに従ってsetup_mysqlのデフォルトrecipeが実行されていることが分かりますよね?
ま、setup_mysqlの中身まだ空っぽですけど。
MySQLインストール
やっっと、もろもろの設定が終わりましたね。では本題のrecipeを書いていきましょう。
Chef recipeを書く際は、まずsupermarktにいい感じのrecipeが置かれていないか検索してみてください。 今回はその名も、mysqlを使います。
metadata.rbに依存性を記述します。最後の行にmysql cookbookに依存している旨を書いておいてください。
./chef-repo/site-cookbooks/setup_mysql/metadata.rb
name 'setup_mysql' maintainer 'YOUR_COMPANY_NAME' maintainer_email 'YOUR_EMAIL' license 'All rights reserved' description 'Installs/Configures setup_mysql' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' depends 'mysql'
続いてdefault.rbに、
mysql_service 'default' do version '5.7' initial_root_password 'password' action [:create, :start] end mysql_client 'default' do action :create end
という感じで記述します。
では、provisioningしましょう。
$ pushd chef-repo; berks vendor cookbooks; popd; $ vagrant provision
MySQLにログインしてみます。
[vagrant@localhost ~]$ mysql -S /var/run/mysql-default/mysqld.sock -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 5.7.8-rc MySQL Community Server (GPL) Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
はい、このように。
データベース作成とユーザー設定
続いて、
データベースの作成とユーザーの設定です。
再びmetadata.rbに必要なcookbookを追記します。
./chef-repo/site-cookbooks/setup_mysql/metadata.rb
name 'setup_mysql' maintainer 'YOUR_COMPANY_NAME' maintainer_email 'YOUR_EMAIL' license 'All rights reserved' description 'Installs/Configures setup_mysql' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' depends 'mysql' depends 'mysql2_chef_gem' depends 'database'
データベースやユーザーの作成にはdatabaseを使います。
これはMySQLに限らずデータベース一般の設定を行うcookbookです。MySQL, PostgreSQL, SQL Serverの設定を同一のインターフェースで行うことができます。
ただし、MySQL用のプロバイダーとしてmysql2_chef_gemも一緒にインストールするのをお忘れなく。
default.rbにもどんどん追記します、
mysql_service 'default' do version '5.7' initial_root_password 'password' action [:create, :start] end mysql_client 'default' do action :create end # MySQL用のプロバイダー mysql2_chef_gem 'default' do action :install end # MySQL接続情報 mysql_connection_info ={ host: 'localhost', username: 'root', socket: '/var/run/mysql-default/mysqld.sock', password: 'password' } # データベース作成 mysql_database 'app' do connection mysql_connection_info action :create end # 管理用ユーザー追加 mysql_database_user 'admin' do connection mysql_connection_info password 'admin_user_password' database_name app privileges [:all] action [:create, :grant] end # 一般ユーザー追加 mysql_database_user 'app_user' do connection mysql_connection_info password 'app_user_password' # 権限はレコードの参照, 更新, 挿入, 削除のみ privileges [:select, :update, :insert, :delete] action [:create, :grant] end
再びprovisioningです。
$ pushd chef-repo; berks vendor cookbooks; popd; $ vagrant provision
ユーザーの確認も、
[vagrant@localhost ~]$ mysql -S /var/run/mysql-default/mysqld.sock -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 13 Server version: 5.7.8-rc MySQL Community Server (GPL) Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SELECT host, user, authentication_string FROM mysql.user; +-----------+----------+-------------------------------+ | host | user | authentication_string | +-----------+----------+-------------------------------+ | localhost | root | *53AC9F6BA434DF6B88399A8DC73B | | localhost | admin | *5EA0EC7BF8157AEF811F44A2BEE9 | | localhost | app_user | *C7730CC2587FA9287C1C00EBF035 | +-----------+----------+-------------------------------+ 3 rows in set (0.01 sec)
ザッとこんなもんですね。
まとめ
そんなわけで、mysqlとdatabaseの合わせ技でMySQLのセットアップを完了しました。
私は当初、mysql cookbookでユーザーの作成までするもんだと思い込んでいたせいでかなりハマりました。ドキュメント読んでも全然書いてないんだもんなぁ。
補足として、
今回はデータベース名やユーザー名、パスワードなどをrecipeに直書きしましたが、実際にはattributesなどで定義した変数で参照してくるのがいいでしょう、柔軟性のためにも。
私事ですが、今回このMySQLのセットアップには丸2日間ほど掛かってしまい完全に苦しめられたのでここに綴ることにしました。
いやぁ、死ぬかと思った。
私からは以上です。