Docker環境のWebアプリをAWS EC2にデプロイする手順

## 作成したアプリ
ふつうのTODOアプリみたいなアプリです。
特段すごい機能があるわけではないので、詳細は割愛します。


## 技術スタック
* **フロントエンド**: Vue3, TypeScript
* **バックエンド(Web API)**: Node.js + Express
* **DB**: PostgreSQL
* **開発環境**: docker(フロントエンド, バックエンド, DB)
* **サーバー**: EC2(AWS)


## デプロイの流れ
1. AWSアカウント作成〜EC2起動
1. ローカルのdockerコンテナ上でWebアプリを実装
1. ローカルのWebアプリのファイルをEC2へ転送
1. EC2でdocker環境を構築
1. EC2でのアプリ動作環境準備
1. デプロイ確認
1. まとめ

## AWSアカウント作成〜EC2起動
### 1. AWSアカウントの作成
AWSアカウントを作成し、マネジメントコンソールにログイン。

### 2. ネットワーク設定とEC2インスタンスの作成
#### VPCの作成
* AMI：Amazon Linux
* インスタンスタイプ：t2micro(無料枠)
* パブリック IP の自動割り当て：有効

#### サブネットの作成
* 作成したVPCに紐付け

#### インターネットゲートウェイの作成
* 作成したVPCにアタッチ

#### ルートテーブルの作成
* ルーティング追加（0.0.0.0/0 → 作成したインターネットゲートウェイ）
* 作成したサブネットに紐付け

#### EC2インスタンスの作成
* 作成したVPC、サブネットに紐付け
* 自動割り当てパブリックIPを有効化
* キーペアを作成・ダウンロード

:::note warn
以降、ダウンロードしたキーペアを使用してSSH接続するので、必ずダウンロードしてください。
:::

#### セキュリティグループ設定
* ポート：8080（フロントエンド用） → 自分のIP（MyIP）のみ許可
* ポート：3000（バックエンド用） → 自分のIP（MyIP）のみ許可
* ポート：22（SSH用） → 自分のIP（MyIP）のみ許可

### 3. EC2インスタンスの起動
設定完了後、EC2インスタンスを起動


## ローカルのdockerコンテナ上でWebアプリを実装
開発環境は以下の構成です。
```
first-moments
├── docker-compose.yml
├── frontend
│   ├── node_modules
│   ├── package.json
│   ├── src
│   ├── Dockerfile
│   └── 他省略
├── backend/
│   ├── node_modules
│   ├── package.json
│   ├── controllers
│   ├── Dockerfile
└── └── 他省略
```
ソースコードは割愛しますが、下記が大まかな内容です。
- dockerコンテナ1：フロントエンド（Vue.js）
- dockerコンテナ2：バックエンド Web API（Node.js+Express） 
- dockerコンテナ3：DB（PostgresSQL） 


## 1. ローカルのWebアプリのファイルをEC2へ転送
### 1. 転送前準備
ローカルプロジェクトのfrontend,backend内の node_modules を削除

:::note alert
EC2上で再度パッケージインストールするためnode_modulesは不要です。またnode_modulesを転送しようとするとかなり時間がかかります。
:::

### 2. ファイル転送
プロジェクト構成は下記です。first-momentsフォルダごと転送しました。
```
first-moments
├── docker-compose.yml
├── frontend
│   └── 省略
├── backend/
└── └── 省略
```
scpコマンドで転送
※今回はそのまま転送しましたが、圧縮転送した方がいいのかもしれません。
※選択するEC2のマシンイメージによっては「ec2-user」とは別のユーザーを指定する必要があるかもしれません。
```sh
scp -i <キーペア名>.pem -r first-moments ec2-user@<EC2-Public-IP>:/home/ec2-user/
```

## EC2でのアプリ動作環境準備
### 1. EC2にSSH接続
```sh
ssh -i <キーペア名>.pem ec2-user@<EC2のパブリックIP>
```

### 2. キーペアのパーミッションを変更（エラー回避）
```sh
chmod 600 <キーペア名>.pem
```

### 3. DockerとDocker-Composeのインストール
```sh
sudo yum update -y
sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

### 4. Dockerを有効化
以降のsudoコマンドを省略するため、ec2-userユーザを追加
```sh
sudo usermod -aG docker ec2-user
```
dockerコンテナに入り直し、dockerコマンドでバージョンが確認できればOK
```sh
docker version
docker-compose version
```

### 5. DBのセットアップ
PostgreSQLコンテナに接続
```sh
docker exec -it <dbコンテナ名> psql -U <ユーザー名> -d <DB名>
```

ローカルで作成したテーブルの作成（必要に応じてデータ投入も）
```sql
CREATE TABLE ...;
```

### 6. npmパッケージのインストール
```sh
cd /home/ec2-user/first-moments/frontend && npm install
cd /home/ec2-user/first-moments/backend && npm install
```

### 7. コンテナの起動
```sh
cd /home/ec2-user/first-moments
docker-compose up --build -d
```

<details open><summary>ちなみに今回作成したアプリのdocker-compose.ymlです</summary>


```yaml
services:
  db:
    image: postgres:latest
    container_name: first_moments_db
    environment:
      POSTGRES_USER: XXXXX
      POSTGRES_PASSWORD: XXXXX
      POSTGRES_DB: XXXXX
    ports:
      - '5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network

  frontend:
    build:
      context: ./frontend
    container_name: first_moments_frontend
    ports:
      - '8080:8080'
    volumes:
      - ./frontend/src:/app/src # ホストのsrcをコンテナのsrcにマウント
      - /app/node_modules # node_modulesはボリュームでマウント
    command: npm run serve # 開発モードで起動
    networks:
      - app-network
    environment:
      - CHOKIDAR_USEPOLLING=true # ホットリロードを有効にする設定

  backend:
    build:
      context: ./backend
    ports:
      - '3000:3000'
    volumes:
      - ./backend:/app # ソースコードをコンテナと同期
      - /app/node_modules # node_modules はマウントしない
    networks:
      - app-network
    depends_on:
      - db
    environment:
      - PGUSER=XXXXX
      - PGPASSWORD=XXXXX
      - PGDATABASE=XXXXX
      - PGHOST=db
      - PGPORT=5432
    restart: unless-stopped # コンテナがクラッシュした場合の自動再起動

volumes:
  postgres_data:
    driver: local

networks:
  app-network:
    driver: bridge
```
</details>


## 動作確認
ローカルPCのブラウザからアクセス
```sh
http://<EC2のパブリックIP>:8080
```
:::note warn
動作確認時にコードの微修正をしようと思い、VSCodeの拡張機能「Remote-SSH」でec2にSSH接続をしようとすると、ec2が動かなくなる事象が発生しました。
調べてみても理由がわからず、一旦今回はec2に直接入りviコマンドで修正を行いました。
:::


### **まとめ**  

- AWSの設定は最初はとっつきづらいですが、ドキュメントが豊富で解説記事も多く、そこまでハマるところはありませんでした。
- EC2を起動するだけでも多くの設定が必要ですが、各サービスの役割を理解すれば直感的に進めることができました。