PackerとAnsibleを使用してAlibaba Cloud ECSインスタンス上にReact JSアプリケーションイメージを作成する

このガイドでは、Packerを使って**Alibaba Cloud** ECSインスタンス上にマシンイメージを構築する方法と、マシンイメージに持たせたいものを書くためのAnsibleについてお話します。

#前提条件
このガイドに従うために専門家である必要はありません。必要なのは、[Alibaba Cloud](https://www.alibabacloud.com/ja/campaign/free-trial)のアカウントと`Access key`だけです。こちらのリンクをクリックすると、Alibaba Cloud上での[アクセスキーの作成方法](https://help.aliyun.com/document_detail/53045.html?spm=a2c65.11461447.0.0.315b4067WKTMvY)が表示されます。

#ステップ1：パッカーのインストール
私たちのシステムにpackerをインストールするには、packerの公式インストールページに従うか、パッケージマネージャ、Windows用のchocolateyとmacOS用のhomebrewを使用することができます。パッケージマネージャを使えば、パスに環境変数を追加する手間が省けます。

**chocolatey を使って Windows に packer をインストールするには**

1、chocolatey パッケージマネージャをインストールします。chocolatey をインストールするには、管理者として cmd を開き、以下のコマンドを貼り付けて chocolatey パッケージマネージャをインストールします。

```
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
```

2、chocolatey がインストールされていることを確認するには、 `choco -v` コマンドを実行します。

3、以下のコマンドを実行して、packerをインストールします。

```
choco install packer
```

4、packerのインストールを確認するには、次のように実行します。

```
packer -v
```

5、Packerのバージョンがインストールされているはずです。
*インストールプロセスを示すGif画像*

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d067076f-769b-002c-d42b-fecdc2959d92.png)

**homebrewを使ってMacOSにpackerをインストールするには**

1、homebrewがまだインストールされていない場合は、インストールしてください。macOSにhomebrewをインストールするには、ターミナルを開いて以下のコマンドを貼り付けます。

```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

2、以下のコマンドを実行して、packerをインストールします。

```
brew install packer
```

3、packerのインストールを確認するには、以下を実行します。

```
packer -v
```

4、Packerのバージョンがインストールされているはずです。
MacOSへのインストールを示すGif画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/56c5deba-f0ca-67d9-3c96-96ba275f1847.png)

#ステップ2: Reactアプリのマシンイメージを構築する
マシンイメージを構築するには、テンプレートファイルを作成する必要があります。テンプレート ファイルは、構築するイメージを定義するために使用します。テンプレートファイルは`JSON`形式で、Packerの様々なコンポーネントを設定する異なるキーのセットを持っています。

1、**example.json**という名前のテンプレートファイルを作成し、以下のコードを貼り付けます。

```
  {
      "variables": {
        "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
        "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
      },
      "builders": [{
        "type": "alicloud-ecs",
        "access_key": "{{user `access_key`}}",
        "secret_key": "{{user `secret_key`}}",
        "region": "us-west-1",
        "image_name": "ReactJS-Application",
        "instance_type": "ecs.t5-lc2m1.nano",
        "source_image": "ubuntu_16_0402_32_20G_alibase_20180409.vhd",
        "io_optimized":"true",
        "image_force_delete":"true",
        "ssh_username": "root",
        "internet_charge_type": "PayByTraffic"
      }],
      "provisioners": [{
        "type": "shell",
        "script": "installAnsible.sh"
       },{
       "type": "ansible",
      "playbook_file": "playbook.yml"
     }]
    }
```

**変数**セクションでは、テンプレートファイル内のどこでも使用できる変数を定義し、
**env**関数を指定して環境変数から**access_key**と**secret_key**の値を取得しています。

**ビルダー**セクションには、特定のビルダーを設定するJSONオブジェクトの配列が含まれています。ビルダーは、マシンを作成し、そのマシンをイメージに変換する役割を担う packer のコンポーネントです。

**プロビジョナー**セクションでは、組み込みのソフトウェアやサードパーティ製のソフトウェアを使用して、起動後のマシンイメージをインストールしたり設定したりします。このガイドでは、プロビジョナーとして **ansible** と **shell** を使います。

2、`ALICLOUD_ACCESS_KEY`と`ALICLOUD_SECRET_KEY`をエクスポートするには、ターミナルで以下のコマンドを実行します。

```
 export ALICLOUD_SECRET_KEY="YOUR_ALICLOUD_ACCESS_KEY"
   export ALICLOUD_SECRET_KEY="YOUR_ALICLOUD_SECRET_KEY"
```

3、**playbook.yml**という名前の新しいAnsibleファイルを作成し、以下のコードを貼り付けます。**example.json**と**playbook.yml**は同じディレクトリまたはフォルダに作成します。

```
   ---
     - hosts: all
       become: true

       vars:
         NODEJS_VERSION: 8
         domain: "localhost"

       tasks:
       - name: Add gpg key for nodejs
         apt_key:
           url:  "https://deb.nodesource.com/gpgkey/nodesource.gpg.key"
           state: present
       - name: Add nodejs LTS to apt repository
         apt_repository:
           repo: "deb https://deb.nodesource.com/node_{{ NODEJS_VERSION }}.x {{ ansible_distribution_release }} main"
           state: present
           update_cache: yes
       - name: Install nodejs
         apt:
           name: nodejs
           state: present
       - name: Setup React application
         shell:
            cmd: |
              npx create-react-app my-app # Setup our react application
              cd /root/my-app
              npm install -g pm2   # Install Pm2, A nodejs process manager which enables us to run our application in the background process
       - name: Install nginx
         apt:
           name: nginx
           state:  present
           update_cache: yes
       - name: Remove nginx default configuration
         file:
           path: /etc/nginx/sites-enabled/default
           state: absent
       - name: enable reverse proxy # This enables us to use the public IP without passing in the port on our browser address bar
         shell:
           cmd: |
             cat > /etc/nginx/sites-available/my-app <<EOF
             server {
               listen 80;
               server_name {{ domain }};
               location / {
                 proxy_pass 'http://127.0.0.1:3000';
               }
             }
             EOF
       - name: create symlinks for nginx configuration # make sure the nginx configuration files are always the same.
         file:
           src:  /etc/nginx/sites-available/my-app
           dest: /etc/nginx/sites-enabled/my-app
           state: link
         notify:
         - restart nginx
       handlers:
         - name: restart nginx # restart nginx service
           service:
             name: nginx
             state: restarted
```

4、テンプレートファイルを検証するには、`packer validate example.json`を実行します。これにより、テンプレートファイルに構文エラーなどのエラーがないことを確認します。
5、`packer build example.json`を実行してReactJSイメージをビルドします。

#ステップ3: Reactイメージを使ってインスタンスを起動する
先ほど作成した画像を使用してインスタンスを作成するには、以下の手順に従います。

1、アリババクラウド[コンソール](https://account.alibabacloud.com/login/login.htm?oauth_callback=https%3A%2F%2Fhome-intl.console.aliyun.com%2F%3Fspm%3Da2c65.11461447.0.0.315b4067sNTw3q&lang=ja#/)にログオン
2、ダッシュボードのサイドナビゲーションバーにあるElastic Compute Serviceをクリックします。
3、インスタンスを作成するには、[Create Instance] の説明が記載されたボタン、またはこの[リンク](https://account.alibabacloud.com/login/login.htm?oauth_callback=https%3A%2F%2Fecs-buy.aliyun.com%2Fwizard%2F%3Fspm%3Da2c65.11461447.0.0.315b406759ktty&lang=ja)をクリックします。
4、基本設定では、以下の操作を行います。

----- 1、`課金方法`については、「`Pay-As-You-Go`」をクリックしてください。
----- 2、`地域`については、ドロップダウンをクリックして、`US West 1 (Silicon Valley)`を選択してください。
----- 3、`インスタンスタイプ`では、「`エントリーレベル（共有）`」をクリックします。
----- 4、`画像`は「`Custom Image`」ボタンをクリックします。`Custom Image`の下のドロップダウンメニューをクリックして、作成したイメージは`ReactJS-Application`を選択します。
----- 5、`Next: Networking`ボタンをクリックします。

5、2つ目のステップである**ネットワーキング**については、以下の手順に従ってください。
-----1、**Network**セクションでは、「VPC」ドロップダウンメニューをクリックし、デフォルトのVPCを選択します。
 -----2、**Security Group**セクションでは、「ポート設定に移動」をクリックして、アプリケーションの**ポートを設定**します。
-----3、**Add Security Group Rule**をクリックし、**Port Range**を80に、**Authorization Objects**を0.0.0.0.0/0に設定します。これを繰り返しますが、**ポート範囲**を3000に設定します。
ポート80はHTTPリクエストのためのもので、ポート300`はリアクトアプリケーションが実行されているポートです。
-----4、セキュリティグループを設定したら、次へをクリックします。**Next: System Configurations** ボタンをクリックします。

6、3番目のステップである**システム設定**については、以下の手順に従ってください。
-----1、詳細設定（**インスタンスRAMのロールまたはクラウドイニットに基づく**）ドロップダウンをクリックして、以下のコードをユーザーデータフィールドボックスに貼り付けます。

```
#!/usr/bin/env bash
cd /root/my-app
pm2 start /root/my-app/node_modules/react-scripts/bin/react-scripts.js --name my-app -- start
```

上記のスクリプトはインスタンスの作成時に実行されるので、アプリケーションを起動するためにインスタンスに SSH 接続する必要はありません。

7、プレビューボタンをクリック
8、利用規約に同意し、「インスタンスの作成」ボタンをクリックします。
9、インスタンスが起動したら、インターネットの IP アドレスをコピーしてブラウザのアドレスバーに貼り付けます。ReactアプリケーションのWebページが表示されるはずです。
*インスタンスを起動するプロセスを示すgif画像は以下の通りです。*

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/856b5ace-c2d2-e677-655c-6716c863ef39.png)


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