Chefのチュートリアル

このチュートリアルでは、**Chef**について学び、Chefを使って**Alibaba Cloud**上でシンプルな**React.js**サイトを設定、構築、デプロイする方法を学びます。

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

#前提条件
このチュートリアルに従うには、以下のものが必要です。

- 2つのAlibaba Cloud [Elastic Compute Service](https://www.alibabacloud.com/ja/product/ecs) (ECS)インスタンスがUbuntu 16.04でインストールされており、少なくとも2GBのRAMを持っています。これらのインスタンスのうち1つはChefノードに、もう1つはChefサーバに使用されます。また、これらのインスタンスでは、ポート22、80、443が開いていることを確認してください。
- Chefノードとサーバの両方に使用する完全修飾登録ドメイン（またはFDQN）。[Alibaba Cloudドメイン](https://www.alibabacloud.com/ja/domain)を購入するか、[Freenom](https://www.freenom.com/ja/index.html?lang=ja)で無料で取得することができます。

#Chefを知る
Chefはいくつかの異なるコンポーネントで構成されています。これらのコンポーネントを理解することが重要なので、ここではChefの3つの主要なコンポーネントを簡単に見ていきましょう。

- **Chefサーバー**:このコンポーネントは設定データのハブとして機能し、設定レシピ、cookbooks、ノードに適用されるポリシー、Chefによって管理されている各登録ノードを記述するメタデータがすべて保存される中心的な場所として機能します。また、このコンポーネントは、組織内の他のすべてのマシン（またはノード）がデプロイメント設定の目的で使用する中央マシンでもあります。
- **Nodes**:ノードの重要なコンポーネントは、物理的または仮想的なもので、Chefを使用して管理できるマシンと理解することができます。[Chef-client](https://docs.chef.io/ctl_chef_client/)は各ノードにインストールされ、各ノードを希望の状態に設定するために使用されます。ノードはchef-clientを使用してchefサーバーと対話し、レシピ、テンプレート、ファイル配布などの設定情報を取得します。
- **Workstation**: Chef ワークステーションは、Chef の設定情報を作成または編集する場所です。設定ファイルはChefサーバーにプッシュされ、任意のノードにデプロイできるようになります。ワークステーションからChefサーバーとのやりとりは、[knife](https://docs.chef.io/workstation/knife/)と[chef](https://docs.chef.io/ctl_chef/)コマンドラインツールを使用して行われます。

#環境の設定
いよいよ実用的なことをする時が来ました。このチュートリアルでは、環境を設定し、chef を使って React.js アプリケーションを構築するために必要なツールをインストールします。

####ワークステーションの設定
ワークステーションは基本的にはChefの設定の詳細をオーサリングする場所です。ワークステーションはあなたが選択したOSであれば何でも構いません。つまり、Linux、MacOS、Windowsの全てがここで動作することになります。

1.ChefDKパッケージをインストールします。Chef DK (開発キット)には、インフラストラクチャの開発とテストに必要なすべてのツールが含まれています。このチュートリアルを書いている時点では、現在の安定版（4.0.60）を使用しています。この[リンク](https://downloads.chef.io/chefdk/?spm=a2c65.11461447.0.0.10585fa7zubQyM)をクリックして、お使いのOSに基づいたChef DKをダウンロードしてください。

2.Chef DKをインストールしたことを確認し、コマンドラインからChef DKにアクセスできることを確認します。これを行うには、`chef —version`を実行してインストールを確認することができます。

```
 chef@workstation:~$ chef --version
   Chef Development Kit Version: 4.0.60
   chef-client version: 15.0.300
   delivery version: master
   berks version: 7.0.8
   kitchen version: 2.2.5
   inspec version: 4.3.2
```

3.  MacおよびLinuxユーザーの場合は、Chef開発キットに含まれているRubyのバージョンがデフォルトのRubyのバージョンとして設定されていることを確認する必要があります。これを行うには、以下の手順に従ってください。

- ターミナルを開き、`which ruby`を実行します。以下のような結果が得られるはずです。

```
   chef@workstation:~$ which ruby
       /usr/bin/ruby
```

- `chef shell-init`サブコマンドを使用して、ChefDKに含まれるRubyをシステムRubyとして設定する必要があります。ChefDKによって使用されるアプリケーションは、ワークステーション上にも存在する可能性のある他のRuby開発ツールから分離することができます。

```
  chef@workstation:~$ echo 'eval "$(chef shell-init bash)"' >> ~/.bash_profile && source ~/.bash_profile
```

`zsh`、`fish`、Windows PowerShell (posh) など、bash とは異なるシェルを使用している場合は、`SHELL_NAME` をシェルに、`SHELL_PROFILE` をシェルプロファイルに置き換えて、以下のコマンドを実行してください。

```
 chef@workstation:~$ echo 'eval "$(chef shell-init SHELL_NAME)"' >> ~/.SHELL_PROFILE && source ~/.SHELL_PROFILE
```

- もう一度`which ruby`コマンドを実行して、Chef開発キット版のrubyを使用していることを確認すると、以下のような出力が得られるはずです。

```
  chef@workstation:~$ which ruby
      /opt/chefdk/embedded/bin/ruby
```

4.次に、ワークステーション用の `react-app-repo` という作業ディレクトリを作成する必要があります。このために`chef generate repo repo_name`コマンドを使用することになります。これにより、Chefが必要とするファイルやフォルダ構造が作成されます。

```
 chef@workstation:~$ chef generate repo react-app-repo && cd react-app-repo
```

5.ワークステーションの設定が完了したので、Chefサーバーの設定と設定を行います。

####Chefサーバーの設定
Chefサーバーを設定するには、以下の手順に従います。

1.ChefサーバーとしてUbuntu 16.04でインストールされたAlibaba Cloud ECSインスタンスをスピンアップし、ポート80、22、443が開いていることを確認します。クラウドでChefサーバを実行している場合は、セキュリティグループを通じてこれらのポートが開いていることを確認します。

2.Chefサーバーには、解決可能なドメインとホスト名が必要です。サーバーのホスト名がパブリックホスト名と一致していることを確認する必要があります。これは、以下のコマンドを実行することで可能です。

```
  chef@chef_server:~$ echo YOUR_DOMAIN_NAME | xargs sudo hostname
```

3.学習のために、このチュートリアルのために以下のbashスクリプトを作成しました。これはChef-serverパッケージをダウンロードしてインストールする際に役立つはずです。

- 以下のスクリプトをサーバ上のパス `/tmp/installChefServer.sh` にコピーしてください。

```
   chef@chef_server:~$ sudo vi /tmp/installChefServer.sh
      
  bash
      #!/usr/bin/env bash

      BOLD='\e[1m'
      GREEN='\e[92m'
      NC='\e[0m'

      updateLinuxPackages() {
        printf "${BOLD}${GREEN}=================================== Updating all packages ============================================ ${NC}\n"
        apt-get update

      }


      createDirectories() {
        if [ ! -d /creds ]; then
          mkdir /creds
        fi
        if [ ! -d /downloads ]; then
          mkdir /downloads
        fi
      }

      downloadAndInstallChefServer() {
        if [ ! -f /downloads/chef-server-core_12.19.26-1_amd64.deb ]; then
          printf "${BOLD}${GREEN}=================================== Downloading the Chef server package... ================== ${NC}\n"
          wget -nv -P /downloads https://packages.chef.io/files/stable/chef-server/12.19.26/ubuntu/16.04/chef-server-core_12.19.26-1_amd64.deb
        fi

        if [ ! $(which chef-server-ctl) ]; then
          printf "${BOLD}${GREEN}=================================== Installing Chef server =================================== ${NC}\n"
          dpkg -i /downloads/chef-server-core_12.19.26-1_amd64.deb
          chef-server-ctl reconfigure

          printf "${BOLD}${GREEN}=================================== Waiting for services ====================================== ${NC}\n"
          until (curl -D - http://localhost:8000/_status) | grep "200 OK"; do sleep 15s; done
          while (curl http://localhost:8000/_status) | grep "fail"; do sleep 15s; done

          printf "${BOLD}${GREEN}============================= Creating user ========================== ${NC}\n"
          # creating user format: chef-server-ctl user-create USER_NAME FIRST_NAME LAST_NAME EMAIL 'PASSWORD' --filename FILE_NAME
          chef-server-ctl user-create admin admin admin admin@example.com 'notsecure' --filename /creds/chefadmin.pem
          
          printf "${BOLD}${GREEN}============================= Creating oganization with user ========================== ${NC}\n"
          # creating org format: chef-server-ctl org-create SHORT_ORG_NAME 'FULL_ORG_NAME' --association_user USER_NAME --filename FILE_NAME
          chef-server-ctl org-create chef "Chef-learn, Inc." --association_user admin --filename organization-validator.pem
          
          printf "${BOLD}${GREEN}============================= Adding Web UI for chef  ========================== ${NC}\n"
          chef-server-ctl install chef-manage
          chef-server-ctl reconfigure
          chef-manage-ctl reconfigure --accept-license
        fi

        printf "${BOLD}${GREEN}==================================== Your Chef server is ready! ================================== ${NC}\n"
      }

      main() {
        updateLinuxPackages
        createDirectories
        downloadAndInstallChefServer
      }

      main
      
```

- このスクリプトでは、以下の情報を持つユーザーも作成します。

   - ユーザー名: admin
   - Firstname：admin
   - Lastname: admin
   - 電子メール: admin@example.com
   - パスワード: notsecure

- 以下のコマンドを実行してスクリプトを実行可能な状態にします。

```
  chef@chef_server:~$ sudo chmod u+x /tmp/installChefServer.sh
```

- 最後にスクリプトを実行します。少し時間がかかるかもしれません。

```
  chef@chef_server:~$ sudo /tmp/installChefServer.sh
```

4.ブラウザ上のChefのWebインターフェースにホスト名でアクセスできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/2b652711-9014-54ed-71f5-6045ec6120a8.png)

####ワークステーションがChef Serverと通信するように設定する
ワークステーションとChefサーバー間の通信を確立するために、すでにChefDKに含まれている`Knife`ユーティリティツールを利用します。

基本的に、Knifeは、Chefサーバーを認証するために2つのファイルを必要とします。


- knifeの設定ファイル `knife.rb` には、Chef サーバーの URL、RSA 秘密鍵（SSH 鍵）の場所、cookbookのデフォルトの場所などの情報が含まれています。


- RSA秘密鍵は、Chefサーバに送信されるすべてのリクエストを認証するために使用されます。公開鍵はChefサーバーが保持し、プライベート鍵はワークステーションが保持します。

ワークステーションとChefサーバー間の通信を確立するには、以下の手順に従ってください。

1.`knife configure`コマンドを実行して、knifeの設定を作成します。

`YOUR-HOST-NAME`をchef-serverドメイン名に、`SHORT-ORG-NAME`を`chef`に置き換えます。また、クライアントキーを配置するための出力で指定されたパスをメモしておきます。

```
  chef@workstation:~$ knife configure
    WARNING: No knife configuration file found. See https://docs.chef.io/config_rb_knife.html for details.
    Please enter the chef server URL: [https://devops1c.mylabserver.com/organizations/chef]: https://YOUR-HOST-NAME/organizations/SHORT-ORG-NAME
    Please enter an existing username or clientname for the API: [user] admin
    *****
    
    You must place your client key in:
    /home/user/.chef/chefadmin.pem
    Before running commands with Knife

    *****
    Configuration file written to /home/user/.chef/credentials
```

2.あなたの `react-app-repo` ディレクトリに `knife.rb` ファイルを作成して、knife が`cookbooks`の場所を知っていることを確認する必要があります。

```
  chef@workstation:~$ mkdir .chef && echo 'cookbook_path ["#{File.dirname(__FILE__)}/../cookbooks"]' >> .chef/knife.rb
```

また、`installChefServer.sh`スクリプトでユーザーを作成した時に作成したSSH鍵ファイルを、`scp`コマンドを使ってchef-serverからワークステーションにコピーする必要があります。

Note `~/.chef/admin.pem`はクライアントキーを置くために指定されたパスですが、`knife configure`コマンドの出力で指定されたパスと異なる場合は変更してください。

そして、以下の項目に置き換えてください。

`IP-ADDRESS-OR-HOSTNAME` は、chef-serverのパブリックIPアドレス/ホスト名に置き換えます。

`USER`はChefサーバーのユーザー名に置き換えてください。

`PATH_TO_YOUR_SSH_KEY` は、Chefサーバの ssh-key へのパスに置き換えてください。

```
   chef@workstation:~$ scp -i PATH_TO_YOUR_SSH_KEY USER@YOUR-IP-ADDRESS-OR-HOSTNAME:/creds/chefadmin.pem ~/.chef/admin.pem
```

あるいは、サーバへのアクセスを得るための認証手段としてパスワードを使用している場合は、以下のコマンドを実行することもできます。

```
 chef@workstation:~$ scp USER@YOUR-IP-ADDRESS-OR-HOSTNAME:/creds/chefadmin.pem ~/.chef/admin.pem
```

3.次に、以下のコマンドを使用してChefサーバーからSSL証明書を取得し、検証する必要があります。

```
chef@workstation:~$ knife ssl fetch
    WARNING: Certificates from www.mydomainname.com will be fetched and placed in your trusted_cert
    directory (/home/user/.chef/trusted_certs).

    Knife has no means to verify these are the correct certificates. You should
    verify the authenticity of these certificates after downloading.

    Adding certificate for www_mydomainname_com in /home/user/.chef/trusted_certs/www_mydomainname_com.crt
```

`knife ssl check`コマンドを実行して、SSL構成を確認します。

```
  chef@workstation:~$ knife ssl check
     Connecting to host ec2-34-207-124-26.compute-1.amazonaws.com:443
     Successfully verified certificates from ec2-34-207-124-26.compute-1.amazonaws.com
```

これでワークステーションの設定が完了し、knifeを使ってChefサーバーに接続できるようになりました。次のパートでは、ノードの設定を行います。

#ノードの設定とブートストラップ
Chefで設定を管理する作業を開始する前に、作業するノードが必要になります。これを行うには、Ubuntu 16.04をインストールした別のECSインスタンスを作成し、ワークステーションから`Knife`と`bootstrap`サブコマンドを利用します。ノードサーバを作成するだけで、ほとんどの作業は`workstation`から行われます。

1.Ubuntu 16.04をインストールした別のAlibaba Cloudインスタンスを作成し、ポート80、22、443、3000が開いていることを確認します。react.jsアプリケーションはポート3000で実行されます。

2.以下のコマンドを実行して、ノードサーバのホスト名がパブリックホスト名と一致していることを確認する必要があります。

では、次のように置き換えます。

- `IP-ADDRESS-OR-HOSTNAME`をノードサーバのパブリック IP アドレス/ホスト名に置き換えます。

- ノードサーバ上のユーザの名前を持つ`USER`

- `PATH_TO_YOUR_SSH_KEY` にノードサーバの ssh-key へのパスを指定します。

```
 chef@workstation:~$ ssh -i PATH_TO_YOUR_SSH_KEY USER@YOUR-IP-ADDRESS-OR-HOSTNAME 'echo YOUR_DOMAIN_NAME | xargs sudo hostname'
```

3.ワークステーションから、新しいクラウドサーバーをChefサーバーが管理するノードにブートストラップするには、以下のコマンドを実行します。

以下では、

- `IP-ADDRESS-OR-HOSTNAME`をノードサーバのパブリックIPアドレス/ホスト名に置き換えます。

- ノードサーバ上のユーザの名前を持つ`USER`

- `PATH_TO_YOUR_SSH_KEY` にノードサーバの ssh-key へのパスを指定します。

- `reactJS-node`で`node_name`を指定します。`node_name`はノードに与えたい名前であることを理解してください。

```
 chef@workstation:~$ knife bootstrap IP-ADDRESS-OR-HOSTNAME --connection-user USER --sudo -i PATH_TO_YOUR_SSH_KEY --node-name NODE-NAME
    Bootstrapping 34.76.3.167
    [34.76.3.167] -----> Installing Chef Omnibus (stable/15)
    [34.76.3.167] 
    [34.76.3.167] downloading https://omnitruck.chef.io/chef/install.sh
    ......
    [34.76.3.167] Getting information for chef stable 15 for ubuntu...
     
    [34.76.3.167] Installing chef 15
    [34.76.3.167] 
    [34.76.3.167] 
    [34.76.3.167] Thank you for installing Chef Infra Client! For help getting started visit https://learn.chef.io
    [34.76.3.167] 
    [34.76.3.167] Starting the first Chef Infra Client Client run...
    [34.76.3.167] 
    [34.76.3.167] +---------------------------------------------+
    [34.76.3.167] 
    [34.76.3.167]  2 product licenses accepted.
    [34.76.3.167] 
    [34.76.3.167] +---------------------------------------------+
    [34.76.3.167] 
    [34.76.3.167] Starting Chef Infra Client, version 15.0.300
    [34.76.3.167] 
    [34.76.3.167] resolving cookbooks for run list: []
    [34.76.3.167] Synchronizing Cookbooks:
    [34.76.3.167] Installing Cookbook Gems:
    [34.76.3.167] Compiling Cookbooks...
    [34.76.3.167] [2019-07-01T06:52:36+00:00] WARN: Node reactJS-node has an empty run list.
    [34.76.3.167] Converging 0 resources
    ........
    [34.76.3.167] Running handlers:
    [34.76.3.167] 
    [34.76.3.167] Running handlers complete
    [34.76.3.167] Chef Infra Client finished, 0/0 resources updated in 02 seconds
```

あるいは、サーバへのアクセスを得るための認証手段としてパスワードを使用している場合は、以下のコマンドを実行してください。

```
 chef@workstation:~$ knife bootstrap IP-ADDRESS-OR-HOSTNAME -N NODE_NAME -x USER -P 'PASSWORD' --sudo
```

4.自分のノードがChefサーバーに関連付けられていることを確認するには、`knife node list`コマンドを実行します。

```
 chef@workstation:~$ knife node list
    reactJS-node
```

`knife node show`コマンドを実行することで、ノードのデータを見ることができます。

```
knife node show NODE-NAME
```

```
chef@workstation:~$ knife node show reactJS-node
   Node Name:   reactJS-node
   Environment: _default
   FQDN:        ip-172-31-56-238.home.internal
   IP:          41.215.245.118
   Run List:    
   Roles:       
   Recipes:     
   Platform:    ubuntu 16.04
   Tags:
```

#React.jsアプリケーションの構築
chefサーバー、ワークステーション、ノードのセットアップが完了したので、 react.jsアプリケーションを構築する準備が整いました。以下のようなcookbookを書くことになります。

- node.jsのセットアップとインストール
- pm2をインストールします。`pm2`はプロセスマネージャーであり、`react.js`アプリの動作を維持するのに役立ちます。
- `react.js`アプリケーションをインストールします。


- `react.js`アプリケーションを起動します。


そのためにchef [execute](https://docs.chef.io/resources/execute/)と[bash](https://docs.chef.io/resources/bash/)リソースを使うことになります。これらのリソースはどちらもコマンドやスクリプトを実行するために使用します。`execute` リソースは単一のコマンドを実行するために、`bash` リソースは複数行のコマンドを実行するために使用します。構文やプロパティの詳細については、[execute](https://docs.chef.io/resources/execute/) リソースと [bash](https://docs.chef.io/resources/bash/) リソースのドキュメントを参照してください。

ワークステーションの `cookbooks/example/attributes/recipes` ディレクトリにある `default.rb` ファイルを開き、以下のように置き換えてください。

```
bash 'Install Node.js' do
 cwd "/home/ubuntu"
 code <<-EOH
   curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh
   bash nodesource_setup.sh
   apt-get install nodejs -y 
   EOH
end

execute 'Install pm2' do
 cwd "/home/ubuntu"
 command 'npm install pm2 -g'
end

execute 'Setup react app' do
 cwd "/home/ubuntu"
 command 'npx create-react-app my-app'
 only_if do ! File.exist?('/home/ubuntu/my-app') end
end

bash 'Start application' do
 cwd "/home/ubuntu"
 code <<-EOH
   cd my-app
   pm2 start npm -- start
   EOH
end
```

cookbookを実行するためには、Chefサーバーにアップロードする必要があります。このファイルに変更を加えるたびに、再アップロードする必要があることに注意してください。`react-app-repo`ディレクトリにあることを確認して、このコマンドを実行してください。

```
chef@workstation:~$ knife upload cookbooks/example
```

以下のコマンドを使用して、ノードの`run_list`にCookbookの`recipe`を追加する必要があります。

```
chef@workstation:~$ knife node run_list add reactJS-node 'recipe[example]'
reactJS-node:
  run_list: recipe[example]
```

`run_list`の詳細は[ドキュメント](https://docs.chef.io/run_lists/)で確認できます。

さて、`knife ssh`コマンドを使って`reactJS-node`にcookbookをデプロイします。

そして、以下のように置き換えてください。

- `USER`をノードサーバ上のユーザ名に置き換えてください。

- `PATH_TO_YOUR_SSH_KEY` にノードサーバの ssh-key へのパスを指定します。

```
chef@workstation:~$ knife ssh 'name:reactJS-node' 'sudo chef-client' --ssh-user USER --ssh-identity-file PATH_TO_YOUR_SSH_KEY
```

これで、ブラウザのウィンドウに `http://NODE_SERVER_IP_ADDRESS:3000` を貼り付けて react.js アプリケーションにアクセスできるようになりました。このようなページが表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/79c9c6bf-887d-6d21-d203-885c04fb5908.png)

#結論
このガイドでは、Chefを始める上での柔軟な基礎を解説しています。Chefの詳細については、Chefの[公式ドキュメント](https://docs.chef.io/?spm=a2c65.11461447.0.0.10585fa7zubQyM)を参照して、[chef rallyを学ぶ](https://learn.chef.io/?spm=a2c65.11461447.0.0.10585fa7zubQyM#/)ことからご覧ください。

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