【AWS】【Serverless Framework】カスタムドメイン設定方法（開発環境と本番環境の分離も考慮）

# はじめに

[こちら](https://qiita.com/sugurutakahashi12345/items/8eafa9ef6ce26531290a)の記事の続編です。
（※ 読まなくても大丈夫です。）

Serverless Framework で REST API をデプロイすると、AWS 側で以下のようなドメインが発行されます。

```
https://5tjwpqfgo2.execute-api.us-east-1.amazonaws.com/dev/hello
```

このドメインに含まれる `5tjwpqfgo2` という値は、こちらで何も設定しないとデプロイするごとに毎回変更されてしまい、使い勝手がよくないです。

なので、これをカスタムドメインで固定しようというのが、今回やりたいことになります。


# サマリー

以下を Serverless Framework で実現しました。

- カスタムドメインを指定できるようにすること
- 開発環境と本番環境で異なるカスタムドメインを指定できるようにすること

その結果、REST API のパスが以下のように固定することができるようになりました。

- Before
  - URL：`https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello`
  - 備考：URL の `xxxxxxx` の部分はデプロイするたびに変わる
- After
  - 開発環境 URL：`https://dev.suguru-takahashi.com/api/hello`
  - 本番環境 URL：`https://suguru-takahashi.com/api/hello`
  - 備考：カスタムドメインは固定である

全体構成図は以下のようになります。
![png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/e6422002-aaa2-9515-a36a-05c90114ad30.png)


全体のソースコードは[こちら](https://github.com/suguruTakahashi-1234/sls-hogehoge)にあげました。


# やりたいこと

やりたいことは以下の 2 つです。

1. **カスタムドメインを指定できるようにすること**
2. **開発環境と本番環境で異なるカスタムドメインを使用すること**
 
これを↓

- カスタム度名指定前（Before）

```
https://5tjwpqfgo2.execute-api.us-east-1.amazonaws.com/dev/hello
```

こうしたい↓

- カスタムドメイン指定後（After その1）

```
https://mogemoge.suguru-takahashi.com/api/hello
```

そして、以下のように開発環境と本番環境で URL を分けたい↓

- 開発環境（After その2）

```
https://dev.suguru-takahashi.com/api/hello
```

- 本番環境（After その2）

```
https://suguru-takahashi.com/api/hello
```

# 全体構成図

- カスタムドメイン指定前（Before）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/82677080-8dd3-8d4b-2595-40635637ff35.png)

- カスタムドメイン指定後（After）
![png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/b79127c6-025b-2582-5ec5-7b98e1b4b3a6.png)


この記事では増築された部分の設定の仕方について触れます。

# カスタムドメインを指定できるようにする

## 前提

以下の操作はすべて済んでいることを前提に手順を説明します。

- Serverless Framework 関連の準備
  - カスタムドメインの設定を除いた serverless.yml の設定
- カスタムドメイン関連の準備
  - 外部サービス or Route 53 でのカスタムドメインの取得
  - Route 53 でのホストゾーンの作成
  - ACM での SSL証明書の作成

これらの操作については以下の記事がわかりやすかったです。

【参考】
- [Serverless Frameworkでカスタムドメインを設定する](https://qiita.com/motchi0214/items/0b43ba924a4814c503e1)
- [【Serverless Framework 入門】 API Gateway + Lambda + DynamoDB サンプル](https://qiita.com/sugurutakahashi12345/items/8eafa9ef6ce26531290a)（過去記事1）
- [【AWS】 Route 53 + ACM + CloudFront + S3 の構成を構築する際につまずいたところ解説](https://qiita.com/sugurutakahashi12345/items/bd99e6b32e4f5f0e8096)（過去記事2）

## プラグインのインストール

[serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) という npm パッケージのプラグインを使用します。

npm パッケージなので Serverless Framework のプロジェクトを Node.js で使用しなければならないという縛りはないです。（例えば Python などのプロジェクトでも使用可能）

```shell
# package.json の作成（すでにあればスキップ）
$ npm init -y

# serverless-domain-manager のインストール
$ npm install serverless-domain-manager --save-dev
```

## serverless.yml の設定

- plugins の設定

`serverless-domain-manager` を追記するだけです。

```yml:serverless.yml
plugins:
  - serverless-domain-manager
```

- custom の設定

`mogemoge.suguru-takahashi.com/api` というドメインを作成する例になります。

```yml:serverless.yml
custom:
  customDomain:
    # API のパスは `https://<domainName>/<basePath>/xxxx` となる
    domainName: mogemoge.suguru-takahashi.com
    basePath: api
    certificateName: '*.suguru-takahashi.com'
    createRoute53Record: true
    endpointType: 'regional'
    securityPolicy: tls_1_2
```

詳しい設定項目の説明は [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) の公式ドキュメントを参照ください。

実のところ、Serverless Framework を使用しなくても、以下の AWS 公式ドキュメントの操作を行えば同じことは実現できます。

【参考】
・[REST API のカスタムドメイン名を設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-custom-domains.html)

`endpointType` などについての設定項目は上記の AWS 公式ドキュメントに詳しく説明があります。

また、事前に Route 53 や ACM の設定は必要です。今回は以下のような設定です。
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/db895504-62dd-d475-db6b-61a4b9fc8bac.png)

## ドメイン設定の AWS への反映

以下のコマンドによって、AWS にドメイン設定を反映させます。

```shell
$ sls create_domain
```

このコマンド実行によって Route 53 に A レコードと AAAA レコードの 2 つのレコードが作成されます。
![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/70a34c96-e9c4-c739-aa9d-0bd374f59245.png)


【参考】
・[DNSレコード](https://qiita.com/leomaro7/items/7d717077dd9165357f16)

## デプロイ

```shell
$ sls deploy
```

## 挙動の確認

Serverless Framework のデフォルトで配置されている `/hello` の API で挙動を確認します。

- A. 今まで通りの AWS が発行のドメインでアクセスする

コンソールにでている AWS 側から発行される `https://xxxxx.execute-api.us-east-1.amazonaws.com/dev/hello` というドメインでもアクセスできます。

※ `/dev` の部分は API Gateway 側のステージ名が反映されます。Serverless Framework でのデフォルトのステージ名は dev になります。
![c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/f3453a53-8278-36fa-8def-38c5eb71fd58.png)
もちろん、証明書は AWS が用意したものになります。
![c-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/12195153-9546-6424-98a8-f23616bbea6a.png)


- B. カスタムドメインでアクセスする（本命）

設定した以下のカスタムドメインでアクセスでします。

```
https://mogemoge.suguru-takahashi.com/api/hello
```

![d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/b6ff9b74-1750-18f3-fcbc-20b07fe4cb13.png)

証明書は ACM に登録したものが設定されています。
![e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/f562a415-27f2-d0fa-e8d9-db3712783b51.png)
API Gateway の設定も確認してみます。以下の画像のように Route 53 に挿入された A レコードと AAAA レコードの対応通りになっていることが確認できます。
![f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/cef6a526-f628-7e01-2173-8832c139b17b.png)
serverless.yml で設定した `basePath: api` も反映されていることが確認できます。
![f-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/b3013136-a238-94e6-a200-52157202c78d.png)

## プロジェクトの削除

こちらのコマンドでプロジェクトは削除されます。

```
$ sls remove
```

先にドメイン設定を削除してしまうと、プロジェクトの削除に失敗するおそれがあるので注意してください。

## ドメイン設定の削除

以下のコマンドによって、AWS にドメイン設定を反映させます。

```shell
$ sls delete_domain
```

Route 53 を確認すると `sls create_domain` で作成された A レコードおよび AAAA レコードが削除されていることが確認できます。
![g.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/f9c84fd6-2f09-c081-6655-38de53d2c793.png)

## 運用

serverless-domain-manager のインストールと serverless.yml の設定が完了すれば、実運用としては以下のコマンドでカスタムドメインを設定した REST API のデプロイから削除までを行うことができます。

```shell
# ドメイン作成
$ sls create_domain

# プロジェクト作成
$ sls deploy

# プロジェクト削除
$ sls remove

# ドメイン削除
$ sls delete_domain
```

Serverless Framework でのカスタムドメイン設定の基礎的な部分は以上になります。

# 開発環境と本番環境で異なるカスタムドメインを指定できるようにする

`sls create_domain` の実行時に `sls deploy` のときと同様にコマンド実行時に `--stage <ステージ名>` で stage を設定することができるので、それを利用して開発環境と本番環境で異なるカスタムドメインを指定します。


## 前提

- 『その 1：カスタムを指定できるようにする』が完了していること

## serverless.yml の設定

変更するのは `serverless.yml` のみです。
ステージ名、カスタムドメインを以下のように設定します。

- 開発環境
  - ステージ名：`dev`（デフォルト）
  - カスタムドメイン：`dev.suguru-takahashi.com`
- 本番環境
  - ステージ名：`prd`
  - カスタムドメイン：`suguru-takahashi.com`

```yml:serverless.yml
custom:
  stage: ${opt:stage, self:provider.stage}
  endpoint:
    dev: "dev.suguru-takahashi.com"
    prd: "suguru-takahashi.com"
  customDomain:
    domainName: ${self:custom.endpoint.${self:custom.stage}}
    stage: ${self:custom.stage}
    basePath: api
    certificateName: '*.suguru-takahashi.com'
    createRoute53Record: true
    endpointType: 'regional'
    securityPolicy: tls_1_2
```

`custom.stage`, `custom.endpoint` はという名称は引数名なので何でもよいです。

ここでのポイントはステージ名を `domainName` に埋め込むようにすると本番環境のカスタムドメインが `prd.suguru-takahashi.com` なってしまい `prd` が格好悪いので、`custom.endpoint` をクッションにおいて自由なカスタムドメインを設定できるようにしています。

## 開発環境

開発環境はデフォルトでステージ名が `dev` なので、デプロイと削除のコマンドに変更はありません。

```shell
# ドメイン作成
$ sls create_domain

# プロジェクト作成
$ sls deploy

# プロジェクト削除
$ sls remove

# ドメイン削除
$ sls delete_domain
```

※ もちろん、`--stage dev` とオプションをつけても同様の挙動になります。

## 本番環境

開発環境との違いはコマンド実行時に `--stage <ステージ名>` のオプションでステージ名を `prd` と指定してあげるだけです。

```shell
# ドメイン作成
$ sls create_domain --stage prd

# プロジェクト作成
$ sls deploy --stage prd

# プロジェクト削除
$ sls remove --stage prd

# ドメイン削除
$ sls delete_domain --stage prd
```

※ あえて、serverless.yml 内で設定していないようなステージ名を `--stage <ステージ名>` でわたすと serverless.yml での変数名が解決できず、デプロイに失敗してくれます。

## 挙動の確認

開発環境と本番環境のデプロイ後の AWS コンソールを確認すると以下のようになります。

両方の環境が別々のドメインがふられていることが確認できます。

- Route 53
![x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/326ad3c2-574d-9098-3512-75991d338824.png)

- API Gateway
![y.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259125/7082a120-0102-b2a2-6289-71f0b5e486d8.png)

以上になります。

# 参考文献

- Serverless Framework 公式のブログでの解説
  - [How to set up a custom domain name for Lambda & API Gateway with Serverless](https://www.serverless.com/blog/serverless-api-gateway-domain)
- serverless-domain-manager
  - [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager)
- わかりやすかった記事
  - [[AWS] Serverless FrameworkでカスタムドメインをSSL付きで設定する](https://blog.katsubemakito.net/aws/lambda-with-serverless-framework2)
- 開発環境/本番環境の切り替えで参考になった資料