EC2 Macインスタンスの構築と運用について

## はじめに
この記事は [Cocone Advent Calendar 2023](https://qiita.com/advent-calendar/2023/cocone) 16日目の記事です

ココネ株式会社でサービスインフラエンジニアを担当しているyamadaです
主にAWSやGCP,オンプレミスのインフラ設計、構築、運用を行っております

あるサービスで新規システムを導入することになり、システムの一部でMacOSを使用したいとプログラマから要望が出ました
なかなか珍しい要件で過去事例は無かったですが、オンプレミスのMacをシステムに導入するのは物理的なリスクや課題も多いと判断し
EC2のMacインスタンスを採用することになりました

導入経験がなく、ネット上にも情報が少なく苦戦したところが多かったので
今回はEC2 Macインスタンスの構築手順や実際の運用で得られたノウハウ等をまとめたいと思います

## 対象者
この記事は下記のような方を対象にしています
* サーバー構築・運用経験がある
* AWSでEC2インスタンスを作成したことがある
* LinuxベースのEC2インスタンスをある程度運用したことがある

EC2 Macインスタンス特有の内容を取り上げていますので
LinuxベースのEC2インスタンスでも共通するような手順は割愛していることがあります

## AWS EC2 Macインスタンスについて
EC2 MacインスタンスはEC2の作成のみで運用することは出来ません
事前にDedicated Host(専有ホスト)を確保する必要があります
AWSの仮想化基盤技術であるAWS Nitro Systemにより
データセンター上のMac Mini筐体を物理ホストに、MacOSを仮想化してEC2インスタンスとして動作させているようです
また、ストレージはEBSを使用しています

そのため物理ホストに障害が発生したら新たにDedicated Hostを確保しそこにインスタンスを移動できますし
ディスクが不足したらコンソールから簡単に拡張もできます
参考： https://aws.amazon.com/jp/ec2/instance-types/mac/

### インスタンスタイプについて
2023年12月現在、Macインスタンスは以下のリージョンで使用することが出来ます

:::note info
Intel Macインスタンス
**mac1.metal (Intel Core i7, 12vCPU, 32GBメモリ)**
インド、ストックホルム、ロンドン、アイルランド、ソウル、東京、シンガポール、シドニー、フランクフルト、北バージニア、オハイオ、オレゴン
:::

:::note info
M1 Macインスタンス
**mac2.metal (M1, 12vCPU, 16GBメモリ)**
アイルランド、シンガポール、北バージニア、オハイオ、オレゴン
:::
:::note info
M2 Macインスタンス
**mac2-m2.metal (M2, 8vCPU, 24GBメモリ)**
北バージニア、オハイオ、オレゴン
:::
:::note info
M2 Pro Macインスタンス
**mac2-m2pro.metal (M2 Pro, 12vCPU, 32GBメモリ)**
オハイオ、オレゴン
:::

今回は東京リージョンで運用する必要があったのと
システムの仕様上Intel CPUが必要だったためmac1.metalを選択しました

### 料金について
EC2 Macインスタンスの稼働時間ではなく**Dedicated Hostの稼働時間**に料金が発生します
使わない間にEC2のみ停止しても料金は発生し続けるので要注意です
（本記事後半にコスト削減のための一時停止・復旧手順を載せています）

リージョンごとに費用が変わるので詳細は割愛します
（参考： https://aws.amazon.com/jp/ec2/dedicated-hosts/pricing/ ）

東京リージョンでmac1.metalを使う場合、大体月額780ドルほどかかります(1.083USD/時間)
最近は円安もあるので1ヶ月で10万円ほどですね…

注意点として、Dedicated Hostを確保したら**24時間分の割当料金が必ず発生します**
また24時間はHostを削除することが出来ません
検証のため数時間だけ利用したい場合でも最低24時間分の料金がかかりますので
計画的に作業を進めたいところです

## Macインスタンスの構築について
### Dedicated Hostの確保
EC2 → インスタンス → Dedicated Hosts → Dedicated Hostsを割り当て
<img width="600" alt="Dedicated_001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/1d649d26-4978-b30a-de52-b9a3904ea51b.png">
<img width="600" alt="Dedicated_002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/cd1bc1d2-ad3e-038e-dd47-8344e70fd9eb.png">

>名前タグ：任意
>インスタンスファミリー：mac1
>インスタンスタイプ：mac1.metal
>アベイラビリティーゾーン：任意
>インスタンスの自動配置：なし
>ホストの復旧：なし
>ホストのメンテナンス：なし(チェックを外す)
>数量：1
>タグ：任意

状態がPendingからAvailableになったらインスタンスの構築が可能
<img width="600" alt="Dedicated_003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/f787a0bb-cb24-44cd-7a04-e627565336c2.png">

#### (補足)Dedicated Hostの復旧、メンテ機能について
LinuxベースのEC2インスタンスをDedicated Hostで稼働させる場合
物理障害に備えてインスタンスの再配置、Hostの復旧、Hostのメンテナンスを自動化出来ますが
Macインスタンスの場合はいずれも構築時点では対象外でした
物理障害が発生したら手作業でインスタンスを別のDedicated Hostに移動させるなどの
対応が必要になってきますのでサービス影響を考慮した設計が必要になります

今回のサービスではAWSのApplication Load BalancerでMacインスタンスを冗長化し
新規システムをマイクロサービスとしてメインシステムから分離することで障害発生時のリスクを減らしました

### Macインスタンスを作成
EC2 → インスタンス → インスタンスを起動でEC2インスタンスを作成
<img width="600" alt="ec2_001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/54e9841d-fcca-505b-06ba-6f1051e09042.png">
>名前：任意
>Amazon マシンイメージ：macOS ※本記事ではVenture 13.6.1を選択
>インスタンスタイプ：mac1.metal
>キーペア名：任意
>サブネット：private-a or private-c
>パブリックIP：無効
>既存のセキュリティグループを選択する：任意
>ストレージ：任意

**高度な詳細で以下の項目を設定**
<img width="600" alt="ec2_002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/0722c701-d334-dbf4-5934-7258508bf823.png">

※サブネットとパブリックIPについては運用されているVPCの構成にあわせて設定してください
普段運用されているEC2インスタンスと同じように作成いただければ問題ないと思います

弊社ではオフィスとVPC間にSiteToSiteVPNを構築しているため
プライベートサブネットにインスタンスを構築しパブリックIP無効にしています

インスタンス作成後、Dedicated Hostの画面に移動し確保したDedicated Hostをチェック
「実行中のインスタンス」で作成したインスタンスが紐づいていることを確認する
<img width="600" alt="ec2_003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/12f41f9b-6b47-cbf2-7005-40aa40646144.png">

#### OS設定
リモートデスクトップ出来るようにインスタンスにSSH接続して初期設定します
```
# ssh接続できるか確認 (初期状態ではec2-userでログイン)
$ ssh ec2-user@{割り当てられたIPアドレス}

# 後でリモートデスクトップ出来るようにパスワードを設定しておく
$ sudo passwd ec2-user

# Apple Remote Desktopエージェントを起動
$ sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart \
-activate -configure -access -on \
-restart -agent -privs -all
```
#### リモートデスクトップ接続
弊社ではオフィスとAWS VPC間にSiteToSiteVPNを構築しているため
プライベートIPアドレスでリモートデスクトップ接続可能ですが
通常はローカルポートフォワードの対応が必要になります
**(Macインスタンスではなく使っているPC端末上で実行してください)**
```
$ ssh -L 5900:localhost:5900　-i 秘密鍵 ec2-user@パブリックIP
```

ローカル環境がMacの場合はFinder → 移動 → サーバーに接続
windowsの場合は任意のvncクライアントソフトを使用
<img width="400" alt="remote-desktop-001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/efee658c-9092-4f2d-626a-5ae7f0359d3d.png">


```
URI： vnc://localhost:5900
ユーザー：ec2-user
パスワード：先程設定したパスワード
```
<img width="600" alt="remote-desktop-005.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/43093bbf-2feb-8070-e23e-e906980b991a.png">

実際の運用ではec2-userはリスクが高いので使用せず
リモートデスクトップ接続後に管理用のユーザーを作成してec2-userは削除しました
（ユーザー追加手順は通常のMacと同じなので割愛します）

## Macインスタンスの運用について
Macを動かすだけなら以上の手順で完了ですが
サービス運用するうえで以下のやり方は把握しておきたかったので調査・導入してみました
* ディスクサイズの変更
* サーバーのメトリクス監視
* サーバーを使わない間のコスト削減手法

### ディスクサイズの変更
#### AWS側の手順
インスタンスの詳細からストレージを確認しボリュームIDをチェック
<img width="600" alt="volume_change_001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/c1ff47c1-e141-1c3f-3f3a-980f71420114.png">

対象のボリュームに移動しアクション → ボリュームの変更
<img width="600" alt="volume_change_002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/869042fc-3ccf-448c-a9b0-e813e6b12af7.png">

任意のサイズに変更する
<img width="600" alt="volume_change_003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/6cac4c3d-84ab-36ae-e081-3402df515154.png">

ボリュームのステータスがoptimizingになったらMac側で拡張作業ができるようになります
<img width="600" alt="volume_change_004.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/819b79d4-eeb3-8ee1-e9dd-f36c45c1aab1.png">

**Macインスタンスが起動している場合、再起動かけないと反映されません**
EC2のマネジメントコンソール画面上から再起動を実施
（Linuxと違い20~30分以上待つ可能性がある）
再起動後、Macにsshログインしパーティション拡張コマンドを実行します

#### Mac側の手順
ディスク状況の確認
```
$ sudo diskutil list physical external
Password:
/dev/disk1 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *536.9 GB   disk1
   1:                        EFI EFI                     209.7 MB   disk1s1
   2:                 Apple_APFS Container disk2         214.5 GB   disk1s2
```
disk1が500GBになったがパーティションに反映されておらず200GBのまま

repairDiskを実行
```
$ sudo diskutil repairDisk disk1
 
$ sudo diskutil list physical external
/dev/disk1 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *536.9 GB   disk1
   1:                        EFI EFI                     209.7 MB   disk1s1
   2:                 Apple_APFS Container disk2         214.5 GB   disk1s2
                    (free space)                         322.1 GB   -
```
322GBのフリースペースが作られた

パーティションの再作成
```
$ sudo diskutil apfs resizeContainer disk1s2 0
 
$ sudo diskutil list physical external
/dev/disk1 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *536.9 GB   disk1
   1:                        EFI EFI                     209.7 MB   disk1s1
   2:                 Apple_APFS Container disk2         536.7 GB   disk1s2
```
パーティションが拡張されました
Linuxとは使用するコマンドが異なるので少しハマった点でした

### サーバーのメトリクス監視
メトリクス監視にはCloudWatchを採用しました
(zabbixも使えたのですがOSバージョンの問題で弊社では導入が難しかったため)

#### EC2 MacインスタンスにIAMロールを割り当てる
CloudWatchAgentAdminPolicyをアタッチしたIAMロールを作成して
Macインスタンスに付与します

IAM → ロール → ロールを作成
<img width="600" alt="role_001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/ecbbe532-d5a2-8464-0394-1bd5ef66f936.png">
<img width="600" alt="role_002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/d1456d2b-4742-584b-608a-4377176b3ea7.png">
<img width="600" alt="role_003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/f272b207-6a58-2931-4039-3afdcc4f3014.png">

EC2 → インスタンスからMacインスタンスをチェック → アクション → セキュリティ → IAMロールを変更
<img width="600" alt="role_004.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/9413a063-29cf-6cb4-c68e-9cc349a43a68.png">
<img width="600" alt="role_005.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/6287f38a-be15-9b53-1408-735f53779050.png">


#### CloudWatchAgentのインストール
MacインスタンスにSSH接続してCloudWatchエージェントのインストールを実施します
```
# Macの場合はS3経由でpkgファイルをダウンロード
$ cd ~/Desktop
$ curl -O https://s3.amazonaws.com/amazoncloudwatch-agent/darwin/amd64/latest/amazon-cloudwatch-agent.pkg
$ sudo installer -pkg ./amazon-cloudwatch-agent.pkg -target /
 
# amazon-cloudwatch-agent-config-wizardでエージェントの初期設定を行う
$ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
ウィザードの選択肢は基本デフォルトで進めてOKですが、システム設計に合わせて適宜選択してください
→ AWS system managerのパラメータストアに以下の名前で設定が保存される
  AmazonCloudWatch-darwin
 
# brewでcollectdをインストール
$ brew install collectd

# インストール時にpermissionエラーが出たら下記実行して改めてインストール
$ sudo chown -R "$USER":admin /usr/local
$ sudo chmod -R g+w /usr/local
$ brew install collectd

# もしcollectdが/usr/share/配下ではなく/usr/local/share/配下にインストールされた場合は
  AWSパラメータストアに移動し、AmazonCloudWatch-darwinのJsonファイルにcollectdのパスを追記する
---------
.....
"metrics_collected": {
            "collectd": {
                "metrics_aggregation_interval": 60,
                "collectd_typesdb":["/usr/local/share/collectd/types.db"]
            }
.....
---------
 
# cloudwatch agentを起動する
$ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:AmazonCloudWatch-darwin
```
CloudWatch → すべてのメトリクス → CWagent
ディスク使用率とメモリ使用率のグラフが生成されていればOK
※CPU使用率はAgent不要でEC2のメトリクスから取得可能です

公式ドキュメントだとcollectdが/usr/share/にインストールされる前提で書かれていたのですが
実際には/usr/local/share/にインストールされたためエージェントが起動できず苦戦しました…

### サーバーを使わない間のコスト削減手法
リリース後に暫くの間システムを停止して機能改修することになったのですが
再リリースまでサーバー環境は残しつつコストを抑える必要がありました
Macインスタンスを停止し、Dedicated Hostをリリースすることで
EC2の情報(ストレージ、IPアドレス、セキュリティ設定等)は保持しつつ
従量課金を最小限に抑えることにしました

#### Macインスタンスを停止
マネジメントコンソール上から対象のインスタンスを停止する
<img width="600" alt="ec2-stop.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/fe11fe95-0448-befe-617a-255b169aee82.png">

#### Dedicated Hsotの開放
インスタンスを停止後、**最大で200分待つ必要がある**

アクション → ホストをリリース
<img width="600" alt="dedicated_rerease.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/6fea0bc4-8d54-875e-044b-53dafd44cea5.png">

**状態がReleasedになっていればOK (しばらく待つとリストから消える)**

### Macインスタンス停止後の復旧手順
#### Dedicated Hostの確保
構築時と同じ手順でDedicated Hostを新規に確保し、確保したDedicated Hostを指定して
EC2インスタンスを起動する流れになります
(Dedicated Host確保の手順は本記事の構築手順を参考にしてください)

#### Macインスタンスを起動
対象のMacインスタンスにチェックを入れて
アクション → インスタンスの設定 → インスタンスの配置の変更
<img width="600" alt="instance-saihaichi.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/687df5e6-648d-d7bc-ddbb-7c8754032d8e.png">

ターゲットの専有ホストを新たに確保したDedicated HostのIDに変更
<img width="600" alt="instance-saihaichi002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3621399/115cc0e6-e052-fc3f-0410-5c88e6e65949.png">

あとはいつも通り起動し、Dedicated Hostsの画面で実行中のインスタンスに紐づいていることを確認する
**ストレージ、IPアドレス、セキュリティ設定等に変化がないことを確認できればOK**

## まとめ
AWS EC2 Macインスタンスの構築と運用についてまとめました
実際に使ってみた感触としてはシステム領域を自由に触れなかったり
Mac独自のコマンドを理解する必要がありますしLinuxと比較すると動作が重たかったりと
運用で苦戦する場面は多かったですが、ノウハウを蓄積できて良い経験になったと感じました

今回まとめた情報をご活用いただいて、EC2 Macインスタンスを導入するきっかけになれば幸いです