🐈

【完結】テスト環境を構築検討していく⑧【NestJSの構築】

2024/06/08に公開

こんにちは投資ロウトです。

背景

ある程度NestJSでAPIの作成も進んできたので、Postmanによる単体テストではなく、アプリからのAPI呼び出しで動作を確認していきたいという思いも進めていくと出てくると思います。

ただなるべくコストを抑えていきたいため、本番環境よりもコストを抑えた類似環境でテスト環境を構築していきます。
※ECSなどはリソースだけで費用がかかるので、EC2などのインスタンスを止めながら開発コストを抑えたい。

https://zenn.dev/doshirote/articles/577d8bb31a152b

そのため本番環境を簡易で構築し、テストを行う環境を整備していきます。

NestJS(NodeJS)の環境構築

①nodejsを入れる

sudo yum update -y

問題1

sudo yum install -y nodejs npm

上記実行時にエラーが発生

読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
パッケージ nodejs は利用できません。
パッケージ npm は利用できません。
エラー: 何もしません

対処1

AWS EC2インスタンスで、yumを利用して直接インストールできないことがあるとのこと

# NodeSourceリポジトリを追加してNode.jsをインストール
curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash -

続き

sudo yum install -y nodejs
# 今度は成功。ただし実行のnodeのバージョンは合わせること
node -v
# nodeのバージョンの修正
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# 対象のバージョンを入力
nvm install 対象のバージョン
nvm use 対象のバージョン

# 対象のバージョンから切り替わっていることを確認
node -v
# npmのバージョンの確認
npm -v
# 自環境と変わらなかった。

②codecommitのソースを引っ張ってくる

git clone リポジトリ

問題2

gitコマンドがない

-bash: git: コマンドが見つかりません

対処2

sudo yum install -y git

続き

# 再度実行
git clone リポジトリ

cd フォルダ名

npm install

# 起動
PORT=対象のポート npm start

問題3

PORT=対象のポート npm start

> xxx@0.0.1 start /home/ユーザー名/リポジトリ名
> xxx nest start


<--- Last few GCs --->

[7700:0x4145e20]    16392 ms: Mark-sweep 482.2 (484.3) -> 482.0 (484.3) MB, 300.0 / 0.0 ms  (+ 115.0 ms in 23 steps since start of marking, biggest step 17.3 ms, walltime since start of marking 472 ms) (average mu = 0.290, current mu = 0.157) allocation f[7700:0x4145e20]    17070 ms: Mark-sweep 482.4 (484.3) -> 481.6 (484.6) MB, 677.3 / 0.0 ms  (+ 0.0 ms in 5 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 678 ms) (average mu = 0.135, current mu = 0.002) allocation failu

<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x140dff9]
Security context: 0x04c38b8c08d1 <JSObject>
    1: iterateCommentRanges(aka iterateCommentRanges) [0x6cc49856a91] [/home/ユーザー名/リポジトリ名/node_modules/typescript/lib/typescript.js:~10713] [pc=0x3f20064f76e4](this=0x226fcbd804b1 <undefined>,0x226fcbd80639 <true>,0x0c7a09d00119 <Very long string[152935]>,74398,0x226fcbd806e9 <false>,0x06cc49856b51 <JSFunction appendCommentRange (sfi = 0x36eeb01c3159)>,0x...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0xa1a640 node::Abort() [node]
 2: 0xa1aa4c node::OnFatalError(char const*, char const*) [node]
 3: 0xb9a9fe v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xb9ad79 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xd57ff5  [node]
 6: 0xd58686 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
 7: 0xd64f45 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
 8: 0xd65df5 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 9: 0xd688ac v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
10: 0xd2f2cb v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
11: 0x107189e v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
12: 0x140dff9  [node]

対処3

# JavaScriptのヒープメモリが不足しているために発生しているとのこと
# EC2インスタンスのメモリを確認
              total        used        free      shared  buff/cache   available
Mem:            965          65         854           0          46         807
Swap:             0           0           0

# メモリが1GBしかないのが問題のようでした。
# スペックを落としすぎたのが、原因のようです。

# ローカルで動く環境を確認してみる
8GBのメモリと1GBのswapがあり、それは動かないと言うことになりました。
               total        used        free      shared  buff/cache   available
Mem:            7859        1240        4664         307        1954        6013
Swap:           1023           0        1023

今度はサーバーのスペックを上げていく。

t2.microが1GBなのでt2.large

問題4

awsのcloudformationで削除時、DBのauroraが削除できない。

Db cluster dm-dev-aurora-cluster is in stopped state (Service: Rds, Status Code: 400, Request ID: aaaa604e-bbbb-cccc-dddd-eeeed4d7ffff
)

対処4

aurora mysqlのインスタンスをとめているのが要因なので、起動してから削除をながす
※よくよく考えたら、DB削除する必要はなかったです。。。

続き

# 一旦上記の流れで再度実施する。
# メモリの件は解消されていた

# Aurora MySQLの接続方法
# RDSから接続とセキュリティを選び、エンドポイントを確認。
# ライターとリーダーがあるが、更新がある場合はライターで接続を行う
# MySQLなので3306のportで、他はAurora MySQLを作成するために作成したものを使用。

ここまででAurora MySQLに接続が成功

検証

PostmanでALBで設定したサブドメインに対してAPIを投げる

問題1

<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
</body>
</html>

解決1

# 原因:ヘルスチェックが行われていないのが原因の一つだった

# 問題点1:ヘルスチェック用のAPIをつけていなかった

cloudwatchで全てのメトリクスでフィルター

cloudwatchでのメトリクスの件数は1334件が12時間で存在した。

Application ALBの項目をクリックする
→メトリクスをUnHealthyHostCountでフィルターをする

ディメンションでフィルターをさらにする
Application Load Balancerは「AvailabilityZone」と「LoadBalancer」と「TargetGroup」でフィルターできる

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html#load-balancer-metric-dimensions-alb

UnHealthyHostCountで引っ掛かっていることが確認できる。
※ヘルスチェックでNGとなっている確認方法

# privateサブネットからヘルスチェックが帰ってくる設定になっているか確認。
# privateサブネットでNestJSを起動した状態で、もう一つprivateサブネットでターミナルを開き、下記コマンドを実施。
curl localhost:対象のポート/

# 結果
{"statusCode":404,"message":"Cannot GET /","error":"Not Found"}

ヘルスチェックの動線を用意する必要があることが判明

NestJSに"/"のGetメソッドで200ステータスが返せれるように修正

# webサーバーのprivateサブネットから再度curlを実施
curl localhost:対象のポート/

# 成功したので、まずはヘルスチェックの口があることは問題ない。

この状態で再度APIを実施してみる

# まだ502が返ってしまう。詳細を確認していく。
# 対象のポートに対してヘルスチェックを行えていないことを確認
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
  Port: 送りたいポートに指定する

問題2

今度はtime outが表示された

<html>

<head>
    <title>504 Gateway Time-out</title>
</head>

<body>
    <center>
        <h1>504 Gateway Time-out</h1>
    </center>
</body>

</html>

解決2

問題なのは、セキュリティーグループ等でアクセスをブロックしていたのが原因だったため、セキュリティグループ及びターゲットとなるポートが問題なく通るように修正をします。

そして環境をまた壊すのは、待ち時間や構築にめちゃめちゃ時間がかかるので、地獄ですね。。。

そして結果。。。無事にリクエストが成功。。。長い格闘だった笑

新たに知ったこと

現状(2024/6/7時点)aws上ではA5M2や、cloudBeaverのようなGUIでDBを確認するツールは存在しない。

新たに知ったこと

ECSのFargateを構築した際に、CloudWatchとの連携が自動的に有効になり、以下の2点が有効となる。

①リソースのモニタリング

CPU使用率やメモリ使用率などのメトリクスが、CloudWatchコンソールやAPIから確認可能

②コンテナアプリケーションのログの永続化

Fargateタスクからのログストリームが、CloudWatch Logsに自動的に送信され、タスクログの確認やモニタリングが可能となる。

繰り返し出るが、cloudformationのかんぺ

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

Discussion