yutaponのブログ

javascript界隈の興味あるネタを備忘録的に残しておく場所

Ansible使ってVagrantのゲストOSの構成管理する【Playbook: jenkins, node.js】

去年辺りからimmutable infrastructureが盛り上がってきている感があります。
サーバーの環境をコードで記述するツールにはPuppet、Chefなどがありますが、
今回はAnsibleを触ってみます。
Ansible is Simple IT Automation

この記事ではVagrantでCentOS6.5を入れて、その上にAnsibleでJenkinsを入れるところまでを説明します。
(入門向けで、Ansibleについて突っ込んだことは書いていません)

Ansibleとは?

AnsibleはPuppet、Chefなどと同様に構成管理ツールです *1

一番の特徴はPuppet, Chefに比べてリモートホストに特別な設定が必要ないことでしょうか。
ssh接続ができて、python2.4以上が入っていれば動きます。

設定はYAMLで記述し、特にpythonを知らなくても使えるようです。


環境について

本記事の環境はこちら。

  • MacOSX 10.9 (ホストOS)
  • CentOS 6.5 (ゲストOS)
  • VBoxManage 4.3.10
  • Vagrant 1.5.2
  • Ansible 1.5.4



Vagrantをインストールする

こちらの記事を読みながら進めれば大丈夫です。
今更聞けない人の為の Vagrant 再入門 | School With Blog


まずはじめにVitrualBoxをインストールします。
下記からOSX用のインストーラをダウンロードしてインストールしました。
Downloads – Oracle VM VirtualBox

バージョン確認で動作を確認します。

$ VBoxManage --version
4.3.10r93012


すでにVitrualBox4.3.8が入っていて、4.3.10がリリースされていたのでインストールしようとしたら「インストールできませんでした。開発元に問い合わせてください」的なエラーが出て焦りました。
インストーラに付属しているアンインストーラを実行後にOSXを再起動して、再度インストールしたら正常に4.3.10が入りました。


Vagrantをインストールします。
こちらもOSX用のインストーラをダウンロードしてインストールします。
Download Vagrant - Vagrant

インストールできたらこちらも確認。

$ vagrant --version
Vagrant 1.5.2




VagrantにゲストOSを設定する

vagrantコマンドが使えるようになったらゲストOSを設定します。

こちらでVagrant用のboxファイルが配布されているので好きなOSを選びます。
A list of base boxes for Vagrant - Vagrantbox.es


$ vagrant box add でVagrantで利用できるOSを設定することができます。

$ vagrant box add -h # ヘルプ表示
Usage: vagrant box add [options] <url> # 大体の使い方はこれ
# いろいろ表示されるけど割愛


CentOS6.5を追加するコマンドはこれ。

$ vagrant box add centos6.5 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box


追加できたか確認します。

$ vagrant box list
centos6.5 (virtualbox, 0)

 

ゲストOSを立ちあげてSSH接続するまで確認してみます。
まず作業ディレクトリに移動します。

$ cd path/to/workspace
$ mkdir vagrant_centos
$ cd vagrant_centos


作業ディレクトリに移動したらVagrantの初期化をします。

$ vagrant init centos6.5 # $ vagrant box list に表示されるboxNameを指定する
... # いろいろ表示される

$ ls
Vagrantfile


作業ディレクトリにVagrantfileが作成されます。
Vagrantfileの中身はRubyで記述されていて、約100行ほどのファイルです。

26行目付近にあるconfig.vm.networkのコメントアウトを消して、ホストOSとゲストOSでネットワークが繋がるようにします。

   # Create a private network, which allows host-only access to the machine
   # using a specific IP.
   config.vm.network "private_network", ip: "192.168.33.10"

 

準備は整ったのでゲストOSを起動させます。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos6.5'...
# 初回だけちょっと時間がかかる


完了したらSSH接続を試します。

$ vagrant ssh

[vagrant@localhost ~]$ pwd  # vagrantユーザでCentOSにログインできた!
/home/vagrant


ゲストOSから抜けるときはexit。

$ exit
logout
Connection to 127.0.0.1 closed.


ゲストOSをshutdownするときはhalt。

$ vagrant halt

$ vagrant status # VMの状態を確認すると
Current machine states:

default                   poweroff (virtualbox) # shutdownされてる

The VM is powered off. To restart the VM, simply run `vagrant up`


VMを捨てるときはdestroy。

$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Destroying VM and associated drives...

$ vagrant status # VMの状態を確認すると
Current machine states:

default                   not created (virtualbox) 

The environment has not yet been created. Run `vagrant up` to
create the environment. If a machine is not created, only the
default provider will be shown. So if a provider is not listed,
then the machine is not created for that environment.

# vagrant upすればまた作成されるようです



OSXでAnsibleを使えるようにする

ここからはこちらの記事を参考にしました。
VagrantでAnsibleのPlaybookを作るためのローカル環境を作る - Qiita


Ansible使うときはpython2.7系に上げといた方がいいらしい。
OSXのシステムに入ってるpythonが2.6系だったのでバージョンを上げることにしました。
普段python使わないし、バージョン管理しなくていいかと思いhomebrewでサクッと上げました。

$ brew install python
...

$ python --version
Python 2.7.6


homebrewでpython入れるとpipコマンドも使えるようになります。
pipコマンドはpython用のパッケージマネージャーらしく、node.jsでいうnpmみたいなやつみたい。

pipコマンド使ってansibleをインストールします。

$ sudo pip install ansible
...

$ ansible --version
ansible 1.5.4

 


ホストOSからゲストOSへAnsibleのコマンドが実行できるようSSHの設定をします。

まずホストOSのSSH設定を追記します。

$ vi ~/.ssh/config

# 以下を追記する
Host 192.168.33.*
IdentityFile ~/.vagrant.d/insecure_private_key
User vagrant


次に、インベントリファイルを作成します。
デフォルトは/etc/ansible/hostsを見に行くのですが、
作業ディレクトリにhostsファイルを作成して、これを参照するようにしました。

$ vi hosts

# hostsの中には以下を記述

[servers]
192.168.33.10  # ゲストOSのIPを指定する


インベントリファイルはINI形式で記述できるので、下記のように複数サーバーをグルーピングして記述することもできます。

[servers]
192.168.33.10
192.168.33.11

[db]
192.168.33.30

 

それではゲストOSが起動した状態にします。
もし起動していなければ $ vagrant up しておきましょう。


この状態で、Ansibleを使ってゲストOSにpingモジュールを実行してみます。

$ ansible -i hosts servers -m ping
192.168.33.10 | success >> {
    "changed": false,
    "ping": "pong"
}

-m の後にはAnsibleのモジュール名を指定するようです。
-i の後にはインベントリファイルと対象ホストのグループ名を指定しています。
もっと詳しい情報はこちら Inventory File · yteraoka/ansible-tutorial Wiki · GitHub



Ansibleを使ってゲストOSの環境を構築する

ここからはこちらの記事を参考に進めます。
Ansible チュートリアル | Ansible Tutorial in Japanese
 

Ansibleで任意のコマンドを実行する

-a の後に実行したいコマンドを文字列で渡すと任意のコマンドが実行できるようです。

$ ansible -i hosts servers -a 'uname -r'
192.168.33.10 | success | rc=0 >>
2.6.32-431.el6.x86_64

$ ansible -i hosts servers -a 'pwd'
192.168.33.10 | success | rc=0 >>
/home/vagrant

 

Ansibleでパッケージをインストールする

yumモジュールを使うとゲストOSにパッケージをインストールできます。
以下はtelnetをインストールするコマンド。

$ ansible -i hosts servers -m yum -s -a name=telnet
192.168.33.10 | success >> {
.. # いろいろ表示される


-s オプションはsudoで実行することを意味しています。
-a とはargumentsのことで、yumモジュールに name=telnet という引数を渡しています。
 

Playbookで構成管理をする

やっと本題に入ります。
Chefでいうレシピ(レシピブック)的な立ち位置にあるのがAnsibleにもありまして、それをPlaybookと呼びます。
中身はYAML形式で記述されます。


ではゲストOSにJenkinsをインストールするPlaybookを書く前に、普通にインストールする手順を確認します。
手順はこちらから引用しました。
Installing jenkins on CentOS 6.4

$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo;
$ sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key;
$ sudo yum install java-1.7.0-openjdk
$ yum install jenkins
$ service jenkins start

 

  1. リポジトリを追加して
  2. Jenkinsが依存しているパッケージを追加して
  3. Jenkinsをインストールして
  4. サービスとして起動


この手順でタスクを作成すれば良さそうです。


では実際にPlaybookを作成します。
playbook_jenkins.yamlとして作業ディレクトリに作成しました。

# playbook_jenkins.yaml
---
- hosts: servers
  sudo: true
  user: vagrant
  tasks:
    - name: get jenkins repo
      get_url: url="http://pkg.jenkins-ci.org/redhat/jenkins.repo" dest=/etc/yum.repos.d/jenkins.repo

    - name: set jenkins repo
      command: rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

    - name: install dependencies
      yum: name=java-1.7.0-openjdk state=latest

    - name: install jenkins
      yum: name=jenkins state=latest

    - name: start jenkins service
      service: name=jenkins state=started

 
tasks.nameはタスクそれぞれのラベルのようなもので、日本語でも記述できます。
nameの次のプロパティがモジュール名で、値にモジュールへの引数を記述しています。


Playbookの構文をチェックするときはこちら。

$ ansible-playbook -i hosts playbook_jenkins.yaml --syntax-check

playbook: playbook_jenkins.yaml

 
Playbookのタスク一覧を確認するときはこちら。

$ ansible-playbook -i hosts playbook_jenkins.yaml --list-tasks

playbook: playbook_jenkins.yaml

  play #1 (servers):
    get jenkins repo
    set jenkins repo
    install dependencies
    install jenkins
    start jenkins service

他の人が書いたPlaybookを流し読みするときに使えそうです。


ではPlaybookを使ってJenkinsをインストールします。

$ ansible-playbook -i hosts playbook_jenkins.yaml

PLAY [servers] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.33.10]

TASK: [get jenkins repo] ******************************************************
ok: [192.168.33.10]

TASK: [set jenkins repo] ******************************************************
changed: [192.168.33.10]

TASK: [install dependencies] **************************************************
ok: [192.168.33.10]

TASK: [install jenkins] *******************************************************
ok: [192.168.33.10]

TASK: [start jenkins service] *************************************************
ok: [192.168.33.10]

PLAY RECAP ********************************************************************
192.168.33.10              : ok=6    changed=1    unreachable=0    failed=0

 
インストールできました。

Jenkinsおじさんは192.168.33.10:8080にて動いてるらしいので、
ホストOSのブラウザから http://192.168.33.10:8080 にアクセスすると

f:id:sskyu:20140405194900p:plain

動いてるのが確認できました。

Ansibleには冪等性があるため、何度実行しても結果は同じになります。
これはゲストOSが複数になっても全て同一の環境を構築できることに繋がります。


【番外編】Ansible使ってNode.jsをインストールする

JavaScript界隈の技術に触れないで終わりそうなので、
Ansible使ってNode.jsを入れるPlaybookを作りました。

タスクの内容としてはnodebrewを使ってnode.jsを入れるってだけです。
これらの記事を参考にしました。
ansibleでshellやcommandを使う時の注意点 - Qiita
ansibleを使ってみる — そこはかとなく書くよん。
Ansible のメモ - Hexa's diary


playbook_nodejs.yamlというファイルで中身はこちら。

# playbook_nodejs.yaml
---
- hosts: servers
  user: vagrant
  vars:
    node_version: '0.10.26'
    nodebrew_path: '.nodebrew/current/bin'
  tasks:
    - name: install perl
      command: sudo yum install -y perl

    - name: download nodebrew
      get_url: url="http://git.io/nodebrew" dest=/var/tmp/nodebrew

    - name: install nodebrew
      command: perl /var/tmp/nodebrew setup

    - name: rewrite .bashrc for export path
      shell: echo export PATH=$HOME/.nodebrew/current/bin:$PATH > $HOME/.bashrc

    - name: install nodejs ver{{ node_version }}
      shell: nodebrew install-binary v{{ node_version }}
      ignore_errors: True
      environment:
        PATH: "/home/vagrant/{{ nodebrew_path }}:{{ ansible_env.PATH }}"

    - name: use nodejs
      shell: nodebrew use {{ node_version }}
      environment:
        PATH: "/home/vagrant/{{ nodebrew_path }}:{{ ansible_env.PATH }}"

 
実行するとこんな感じ。

$ ansible-playbook -i hosts playbook_nodejs.yaml

PLAY [servers] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.33.10]

TASK: [install perl] **********************************************************
changed: [192.168.33.10]

TASK: [download nodebrew] *****************************************************
ok: [192.168.33.10]

TASK: [install nodebrew] ******************************************************
changed: [192.168.33.10]

TASK: [rewrite .bashrc for export path] ***************************************
changed: [192.168.33.10]

TASK: [install nodejs ver0.10.26] *********************************************
failed: [192.168.33.10] => {"changed": true, "cmd": "nodebrew install-binary v0.10.26 ", "delta": "0:00:00.131961", "end": "2014-04-06 01:14:08.487010", "rc": 1, "start": "2014-04-06 01:14:08.355049"}
stdout: v0.10.26 is already installed
...ignoring

TASK: [use nodejs] ************************************************************
changed: [192.168.33.10]

PLAY RECAP ********************************************************************
192.168.33.10              : ok=7    changed=5    unreachable=0    failed=0

 
ゲストOSにログインして確認します。

$ vagrant ssh

[vagrant@localhost ~]$ node --version
v0.10.26

node コマンドが使えることが確認できました。

補足

.bashrcを上書きするタスクがあります。

# > の部分。もし参考にされる場合は注意してください。
shell: echo export PATH=$HOME/.nodebrew/current/bin:$PATH > $HOME/.bashrc

追記にしちゃうとPlaybookを実行するたび追記され、
冪等性が壊れる気がしたので、.bashrcは上書きされる前提ということにしました。
(ここ納得いかないので何とかしたい・・。)

nodebrew install を実行するタスクでは2回め移行は既にインストールされているという
メッセージが表示されてタスクが失敗します。
それだと困るのでタスクに ignore_errors: True を指定してエラーを無視しました。



おわりに

今回初めてVagrantを使ってみましたが、手軽に仮想環境を構築できるのは勉強するときにありがたいですね。

Ansibleはもっといろいろなことができて、今回紹介したPlaybookは基礎中の基礎みたいなものです。
GithubにあるAnsibleのサンプルはディレクトリが細かく分かれていて、
あの書き方に慣れるにはもう少し勉強が必要そう。
この流れでDockerも触りたい。

これを見ると、VagrantとAnsibleで効率のいい設定ができそう。
Ansible - Provisioning - Vagrant Documentation



VagrantもAnsibleも1日勉強したらそこそこ使えるようになったので、
Chefより学習コストは低いと思います。
Ansible使ってVPSを管理するといい感じになりそうなので、DigitalOceanあたりで使いたい。

*1:厳密に言うとどれも構成管理ツールという括りに収まらないです。 こちらの記事ではAnsibleの設計思想などを詳しく述べられてるので一読しておくとよいかと。 Ansibleのアーキテクチャー: 構成管理を超えて — そこはかとなく書くよん。