EC2に構築したNode.jsアプリの起動時間をスケジューリングする

2024/09/04に公開

社内サービス(EC2とNode.js)を作ったはいいのですが、業務時間外もサービスを起動したままだと、無駄なコストとなってしまう場合が、多々あると思います。そんな時のために、EC2インスタンスの起動時間をスケジューリングして、EC2インスタンスの起動時にNode.jsアプリを自動起動させる方法をメモりました。

全体的なイメージはこんな感じになります。

AmazonEventBridgeでEC2インスタンスを自動起動•自動停止

まず、EC2インスタンスの起動時間をスケジューリングします。EC2の自動起動・停止には色々な方法が考えられますが、今回は、AmazonEventBridgeのスケジューラーを使って実装しました。

EventBridge用IAMロールの作成

EventBridgeがSystemsManagerを操作するための権限、を与えるIAMロールを作成します。

  1. IAM > ロール > ロールを作成 を選択
  2. 信頼されたエンティティを選択にて カスタム信頼ポリシー を選択
  3. カスタム信頼ポリシーを入力
   {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
  1. 許可を追加にて、許可ポリシー AmazonEC2FullAccess を付与
  2. 名前、確認、および作成にて、ロール名を設定。今回はわかりやすく ec2-start-stop-role とする。
  3. ロールを作成 でEventBridge用IAMロール作成完了です。

EventBridge起動用ルールの作成

ここからはEventBridgeで、EC2自動起動用のルールを作成します。今回は、月~金曜日の10:00にEC2を起動するルールを想定しています。

  1. EventBridge > ルール > ルールを作成 を選択
  2. ルールの詳細を定義
    • 名前 : ec2-start
    • ルールタイプ : スケジュール
  3. スケジュールの詳細の指定
    • 頻度 : 定期的なスケジュール
    • スケジュールの種類 : cronベースのスケジュール
    • cron式 : 010 時間 ? 日付 *Mon-Fri 曜日 *
    • フレックス : オフ
  4. ターゲットの選択
    • ターゲットAPI : StartInstances
      入力に対象となるEC2インスタンスのIDを記載します
{
  "InstanceIds": [
    "i-****",
    "i-****"
  ]
}


6. 設定
+ アクセス許可 : 既存のロールを使用 を選択し、先ほど作成したIAMロールを選択

7. スケジュールを作成 を選択して完了です。

同様の手順で停止時のルールを作成します。これで、EC2インスタンスが自動起動・停止するようになりました。試しにアプリのURLにアクセスしてみると、停止時には504エラー、起動時には502エラーが返されるようになりました。

EC2インスタンス起動時にsystemdのサービスファイルでNode.jsアプリを起動

EC2インスタンスのスケジューリングの設定は完了しましたが、そのままだと、EC2インスタンスの起動時にNode.jsアプリは起動せず、502エラー等が返されてしまいます。サービス起動時に実行したい処理を記述するファイルとして、rc.localがありますが、仕様として少し古いようです。CentOS7からサービス起動の管理がrc.localから、systemdが採用されているようで、rc.localファイルに自動実行のコマンド等を書いても、そのままだと動かないようです。rc.localを使い続ける方法もあるようですが、今回は仕様にのっとって、systemdを採用していきます。

  1. まず、SSHで、ログインしてrootに切り替えます。環境は、AmazonLinux2です。
// SSHログイン
ssh -i ~/.ssh/***.pem ec2-user@00.00.00.00
// root切り替え
sudo -i
  1. /etc/systemd/systemの下に、拡張子.serviceのテキストファイルを作り、以下の内容を書きます。
// サービスファイルの新規作成
vi /etc/systemd/system/mydaemon.service

▼ サービスファイル(mydaemon.service)

[Unit]
Type=simple // プロセスの起動タイプ。
Description=【詳細】 // サービスの説明。

[Service]
WorkingDirectory=/home/ec2-user/***/ // 実行したいプログラムのディレクトリ
ExecStart=/root/.nvm/versions/node/v16.9.1/bin/node server.js // 実行したいプログラム
Restart=always // プロセスが落ちても自動的に復帰

[Install]
WantedBy=multi-user.target // どのターゲットで動かすか(マルチユーザー)

サービスファイルについてもう少し詳しく知りたい方
https://qiita.com/bluesDD/items/eaf14408d635ffd55a18

  1. 作成したサービスファイルの自動起動を有効化し、一覧表示して、作成したサービスがenabledになっているのを確認します。
// 自動起動有効化
systemctl enable mydaemon.service
// 一覧表示
systemctl list-unit-files

...
microcode.service                             enabled 
mydaemon.service                              enabled 
nfs-blkmap.service                            disabled
...

試しに、サービスを開始すると、プログラムが実行されるので、期待通りNode.jsアプリが動いているか確認できます。

// サービス開始
systemctl start mydaemon.service

サービスファイルを書き換えた時には、再読み込みを行います。

// サービスファイル再読み込み
systemctl daemon-reload
  1. サービスを再起動して、Node.jsアプリが動いているか確認できれば完了です。
// 再起動
reboot

これで、スケジューリング通りに、EC2インスタンスが起動した時、Node.jsアプリが自動で起動するようになりました。

Discussion