n8nをAWS ECS上にセキュアにデプロイして業務自動化を実現した話

こんにちは!広告事業本部でクライアントの受発注システムを担当しているアプリケーションエンジニアの石井です。

前回の記事執筆が2024年の6月ということでおよそ1年半ぶりの投稿となります!

今回はADWAYS DEEEの方でも導入を進めている「n8n」に関してのお話になります。AIによる業務効率化がさまざまな部署から行われる中でその一環として、広告事業本部の各部署でもさまざまなツールの導入・検証が行われております。

「n8nとは何か」や「実際に導入してどう活用しているか」は、上記の記事で紹介されています。 そのため本記事では、「n8nをAWS上の環境でセキュアにデプロイする」といった技術的な話を中心に、あわせて、私の部署で行っている自動化の例をいくつかご紹介します!

構築しているリソースの構成自体はとてもシンプルですが、社内用にセキュアなn8n環境を構築したい場合などに参考にしていただければうれしいです!

導入の背景

エンジニアチームの課題としてあった点として、日々の業務の中に固定作業の時間が多く、メインの開発・運用業務に携わる時間が減ってしまうといったことがありました。

定例作業の一例として以下のようなものがあります。

  • AWSなどからメールで届く、各種リソースのアップデート告知をタスク管理ツールに起票する
  • スプリントレビューに向けて、各個人の担当タスクを逐一確認し社内Wikiにコピペする
  • サービスへのアクセス履歴をS3やCloudWatchからダウンロードし、棚卸し管理用のGoogleドライブにアップロードする

これらひとつひとつの作業はそこまで作業工数のかかるものではないのですが、それらが積み重なることで本来着手したいタスクに遅れが生じてしまうといった懸念がありました。

そこで、私たちのユニットでは「定例作業の5割削減」を目標として、さまざまな自動化ツールやAI Agentの導入・検証を実施し活用することをOKRとして策定しました。 その調査の一環で、私たちのユニットにクリティカルにヒットしたものが「n8n」でした。

n8nを選定した理由

AIとの親和性

n8nで提供しているノードの1つにAI Agentノードがあります。 AI Agentの提供する機能としては、ChatGPTやGeminiといったLLMにプロンプトを渡してAIからの応答を出力とするノードです。n8nはこれを1ノードとして提供していることにより、以下の画像のようなマルチエージェントからなるワークフローを簡単に作成できます。

上記のワークフローでは私の過去記事に対し、要約を作成し、その要約をもとにXへの投稿文を作成させています。結果として以下のような出力が得られました。

新卒1年目でここまで!?😳新卒エンジニアが経験した、社内DX化プロジェクトのリアルが詰まった記事。
請求書郵送→メール移行のkintone新機能開発を要件定義からリリースまで担当!
kintone, SendGrid, Lambdaを駆使し、タスク具体化や工数見積もり、そして「20分迷ったら相談」の学びまで、新卒とは思えない成長がここに。
若手エンジニア必見の学びと経験👇
https://blog.engineer.adways.net/entry/2024/06/07/160000
#新卒エンジニア #DX化 #kintone #エンジニアの学び #アドウェイズ

Xのインプレッションを意識した投稿文が生成されています。 この一例はあくまでごく単純なものですが、AIによる自律的思考をするためのマルチエージェントシステムによる処理の流れが視覚的にわかりやすく管理できるというのはかなり有用です。

セルフホストが可能

n8nでは公式よりDockerイメージが提供されています。 https://docs.n8n.io/hosting/installation/docker/

私たちの部署ではこのDockerイメージをAWSのECS上にデプロイしています。 これにはセキュアに扱えるという理由もありますがそれ以上に、社内の他のAWSアカウントからIAMロール経由でアクセスできるようになるという利点があります。

これにより、アクセスキーの管理やローテーションといったセキュリティ上の懸念を排除でき、sts:AssumeRoleによる一時的なアクセス権限の取得のみで他アカウントのリソースにアクセスできます。

ここからは、実際にn8nを用いてどのような自動化を実現したか、そしてこの記事のメイン部分であるAWS上での構築方法について詳しく説明します。

n8nで実現した自動化の例

n8nを導入したことで、私たちのユニットでは以下のような定例作業の自動化を実現しました。ここでは代表的な3つのワークフローをご紹介します。

1. メール通知からタスクを自動起票

GMail宛に届いたメール内容を元にし、AIエージェントが優先度を判断。対応を要するタスクに関しては、タスクの内容を要約するAIエージェントがタスク内容を生成しJiraにタスクを起票しています。 AI Agentの生成内容を評価するために、Evaluationノードを使用しています。

このノードはスプレッドシートにあらかじめ用意した、ダミーのメール内容を実際にワークフローに読み込ませ、想定通りの出力がされるかを検証しています。上記ワークフローでは、メール内容に対して想定通りの優先度を判断がなされるかを検証しています。

Before

AWSから届くメンテナンス通知やアップデート告知を手動でメールから確認し、内容を理解した上でタスク管理ツール(Jira)に起票する作業が発生していました。この作業は週に2〜3回発生し、1回あたり5〜10分程度かかっていました。

After

n8nでGmailとJiraを連携させることで、AI Agentノードで内容を要約・重要度を判定した上で、Jiraに自動起票するワークフローを構築しました。

効果

  • 週あたり約15〜30分の工数削減
  • メール見落としによる対応遅れのリスク低減
  • 起票内容の統一化による管理の効率化

2. スプリントレビュー資料の自動生成

私たちのチームでは2週間ごとにスプリントレビューを実施しています。そこでプロダクトオーナーに各タスクの作業内容を確認するために、スプリント内に着手したタスクの一覧を社内Wiki(Confluence)に転記しています。 このような定型的なデータ転記作業は、わざわざスケジューラやスクリプトを準備せずに、n8nのワークフローで簡単に自動化できます。サービス間のデータ整形・同期といった処理は、PoCとしても実運用としても効果的に開発できる点がn8nの強みです。

Before

2週間ごとのスプリントレビューに向けて、Jiraから各メンバーの担当タスクを手動で確認し、社内Wikiにコピペして整形する作業が必要でした。この作業には毎回20分程度かかっていました。

After

n8nのスケジュールトリガーを使用して、スプリント終了日の前日に自動でJira APIからデータを取得し、テンプレートに沿って整形して社内Wikiを構築するワークフローを構築しました。

効果

  • 隔週で約20分の工数削減
  • 人的ミスによる漏れや記載ミスの削減
  • レビュー準備の時間を本質的な振り返りに充てられる

3. アクセスログの定期アップロード

Execute Commandノード経由で、AWS CLIを呼び出しアクセスログを取得しそれをGoogleドライブにアップロードしています。アクセスログを取得に当たっては他のAWSアカウントからのログも取得したいため、IAM でのクロスアカウントのリソースへのアクセスを参考にIAMロールの設定を行なっています。

Before

セキュリティ監査のため、毎月S3やCloudWatch Logsからアクセスログをダウンロードし、特定のフォーマットに変換した上でGoogleドライブの指定フォルダにアップロードする作業が必要でした。この作業は月初に約1時間かかっていました。

After

n8nのAWS CLI(カスタムイメージで追加)を使用して、STSで他アカウントのIAMロールを引き受け、S3からログを取得し、必要な加工をした上でGoogle Driveに自動アップロードするワークフローを構築しました。

効果

  • 月あたり約1時間の工数削減
  • IAMロール経由のため、アクセスキー管理が不要
  • 定期実行により作業忘れのリスク低減

これらの自動化により、チーム全体で月あたり約3〜4時間の工数削減を実現しました。削減された時間は、新機能開発や品質改善といった本来注力すべきタスクに充てることができています。

AWS上での構築方法

AWS環境では以下のアーキテクチャ図のようなシングルAZの構成をとっています。

この図の中で特記する点としては、以下の点があります。

  • n8n公式のイメージではなくECR上のカスタムイメージからプルしている
    • 公式イメージでは提供されていないAWS CLIなどをn8n上から使えるようにするためにDockerfile内でインストールをしています
  • コンテナの再起動でデータが消失しないようにEFSにマウントを行っている点

ただし、ALBは本来2つ以上のAZが必要ですが、本環境ではダミーのAZを設定することでこの要件をクリアしています。社内運用のワークフロー管理用途では、冗長性よりもコスト最適化を優先し、実質的にシングルAZ構成としています。

実装のポイント

リソースの作成にあたって、各種リソースはTerraformで構成しています。以下にTerraformでのコード例を交えながら実装時に気をつけたポイントを記載します。

EFSによるデータ永続化

ECSで作成したサービスの場合、コンテナのストレージは一時的なものとなるため、永続的にマウントするボリュームが必要となります。 本環境はFargateで構築しているため、永続化の方法としては一般的なEFSを使用しています。 参考: Amazon ECS タスクのストレージオプション

EFSをTerraform管理する場合、以下のようになります。

resource "aws_efs_file_system" "n8n_efs" {
    creation_token   = "${local.project}-efs"
    performance_mode = "generalPurpose"
    throughput_mode  = "bursting"

    encrypted = true

    lifecycle_policy {
        transition_to_ia = "AFTER_30_DAYS"
    }

    tags = {
        Name = "${local.project}-efs"
    }
}

resource "aws_efs_mount_target" "n8n_efs_mt" {
    for_each = local.subnet_numbers

    file_system_id  = aws_efs_file_system.n8n_efs.id
    subnet_id       = aws_subnet.private[each.key].id
    security_groups = [aws_security_group.efs_sg.id]

    depends_on = [aws_efs_file_system.n8n_efs]
}

スペックを考慮する点としてはperformance_modethroughput_modelifecycle_policyがあり、それぞれのスペックの比較は以下のようになります。

Performance Mode
モード 特徴 レイテンシ IOPS 用途 変更可否
General Purpose 汎用的な性能 低レイテンシ 最大35,000 IOPS Webサーバー、CMS、開発環境など一般的なワークロード 作成後は変更不可
Max I/O 高いスループット重視 やや高いレイテンシ 500,000以上のIOPS ビッグデータ、メディア処理など大規模な並列処理 作成後は変更不可
Throughput Mode
モード 特徴 スループット コスト 用途
Bursting バースト型 ストレージサイズに比例(最小1MB/s)
バースト時最大100MB/s
基本料金のみ 断続的なアクセスパターン
Provisioned プロビジョニング型 ストレージサイズに関係なく固定
1-1024 MB/sで設定可能
基本料金 + プロビジョニングスループット料金 継続的に高スループットが必要
Elastic 自動スケーリング ワークロードに応じて自動調整
最大3GB/s (読み取り)、1GB/s (書き込み)
基本料金 + 使用量課金 予測不能なワークロード
Lifecycle Policy
ポリシー 対象 移行先 アクセス後の動作 備考
なし - - 全データがStandard(またはOne Zone)に保持
AFTER_xx_DAYS xx日間アクセスのないファイル IA (Infrequent Access) Standard/One Zoneに自動復元 xxには7, 14, 30などを設定可能

本実装では、高いスループットを要するような要件ではないため、コストを抑えるべくGeneral PurposeかつBursting型での運用をしています。また、アクセス頻度の低いファイルについては30日後に低頻度アクセスストレージクラス(IA)に自動移行させることで、ストレージコストを最適化しています。

カスタムイメージの使用

n8nの公式Dockerイメージはシンプルな構成となっており、n8n本体の実行に必要な最小限の環境のみが含まれています。しかし、実際の業務での活用を考えた場合、以下のような追加ツールが必要になるケースがあります。

  • AWS CLI: S3へのファイルアップロード・ダウンロード、STS経由での他アカウントへのアクセス
  • Python・Node.js追加パッケージ: カスタムスクリプトの実行
  • その他CLIツール: jq、curl、gitなど

そのため、本環境では公式イメージをベースにカスタムイメージを作成し、ECRにプッシュして使用しています。

以下はDockerfileの例です。

FROM n8nio/n8n:latest

USER root

# AWS CLIのインストール
RUN apk add --no-cache \
        python3 \
        py3-pip \
        curl \
        jq \
        git \
        && pip3 install --upgrade pip \
        && pip3 install awscli

# 追加で必要なNode.jsパッケージがあればインストール
# RUN npm install -g <package-name>

USER node

WORKDIR /home/node

このDockerfileをビルドし、ECRにプッシュする手順は以下の通りです。

# ECRログイン
aws ecr get-login-password --region ap-northeast-1 | \
    docker login --username AWS --password-stdin <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com

# イメージのビルド
docker build -t n8n-custom .

# タグ付け
docker tag n8n-custom:latest <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/n8n-custom:latest

# プッシュ
docker push <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/n8n-custom:latest

ECSのタスク定義では、このECRイメージを参照するように設定します。

resource "aws_ecs_task_definition" "n8n" {
    family                   = "${local.project}-task"
    network_mode             = "awsvpc"
    requires_compatibilities = ["FARGATE"]
    cpu                      = "512"
    memory                   = "1024"
    execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn
    task_role_arn            = aws_iam_role.ecs_task_role.arn

    container_definitions = jsonencode([
        {
            name  = "n8n"
            image = "${aws_ecr_repository.n8n.repository_url}:latest"

            environment = [
                { name = "N8N_HOST", value = var.n8n_host },
                { name = "N8N_PROTOCOL", value = "https" },
                { name = "WEBHOOK_URL", value = "https://${var.n8n_host}/" }
            ]

            mountPoints = [
                {
                    sourceVolume  = "n8n-data"
                    containerPath = "/home/node/.n8n"
                    readOnly      = false
                }
            ]

            logConfiguration = {
                logDriver = "awslogs"
                options = {
                    "awslogs-group"         = aws_cloudwatch_log_group.n8n.name
                    "awslogs-region"        = var.aws_region
                    "awslogs-stream-prefix" = "n8n"
                }
            }
        }
    ])

    volume {
        name = "n8n-data"
        efs_volume_configuration {
            file_system_id = aws_efs_file_system.n8n_efs.id
            root_directory = "/"
        }
    }
}

カスタムイメージを使用することで、n8nのワークフロー内から直接AWS CLIコマンドを実行したり、複雑なスクリプト処理を行うことができるようになります。

コンテナの初期化処理

EFSをECSコンテナにマウントする際、ファイルシステムのパーミッション設定が重要になります。

n8nコンテナはデフォルトでnodeユーザー(UID: 1000, GID: 1000)で実行されます。一方、EFSのルートディレクトリはデフォルトではroot所有となっているため、そのままではn8nがデータを書き込めません。

この問題を解決するため、本環境では初期化用のコンテナを使用してEFSのパーミッションを事前に設定しています。

ECSのタスク定義で、依存関係を持つ複数のコンテナを定義することで、以下のような起動順序を制御できます。

  1. init-permissions コンテナが起動
  2. EFSマウントポイントの所有者とパーミッションを変更
  3. 初期化完了後にコンテナが終了
  4. n8n コンテナが起動

Terraformでの実装例は以下の通りです。

resource "aws_ecs_task_definition" "n8n" {
    family                   = "${local.project}-task"
    network_mode             = "awsvpc"
    requires_compatibilities = ["FARGATE"]
    cpu                      = "512"
    memory                   = "1024"
    execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn
    task_role_arn            = aws_iam_role.ecs_task_role.arn

    container_definitions = jsonencode([
        # 初期化コンテナ
        {
            name      = "init-permissions"
            image     = "busybox:latest"
            essential = false

            command = [
                "sh",
                "-c",
                "chown -R 1000:1000 /data && chmod -R 755 /data && echo 'Permissions set successfully'"
            ]

            mountPoints = [
                {
                    sourceVolume  = "n8n-data"
                    containerPath = "/data"
                    readOnly      = false
                }
            ]

            logConfiguration = {
                logDriver = "awslogs"
                options = {
                    "awslogs-group"         = aws_cloudwatch_log_group.n8n.name
                    "awslogs-region"        = var.aws_region
                    "awslogs-stream-prefix" = "init"
                }
            }
        },
        # メインコンテナ
        {
            name      = "n8n"
            image     = "${aws_ecr_repository.n8n.repository_url}:latest"
            essential = true

            # 初期化コンテナの完了を待つ
            dependsOn = [
                {
                    containerName = "init-permissions"
                    condition     = "SUCCESS"
                }
            ]

            portMappings = [
                {
                    containerPort = 5678
                    protocol      = "tcp"
                }
            ]

            mountPoints = [
                {
                    sourceVolume  = "n8n-data"
                    containerPath = "/home/node/.n8n"
                    readOnly      = false
                }
            ]

            logConfiguration = {
                logDriver = "awslogs"
                options = {
                    "awslogs-group"         = aws_cloudwatch_log_group.n8n.name
                    "awslogs-region"        = var.aws_region
                    "awslogs-stream-prefix" = "n8n"
                }
            }

            healthCheck = {
                command     = ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5678/healthz || exit 1"]
                interval    = 30
                timeout     = 5
                retries     = 3
                startPeriod = 60
            }
        }
    ])

    volume {
        name = "n8n-data"
        efs_volume_configuration {
            file_system_id          = aws_efs_file_system.n8n_efs.id
            root_directory          = "/"
            transit_encryption      = "ENABLED"
            authorization_config {
                access_point_id = aws_efs_access_point.n8n.id
            }
        }
    }
}

ポイントとなるのは以下の点です。

  • essential = false: 初期化コンテナは必須ではなく、完了後に終了することを示す
  • dependsOn: n8nコンテナが初期化コンテナの成功を待つよう設定
  • condition = "SUCCESS": 初期化コンテナが正常終了した場合のみn8nを起動
  • chown -R 1000:1000 /data: n8nのユーザーID/グループIDに所有者を変更

さらに、EFSアクセスポイントを使用することで、よりセキュアな権限管理も可能です。

resource "aws_efs_access_point" "n8n" {
    file_system_id = aws_efs_file_system.n8n_efs.id

    posix_user {
        uid = 1000
        gid = 1000
    }

    root_directory {
        path = "/n8n"
        creation_info {
            owner_uid   = 1000
            owner_gid   = 1000
            permissions = "755"
        }
    }

    tags = {
        Name = "${local.project}-efs-access-point"
    }
}

この設定により、コンテナの再起動時にもデータが永続化され、かつ適切な権限でn8nが動作することが保証されます。初回起動時にCloudWatch Logsで初期化コンテナのログを確認することで、パーミッション設定が正しく行われたことを確認できます。

運用コスト

実際に運用を始める際の見積もりコストとしては、下記の内訳で約$100となりました。

基本インフラ構成コスト($42〜47)

  • ECS Fargate (0.5 vCPU, 1GB Memory): 約$22/月
  • EFS (5GB想定): 約$1.5/月
  • Application Load Balancer: 約$20/月
  • CloudWatch Logs (1GB/月想定): 約$0.5/月
  • CloudWatchカスタムメトリクス: 約$10-15/月 (使用量による)

NAT Gateway追加コスト($50)

  • NAT Gateway x1 (高可用性構成): 約$45/月
  • Elastic IP x1: 約$4/月

n8n単体で見ると割高に感じられるコストですが、セキュアな運用環境と社内の他のAWSアカウントとのシームレスな連携を考慮すると、妥当な投資と言えます。さらに、本AWSアカウントでは今後n8nに加えてDifyなどの他のセルフホスト型AIツールの導入も検討しており、その際は既存のインフラを活用してECSコンテナ料金の増加のみで対応できます。この拡張性の高さが、初期投資の価値となっています。

上記が実際にかかったコストになりますが、概ね見積もり通りの月額使用料となっています。

他事例とのコスト比較

本環境のコストを他の構成方法やサービスと比較すると以下のようになります。

構成 月額コスト(概算) メリット デメリット
本プロジェクト
(ECS Fargate + NAT Gateway)
$100〜110 ・セキュアなPrivate構成
・IAMロール連携が容易
・Terraform管理
・NAT Gatewayによるコスト増
・シングルAZのため冗長性は限定的
ECS Fargate Spot
(ElasticScale事例)
$2.66〜5 ・非常に低コスト
・Terraformモジュール提供
・Spot中断のリスク
・安定性に欠ける
EC2 t3.small + ALB $15〜30 ・コストと安定性のバランス
・柔軟なカスタマイズ
・EC2管理の運用負荷
・セキュリティパッチ適用が必要
DigitalOcean VPS $7〜12 ・最安クラス
・シンプルな構成
・AWS外のため既存インフラと分離
・IAMロール連携不可
Zapier 有料プラン $20〜599 ・SaaSで管理不要
・即座に利用開始可能
・従量課金で高額化のリスク
・データ主権の懸念
・カスタマイズ制限
n8n Cloud $20〜 ・公式マネージドサービス
・運用負荷ゼロ
・セルフホストより割高
・AWS連携に制約

コスト評価のポイント

本プロジェクトのコスト($100〜110/月)は、セルフホストの他事例と比較すると高めですが、以下の点を考慮すると妥当なコストと判断しています。

1. セキュリティとコンプライアンスの要件

Private Subnet + NAT Gateway構成により、社内のセキュリティポリシーに準拠した運用が可能です。特に、顧客データを扱う業務自動化においては、このセキュリティレベルが必須となります。

2. AWS環境との統合メリット

IAMロール経由での他アカウントへのアクセスが可能なため、アクセスキーの管理やローテーションの手間が不要です。これにより、セキュリティリスクの低減と運用負荷の削減を同時に実現しています。

3. 拡張性への投資

今後、本AWSアカウントではn8n以外にもDifyなどの他のセルフホスト可能なAIツールを追加展開する予定です。その場合、ECSのコンテナ料金のみの増大に抑えられるため、インフラ基盤への初期投資として妥当と考えています。

4. SaaSとの比較

Zapierの有料プランは$20/月からスタートしますが、タスク実行数が増えると$299や$599のプランへの移行が必要になります。本環境では実行数に関わらず定額のため、長期的にはコストメリットが大きいと試算しています。

コスト最適化の余地

今後の検討事項として、以下のコスト最適化施策を検討しています。

  • CloudWatch Logsの保持期間の最適化: 現在は無期限だが、30日程度に短縮を検討
  • Fargate Spotの部分的導入: 非クリティカルなワークフローへの適用
  • VPC Endpointの検討: S3/ECR向けにNAT Gateway経由の通信を削減

これらの施策により、さらに月額$10〜20程度のコスト削減が見込めます。

これからの展望

現在の構成で安定的に運用できていますが、今後はさらなる改善と拡張を計画しています。

1. Auto Scaling対応

現在はECS Serviceのタスク数を1に固定していますが、ワークフロー実行数の増加に伴い、以下のAuto Scaling設定を検討しています。

  • ターゲット追跡でのスケーリング
    • CPU使用率が70%を超えた場合にタスクを追加(最大3タスクまで)
    • メモリ使用率が80%を超えた場合にもスケールアウト
  • スケジュールベースでのスケーリング
    • 平日9:00-18:00は2タスクで稼働(業務時間のワークフロー集中に対応)
    • 夜間・休日は1タスクに縮小してコスト最適化

ただし、n8nはステートフルなアプリケーションのため、複数タスクでの実行には以下の課題があります。

  • Webhookの受信をどのタスクで処理するか
  • スケジュール実行の重複防止

これらの課題に対しては、n8n公式が推奨するQueue Modeの導入を検討中です。Queue ModeではRedisを使用してジョブキューを管理し、複数のワーカーで分散処理が可能になります。

2. WAF導入検討

現在はALBでIP制限のみを実施していますが、より堅牢なセキュリティのためにAWS WAFの導入を検討しています。

  • 想定するWAFルール例
    • レート制限: 1分間に60リクエスト以上の場合はブロック(DDoS対策)
    • 地理的制限: 日本国内のIPアドレスからのアクセスのみ許可
    • SQLインジェクション/XSS対策: AWS Managed Rulesの適用
    • Bot対策: 既知の悪質なボットからのアクセスをブロック

WAF導入により月額約$10〜15の追加コストが見込まれますが、セキュリティインシデントのリスク低減を考慮すると投資価値があると判断しています。

3. ワークフロー拡充計画

現在稼働しているワークフローに加えて、以下のような新規ワークフローの開発を計画しています。

  • 検討中のワークフローの一例
    • GitHub Actionsの失敗通知: CI/CDパイプラインの失敗を即座にSlackへ通知し、エラーログをAI要約
    • 定期的なコスト分析レポート: AWS Cost Explorerからデータを取得し、前月比較や異常検知を自動実施
    • オンボーディング自動化: 新メンバー参加時のアカウント作成やドキュメント共有を自動化
  • AI Agentの高度化:
    • 複数のAI Agentを組み合わせたマルチエージェントシステムの構築
    • RAG(Retrieval-Augmented Generation)を活用した社内ドキュメント検索の統合
    • ワークフロー実行結果のフィードバックループによる継続的改善

4. PostgreSQLへのデータベース移行

現在はSQLite + EFSで運用していますが、ワークフロー数とワークフローの実行履歴が増加した場合、パフォーマンスの問題が発生する可能性があります。

運用コストは増加しますが、今後のn8nの業務範囲拡大を見据えて、よりスケーラブルなデータベースへの移行も検討しています。

  • 移行先の例:
    • RDS PostgreSQL(db.t4g.micro): 月額約$15の追加コスト
    • Aurora Serverless v2も選択肢として検討中(使用量に応じた自動スケーリング)

これらの展望により、n8nを中心としたAI活用・自動化基盤をさらに強化し、エンジニアの生産性向上やさまざまな部署の業務自動化を継続的に実現していきます。

まとめ

本記事では、n8nをAWS ECS Fargate上にセキュアにデプロイする方法と、実際の運用での知見を共有しました。

得られた知見

技術面

  1. EFS + 初期化コンテナの組み合わせが有効

    • n8nのようなステートフルアプリケーションでは、EFSによるデータ永続化が必須
    • init-permissionsコンテナによるパーミッション制御で安定稼働を実現
  2. カスタムイメージによる拡張性

    • 公式イメージにAWS CLIなどを追加することで、AWS環境との連携が容易に
    • ECRでのイメージ管理により、バージョン管理と迅速なロールバックが可能
  3. IAMロールベースの認証が運用負荷を大幅削減

    • アクセスキー管理やローテーションの手間が不要
    • セキュリティリスクの低減とコンプライアンス要件への対応を両立

運用面

  1. 自動化による工数削減効果は想定以上

    • 月あたり約5時間以上の工数削減を実現
    • 定型作業からの解放により、エンジニアが本質的な価値創造に集中できる環境を構築
  2. AI Agentとの親和性が高い

    • ワークフローの視覚化により、マルチエージェントシステムの設計・デバッグが容易
    • プロンプトの調整やエージェントの追加がGUIで直感的に実施可能
  3. コストは投資対効果で判断すべき

    • 月額$100〜110はセルフホスト事例としては高めだが、セキュリティ要件とAWS統合のメリットを考慮すると妥当
    • 長期的にはSaaS型ツールより大幅にコスト削減が見込める

読者へのメッセージ

n8nをAWSで運用したい方へ:

本記事で紹介した構成はシンプルですが、実運用に耐える設計となっています。特に以下のような方にお勧めです。

  • 社内でセキュアな自動化基盤を構築したい方: Private Subnet + NAT Gateway構成により、社内ポリシーに準拠した運用が可能です
  • AWS環境との統合を重視する方: IAMロールベースの認証により、既存のAWSリソースとシームレスに連携できます
  • Infrastructure as Codeで管理したい方: Terraformによる完全なコード管理で、再現性の高いインフラを構築できます

コストを最小限に抑えたい方へ:

より低コストで運用したい場合は、以下のアプローチも検討してください。

  • Fargate Spotの活用: ElasticScale事例を参考に月額$3程度で運用可能
  • VPC Endpointの導入: NAT Gatewayのコストを削減
  • 開発環境と本番環境の分離: 開発環境は低スペック・単一AZで運用

これからn8nを導入検討している方へ:

n8nの導入は技術的なハードルは低いですが、「何を自動化するか」の見極めが最も重要です。以下のステップをお勧めします。

  1. 小さく始める: まずは1つの定型作業を自動化してみる
  2. 効果を測定する: 工数削減効果を定量的に評価する
  3. 横展開する: 成功事例を他のチームにも共有し、組織全体で活用を広げる

参考リンク集

最後までお読みいただきありがとうございました!

本記事の構築に使用したTerraformコードを以下の附録欄に一部添付いたします! 環境構築の際にご参考にしていただけると嬉しいです!

附録