FlutterのCI/CD環境のAWS EC2(Mac)環境に構築してパイプライン実行速度を改善する

# 背景
現在Flutterを用いたモバイルアプリの開発を行なっているが、その中でGitlabによるCI/CDを構築している。
こちらを運用していくにあたって1点問題が発生した。
それは、メンバーの増加によって相対的にCI/CDのビルド端末が不足し、pipelineの滞留が発生したことだ。

pipeline待ちがDeveloperのストレスになったり、作業効率の低下を招いたりすることは既に広く知られてはいるため本件を早急に解決する必要がある。
基本的には、CI/CDのRunnerとなるMacを購入して、処理台数を増やすことで、本件を解決することができる。
しかし、この問題の解決には以下のような課題がある。

- Mac端末の購入にはリードタイムが必要であり、解決まで時間がかかる。
- 新規で購入した端末では新たに環境構築が必要であり、人的リソースがかかる。
- メンバーの増加は一時的なものであり、減少した際はまた今の台数で足りる見込みのため、一時的な対応としては予算的に少し勿体無い。
- （そもそも実機端末を多く保管するためには広いオフィスが必要であり、またマシンをメンテナンスするためには都度出社が必要になる。）

これらの課題を解決するため、
「一時的なリソース増強」という点からクラウドサービスであるAWSのEC2(MacOS)を利用して、EC2のインスタンスをCI/CDのRunnerに設定することで、
上記のリソース問題を解決しようと試みる。

# 構築手順


## モバイルアプリケーション側の準備

こちらは既に準備済みで、以下の動作をするようになっている。
- RemoteにブランチがPushされた時に、Unitテストである`flutter test`を実行する。
- MergeRequestが更新されたときに、簡易なUIテストとしての`smoke test`を実行する。
- `git tag push` をしたときにローカルでiOSとAndriodの資材(ipa/app_bundle)を作成し、その資材をVisualStudioAppCenterにUploadしてテストユーザに配信する。

上記の設定が実装されたgitlab-ci.yamlを以下に示す。
（実際のシェルスクリプトの中身は割愛とさせてください。）
```.gitlab-ci.yml
before_script:
  - flutter pub get

stages:
  - build
  - test
  - deploy
  - schedule

unitTest:
  stage: build
  tags:
    - mac-ci-runner
  script:
    - flutter test
  except:
    - tags
    - merge_requests


mergeRequestSmokeTest:
  stage: build
  tags:
    - mac-ci-runner
  script:
    - ./smoke-test.sh
  only:
    - merge_requests

deployment:
  stage: build
  tags:
    - mac-ci-runner
  only:
    - tags
  script:
    - ./deploy-pipeline-for-artifacts.sh
```

## AWS側の準備

### MacOSインスタンスの作成・立ち上げ
まずはMacOS向けには`Dedicated Hosts`が必要なため、こちらのリソース申請を実施します。（この段階ではまだ課金は発生しません）
参考：[WindowsでiOS向けFlutter開発環境を用意する](https://zenn.dev/winteryukky/articles/2453a109825860)

申請が完了すると以下のような画面になります
![スクリーンショット 2023-06-01 10.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/3a308b32-58ef-6b8d-8da5-ac8cdb4c97df.png)

さて、それでは実際にインスタンス上に環境構築をしていきます。
ここから料金がかかるので注意です！

まず、申請が通ったら`Dedicated Hosts`の割り当てを実施します。
![スクリーンショット 2023-06-01 10.49.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/b59b4095-8718-1a92-f08e-cf19484a11eb.png)

割り当てが完了すると以下のような画面になります。
![スクリーンショット 2023-06-01 10.50.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/69d91076-8320-7c98-0da4-3087615bdae6.png)

その後、アクション＞インスタンスをホスト で実行します。

![スクリーンショット 2023-06-01 10.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/7be3a3f5-3981-da39-bd87-edc4ad988b26.png)

インスタンスの作成時にMacOSを指定します。ここでOSを指定するのですが、今使っている環境と差分が出ないように注意してください。
![スクリーンショット 2023-06-01 10.54.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/fd7cf6b2-ea23-9729-3961-3e238a707d7e.png)

インスタンスが作成されるとこのように完了の表示がされます。
![スクリーンショット 2023-06-01 11.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/08c3bcd4-ae92-1201-1a10-01716c1007e1.png)


インスタンスを立ち上げたら、SSHクライアントでインスタンスに接続します
```bash
$ ssh -i "key.pem" ec2-user@ec2-44-198-162-195.compute-1.amazonaws.com
The authenticity of host 'ec2-44-198-162-195.compute-1.amazonaws.com (44.198.162.195)' can't be established.
ED25519 key fingerprint is SHA256:95QOrDftB2aLiWO4FzNsxAxCV7iAMCz+HzRgVBWBxJY.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'ec2-44-198-162-195.compute-1.amazonaws.com' (ED25519) to the list of known hosts.

    ┌───┬──┐   __|  __|_  )
    │ ╷╭╯╷ │   _|  (     /
    │  └╮  │  ___|\___|___|
    │ ╰─┼╯ │  Amazon EC2
    └───┴──┘  macOS Monterey 12.6.3

ec2-user@ip-172-31-1-72 ~ % 
```

接続が完了したら以下の手順でVNC接続してリモートデスクトップで操作できるようにします
参考：https://tech.arms-soft.co.jp/entry/2020/12/09/090000

自分はその時、以下のエラーが出てきてうまく接続できませんでしたが、以下の動画を参考にして実施することで事象が解消しました
```bash
Screen recording might be disabled. Screen Sharing or Remote Management must be enabled from System Preferences or via MDM.
Screen control might be disabled. Screen Sharing or Remote Management must be enabled from System Preferences or via MDM.
```
参考：https://www.youtube.com/watch?v=FtU2_bBfSgM

無事立ち上がりました。
![スクリーンショット 2023-06-01 14.33.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/82869c2e-fde3-0461-03c5-d73e0d38f504.png)



### MacOSインスタンス上での環境構築
ここからは通常のFlutterの環境構築をしていきます。
この記事を見ている方は通常の環境構築は問題ないかと思いますが、念の為参考の動画だけ貼っておきます。
参考：https://www.youtube.com/watch?v=d0Gwro3j81g


今回の環境の気をつける点で言えばポイントは以下です。
- 開発端末と同様のOS、IDEのバージョン、Shellの統一(zsh,bash)などを揃える。
- Storageが限られるため、不要なソフトなどは入れない。
- リモートデスクトップなので動作が少しもっさりしているため、何かと並行して作業した方がストレス少なく作業できる




### Gitlab CI Runnerとしての登録
上記が完了したらGitLab CIへのRunnerとして登録していきます。
こちらも通常の実機Macと同様の手順のため詳細な手順は、記事にて割愛させていただきます。
参考：https://qiita.com/takamii228/items/64fd6879e6acce845980

※Runner環境を作成するときは、`bash`での作成をすること

結果、以下のようにGitLab側にRunnerとして登録されました。
![スクリーンショット 2023-06-01 14.55.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/a9951375-6ed4-c079-af6f-abec34226ebe.png)

EC2側もちゃんと動いてますね
![スクリーンショット 2023-06-01 15.01.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/967462d4-0707-c24b-bb27-e6999619855f.png)

これで完了です。


### AMIを用いた開発環境のsnapshotの保存
ここまで実施できたら、私は実際にはやっていませんが、AMIを用いて開発環境のsnapshotを保存すると良いと思います。
Runnerを複数作成したりするのに有効に活用できると思います。


## 実行速度
| 環境 | UnitTest | E2E Test |
| :---: | :---: | :---: |
| 実機(M1 Pro 16GB) | 25s | 4m37s |
| EC2(M1 16GB) | 22s | 4m3s |

## 利用料金体系・メリット
この一番下のmac2が今回利用したものの料金になります。
0.65USD/h ですが、`Dedicated Hosts`のため、24時間の利用が必要となりますので、最低でも15.6USD。
現在の日本円に直すと2,174円となります。（2023年6月1日時点）
![スクリーンショット 2023-06-01 10.34.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555381/b9e42346-2de4-40f2-a59f-c9a6bed48fef.png)
参考：https://aws.amazon.com/jp/ec2/dedicated-hosts/pricing/

これを1ヶ月使用するとなると、2,174円*30日=65,220円/月

M2のMac mini メモリ16GBを購入しようと思うと140,800円なので、2ヶ月より長い期間使用しようと思ったらEC2よりもMac miniの実機を購入した方が費用的には安くなりますね。

# 結論


# 参考にした記事
- [WindowsでiOS向けFlutter開発環境を用意する](https://zenn.dev/winteryukky/articles/2453a109825860)
- [EC2 Mac インスタンスに入門してみた](https://speakerdeck.com/hisa9chi/ec2-mac-insutansuniru-men-sitemita?slide=17)
- [AWS EC2利用料金](https://aws.amazon.com/jp/ec2/dedicated-hosts/pricing/#On-Demand_Pricing)
- 実際に使用したCI/CDの内容はこちらを参考に構築しています[Flutterを使ったAndroid・iOSアプリ開発のCIパイプラインを構築する](https://takamii.hatenablog.com/entry/2020/04/26/134936)