Chefを利用して運用者の作業を増やす５つの方法

# お決まり
* [CYBIRDエンジニア Advent Calendar 2016](http://qiita.com/advent-calendar/2016/cybird) 3日目担当の@gotyooooです。
* サイバードのインフラ部に所属しており、主にスマートフォンアプリゲーム案件のインフラ開発・構築・運用を行っています。
    * オンプレもクラウドも存在するので毎日色々起きて楽しいばかりです。
* 昨日は@keitarouの[npmパッケージの健康管理をしよう](http://qiita.com/keitarou/items/9404e8cae19064f2cab6)でした。
    * VersionEye恥ずかしながら知りませんでした。
    * セキュリティに関心が寄せられている昨今、依存パッケージのセキュリティアップデートを見逃さない取り組みはほんとに素晴らしいと思います。
    * Berkshelf (Chef)も対応しているので、インフラとしてもガッツリ使えそうですね。
* そういえばQiitaに記事を書くの[去年のアドベントカレンダー](http://qiita.com/advent-calendar/2015/cybird)以来です。
    * サボり魔ですね。皆さん真似しましょう。

---

# はじめに
* サイバードのインフラ部ではかれこれ４年近くサーバプロビジョニングに[Chef](https://www.chef.io/chef/)を利用しています。
* 自動化や冪等性の担保の為に利用することになったChefですが、作業量を増やすことに~~なってしまった~~成功した運用手法がいくつか存在します。
    * この記事はその手法を世の中に共有しChef利用者の作業量を~~減らす~~増やすために共有します。
    * 今後も作業工数を増やすことに~~なってしまったら~~成功したら、追記したいと思います。

---

# 1. 1リポジトリで複数コンテンツに対応する
* 以下のようなディレクトリ構成でGitリポジトリ１つで運用すると作業が増えます。
    * 新規コンテンツ追加時ミドルウェア設定等に変更があると、旧コンテンツも影響を受けるため**怖いからこのコンテンツにはもうChef流せないよね**みたいな状態にすることが出来ます。
    * 作業手順書を作ってCUI操作でコマンド入力してEnterターンしながら作業するのが好きなあなたには特におすすめです。
    * Heartbleed, ShellShockの様な脆弱性対策時にもれなく全サーバ手動で```yum update hoge```できます。
* 「Cookbookをちゃんと抽象化しておけば大丈夫じゃね？」と思う方がいると
    * 大体みんな旧コンテンツの内部の事は忘れることができるので確実に運用負荷を増大させることが出来るでしょう。
    * またRecipeの可読性を下げることにも貢献できます。

```
chef-repo
├── README.md
├── cookbooks
├── data_bags
├── roles
│   ├── コンテンツA用Role群
│   │   ├── WEB.json
│   │   └── MYSQL.json
│   ├── コンテンツB用Role群
│   :
│   └── 新規コンテンツ用Role群
└── site-cookbooks
```
---

# 2. Roleでインストールするパッケージを完全固定指定する
* 自作Cookbook/Roleでpackage install時にマイナーバージョンやリビルドバージョンなどまで事細かに指定するようにすると作業が増えます。
    * 実行時にリポジトリから削除されたり、パッケージ依存関係を解決できない事があるので、もれなくChef実行が失敗します。
    * その度にそのバージョンを利用している全Roleのバージョン変更という作業が発生します。
* サンプルchef-repo
    * https://github.com/gotyoooo/chef-anti-pattern1
    * 以下サンプルとその実行結果から抜粋

```ruby:site-cookbooks/php/recipes/install.rb
# Cookbook Name:: php
# Recipe:: install
#
include_recipe "yum::remi"
package "php" do
  action :install
  version node['php']['version']
end
```
```json:FAIL.json
{
  "name": "FAIL",
  "description": "絶対に失敗するPHPインストール",
  "json_class": "Chef::Role",
  "default_attributes": {
  },
  "override_attributes": {
    "php": {
      "version" : "5.4.45-7.el6.remi"
    }
  },
  "chef_type": "role",
  "run_list": [
    "php::install"
  ],
  "env_run_lists": {
  }
}
```
```bash:実行結果

(省略)

Recipe: php::install
  * yum_package[php] action install

    ================================================================================
    Error executing action `install` on resource 'yum_package[php]'
    ================================================================================

    Chef::Exceptions::Package
    -------------------------
    Version 5.4.45-7.el6.remi of php not found. Did you specify both version and release? (version-release, e.g. 1.84-10.fc6)

    Resource Declaration:
    ---------------------
    # In /home/centos/chef-solo/local-mode-cache/cache/cookbooks/php/recipes/install.rb

      5: package "php" do
      6:   action :install
      7:   version node['php']['version']
      8: end

(省略)

```
```json:SUCCESS.json
{
  "name": "SUCCESS",
  "description": "2016/11/30時点で成功するPHPインストール",
  "json_class": "Chef::Role",
  "default_attributes": {
  },
  "override_attributes": {
    "php": {
      "version" : "5.4.45-11.el6.remi"
    }
  },
  "chef_type": "role",
  "run_list": [
    "php::install"
  ],
  "env_run_lists": {
  }
}
```
```bash:実行結果

(省略)

Recipe: php::install
  * yum_package[php] action install
    - install version 5.4.45-11.el6.remi of package php

(省略)

```
```bash:diff
$ diff SUCCESS.json FAIL.json
2,3c2,3
<   "name": "SUCCESS",
<   "description": "2016/11/30時点で成功するPHPインストール",
---
>   "name": "FAIL",
>   "description": "絶対に失敗するPHPインストール",
9c9
<       "version" : "5.4.45-11.el6.remi"
---
>       "version" : "5.4.45-7.el6.remi"
```

---

# 3. Chef Supermarketで公開されているCookbookを利用せず、ほぼ全て自作する
* [Chef Supermarket](https://supermarket.chef.io/)を利用しないと、世の中の多くのChefユーザの知恵を借りず全て自分たちでやることになり、作業が増えます。
    * [Berkshelf](http://berkshelf.com/)を利用しないと、Cookbookを自前でUpdateすることになるので作業量がさらに増加します。
    * また付加価値として痒いところに手が届かないCookbookにPullRequestを送りオープンソースに貢献する事もしなくて済みます。

---

# 4. テストコードを書かない
* [Serverspec](http://serverspec.org/)、[Test Kitchen](http://kitchen.ci/)などのChefのテストコードを最初に準備しておかないと、Chefのコード修正時やUpdate時の確認項目が増え作業を増やすことが出来ます。
    * テストコードを書いていてもGitHubリポジトリでのPullRequest時等に自動でテストが走るような仕組みを作らないと少々の作業量増加に貢献できます。
    * なお最初にテストコードを準備しないので最初の最初だけ作業量が減ります。

---

# 5. Recipe内でmakeする
* 以下のようにcookbook_fileにソースを置いてrecipe内でmakeしているとupdateに苦労し作業が増えます。
    * ただしどうしても古いバージョンが欲しいときや、updateは考えにくいもしくはしない場合、この方法を選択すると作業が減る場合もあるので注意しましょう。

```rb:site-cookbook/redis/install.rb
cookbook_file "redis-2.8.9.tar.gz" do
  source "redis-2.8.9.tar.gz"
  path "/usr/local/src/redis-2.8.9.tar.gz"
end

bash "install redis" do
  user     redis
  cwd      /usr/local/src
  code   <<-EOH
    tar xzf redis-2.8.9.tar.gz
    cd redis-2.8.9
    make
    make install
  EOH
end
```

---

# 最後に
* 記事の内容を実践すればChef利用者皆様の運用コストを増大させることが出来るかと思います。
    * ぜひお試しあれ。
* [CYBIRDエンジニア Advent Calendar 2016](http://qiita.com/advent-calendar/2016/cybird) 明日4日目担当は@yusiさんで「現場改善のすすめ」です。
    * 私の新卒入社時の上長です。
    * @yusiさんからRaspberry Pi借りパクしてます。返さないと。
    * 最近は現場改善の取り組みを社内チームへ行っている方です。その内容を広く共有するみたいですよ。
* Advent Calendar 21日も書きます。何書こうかな。