【風前の灯】世界でたった１例のAWS Proton実用事例

2021年頃にGAして以降ほとんど話題にもならず、2023年頃から開発もストップしており既に風前の灯なAWS Protonですが、特定のユースケースではとても便利に使えますので、実用例を紹介します。

Protonについてはこちらをご覧ください。

https://docs.aws.amazon.com/proton/latest/userguide/Welcome.html

# Protonをどう使っているか

Protonというサービス自体が想定しているユースケースはおそらく、「WordPress設置代行・運用代行」のようなビジネスだと思います。
WordPressを運用するための環境をテンプレート化し、エンジニア以外でも管理画面からポチポチでインフラを起動でき、インフラのアップデートも管理画面ポチポチで一気に大量に完了する。といった活用を想定しているように見えます。
申し込まれたプランやオプション等の設定もProtonでパラメーター化しておくことで、エンジニア不在でも顧客の希望通りのインフラを構成できるようにしておけます。

このユースケースではProton用のテンプレートは１パターンだけ用意しそれを上手にバージョン管理して運用していけば良く、Proton側はそういう想定をしていると思います。しかしながら、私は少し違う使い方をしているので実用例として紹介したいと思います。

## 活用プロジェクト

Protonを実際に活用しているプロジェクトの特徴は以下です。

- 同じソースコードから派生した複数のOEMアプリケーションを運用している
- OEM顧客からの要望により、１パターンのテンプレートでは分岐しきれないような構成の追加・変更がある
- OEM毎に最低でも本番環境・ステージ環境があり、開発状況によっては「ステージ２」のような環境も起動することがある
- （今のところはないが）顧客のAWS環境に設置して欲しいと言われても対応可能

このようなプロジェクトで、プロジェクトの運営に必要なインフラ環境を全てProtonで構築しています。

## 構成イメージ

イメージとしてはこのような感じです。

OEM毎に微妙に異なる構成を実現しつつ、本番・ステージ、低負荷・高負荷などに合わせて各種パラメーターを選択することで、それぞれ希望のインフラを起動できるようになっています。

![Proton実用例紹介.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64098/4da9daff-8110-485f-a828-93e55605a156.png)
（インフラ内のサービスアイコンは例です）

# 運用例

WordPress設置代行業であれば、どういうインフラをどいうプランで提供するかなどは自社の都合で決めることができます。しかし、このプロジェクトの場合はOEM顧客の要望により独自のカスタマイズが発生することがあります。
このOEM毎の差分を全てパラメーターで分岐するようにテンプレート化するととても複雑で巨大なテンプレートとなってしまい、管理が大変になるため、そうならないような工夫をいくつか行っています。

## テンプレート管理

まずはGitリポジトリを１つだけ作成し、以下の様にOEM毎のディレクトリにテンプレートが配置されるようにします。

```
oem-base/
├── oem-base-env/v1/
├── oem-base-svc/v1/
oem-a/
├── oem-a-env/v1/
├── oem-a-svc/v1/
oem-b/
├── oem-b-env/v1/
├── oem-b-svc/v1/
...
oem-z/
├── oem-z-env/v1/
├── oem-z-svc/v1/
Makefile
README.md
```

oem-baseはプロジェクト全体のベースとなるテンプレートです。
特に要望のないOEMはbaseをディレクトリごとコピーして完成とし、要望のあるOEMはコピーした上でカスタマイズをしていきます。

:::note info
※本当は、要望が出てくるまでbaseのテンプレートを使用し、要望が出たらそこで初めてコピーを作成してカスタマイズする、といったことができれば理想的です。しかし起動してしまったインフラのテンプレートをあとから差し替えることはできない（内容が全く一緒のテンプレートだとしてもできない）ため、予めOEM毎にコピーを作成しています。
:::

### 作業用コマンド

Makefileには以下の様に作業用コマンドを用意しておき、楽に作業できるようになっています。

```makefile
# Makefile
...

ifdef P
SOURCE_PROJECT := $(P)
else
SOURCE_PROJECT := base
endif

ifdef R
REVISION := $(R)
else
REVISION := HEAD
endif

diff: (SOURCE_PROJECTと全OEMディレクトリとのdiffを取る)
	@for d in $(shell ls -d oem-* | grep -v oem-${SOURCE_PROJECT}); \
	do \
		diff -r oem-${SOURCE_PROJECT}/oem-${SOURCE_PROJECT}-env/v1 $${d}/$${d}-env/v1; \
		diff -r oem-${SOURCE_PROJECT}/oem-${SOURCE_PROJECT}-svc/v1 $${d}/$${d}-svc/v1 --exclude .compatible-envs; \
	done

patch: ...(特定のRevisionのdiffからpatchファイルを作成し、全OEMディレクトリにパッチ適用する処理)

clone: ...(SOURCE_PROJECTをコピーして新OEMを作成する処理)
```

各OEMがbaseからどのくらい変更されているか（どんな顧客要望に対応しているか）を確認したいときは、 `make diff` と打てば差分が出力されるようになっています。

また、全OEM共通のインフラアップデートを行いたいときは、oem-baseで設定値の変更とコミットを行った後、 `make patch` と打てば全てのOEMに同じ変更が適用されるようになっています。

### テンプレートバージョンをリリース

oem-base用のテスト環境で十分テストした後、各OEMにもリリースしていきます。
OEM毎にバージョンリリースを管理するため、多少面倒ですがそれぞれのリリースブランチを作成しています。

```console
$ git push origin HEAD:release/oem-a
$ git push origin HEAD:release/oem-b
...
$ git push origin HEAD:release/oem-z
```

### テンプレート同期設定

このような構成でGitリポジトリとリリースブランチを作成したら、Protonと同期させていきます。

https://docs.aws.amazon.com/ja_jp/proton/latest/userguide/create-template-sync.html

GitリポジトリはAWS CodeStarでリンクしておくことで、Protonのテンプレート同期設定の画面でGitHubのリポジトリ一覧から簡単に選択できるようになります。
リポジトリを選択したら、「ブランチ」で「`release/oem-a`」を選択し、「サブディレクトリ」の項目に「`/oem-a`」のように入力します。設定を更新してしばらくすると同期が完了し、新しいバージョンとして登録されます。

## インフラ環境を起動

テンプレートの用意ができたら、あとは好きなだけインフラ環境を起動できます。

### 環境

環境はこのようなパラメーターから好きな構成を選択可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64098/a342abb3-3302-4e53-8627-a9432218496d.png)

| パラメーター | 設定値/例 |
|:-:|:-:|
| project_base_name | myproject |
| project_short_name | oem_a |
| service_environment | prod: 本番用<br>stg: ステージ用 |
| service_certificate_arn | Certificate ManagerのArn |
| has_elastic_ip | Yes: AZ毎のNATGatewayと固定IP<br>No: なし |
| db_engine_mode | provisioned/serverless_v2 |
| provisioned_db_instance_class | db.t4g.medium/large/2xlargeなど<br>※EngineModeがprovisionedのときのみ |
| serverless_db_capacity | autopause/low/middle/highなど<br>※EngineModeがserverless_v2のときのみ |
| container_insights | enabled: 主にprodのとき<br>disabled: stgのとき 
| deletion_protection | enabled: 主にprodのとき<br>disabled: stgのとき |

### サービスインスタンス

サービスインスタンス（主にFargateタスク）はこのようなパラメーターで構成を選択可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64098/7e2ca0bb-d8a0-449f-ae6d-1fabfdbd5b7a.png)

| パラメーター | 設定値/例 |
|:-:|:-:|
| service_domain | ドメインを記入 |
| task_size | small/medium/large |
| auto_scalling_***_capacity | スケーリング値 |
| image | ※最初は空欄で起動。CI/CDから自動的にセットされる  |
| log_level | debug/info/.../fatal（RailsのENV） |
| in_maintenance | No: セットされたimageでアプリケーションタスクを起動<br>Yes: プレーンなNginxのimageだけのタスクを起動 |

### 起動の手順

1. まずは環境の方を起動します。RDSやElastiCacheの起動に１５〜２０分かかるため、気長に待ちます。
1. 環境の起動が完了したら、サービスインスタンスとパイプラインを起動します。デプロイするOEMのリポジトリとブランチを選択し、パラメーターに必要事項を入力し、「in_maintenance」を「Yes」にした状態で起動します。
1. サービスの起動が終わると、パイプラインがイメージのビルドを始めます。ビルドを待っている間はアプリケーションのドメインにはNginxのデフォルト画面が表示されています。
1. パイプラインのビルドが終わると、パイプラインのデプロイステップからAWS CLIでProtonの更新を行い、インスタンスの「image」を自動的に変更します。
1. イメージが更新されたことを確認後、「in_maintenance」を「No」にしてサービスインスタンス構成を変更します。変更するとFargateのデプロイが進行し、アプリケーションのデプロイが完了します。

これでアプリケーションの起動とCI/CDの設定が完了です。
これ以降は指定したブランチにpushがある度にCI/CDが動いてくれます。

テンプレート側でパイプラインの進行状況毎のSlack通知も設定しているため、ビルドの成功・失敗などはSlack通知で確認することもできます。

## インフラ管理・インフラアップデート

日常的なインフラ管理と定期/不定期なインフラアップデートは以下の様なポリシーで運用しています。
無理して全てをIaCせず、AWSコンソールからポチポチで変更できる利便性も最大限生かすようにしています。

- アプリケーションのスケールはProtonのパラメーターで変更可能
  - Aurora ServerlessのACUを「autopause: 0~0.5ACU」「low: 1~2ACU」「middle: 1~4ACU」・・・などとパラメーター化してあるため、必要に応じてProtonの画面から変更可能
  - 同様にFargateのCPU・メモリの組み合わせやタスク数もパラメーター化してあるため、いつでも画面ポチで変更可能
- パラメーター化するよりもAWSコンソール上で変更した方が便利なものについてはAWSコンソール上から好きに変更可能
  - ALBの固定レスポンスを返すルール
  - Lambdaのコード
  - WAFのIPSet
  - SecretManagerのSecret値（証明書テキストなど）
  - パイプラインに紐づけるGitHubリポジトリ・ブランチ（一時的に別ブランチを設定したいときなど）
  - など
- アプリケーションへの特権アクセスを一時的に許可可能
  - ECS Exec用のパラメーターをONにすることで、ECS Execが有効になったタスクが起動する。ロールを適切に編集すれば特定IAMユーザーだけECS Execを利用可能になる
- テンプレートの更新を反映してもダウンタイムが発生しないアップデート類は通常通りテンプレートを反映してアップデートを行う
- AWSコンソール側に無停止アップデートのボタンが出ている場合や、メンテナンスウィンドウで自動的に更新される方が簡単な場合はそれに従い、アップデートが完了してからテンプレートを合わせる（Proton上で辻褄が合うようにテンプレートを更新できれば問題なし）

このようなポリシーにしておくことで、インフラの管理はProton上とAWSコンソール上のみで行えるようになり、インフラの管理にローカル環境の構築やAWSの権限設定などが一切不要になっています。
テンプレートリポジトリはローカルでも編集しますが、ただのYAMLだけなので何かしらのエディタさえあれば問題ありません。（GitHub上の鉛筆ボタンから直接編集してもOKだし、CodeSpacesからでもOK）

### コスト管理

OEM毎のコスト管理ができるよう、各リソースにコストタグを設定しています。
各値はProton起動時に入力したパラメーターから自動的に設定されます。

| コストタグ | 設定値/例 |
|:-:|:-:|
| `Cost:Project` | myproject |
| `Cost:Service` | oem_a |
| `Cost:Env` | staging/production |

# おわり

いかがでしたでしょうか？
まったく話題になっていないAWS Protonの実例をご紹介しました。

ググってもTwitterで検索しても一切話題になっておらず、開発も止まっており、近いうちにサービス終了告知が来る気しかしていないですが、ご興味持たれましたら是非ご利用ください。 :smile: