Macの開発環境構築(Ansible + Serverspec + Travis CI)




## 概要

AnsibleでMacの開発環境構築を自動化してみたので、やった事や調べたものをメモしておく。

Boxenみたいなのをansibleでやりたいなぁと思ったのがきっかけ。なので[our-boxen](https://github.com/boxen/our-boxen)(テンプレ)みたいなのと、それをコピーしてカスタマイズした自分専用のリポジトリの二つを用意した。

- https://github.com/tell-k/blister-pack .... 基本的なセットアップ内容
- https://github.com/tell-k/blister-pack-mine ... 自分個人のセットアップ内容

blister-packの利用方法は[README.rst](https://github.com/tell-k/blister-pack/blob/master/README.rst)を参照してください。

以下のような事をやった。

- Mac OSX の開発環境構築をansibleで自動化する。主にhombrew + homebrew-caskで必要なパッケージをインストール
- Boxenのように、共通部分、個人、プロジェクトに分けて構築内容(role)を管理する。
- Serverspecでテストする。
- Travis CIでビルド/テストを実行する

## Ansibleで環境構築

- 主にansibleの[hombrewモジュール](http://docs.ansible.com/homebrew_module.html)と[hombrew-caskモジュール](http://docs.ansible.com/homebrew_cask_module.html)で大概のものはインストールできた。
- ansibleのローカル環境への実行はinventryファイルに以下のように書けば良い。

```
[localhost]
127.0.0.1

[localhost:vars]
ansible_connection=local
```

## Roleの管理

- Boxenのように共通分/個人/プロジェクト毎に、構築内容を分けて管理するようにした。

```
├── Makefile
├── README.rst
├── Rakefile
├── callback_plugins
├── hosts
├── playbook.yml
├── roles
│   ├── common   # <- 共通で使うrole群
│   ├── people   # <- 個人毎のrole群
│   └── projects # <- プロジェクト毎のrole群
└── spec
    ├── common
    ├── people
    ├── projects
    └── spec_helper.rb
```

## Serverspecでテスト

- ServerspecでAnsibleでの構築内容をテストできるようにした。
- [envassert](https://pypi.python.org/pypi/envassert)とansibleの[assertモジュール](http://docs.ansible.com/test_strategies.html#modules-that-are-useful-for-testing)を使うという選択肢もある。
- specファイルもcommon/people/projectsで分けるようにした。
- Serverspecはやりたい事が大概[ドキュメント](http://serverspec.org/resource_types.html)に書いてあるので便利

## Travis CIでビルド/テスト

- Travis CI はビルド環境として[OS X環境を提供してくれる](http://docs.travis-ci.com/user/osx-ci-environment)
- それを使ってビルド/テストをする
- OS Xを利用する場合は.travis.ymlで明示するだけ
- homebrew、xcodeなどは事前に入ってるらしい
- sudoはパスワード無しで実行できるらしいので、ansibleの「-K」オプションを抜かして実行している

```
language: objective-c
os:
  - osx
before_install:
  - brew update
  - brew install python
  - brew install ansible
  - ansible-playbook -vv playbook.yml -i hosts
script:
  - make test
```

## TIPS

### hombrewのupdate/upgrade

- homebrewのupdateはFormulaやhomebrew自身をアップデートする。
- upgradeは対象パッケージを再ビルド/インストールする。
- ansibleのhomebrewモジュールには「update_homebrew」「upgrade_all」というオプションがある。
- このオプションを適宜変更できるようにinventoryに変数をもたせた。

```
[localhost]
127.0.0.1

[localhost:vars]
homebrew_update=yes     # 常にupdateするためyes。遅いと感じるならnoにする
homebrew_upgrade_all=no # 全パッケージの更新が必要になったらyesにする
```

homebrewモジュールにそのままパスしてる

```
- name: Update homebrew
  homebrew: update_homebrew={{ homebrew_update }} upgrade_all={{ homebrew_upgrade_all }}
```

### 環境変数やパスの参照

- homebrewなどは「hombrew --prefix」「homebrew --cellar」などでパスを取得できる
- これをansibleやserverspecにどうやって渡すか。
- Ansibleの場合 lookup プラグインで、環境変数や、シェルの実行結果などを、参照できる。
- それを利用してinventoryファイルに定義すると、playbookの中で参照可能

```
[localhost]
127.0.0.1

[localhost:vars]
home_path={{ lookup('env','HOME') }}
homebrew_prefix={{ lookup('pipe','echo `brew --prefix`') }}
homebrew_cellar={{ lookup('pipe','echo `brew --cellar`') }}
```

これをroleの中で下記のように利用する

```
- name: Copy dnsmaq.conf
  copy: src=roles/common/dnsmasq/files/dnsmasq.conf dest="{{ homebrew_prefix }}/etc/dnsmasq.conf" backup=yes
```

Serverspecの場合は何も考えずに、spec_helper.rbに定義した。

```
require 'serverspec'

set :backend, :exec

def home_path
  home_path = `echo ~/`
  home_path.chomp
end

def homebrew_prefix
  prefix = `brew --prefix`
  prefix.chomp
end

def homebrew_cellar
  cellar_path = `brew --cellar`
  cellar_path.chomp
end
```

### AnsibleのCallbacksプラグインによる完了通知

- Ansibleを実行すると時間がかかるので、終わったら通知するようにした。
- Ansibleの[Callbacksプラグイン](http://docs.ansible.com/developing_plugins.html#callbacks)を使って、OS Xの通知センターに表示されるようにした。

![](https://dl.dropboxusercontent.com/spa/ghyn87yb4ejn5yy/ei_j0jno.png)

- スクリプトは[ここ](https://github.com/tell-k/blister-pack/blob/master/callback_plugins/osx_notify.py)

### Travisのビルド時間制限の対応

Travisは以下のような条件でビルドが強制的に終了してしまう。

- 50分以上ビルドにかかると強制終了される
- 一定時間標準出力がなくても強制終了される。
- http://docs.travis-ci.com/user/build-configuration/#Job-Timeouts

とりあえずインストールに時間がかかるものは、Travisではスキップするようにした。

1. [TRAVIS_BUILD_SKIPという環境変数を用意](https://github.com/tell-k/blister-pack-mine/blob/master/.travis.yml)
2. [role/specではこの環境変数をチェックして実行しないようにする](https://github.com/tell-k/blister-pack-mine/blob/master/roles/people/tell_k/tasks/homebrew.yml#L68-L72)

- 本末転倒感がすごいのでより良い方法を探す。

### lint的なチェック

- Ansibleは標準で--syntax-checkをしてくれるオプションがある
- ansible-lintというライブラリもある。-> tarvisでエラーが出た+大したチェックをしてくれないので今は使用してない
- serverspecの方は、[rubocop](https://github.com/bbatsov/rubocop)を利用している。「--auto-correct」オプションである程度、自動で整形してくれるの良い。
- [Makefileにcheckstyleと定義してチェックできるようにした](https://github.com/tell-k/blister-pack-mine/blob/master/Makefile)


### プライベートな構築内容の管理

- 例えばプライベートなプロジェクトのrole/spec群はそれぞれ「private」というディレクトリ以下において、gitignore設定している。
- プライベート用のYAML(private.yml)を用意して、毎回ansible実行時に、playbook.ymlとprivate.ymlを[マージ](https://github.com/tell-k/blister-pack-mine/blob/master/Makefile#L9-L11)してる。

### githubのリポジトリの取り扱い

- Travis CI上でansibleのgitモジュールを使ってSSH経由でgithubのリポジトリをcloneするためには秘密鍵が必要
- その場合、[デプロイキーを各リポジトリに設置する](http://blog.eiel.info/blog/2014/02/18/github-push-from-travis/)か、もしくはHTTPSでcloneするようにする必要がある。面倒なのでだいたいHTTPSにしている。

```
例: gitモジュールのrepoをhttpsで始まるURLにしている。
- name: Git clone tell-k/sphinxjp.themes.basicstrap
  git: repo=https://github.com/tell-k/sphinxjp.themes.basicstrap.git dest=~/Work/python/sphinxjp.themes.basicstrap update=no
```

## 余談

- Travis CI以外は半年くらい同じ構成で試したけど、大きな問題は特になかった。Ansible素晴らしい。
- Serverspec + Travisでのチェックが割と安心感ある。Serverspecでテスト書くのはなんか楽しい。
- ライセンスの関係でApp Store経由で購入しているものもあるので、完全自動化は難しそう。
- 個人/プロジェクト毎みたいな話を書いたけど、ボッチなので自分以外の他に人と共有したり、使ってもらったりした事はない。
- blister-packていう珍妙な名は[BoxenのTOP絵](https://boxen.github.com/)を見てつけた。

## 代替

Ansibleを利用してMac OS Xの環境構築をするアイディア/ツールは他にもいくつかある。

- [geerlingguy/mac-dev-playbook](https://github.com/geerlingguy/mac-dev-playbook)
- [osxc](http://osxc.github.io/)
- [battleschool](https://github.com/spencergibb/battleschool)

当初はこれらのツールを利用しようとしたが以下のような理由でやめた。

- Ansible以外のツール固有の何かを覚えるのが面倒そうだった。
- Boxenのようにラッパーコマンドがあったりするが、Ansibleのプラグイン機構で代用が効きそうだった。
- 単純にショートカットとしてのラッパーだったら、Makefileで十分だった。

## 参考

### 書籍

- [入門Ansible](http://www.amazon.co.jp/dp/B00MALTGDY) ... Ansibleで何ができるのかよく分かる本
- [Pythonプロフェッショナルプログラミング第2版](http://www.amazon.co.jp/dp/479804315X/) ... Ansibleの章があります(ステマ)
- [Pythonエンジニア養成読本](http://www.amazon.co.jp/dp/4774173207) ... 入門Ansibleの中の人がAnsibleについて書いてるので、買って読む予定。
- [Serverspec](http://www.amazon.co.jp/dp/4873117097) ... まだ買ってないので買わなあかん > 俺

### Ansible関連

- [All Modules — Ansible Documentation](http://docs.ansible.com/list_of_all_modules.html)
- [Mac の開発環境構築を自動化する (2015 年初旬編)](http://t-wada.hatenablog.jp/entry/mac-provisioning-by-ansible)
- [geerlingguy/mac-dev-playbook](https://github.com/geerlingguy/mac-dev-playbook)
- [ansibleコトハジメ](http://qiita.com/seizans/items/54da2077ac8e2dcf5d6f)
- [ansible虎の巻](https://gist.github.com/voluntas/7844901)
- [構成管理ツール Ansibleについて](http://apatheia.info/blog/2013/04/06/about-ansible/)
- [Ansible入門...?という発表をしました](http://tdoc.info/blog/2013/12/03/ansible_advent.html)
- [ansibleのログをfluentdに流す](http://tdoc.info/blog/2014/02/16/ansible_callbacks.html)
- [Ansible 1.5 リリース](http://tdoc.info/blog/2014/03/01/ansible_1_5.html)
- [Ansibleのroleを使いこなす](http://qiita.com/hnakamur/items/63f2d94badf89246e04a)
- [Ansibleで自作モジュールを作成してplaybookと一緒に配布](http://qiita.com/hnakamur/items/b20458110777c3ceea3a)
- [Ansible チュートリアル](http://yteraoka.github.io/ansible-tutorial/#best-practices)
- [Ansibleをローカルで使ってみた](http://cloverrose.hateblo.jp/entry/2013/09/07/112153)

### Boxen関連

- [Boxen使わなくても許されるのは2012年までだよね](http://qiita.com/yuku_t/items/c6f20de0e4f4c352046c)
- [Boxenを実行すると何が起こるのか](http://r7kamura.hatenablog.com/entry/2013/02/22/152133)
- [boxenでの構築でハマった所メモ](http://qiita.com/kibitan/items/103dcdd32f0b22731487)
- [boxenを導入した話](http://trapezoid.hatenablog.com/entry/2013/04/21/005524)
- [Boxenを利用したMacのセットアップ](https://github.com/ae06710/my-boxen)
- [もうすぐ 2014 年だけど boxen を独りで使ってみる](http://inokara.hateblo.jp/entry/2013/12/31/184645)
- [Boxenを利用したMacのセットアップ](http://threetreeslight.com/post/58009785849/boxen-mac)

### Homebrew-cask関連

- [BoxenやめてBrewfile+homebrew-caskにした](http://nomnel.net/blog/boxen-to-brewfile/)
- [brew cask でインスコできるリスト](https://github.com/phinze/homebrew-cask/tree/master/Casks)
- [Mac OSX をクリーンインストールしてからの環境構築メモ](http://www.1x1.jp/blog/2014/04/how-to-setup-application-on-osx.html)
- [homebrew-cask でインストールしたアプリを Alfred から呼び出せない](http://tnakamura.hatenablog.com/entry/2014/04/30/103937)
- [MacBook にインストールしているアプリやツールをメモする代わりに Brewfile を作った](http://tnakamura.hatenablog.com/entry/2014/04/29/113727)
- [Homebrew vs Boxen を比較して、brewproj に着手](http://ja.ngs.io/2014/05/08/homebrew-boxen/)
- [Boxenとbrew Bundle && Homebrew-caskを比較した感想](http://befool.co.jp/blog/jiska/compare-boxen-and-homebrew-cask/)

### Serverspec関連

- [Serverspec - Resource Types](http://serverspec.org/resource_types.html)
- [serverspec のテストをホスト間で共有する方法](http://mizzy.org/blog/2013/05/12/1/)
- [構築済みサーバを RSpec でテストする serverspec という gem をつくった](http://mizzy.org/blog/2013/03/24/3/)