LINEアプリケーションをHerokuからAWSに移行しよう

# はじめに

この記事はLINE API開発者のためのイベント、REV UP2022のハンズオンセッション「LINEアプリケーションをHerokuからAWSに移行しよう」のハンズオン資料をまとめた記事です。

https://revup.jp/session/aj9vx8bgy

準備編もございますので、そちらも合わせてご覧下さい。

https://qiita.com/ufoo68/private/6c9e7154993e1af81b2f

# HerokuでLINE botを作成する

## LINE DevelopersでMessaging APIを登録

LINE Developersにログインして[コンソール画面](https://developers.line.biz/console)を開きます。
最初にプロバイダーを作成する必要があるので、まだ作成されていない場合は作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/de038de2-38b3-e1b7-7663-3a9e117c99e8.png)

プロバイダーを選択したら新規チャネルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/4c2bad1d-b42b-4e19-7a91-5d45d3989ca7.png)

チャネルの種類はMessaging APIを選びます。新規チャネル作成のフォームに移動するので、必須項目を記入していきます。項目については適当に入力して問題ないので説明は省略します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/b2de1a69-bc04-4ba0-ff49-1d23d78975f1.png)

チャネルの作成が完了したら以下の２つを確認して、文字列をメモしておきます。

- チャネルシークレット
    - チャネル基本設定のタブにある
- チャネルアクセストークン（長期）
    - Messaging API設定のタブにある

ついでに自動応答もOFFにしておきます。LINE Official Account Manageを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/7cc7a201-4943-5e58-292f-0e049d34041d.png)

ホームタブから左メニューで自動応答メッセージを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/81b52546-2280-e100-ab72-78b06d8b9f76.png)

Defaultというやつがオンになっていると思いますのでオフにしてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/40b0fec9-eec7-c49b-f065-97e8b8bb5467.png)

## Herokuにアプリケーションをデプロイ

まずは適当な開発用フォルダーに以下のファイルを作成していきます。

```json:package.json
{
  "name": "revup2022",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@line/bot-sdk": "^7.5.2",
    "dotenv": "^16.0.3",
    "express": "^4.18.1"
  }
}

```

```js:index.js
'use strict';

require('dotenv').config();
const express = require('express');
const line = require('@line/bot-sdk');

const app = express();

const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET,
};


const client = new line.Client(config);

const handleEvent = (event) => {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }
  const echo = { type: 'text', text: event.message.text };
  return client.replyMessage(event.replyToken, echo);
}

app.post('/handler', line.middleware(config), (req, res) => {
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result))
    .catch((err) => {
      console.error(err);
      res.json(err);
    });
});

app.get('/', (_, res) => {
  res.send('ok');
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`listening on ${port}`);
});
```

`Procfile`はHerokuの設定ファイルとなります。

```:Procfile
web: node index.js
```

`.env`に前回のセクションでメモした`チャネルシークレット`と`チャネルアクセストークン`を入力します。

```:.env
CHANNEL_ACCESS_TOKEN=チャネルアクセストークン
CHANNEL_SECRET=チャネルシークレット
```

ファイルの準備ができたらgitコマンドを打ちます。

```bash
git init
git add .
git commit -m "first commit"
```

次にherokuの準備をします。

```bash
heroku login # ログインを既にした場合は不要
heroku create
```

`heroku create`が無事終了したら以下のようなものが表示されると思います。

```
Creating app... done, ⬢ fast-headland-xxxx
https://fast-headland-xxxx.herokuapp.com/ | https://git.heroku.com/fast-headland-xxxx.git
```

`Creating app... done, ⬢ `に続く文字列がherokuのアプリ名です。
Herokuのデプロイにはgitコマンドを使います。以下のコマンドでデプロイをします。

```bash
git push heroku master
```

デプロイが無事終了したら`https://fast-headland-xxxx.herokuapp.com/ deployed to Heroku`のような表示がターミナルの下の方に表示されるので、そのURLをメモします。

## 動作確認

LINE Developersを開いて、チャネルの設定画面からMessaging APIタブを選択して、Webhook設定の項目を埋めます（`Webhookの利用`にチェックがあることを確認してください）。
URLには`https://herokuのURL/handler`と入力します。`/handler`というパスを指定していることに注意してください。問題がなければ`検証`を押して`成功`が表示されると思います。

今回作成したLINE botは単純なオウム返しbotです。作成したLINE botが正しく動作できていれば、入力したメッセージがそのまま返信されるbotになっていると思います。

# HerokuのLINE botをAWSに移動する

ここからが今回の本題となります。HerokuからAWSへのアプリケーションの移行をソースコードの変更なしに実現できることを、このハンズオンで体験してもらえれば幸いです。

## Dockerの設定ファイルを用意する

HerokuにデプロイしたアプリケーションをAWS上で動かすために今回は[ECS](https://aws.amazon.com/jp/ecs/)を使います。
まずはDockerファイルを作成してアプリケーションをコンテナ化するための準備をします。

```dockerfile:Dockerfile
FROM node:14.5.0-alpine

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD npm start
```

これだけでAWSへの移行の準備は完了しました。

## AWS Copilotでデプロイ

今回はAWS CopilotというECSの構築を自動で行ってくれるサービスを使ってアプリケーションのデプロイを行います。
以下のコマンド実行のみでAWSへのデプロイが完了します。

```bash
copilot init
```

アプリケーションの名前を最初に聞かれるので適当に入力してEnterで進めます。

```
What would you like to name your application? [? for help]
```

どのタイプのアプリケーションを構築するか質問されるので、`Load Balanced Web Service`を選択します。

```
  Which workload type best represents your architecture?  [Use arrows to move, type to filter, ? for more help]
    Request-Driven Web Service  (App Runner)
  > Load Balanced Web Service   (Internet to ECS on Fargate)
    Backend Service             (ECS on Fargate)
    Worker Service              (Events to SQS to ECS on Fargate)
    Scheduled Job               (Scheduled event to State Machine to Fargate)
```

ECSのサービス名を聞かれます。特に理由がなければアプリケーション名と同じで問題ないと思います。

```
What do you want to name this service? [? for help]
```

どのDockerfileを使うかを聞かれます。デフォルトで選択されているやつで大丈夫です。

```
Which Dockerfile would you like to use for revup2022?  [Use arrows to move, type to filter, ? for more help]
  > ./Dockerfile
    Enter custom path for your Dockerfile
    Use an existing image instead
```

色々と処理が走ったあとにテスト環境で構築して良いかを聞かれます。今回はハンズオンなので`y`で回答していいです。

```
Would you like to deploy a test environment? [? for help] (y/N) 
```

その後色々処理が走って無事デプロイが終了すれば、アプリケーションのURLが表示されるのでそれをメモします。

```
√ Deployed service revup2022.
Recommended follow-up action:
  - You can access your service at http://xxxxx.ap-northeast-1.elb.amazonaws.com over the internet.
```

## API GatewayでHTTPプロキシーを構築する

メモしたURLを見てもらうと`https`ではなく`http`となっていることがわかると思います。Messaging APIのWebhookに登録するためには`https`が必要となります。
おそらく通常だとRout53やACMを使った方法を想像すると思うのですが、今回は簡単のためにAPI Gatewayを使ってプロキシーサーバーのようなものを構築します。

まずはAWSのコンソール画面でAPI Gatewayを探します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/ff2906f3-d8eb-d347-b596-4b824124b248.png)

API Gatewayを開くことができたら`APIを作成`を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/69d607b5-ae89-3743-fe1c-ca25b07f6636.png)

REST APIを選んで`構築`を押下。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/c808f4c4-fc5b-f2c0-8a8b-0b9ca9743749.png)

プロトコルは`REST`、既存のものからインポートするものも特にないので`新しいAPI`で大丈夫です。API名は適当な名前を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/42b8e50d-92ab-f3fb-5119-0d5dd6dc8ada.png)

APIの設定画面に遷移するので`アクション`から`リソースの作成`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/5442eee1-6dda-9305-6665-2e005522817b.png)

`プロキシリソースとして設定する`にチェックを入れたら`リソース名`や`リソースパス`が勝手に入力されるのでそのまま`リソースの作成`を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/51ea09a3-5575-96f8-7a94-ab11f0eeead4.png)

そのままセットアップ画面に遷移するので、`HTTPプロキシ`を選んで`エンドポイントURL`には先程メモしたURLに`/{proxy}`といパスを加えたものを入力して`保存`を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/023de9a6-fd61-ba5f-fa09-cddd348c4d06.png)

リソースの作成が終われば、次に`アクション`から`APIのデプロイ`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/25315469-6714-d54b-0780-85381829f0c9.png)

`デプロイされるステージ`は`[新しいステージ]`で、`ステージ名`は何を入力しても構いません。入力したらそのままデプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/b4efdaec-990c-b6e0-f53b-cb5ca3f91524.png)

公開されたURLが表示されるので、それをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/14bd2869-acaa-61f1-a9c1-3c688f71fb3c.png)

これでAWSの作業は完了です。

## 動作確認

API GatewayのURL`https:///xxxxexecute-api.ap-northeast-1.amazonaws.com/ステージ名`に`/handler`のパスを加えたものをMessaging APIのWebhookのURLに新しく取り替えます。
Herokuのときと同じ動作をすれば成功です。

# 後片付け

## Heroku

Herokuの後片付けに関しては、以下のコマンドを実行するだけで大丈夫です。

```bash
heroku apps:destroy --app アプリ名
```

## AWS

まずはCopilot側の後片付けは、以下のコマンド実行を作業しているディレクトリ内で実行して完了です。

```bash
copilot app delete
```

API Gatewayに関しては、APIの一覧画面から対象のAPIを選択して`アクション`から`Delete`を実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/9f2fd072-5b5b-5689-21dc-f04eb175bd61.png)