🌐

Next.js(&TypeScript)アプリをEC2にデプロイする

2024/05/06に公開

参考になる記事は、いくつかありましたが、少々詰まる部分があったので、備忘録も兼ねてまとめていきます。

誤りなどがあれば、指摘頂けると非常に嬉しいです!

対象

  • Next.jsで作ったアプリをとりあえず、EC2にデプロイしてみたい

本記事で触れないこと

  • Next.jsでのアプリ作成手順。リポジトリ作成
  • EC2インスタンス作成方法

開発環境

  • Mac
  • Next.js v14.2.3
  • TypeScript v5.4.5
    (筆者がMacを使用しているので、説明中のコマンドで、Macのみのコマンドしか説明されていない可能性があります。ご了承ください。)

1.アプリ作成

2. EC2インスタンス作成

略。
私は下記記事を参考にさせて頂きました。
https://qiita.com/y_kato_eng/items/7f7878a40a23412e5fc2
基本的には、記事の通りに進めれば問題ないですが、一部異なる部分があったので、補足します。

  1. マシンイメージのデフォルトが記事と少し異なりますが、画像のものを使用しました。
  2. キーペアーはなかったので、新しいキーペアを作成しました。
    後でSSHを行うので、画像のような設定にしています。
    「キーペアを作成」を押下すると、自動的にダウンロードされます。
    ダウンロードされたキーペアは、適切をディレクトリに移動しておきます。(自動的に移動されるケースがあるかもです)
    (macであれば、~/.ssh,windowsであれば、C:\Users\YourUserName\.ssh)

3. デプロイ

SSHを使用して、EC2インスタンスに自分の作ったアプリをデプロイしていきます。

3-1. SSHでEC2インスタンスに接続

$ ssh -i 保存したキーペアのpath ec2-user@パブリックIPアドレス

保存したキーペアのpathは、2-2で作成しダウンロードしたファイルのpathになります。
私はMacなので、pathは ~/.ssh/keyname.pemとなります。
パブリックIPアドレスは、

  1. AWSコンソールトップ
  2. EC2と検索し「EC2」を選択
  3. 左のサイドバーに表示されている「インスタンス」を選択
  4. 選択後、右上に表示されているリージョンを先ほど作成した時に選択したリージョンを選択し、パブリック IPv4 アドレスという項目になります。

実行後、初めて接続するので下記のような警告が表示されますが、特に問題はないので yesでEnter

The authenticity of host 'ip adress' can't be established.
...
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

その後、下記のようなエラーになり、接続に失敗することがあります。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/Users/username/.ssh/keyname.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/Users/username/.ssh/keyname.pem": bad permissions
ec2-user@3.87.129.193: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

これは、プライベートkeyファイル(キーペア作成で生成されたファイル)の権限が他のユーザも読み書きできる状態で権限が緩すぎることによるエラーです。
下記を実行し、所有者のみ。に権限を変更します。
その後再度SSH接続を行い、鳥みたいなのが表示されれば、SSHが成功になります。

$ chmod 600 ~/.ssh/keyname.pem
$ ssh -i ~/.ssh/keyname.pem ec2-user@ipアドレス

   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'

3-2. 必要なソフトウェアをインストール/アップデート

(SSH接続中前提)
アプリをEC2で動かすために必要なソフトウェアをインストールします。
nodeとnpmが必要ですが、下記のようにまだインストールされていないので、行っていきます。

[ec2-user@ip-adress ~]$ node -v
-bash: node: command not found
[ec2-user@ip-adress ~]$ npm -v
-bash: npm: command not found

3-2-1. EC2インスタンスのOS(Linux)のパッケージを最新にアップデート

[ec2-user@ip-adress ~]$ sudo yum update -y

Last metadata expiration check: 1:15:10 ago on Mon May  6 03:49:25 2024.
Dependencies resolved.
Nothing to do.
Complete!

3-2-2. npm/node インストール

sudo su - rootで、rootユーザに切り替えてインストールを行います。
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - yum install -y nodejsインストールするコマンド。コマンドは、こちらに記載されています。
今回は、アプリのnodeバージョンと同じ20.xを使用しました。

[ec2-user@ip-adress ~]$ sudo su - root
[root@ip-adress ~]# curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
yum install -y nodejs

たくさんログでます

(ちなみに、-yというオプションは、yesの略で、更新中によく聞かれたりする「yes」or「no」の入力が必要になる場合、自動的にyesと回答してくれるオプション)

正常にインストールされていたか確認(ctrl + dで現在のユーザに戻ってから)

[ec2-user@ip-adress ~]$ node -v
v20.12.2
[ec2-user@ip-adress ~]$ npm -v
10.5.0

3-2-3. git インストール

アプリのリポジトリをクローンするために、EC2上でgitをインストールします。

[ec2-user@ip-adress ~]$ sudo yum -y install git

ログがたくさん出ます
...Complete!

正常にインストールされていることを確認

[ec2-user@ip-adress ~]$ git -v
git version 2.40.1

3-3. アプリのリポジトリをクローン

SSHでアプリのリポジトリをEC2にクローンします。
既に生成済みkeyをコピーしてきて利用する方法もありますが、今回は新しく生成する方法で説明させていただきます。

まず、SSHキーを生成します。(SSH接続中が前提)
your_email@example.comはGitHub上に設定しているemailになります。(Settings > email)
3回選択が求められますが、全てEnter。

[ec2-user@ip-adress ~]$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

Generating public/private rsa key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 

正常に生成できているか確認します。
ssh-rsaから始まりemailで終わるkeyが表示されていれば、成功です。keyは次必要になるので、コピーしておきます。

[ec2-user@ip-adress ~]$ cat ~/.ssh/id_rsa.pub

ssh-rsa ...== your_email@example.com

その後、GitHub>Settings>SSH and GPG keys を選択し、右上の「New SSH key」を選択。

titleは任意(プロジェクトやアプリ・リポジトリ名など)
Key typeはデフォルトのまま(Authenticaton Key)
Keyに先ほどコピーしたkeyをペーストします。
そして、「Add SSH key」を押下。

対象アプリのクローンしていきます。
GitHubで対象アプリを選択し、Code>SSHを選択し、コピー
クローンする

[ec2-user@ip-adress ~]$ git clone git@github.com:yourname/リポジトリ名.git
Cloning into 'リポジトリ名'...
remote: Enumerating objects: 18, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (18/18), done.
Receiving objects: 100% (18/18), 30.45 KiB | 15.23 MiB/s, done.
remote: Total 18 (delta 0), reused 18 (delta 0), pack-reused 0
[ec2-user@ip-adress ~]$ ls
ec2-deploy-sample

実行後、初回クローンで確認が求められるかもですが、yesでEnter。

Cloning into 'リポジトリ名'...
...
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

正常にクローンできているか確認
リポジトリ名が表示されていれば、成功です。

[ec2-user@ip-adress ~]$ ls
リポジトリ名

3-4. リバースプロキシの設定

ここが若干わかりづらいです。。

Next.jsアプリケーションは、デフォルトで3000番ポート(意図的に4000とかに変更している場合は、4000番)でアプリを起動(=3000番でリクエストを受け取る)しますが、公開のアプリは、80(http)/443(https)番ポートでリクエストが行われるため、今のままだとNext.jsで作成したアプリケーションを表示することができません。

そのため、80/443番ポートでリクエストされた時に、3000番ポートで起動(=受け付けている)しているNext.jsアプリケーションをリクエストを転送する仲介役が必要になります。
それがリバースプロキシで、今回はNginxを使用します。

イメージ

ルートに移動し、Nginxをインストール

[ec2-user@ip-adress ~]$ sudo su - root
[root@ip-adress ~]# yum install nginx -y

...
Complete!

Nginxの設定を変更する
前述の通り、Next.jsアプリケーションのポートに転送されるように設定していきます。

[root@ip-adress ~]# sudo vi /etc/nginx/nginx.conf

// vimの説明: `i`でインサートモードにし、入力し、`etc`押した後`:wq`で保存して終了)
http {
 ...
 server {
   ...
   // 下記を追加
   location / {
    proxy_pass http://localhost:3000;
   }
  ...
 }
 ...
}

再起動

[ec2-user@ip-adress ~]$ sudo systemctl restart nginx

実行後、サイトに(パブリックIPアドレス)アクセスした時に、画像のように表示されていれば、OKです。

3-5. アプリをビルド/起動

ここまで来ればあとは、起動するだけです!

[ec2-user@ip-adress ~]$ cd リポジトリ名

プロジェクトのパッケージのインストールに必要
[ec2-user@ip-adress ~]$ npm install
added 138 packages, and audited 139 packages in 15s
31 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

[ec2-user@ip-adress ~]$ npm run build
> ec2-deploy-sample@0.1.0 build
> next build
...

[ec2-user@ip-adress ~]$ npm run start

サイト(パブリックIPアドレス)にアクセス

これで、Next.jsアプリケーションをEC2にデプロイ。は完了です!


ただ、これだけだとSSH接続中のみしか動作しません。。

SSH接続を中断したとして、動作させるには、プロセス管理を行うものを導入する必要があり、今回はpm2を使用した方法で説明します。

ルートに移動し、インストール

[ec2-user@ip-adres ~]$ sudo su - root
[root@ip-adress ~]# npm install -g pm2

npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.

added 162 packages in 13s

14 packages are looking for funding
  run `npm fund` for details
npm notice 
npm notice New minor version of npm available! 10.5.0 -> 10.7.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.7.0
npm notice Run npm install -g npm@10.7.0 to update!
npm notice

正常にインストールされたか確認

[root@ip-adress ~]# npm list -g pm2
/usr/lib
└── pm2@5.3.1

ctrl + d でルートを抜けて、リポジトリに移動

[ec2-user@ip-adress ~]$ cd リポジトリ名

// npm run startではなく、下記を実行
// `name`は、任意でプロセス管理の状況を確認するための名前のようなものになります。
pm2 start npm --name "app-name" -- start
[ec2-user@ip-adress リポジトリ名]$ pm2 start npm --name "name" -- start


                        -------------

__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
 _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
  _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
   _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
    _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
     _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
      _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
       _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
        _\///______________\///______________\///__\///////////////__
...

その後、SSHを中断し、サイトにアクセスした時に正常にアプリが表示されていれば、成功です!

ちなみに、プロセスの状況は下記コマンドで確認できます。

// アプリケーションのステータス確認
$ pm2 status "app-name"
// ログ確認
$ pm2 status "app-name"
// 停止
$ pm2 stop "app-name" 停止

終わりに

普段の業務では、CircleCiなどCI/CDツールを使用しているので、マージするだけでこれだけの作業を行われている。と思うと、めちゃクチャ便利なツールだなぁと実感しました。

初の投稿でしたが、結構なボリュームになってしまいました。。
最後まで読んでいただき、ありがとうございました!

参考

https://qiita.com/tag1216/items/5d06bad7468f731f590e
https://qiita.com/longtime1116/items/18553e43bfb44cbc9d81
https://www.youtube.com/watch?v=npv0fSQ5stM
https://yutag.net/aws/ssh-error-key/

Discussion