EC2インスタンスでDBを運用する際にやっておいたこと

諸所の事情があり、EC2インスタンス上にDBを構築し運用するシチュエーションがあると思います。
このような場合、いくつかのチーム（や会社）で共同開発しているなど、一つのDBを共有出来ないような場合、
DBの更新（データなど）があった場合に、更新を起こったDBでAMIを作成し全体に配布することで、
DBのバージョン、スキーマ、データの統一性を割と安易に取れるのかなと思います。

また、EC2インスタンスのDBへ接続するアプリケーションでは、
EC2インスタンス自体にEIPを設定することで、パブリックIPアドレスを固定化することは出来ますが、DBへ接続するアプリケーションがパブリックIPアドレスへ接続するとなると、セキュリティグループの設定などをする必要が出てきます。
ですので、一般的にはDBインスタンスをプライベートなサブネットに配置し、プライベートな口に接続させるのではないかと思います。

### EC2インスタンスをCloudFormationで起動

CloudFormationでEC2インスタンスをデプロイさせる場合、以下のようなテンプレートファイルから起動させることでEC2インスタンスのプライベートIPを固定（172.31.46.80）させ起動させることが可能です。

```yaml:mariadb-instance.yml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  MariaDbAmi:
    Type: String
    Default: ami-98765432109876543

Resources:
  MariaDbInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref MariaDbAmi
      InstanceType: t2.medium
      SecurityGroupIds:
        - sg-10293847561029384
      PrivateIpAddress: 172.31.46.80
      Tags:
        - Key: Name
          Value: MariaDbInstance
```

```sh
aws cloudformation create-stack \
  --stack-name mariadb-instance \
  --template-body file://mariadb-instance.yml
```

このようにプライベートIPを固定しないと、インスタンスが起動されたタイミングで自動で振られるプライベートIPアドレスを接続先として、都度アプリケーションのDB接続先の変更が必要になってしまいます。

### インスタンスの更新が発生した場合

`mariadb-instance.yml` であれば、以下のように、新しいインスタンスのAMI(ami-01234567890123456)を指定し、CloudFormationのスタックのアップデートかければいいのですが、プライベートIPアドレスを固定していると、

```sh
aws cloudformation update-stack \
  --stack-name mariadb-instance \
  --parameters ParameterKey=MariaDbAmi,ParameterValue=ami-01234567890123456 \
  --template-body file://mariadb-instance.yml
```

CloudFormatinoが、途中でエラーが発生しロールバックが走ります。

```
Address 172.31.46.80 is in use. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidIPAddress.InUse; Request ID: 9ce599e8-0317-44d2-9f84-77038eea6819)
```

なぜなら、CloudFormationでは、インスタンスの更新が必要なスタックのアップデートを行った場合に、インスタンスを新たに立ち上げ、問題なければ、既存のインスタンスを終了させます。
つまり、そのタイミングはプライベートIPアドレスが重複することになるため、このケースの場合、同じプライベートIPアドレスを使い続けることが難しくなります。

### プライベートホストゾーンの設定

そこでプライベートホストゾーンです。
CloudFormationで利用するテンプレートファイルに **Route53**の **HostedZone** と **RecordSet** を追加します。
HostedZone のドメイン何でもいいですが、今回は `instance.local` としています。また、プライベートで利用するため、VPCを指定しています。VPCを指定しない場合はパブリックな HostedZone になります。
RecordSet はAレコードを作成します。サブドメインは `db.instance.local` とし、ResourceRecords にインスタンスのプライベートIPアドレスを指定しています。

また、インスタンスの **PrivateIpAddress** は使わないように、コメントアウトや削除するなりして下さい。

```yaml:mariadb-instance.yml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  MariaDbAmi:
    Type: String
    Default: ami-98765432109876543
  VPCRegion:
    Description: VPCRegion
    Type: String
    Default: ap-northeast-1

Resources:
  MariaDbInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref MariaDbAmi
      InstanceType: t2.medium
      SecurityGroupIds:
        - sg-10293847561029384
      #PrivateIpAddress: 172.31.46.80
      Tags:
        - Key: Name
          Value: MariaDbInstance
  HostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      Name: instance.local
      VPCs:
        - VPCId: vpc-abcdefgh
          VPCRegion: !Ref VPCRegion
  RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref HostedZone
      Name: db.instance.local
      Type: A
      TTL: 60
      ResourceRecords:
        - !GetAtt MariaDbInstance.PrivateIp
```

```sh
aws cloudformation update-stack \
  --stack-name mariadb-instance \
  --template-body file://mariadb-instance.yml
```

これでインスタンスが更新され、プライベートホストゾーンに作成したドメイン（`db.instance.local`）で接続できるようになります。

最後に、インスタンスの変更が必要になった場合です。

```sh
aws cloudformation update-stack \
  --stack-name mariadb-instance \
  --parameters ParameterKey=MariaDbAmi,ParameterValue=ami-01234567890123456 \
  --template-body file://mariadb-instance.yml
```

スタックのアップデート後、インスタンスは新しくなっていますが、問題なく `db.instance.local` で接続できます。

### まとめ

これでアプリケーション側からは、このプライベートホストゾーンのドメインを指定しておけば、インスタンスの更新があり、プライベートIPアドレスが変更することになっても、アプリケーションからの接続先の変更をする必要がなくなります。