質の高いChef Cookbookの書き方

この記事では、質の高いChef Cookbookの書き方と、Chef Cookbookを最新の状態に保つ方法について、いくつかの例を挙げながらお話しします。

本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/best-practices-for-writing-chef-cookbooks_594943)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

[Alibaba Cloud Tech Share](https://www.alibabacloud.com/campaign/techshare?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)執筆者、イルハン・アディヤマン。[Tech Share](https://www.alibabacloud.com/campaign/techshare?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)は、クラウドコミュニティ内での技術的な知識やベストプラクティスの共有を促進するためのAlibaba Cloudのインセンティブプログラムです。


#前提条件
このウォークスルーを完了するためには、ローカル開発のために設定する必要があります。これには、変更を本番環境にデプロイする前に、ローカルのテストインフラストラクチャ上で作業を検証することが含まれます。以下の手順に従って、ローカル開発環境を設定してください。

- Alibaba Cloudアカウントを持っている必要があります。まだアカウントをお持ちでない場合は、[無料トライアルのページ](https://www.alibabacloud.com/campaign/free-trial?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)にアクセスして、無料アカウントを取得してください。
- サーバーとして[Elastic Compute Service（ECS）](https://www.alibabacloud.com/product/ecs?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)インスタンスを購入します。
- [Chefワークステーションをインストール](https://downloads.chef.io/chef-workstation/?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)します。
- ターミナルで `chef —version` を実行して、Chef ワークステーションのインストールを確認します。
- [Git](https://git-scm.com/downloads?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) のインストールと設定
- [VirtualBox](https://www.virtualbox.org/wiki/Downloads?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)と[Vagrant](https://www.vagrantup.com/docs/installation/?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)をインストールします。

#Chefについて
Chefは非常に人気のあるオープンソースの強力なインフラ自動化プラットフォームで、インフラを管理しやすいバージョンのコードブロックに変換します。Chefを使用すると、インフラストラクチャの設定、デプロイ、ネットワーク全体の管理方法を自動化できます。クラウドでもオンプレミスでも、その規模に関係なく動作します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/a48661c7-4948-351b-74e4-a66f1c631c54.png)


Chefには3つの基本的なコンポーネントがあります。

- Chefワークステーションは、ユーザーがChefと通信する場所です。Cookbookの開発・テスト環境であり、本番で使用するためにChefサーバーに公開します。また、ロールや環境などの組織ポリシーを設定して責任を分けることもできます。
- Chefサーバーは設定データの中心です。Cookbook、ノードに関するポリシー、そのサーバーで管理されているノードに関する情報が保存されます。ノードは設定の詳細についてChef Serverと通信します。
- Chefクライアントノードは、Chef Serverによって設定され、管理されているマシンです。ノード上でChefクライアントを設定した後、Chefサーバに接続して所望の状態に設定します。
これらの3つの基本的なコンポーネントとは別に、Chefの主な機能は、Ruby言語でインフラストラクチャを書くことを可能にするCookbookの後ろに隠されています。

#Cookbookについて
Chefは、設定やポリシーを渡すためにノードと通信するためにCookbookを使用します。Cookbookの内部には以下が入っています。

- `recipes` は、使用するリソースと適用する順番を指定します。
- `file`：ファイルをノードに転送できるようになります。
- `attributes`：ノードのデフォルト設定を上書きするために使用できます。
- `metadata`：クライアントとサーバが正しくデプロイするための情報を提供します。
- `libraries`を使用して、任意のRubyコードをCookbookに含めることができます。
- `templates`は、静的なテキストファイルを動的に生成するために使用される埋め込みRuby(erb)テンプレートです。
- `test`：cookbooksの品質を向上させるためのテスト。

#Cookbookの更新が重要な理由
Chefは通常、毎月Chefクライアントの下位互換性のあるアップデートをリリースしています。しかし、毎年、彼らは古い機能のためのいくつかの非推奨事項を含む可能性がある新しいメジャーバージョンをリリースします。例えば、Chef 13は2017年4月にリリースされ、Chef 14は2018年4月にリリースされました。[ここ](https://github.com/chef/chef/blob/master/RELEASE_NOTES.md?spm=a2c65.11461447.0.0.79dc3a8ceXfCub#new-deprecations)から、今後のアップデートにはいくつかの非推奨事項が含まれていることがわかります。

さらに、私たちは一般的に、Apache ServerやPHP、MySQLなどのいくつかの特定のソフトウェアソリューションをノードにインストール、設定、管理するためにchef cookbooksを使用しています。しかし、これらのソフトウェアも頻繁にアップデートされています。最新の機能をレシピ上で利用できるようにするためには、定期的にCookbookを更新する必要があります。

#チームがCookbookを更新する方法
組織の規模や構造にもよりますが、一部のチームでは毎月または四半期ごとにノートブックの定期的なメンテナンスを行っているのに対し、他のチームでは更新頻度が低くなっています。組織の規模や、チームがメンテナンスしているCookbookの数、Cookbookに機能を追加する頻度などにもよりますが、レガシー部分を削除することが重要です。しかし、本番環境にデプロイする際に健全なインフラを確保するためには、Cookbookからレガシー部分を削除することが重要です。

以下に、Cookbookを最新の状態に保つために適用すべき手順を示します。

- 不要になったファイルを削除します。
- FoodcriticとCookstyleのテストを実行してください。
- Test Kitchen を使用して、Cookbookがローカルで成功するかどうかを確認します。
- 合格するユニットテストと統合テストを作成します。
- 適切なメタデータとドキュメントを追加します。


それでは、これらの手順を使ってサンプルの Apache Cookbookを更新してみましょう。サンプルのCookbookはChef 12をターゲットにしています。 ここでは、このCookbookを使って更新プロセスを練習してみましょう。

###custom_apache Cookbookをクローンする
まず、githubから`custom_apache`のCookbookをChefのワークステーションにクローンしてみましょう。

```
$ git clone https://github.com/learn-chef/custom_apache.git
```

cookbookには、デフォルトの`attributes`と`recipe`、Apacheを設定するための`リソース`、ChefSpec用の`matcher`、ChefSpecテスト、テストキッチンの設定ファイル、`Vagrantfile`、`メタデータ`、ドキュメント用の`README`が含まれています。

###不要なファイルの削除
Virtual Box上で動作する一時的なVM上でCookbookを実行して検証するために`Vagrantfile`を使用しています。Test Kitchenを導入する前は、テストを実現するために別の`Vagrantfile`を使わなければなりませんでした。しかし、Test Kitchenでは`Vagrantfile`を作成して管理しています。そのため、Cookbookの内容からこのファイルを削除することができます。

```
$ rm Vagrantfile
```

###FoodcriticとCookstyleテストの実行
[Foodcritic](https://docs.chef.io/foodcritic.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)と[Cookstyle](https://docs.chef.io/cookstyle.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)は、Cookbookのための2つの人気のあるリンティングツールです。リンティングツールは、あなたのコードが共通の問題を避けるために、あなたが書いた言語の標準スタイルガイドラインに準拠しているかどうかを確認するのに役立ちます。どちらのツールもテストインスタンスを作成する必要がないので、Cookbookの正しさをより早く検証することができます。

Cookstyle は [RuboCop の Ruby](https://www.rubocop.org/?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) linting ツールをベースにした linting ツールです。Cookstyle は、スタイル規約やベストプラクティスの実施を支援し、「行の長さ」や「関数のサイズ」のようなメトリクスに対してコードを評価し、ソースコードの統一性を確立します。

一方、Foodcriticでは、Cookbookの正しさ、間違い、Cookbookの非推奨など、よくある問題点をチェックしています。

まずはFoodcriticを実行して問題点を確認してみましょう。

```
$ foodcritic ~/cookbooks/custom_apache
Checking 5 files
x....
FC019: Access node attributes in a consistent manner: ./recipes/default.rb:13
FC019: Access node attributes in a consistent manner: ./recipes/default.rb:14
FC019: Access node attributes in a consistent manner: ./recipes/default.rb:15
FC064: Ensure issues_url is set in metadata: ./metadata.rb:1
FC065: Ensure source_url is set in metadata: ./metadata.rb:1
FC066: Ensure chef_version is set in metadata: ./metadata.rb:1
FC067: Ensure at least one platform supported in metadata: ./metadata.rb:1
```

7つの問題が報告されました。`メタデータ`とデフォルト`レシピ`の問題があります。まずはメタデータの問題を解決しましょう。

`metadata.rb`の最後に以下の行を追加してください。FC064, FC065, FC066, FC067と番号が付けられている問題に応じて、これらの行を追加します。これらの問題の修正は、問題の説明を読んだ後、非常に簡単です。

```
...
supports 'ubuntu'
issues_url 'https://github.com/learn-chef/custom_apache/issues'
source_url 'https://github.com/learn-chef/custom_apache'
chef_version '>= 12' if respond_to?(:chef_version)
```

問題番号 FC019 は、レシピのノード属性へのアクセス方法に関するものです。古い "メソッド "や "ドット "構文よりも、標準的なRubyの大括弧構文が推奨されています。それに合わせて `recipes/default.rb` を変更してみましょう。

```
...
# Load from node attributes some variables we'll need.
site_name = node['site']['name']
content_owner = node['site']['content']['owner']
content_group = node['site']['content']['group']
...
```

さて、再びFoodcriticを実行してください。

```
$ foodcritic ~/cookbooks/custom_apache
Checking 5 files
.....
```

完ぺきですね！もう問題はありません。では、いよいよコーディングスタイルをチェックして、Cookstyleで違反を自動修正していきましょう。以下のようにCookstyleを実行してください。

```
$ cookstyle -a ~/cookbooks/custom_apache
Inspecting 8 files
C...C..C

Offenses:

...
8 files inspected, 12 offenses detected, 12 offenses corrected
```

すばらしいです! 12個のコーディングスタイル問題を発見し、自分たちで修正することなく全て修正してくれました。再度クックスタイルを実行してみると、出力として問題が検出されていないことがわかります。

###テストキッチンを使って、Cookbookがローカルで成功するかどうかを検証
[テストキッチン](https://docs.chef.io/kitchen.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)は、`kitchen.yml`ファイルで定義されたプラットフォームとテストスイートの任意の組み合わせで、あなたのCookbookを自動的にテストします。ここでは、vagrantドライバを使ってテストVMでcookbookをテストします。`kitchen.yml`ファイルで定義されたUbuntu仮想マシンにcookbookを適用するには、以下のように`kitchen converge`を実行してください。

```
$ kitchen converge
-----> Creating <default-ubuntu-1604>...
       ==> vagrant: A new version of Vagrant is available: 2.2.4!
       ==> vagrant: To upgrade visit: https://www.vagrantup.com/downloads.html
...

Deprecated features used!
         Cloning resource attributes for apt_package[install libapache2 package] from prior resource
       Previous apt_package[install libapache2 package]: /tmp/kitchen/cache/cookbooks/custom_apache/recipes/default.rb:32:in `block in from_file'
       Current  apt_package[install libapache2 package]: /tmp/kitchen/cache/cookbooks/custom_apache/recipes/default.rb:32:in `block in from_file' at 1 location:
           - /tmp/kitchen/cache/cookbooks/custom_apache/recipes/default.rb:32:in `block in from_file'
          See https://docs.chef.io/deprecations_resource_cloning.html for further details.
         supports { manage_home: true } on the user resource is deprecated and will be removed in Chef 13, set manage_home true instead at 1 location:
           - /tmp/kitchen/cache/cookbooks/custom_apache/recipes/default.rb:52:in `block in from_file'
          See https://docs.chef.io/deprecations_supports_property.html for further details.
         supports { non_unique: false } on the user resource is deprecated and will be removed in Chef 13, set non_unique false instead at 1 location:
           - /tmp/kitchen/cache/cookbooks/custom_apache/recipes/default.rb:52:in `block in from_file'
          See https://docs.chef.io/deprecations_supports_property.html for further details.

       Chef Client finished, 11/16 resources updated in 30 seconds
       Finished converging <default-ubuntu-1604> (0m53.43s).
```

出力からわかるように、キッチンは正常に収束しました。しかし、よく見ると非推奨の機能が使われていることにも気がつきます。これらの非推奨機能をすべて修正するには、1つずつ修正していくのが簡単です。そのためには、`deprecations_as_error`設定を有効にして、非推奨機能をエラーとして報告するようにしましょう。`kitchen.yml`ファイルの`provisioner`セクションに以下の行を追加して、再度`kitchen converge`を実行してください。

```
...
  deprecations_as_errors: true
...
```

これで、うまく収束しなかったことがわかります。エラーの説明を読むと、[deprecations_resource_cloning deprecations](https://docs.chef.io/deprecations_resource_cloning.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub)を指しています。ページでは、Chefは各リソースに固有の名前を付けることを推奨しています。`recipe/default.rb`ファイルで以下のように変更してみましょう。

```
...
  package "install libapache2 package #{p}" do
...
```

再度 `kitchen converge` を実行すると、今度は [deprecations_supports_property](https://docs.chef.io/deprecations_supports_property.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) について別のエラーを返します。ページでは、Chefでは`supports` metapropertyをどのように使うべきかが説明されています。`recipe/default.rb`ファイルのその行を適宜変更してみましょう。

```
...
  manage_home true
  non_unique false
...
```

これで、`kitchen converge`を実行すると、エラーが出ずに正常に収束することがわかります。

###テスト
[ChefSpec](https://docs.chef.io/chefspec.html?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) は Ruby の [RSpec](https://rspec.info/?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) を拡張したもので、Ruby 用のビヘイビア駆動開発フレームワークです。`ChefSpec`はリソースとレシピをシミュレートされたシェフクライアントの実行の一部としてテストするフレームワークです。私たちの例のCookbookでは、すでにいくつかのテストが`spec/unit/recipes/default_spec.rb`で定義されています。それらをエラーなく実行してみましょう。

以下のようにChefSpecのテストを実行します。

```
$ chef exec rspec ~/cookbooks/custom_apache
WARNING: you must specify a 'platform' and 'version' to your ChefSpec Runner and/or Fauxhai constructor, in the future omitting these will become a hard error. A list of available platforms is available at https://github.com/customink/fauxhai/blob/master/PLATFORMS.md
...
```

`spec/unit/recipes/default_spec.rb`で、以下のようにプラットフォームをubuntuとバージョン16.04に指定して、この警告を修正してみましょう。

```
...
  runner = ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '16.04')
...
```

再度ChefSpecを実行すると、3つのテストがすべて合格していることがわかります。

```
$ chef exec rspec ~/cookbooks/custom_apache
Finished in 0.53752 seconds (files took 2.17 seconds to load)
3 examples, 0 failures
```

チュートリアルを簡潔にするために、ここでは統合テストを実装するつもりはありません。しかし、複数のコンポーネントが正しく機能しているかどうかを検証するのに役立つ統合テストを書くために、[InSpec](https://www.chef.io/products/chef-inspec/?spm=a2c65.11461447.0.0.79dc3a8ceXfCub) を参考にしてください。

###適切なメタデータとドキュメントを追加する
Cookbookが何をするのか、どのように使うのかを他のチームメンバーに理解してもらうために、適切なメタデータとドキュメントを提供することが重要です。そのためには、最低でも更新していることを確認してください。

- ファイルの著作権
- メタデータでのCookbookの詳細な説明
- cookbookの最新版
- README.mdファイルには、cookbookクの使用方法についての詳細が記載されています。
- 最新の変更点についてのCHANGELOG.mdファイル

ケースに応じてドキュメント用のファイルを追加しても良いでしょう。ただし、cookbookディレクトリの中に整理整頓しておくようにした方が良いでしょう。

パーフェクト! このチュートリアルでは、ベストプラクティスに従うことを保証するためにChefが提供するツールを使用して、Cookbookを更新する方法を学びます。

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*