Azure WebAppsにNode-REDのインスタンスを複数台作る

AzureにNode-REDのインスタンスを立てるメモ記事を書きましたがこれをもとに複数台のインスタンスを作成するスクリプトを書いたのでメモ

https://qiita.com/n0bisuke/items/9f87886513aa0ec75270

## インスタンスとサービスプランの数を良い感じに

99%Claudeに書いてもらったけど

- インスタンス数を指定
- サービスプランあたりの最大インスタンス数を指定
- プランの設定を指定 （SKUとリージョン）
- サービスグループ名を指定

をすることでインスタンス数にあわせてサービスプランを作成してくれるスクリプトにしました。

10台インスタンス作ろうとして1サービスプランあたりの最大数を3にした場合は4つのサービスプランも同時に作られる感じ。

```
以下のイメージ

- サービスプラン1
    - インスタンス1
    - インスタンス2
    - インスタンス3
- サービスプラン2
    - インスタンス4
    - インスタンス5
    - インスタンス6
- サービスプラン3
    - インスタンス7
    - インスタンス8
    - インスタンス9
- サービスプラン4
    - インスタンス10
```

> ![CleanShot 2025-05-10 at 18.14.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/970df500-ce7d-4412-908c-5e7c962bb956.png)

### シェルスクリプト
```sh
#!/bin/bash

# ユーザー入力
read -p "作成したいNode-REDインスタンスの総数を入力してください: " TOTAL_INSTANCES
read -p "1つのサービスプランあたりの最大インスタンス数を入力してください [推奨: 3-5]: " MAX_INSTANCES_PER_PLAN
read -p "サービスプランのSKUを入力してください [B1/B2/B3/S1など]: " SKU
read -p "リージョンを入力してください [japaneast/japanwest/eastus など]: " LOCATION
read -p "リソースグループ名を入力してください: " RESOURCE_GROUP

# デフォルト値の設定
TOTAL_INSTANCES=${TOTAL_INSTANCES:-20}
MAX_INSTANCES_PER_PLAN=${MAX_INSTANCES_PER_PLAN:-3}
SKU=${SKU:-"B1"}
LOCATION=${LOCATION:-"japaneast"}
RESOURCE_GROUP=${RESOURCE_GROUP:-"node-red-resources"}

# 基本名の設定
BASE_PLAN_NAME="node-red-plan"
BASE_APP_NAME="node-red-app"

# Docker Compose ファイルパス
COMPOSE_FILE="./docker-compose.yml"

# 必要なサービスプラン数を計算 (切り上げ)
PLAN_COUNT=$(( ($TOTAL_INSTANCES + $MAX_INSTANCES_PER_PLAN - 1) / $MAX_INSTANCES_PER_PLAN ))

echo "----------------------------------------"
echo "デプロイ設定:"
echo "- 作成するインスタンス総数: $TOTAL_INSTANCES"
echo "- 1プランあたりの最大インスタンス数: $MAX_INSTANCES_PER_PLAN"
echo "- 必要なサービスプラン数: $PLAN_COUNT"
echo "- サービスプランのSKU: $SKU"
echo "- リージョン: $LOCATION"
echo "- リソースグループ: $RESOURCE_GROUP"
echo "----------------------------------------"

read -p "この設定でデプロイを開始しますか？ (y/n): " CONFIRM
if [[ $CONFIRM != "y" && $CONFIRM != "Y" ]]; then
    echo "デプロイをキャンセルしました"
    exit 0
fi

# リソースグループが存在しない場合は作成
echo "リソースグループを確認/作成します..."
az group show --name $RESOURCE_GROUP > /dev/null 2>&1
if [ $? -ne 0 ]; then
  echo "リソースグループ '$RESOURCE_GROUP' を作成中..."
  az group create --name $RESOURCE_GROUP --location $LOCATION
else
  echo "リソースグループ '$RESOURCE_GROUP' は既に存在します"
fi

# Docker Compose ファイルの存在を確認
if [ ! -f "$COMPOSE_FILE" ]; then
  echo "Docker Compose ファイルが見つかりません: $COMPOSE_FILE"
  echo "以下の内容で docker-compose.yml を作成します..."
  
  cat > $COMPOSE_FILE << 'EOF'
version: "3.8"

services:
  node-red:
    image: nodered/node-red-dev:v4.0.9
    volumes:
      - ${WEBAPP_STORAGE_HOME}/Data:/data
    stdin_open: true
    tty: true
EOF
  
  echo "docker-compose.yml ファイルを作成しました"
fi

# カウンター変数の初期化
created_instances=0

# サービスプランとインスタンスの作成
for plan_num in $(seq 1 $PLAN_COUNT); do
  # サービスプラン名
  plan_name="${BASE_PLAN_NAME}-${plan_num}"
  
  echo "サービスプラン '$plan_name' を作成中..."
  az appservice plan create \
    --name $plan_name \
    --resource-group $RESOURCE_GROUP \
    --sku $SKU \
    --is-linux \
    --location $LOCATION
  
  # このプランに作成するインスタンス数を計算
  # 最後のプランの場合、残りのインスタンスを作成
  instances_to_create=$MAX_INSTANCES_PER_PLAN
  remaining_instances=$(($TOTAL_INSTANCES - $created_instances))
  
  if [ $remaining_instances -lt $instances_to_create ]; then
    instances_to_create=$remaining_instances
  fi
  
  # このプランにインスタンスを作成
  for instance_num in $(seq 1 $instances_to_create); do
    # 既に目標数に達した場合は終了
    if [ $created_instances -ge $TOTAL_INSTANCES ]; then
      break
    fi
    
    # インスタンス名（一意になるようにする）
    instance_id=$(($created_instances + 1))
    app_name="${BASE_APP_NAME}-${instance_id}"
    
    echo "インスタンス '$app_name' を作成中... ($instance_id/$TOTAL_INSTANCES)"
    az webapp create \
      --resource-group $RESOURCE_GROUP \
      --plan $plan_name \
      --name $app_name \
      --multicontainer-config-type compose \
      --multicontainer-config-file $COMPOSE_FILE
    
    # 必要な環境変数の設定
    echo "インスタンス '$app_name' の設定を構成中..."
    az webapp config appsettings set \
      --resource-group $RESOURCE_GROUP \
      --name $app_name \
      --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
    
    # always-on の設定（Basic プラン以上で利用可能）
    echo "always-on 設定を有効化中..."
    az webapp config set \
      --resource-group $RESOURCE_GROUP \
      --name $app_name \
      --always-on true
    
    # 作成済みインスタンス数をインクリメント
    created_instances=$((created_instances + 1))
  done
  
  # 目標数に達した場合はループを抜ける
  if [ $created_instances -ge $TOTAL_INSTANCES ]; then
    echo "目標インスタンス数 $TOTAL_INSTANCES に達しました"
    break
  fi
done

echo "完了！${created_instances}個のNode-REDインスタンスを${PLAN_COUNT}個のサービスプランに分散して作成しました"

# 作成されたリソースの一覧を表示
echo "作成されたサービスプラン:"
az appservice plan list --resource-group $RESOURCE_GROUP --query "[].{Name:name, Sku:sku.name, NumberOfSites:numberOfSites}" --output table

echo "作成されたWebアプリケーション:"
az webapp list --resource-group $RESOURCE_GROUP --query "[].{Name:name, DefaultHostName:defaultHostName, State:state}" --output table
```

あとは実行権限渡して実行

```
chmod +x ./deploy-node-red.sh
```

```
./deploy-node-red.sh
```

一気に作ってくれるの便利

> ![CleanShot 2025-05-10 at 17.57.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/a54986d3-c1fe-4b81-b315-6112b3ab91cc.png)


## Node.jsのバージョンどうなってるの？

デプロイされたNode.jsのバージョンはどうなってるのか確認したくてFunctionノードで以下を試してみました。

### processはブロック

```js
msg.payload = {
    node_version: process.version,
    node_env: process.env.NODE_ENV,
    platform: process.platform
};
return msg;
```

> ![CleanShot 2025-05-10 at 18.39.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/60c62633-900b-497a-9a68-ebf18b3f2912.png)


見事にブロック processへのアクセスはできないみたいですね

> ![CleanShot 2025-05-10 at 18.40.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/0f227e3b-81ea-431a-8fb6-edc468b88ec9.png)

### 環境変数はいけた

```js
msg.payload = {
    env: {
        NODE_ENV: env.get("NODE_ENV"),
        // 他の環境変数も試してみる
        NODE_VERSION: env.get("NODE_VERSION"),
        PATH: env.get("PATH")
    }
};
return msg;
```

> ![CleanShot 2025-05-10 at 18.41.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/27f20001-5dec-419e-ba3c-bb84601dcbe0.png)

> ![CleanShot 2025-05-10 at 18.40.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/bcb7b90d-0a18-4b37-a021-813b00aec2c5.png)

ということでNode.js v20系が使われてるみたいですね。

v24がでてv18がサポート切れな話題が出てたので安心です。