MENU

問い合わせ


    【Laravel+Vue.jsで作る管理画面をECSで動かす 第9回】ECR・ECS on Fargate・ALB:管理画面をインターネットに公開する

    いよいよ、第6回で作ったコンテナイメージを AWS の ECS on Fargate(=サーバーを自分で管理せずにコンテナを動かせるサービス)で動かし、ALB(=ロードバランサー。利用者からのアクセスを受けて複数のコンテナに振り分ける装置)経由で HTTPS 公開します。

    「コンテナで動かすと聞いたけれど、ECS のタスク定義・サービス・ALB・IAM ロールと部品が多すぎて、どれが何をしているのか分からない…」

    結論から言うと、ECS で Laravel の管理画面を公開するときの部品は「イメージ置き場(ECR)」「コンテナの設計図(タスク定義)」「台数を保つ係(サービス)」「入口(ALB)」の4つで、権限は「ECS が使うロール」と「アプリが使うロール」に分けます。この回ではこれらを Terraform で定義し、あわせて ALB の裏で Laravel が HTTPS を正しく認識するための設定(信頼するプロキシ)を入れます。

    前回(第8回:RDS for MySQL と秘密情報)では、RDS と秘密情報の置き場所、秘密情報を読むためのタスク実行ロールを作りました。今回はそれらをタスク定義から参照します。

    これまでと同じく、執筆環境には AWS アカウントがないため、terraform apply は行っていません。terraform validate と、AWS に接続しない形での plan、ローカルの Docker での動作確認までを行っています。

    目次

    ECS で管理画面を公開するための部品

    今回追加・変更するファイルは次のとおりです。

    ファイル内容
    infra/terraform/ecr.tfイメージの置き場所(app = php-fpm、web = nginx)と古いイメージの自動削除
    infra/terraform/logs.tfコンテナのログの保存先(CloudWatch Logs)と保持期間
    infra/terraform/ecs_cluster.tfECS クラスターと、タスク定義で共通に使う環境変数・秘密情報の参照
    infra/terraform/ecs_web.tfweb のタスク定義(nginx + php-fpm)とサービス
    infra/terraform/alb.tfALB、ターゲットグループ(ヘルスチェック /up)、HTTP→HTTPS リダイレクト、HTTPS リスナー
    infra/terraform/iam.tfアプリ用のタスクロールを追加
    infra/terraform/security_groups.tfALB に接続できる IP 範囲を変数化
    config/trustedproxy.php、tests/Feature/TrustedProxyTest.phpALB を信頼するプロキシとして扱う設定とテスト

    リクエストの流れは次のようになります。

    • 利用者のブラウザ →(HTTPS)→ ALB(public サブネット)
    • ALB →(HTTP 8080)→ web タスクの nginx(private サブネット)
    • nginx →(127.0.0.1:9000)→ 同じタスクの php-fpm(Laravel)
    • php-fpm → RDS(3306)、ログは標準出力 → CloudWatch Logs

    ECR:イメージの置き場所を作る

    infra/terraform/ecr.tf(抜粋)

    locals {
      ecr_repositories = toset(["app", "web"])
    }
    
    resource "aws_ecr_repository" "this" {
      for_each = local.ecr_repositories
    
      name                 = "${local.name_prefix}-${each.key}"
      image_tag_mutability = "IMMUTABLE"
    
      # push 時に既知の脆弱性をスキャンする(基本スキャン)
      image_scanning_configuration {
        scan_on_push = true
      }
    
      # (encryption_configuration と tags は省略)
    }
    
    # 古いイメージを自動で削除する(保管費用を抑える)。切り戻し用に直近の数世代は残す
    resource "aws_ecr_lifecycle_policy" "this" {
      for_each = aws_ecr_repository.this
    
      repository = each.value.name
      policy = jsonencode({
        rules = [
          # (rulePriority = 1:タグなしのイメージを1日で削除、は省略)
          {
            rulePriority = 2
            description  = "Keep only the latest ${var.ecr_keep_images} images"
            selection = {
              tagStatus   = "any"
              countType   = "imageCountMoreThan"
              countNumber = var.ecr_keep_images
            }
            action = { type = "expire" }
          },
        ]
      })
    }

    ポイントは image_tag_mutability = "IMMUTABLE" です。同じタグでイメージを上書きできなくするので、latest のような使い回しのタグではなく、Git のコミット ID などをタグにします。「今どのコードが動いているか」をタグから必ずたどれるようにし、切り戻し(前の版に戻す)のときも迷わないようにするためです。

    ライフサイクルポリシーは、イメージを無制限にためないための設定です。ただし、残す世代数は「どこまで前の版に戻せるか」と同じ意味になるので、少なくしすぎないようにします。

    📰 出典:Amazon ECR のライフサイクルポリシー(Amazon ECR ユーザーガイド)

    タスク定義:nginx と php-fpm を1つのタスクにまとめる

    共通の環境変数と秘密情報

    web・worker・scheduler・migrate(第10回)の php コンテナには、同じ環境変数と秘密情報を渡します。そこで、ecs_cluster.tf の locals に一度だけ書いて使い回します。

    infra/terraform/ecs_cluster.tf(抜粋)

    locals {
      # php コンテナ(web・worker・scheduler・migrate 共通)に渡す環境変数。秘密ではない値だけ
      app_environment = [
        { name = "APP_NAME", value = "Admin Console" },
        { name = "APP_URL", value = "https://${var.app_domain}" },
        { name = "APP_LOCALE", value = "ja" },
        { name = "DB_CONNECTION", value = "mysql" },
        { name = "DB_HOST", value = aws_db_instance.main.address },
        { name = "DB_PORT", value = tostring(aws_db_instance.main.port) },
        { name = "DB_DATABASE", value = aws_db_instance.main.db_name },
        { name = "SESSION_DRIVER", value = "database" },
        { name = "SESSION_SECURE_COOKIE", value = "true" },
        { name = "CACHE_STORE", value = "database" },
        { name = "QUEUE_CONNECTION", value = "database" },
        # ALB からの X-Forwarded-* を信用する範囲(config/trustedproxy.php)
        { name = "TRUSTED_PROXIES", value = var.vpc_cidr },
        # (APP_FALLBACK_LOCALE は省略)
      ]
    
      # 秘密情報:値ではなく「どこにあるか(ARN)」だけを書く。取得はタスク実行ロールが行う(第8回)
      app_secrets = concat(
        [
          { name = "DB_USERNAME", valueFrom = "${aws_db_instance.main.master_user_secret[0].secret_arn}:username::" },
          { name = "DB_PASSWORD", valueFrom = "${aws_db_instance.main.master_user_secret[0].secret_arn}:password::" },
        ],
        [for name, p in aws_ssm_parameter.app_secret : { name = name, valueFrom = p.arn }],
      )
    
      app_image = "${aws_ecr_repository.this["app"].repository_url}:${var.image_tag}"
      web_image = "${aws_ecr_repository.this["web"].repository_url}:${var.image_tag}"
    }

    secrets に書くのは値ではなく ARN(=AWS 上の場所を表す識別子)です。RDS が管理するシークレットは username と password を持つ JSON なので、ARN の後ろに :password:: のようにキー名を付けて、その項目だけを環境変数に入れます。ECS がタスクの起動時にタスク実行ロールの権限で値を取り出し、コンテナの環境変数として渡します。

    📰 出典:Secrets Manager シークレットを環境変数として Amazon ECS に渡す(Amazon ECS デベロッパーガイド)

    第6回の本番イメージは .env を含まず、APP_ENV=production・APP_DEBUG=false・LOG_CHANNEL=stderr だけをイメージの既定値にしています。それ以外の設定は、すべてこのタスク定義から渡すことになります。

    web のタスク定義

    infra/terraform/ecs_web.tf(タスク定義の抜粋)

    resource "aws_ecs_task_definition" "web" {
      family                   = "${local.name_prefix}-web"
      requires_compatibilities = ["FARGATE"]
      network_mode             = "awsvpc"
      cpu                      = var.web_cpu
      memory                   = var.web_memory
    
      # タスク実行ロール:ECS がイメージ取得・ログ送信・secrets の取得に使う(第8回)
      execution_role_arn = aws_iam_role.ecs_task_execution.arn
      # タスクロール:アプリ(Laravel)が AWS のサービスを使うときの権限(第10回で SQS を追加)
      task_role_arn = aws_iam_role.ecs_task.arn
    
      container_definitions = jsonencode([
        {
          name      = "php"
          image     = local.app_image
          essential = true
          # CMD の既定(php-fpm)のまま。ENTRYPOINT が起動時に php artisan optimize を実行する(第6回)
          environment = local.app_environment
          secrets     = local.app_secrets
          logConfiguration = {
            logDriver = "awslogs"
            options = {
              awslogs-group         = aws_cloudwatch_log_group.ecs["web"].name
              awslogs-region        = var.region
              awslogs-stream-prefix = "php"
            }
          }
        },
        {
          name      = "nginx"
          image     = local.web_image
          essential = true
          portMappings = [
            { containerPort = 8080, protocol = "tcp" }
          ]
          # php コンテナが起動してから nginx を起動する
          dependsOn = [
            { containerName = "php", condition = "START" }
          ]
          # (logConfiguration は php と同じ。awslogs-stream-prefix = "nginx")
        },
      ])
    
      # (runtime_platform と tags は省略)
    }

    network_mode = "awsvpc" では、同じタスクのコンテナは localhost を共有します。第6回で nginx の転送先を 127.0.0.1:9000 にし、compose.prod.yaml で network_mode: service:app を使って確認したのは、この動きをローカルで再現するためでした。

    CPU とメモリはタスク全体(nginx と php-fpm の合計)の値で、Fargate では決まった組み合わせから選びます。0.5 vCPU(512)なら、メモリは 1GB〜4GB の範囲です。

    📰 出典:Fargate の Amazon ECS タスク定義の CPU とメモリ(Amazon ECS デベロッパーガイド)

    タスク実行ロールとタスクロールを分ける

    task_role_arn に指定したタスクロールは、今回新しく作ったロールです。

    infra/terraform/iam.tf(追加部分)

    # 第9回の時点では権限を付けない(DB へは SG と DB ユーザーで接続し、IAM 権限は使わない)。
    # 第10回で SQS の送受信の権限をここに追加する。
    resource "aws_iam_role" "ecs_task" {
      name               = "${local.name_prefix}-ecs-task"
      assume_role_policy = data.aws_iam_policy_document.ecs_tasks_assume.json
    }

    2つのロールの違いは次のとおりです。

    ロール使う主体主な権限
    タスク実行ロール(第8回)ECS(タスクを起動する側)ECR からのイメージ取得、ログ送信、secrets の取得
    タスクロール(今回)コンテナ内のアプリ(Laravel)アプリが呼ぶ AWS サービス(第10回で SQS)

    分けておくと、アプリに脆弱性があって AWS の認証情報を悪用されたとしても、秘密情報の取得権限までは渡りません。今の時点でタスクロールに権限は何もありません。

    📰 出典:Amazon ECS タスクの IAM ロール(Amazon ECS デベロッパーガイド)

    サービスと ALB:2つの AZ で動かし、/up で見張る

    infra/terraform/ecs_web.tf(サービスの抜粋)

    resource "aws_ecs_service" "web" {
      name            = "web"
      cluster         = aws_ecs_cluster.main.id
      task_definition = aws_ecs_task_definition.web.arn
      desired_count   = var.web_desired_count
      launch_type     = "FARGATE"
    
      # private サブネット(2AZ)に置き、パブリック IP は付けない。外向きは NAT Gateway 経由(第7回)
      network_configuration {
        subnets          = aws_subnet.private[*].id
        security_groups  = [aws_security_group.web.id]
        assign_public_ip = false
      }
    
      load_balancer {
        target_group_arn = aws_lb_target_group.web.arn
        container_name   = "nginx"
        container_port   = 8080
      }
    
      # 起動直後(php artisan optimize の実行中など)にヘルスチェック失敗で入れ替えられないよう、猶予を置く
      health_check_grace_period_seconds = 60
    
      # デプロイ時:新しいタスクが healthy になってから古いタスクを止める(台数が一時的に最大2倍になる)
      deployment_minimum_healthy_percent = 100
      deployment_maximum_percent         = 200
    
      # (depends_on と tags は省略)
    }

    サービスは「指定した台数(desired_count)のタスクを動かし続ける係」です。タスクが落ちれば新しく起動し直し、ALB のターゲットにも自動で登録します。既定の2台は、2つの AZ(=データセンターのまとまり)に分かれて置かれるので、片方の AZ に障害が起きても画面は使えます。

    infra/terraform/alb.tf(ターゲットグループと HTTPS リスナーの抜粋)

    resource "aws_lb_target_group" "web" {
      name        = "${local.name_prefix}-web"
      target_type = "ip" # Fargate(awsvpc)はタスクの IP アドレスを登録する
      port        = 8080
      protocol    = "HTTP"
      vpc_id      = aws_vpc.main.id
    
      health_check {
        path                = "/up" # Laravel のヘルスチェック用ルート(bootstrap/app.php の health: '/up')
        matcher             = "200"
        interval            = 15
        timeout             = 5
        healthy_threshold   = 2
        unhealthy_threshold = 3
      }
    
      # デプロイ時、古いタスクへの処理中リクエストを待つ時間(既定 300 秒は長いので短くする)
      deregistration_delay = 30
    }
    
    resource "aws_lb_listener" "https" {
      load_balancer_arn = aws_lb.main.arn
      port              = 443
      protocol          = "HTTPS"
      ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06" # TLS 1.2 以上
      certificate_arn   = var.acm_certificate_arn
    
      default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.web.arn
      }
    }

    80番(HTTP)のリスナーは、443番(HTTPS)へ 301 リダイレクトするだけにしています。証明書は、ドメインの DNS で検証して ACM(=AWS の無料の証明書発行サービス)に発行したものの ARN を変数で渡します。ドメインの取得や DNS の設定は、会社ごとに管理の仕方が違うため Terraform の外に出しました。

    ALB は /up に定期的にアクセスし、200 が返るタスクだけにリクエストを流します。

    📰 出典:ターゲットグループのヘルスチェック(Application Load Balancer ユーザーガイド)

    接続できる IP を絞る

    infra/terraform/security_groups.tf(変更部分)

    # 接続を許可する元の IP 範囲は変数で指定する(第9回)。既定はインターネット全体、
    # 検証環境や社内専用の管理画面なら、社内・VPN の IP だけに絞る
    resource "aws_vpc_security_group_ingress_rule" "alb_https" {
      for_each = toset(var.alb_ingress_cidrs)
    
      security_group_id = aws_security_group.alb.id
      description       = "HTTPS"
      cidr_ipv4         = each.value
      ip_protocol       = "tcp"
      from_port         = 443
      to_port           = 443
    }

    社内向けの管理画面なら、インターネット全体に公開する理由はあまりありません。terraform.tfvars の alb_ingress_cidrs に社内や VPN の IP 範囲を書けば、それ以外からは ALB に届かなくなります。特にこの連載のサンプルは、スターターキットの会員登録画面を残したままです(第3回で触れたとおり)。誰でもアカウントを作れる状態でインターネット全体に公開しないよう、最初は IP で絞っておくことをおすすめします。

    ALB の裏で HTTPS を正しく認識させる(TrustProxies)

    ALB で HTTPS を終端すると、Laravel から見た通信は「ALB から来た HTTP」です。そのままだと、リダイレクト先やフォームの送信先の URL が http:// で作られ、画面が正しく動きません。

    Laravel には、リバースプロキシ(=利用者とアプリの間に立って中継するサーバー。ここでは ALB)が付ける X-Forwarded-Proto などのヘッダーを信用する仕組み(TrustProxies ミドルウェア)が最初から入っています。信用する接続元を設定ファイルで指定しました。

    config/trustedproxy.php

    <?php
    
    declare(strict_types=1);
    
    /*
    | 信頼するリバースプロキシ(ALB)の IP アドレス範囲(第9回)
    | (中略)
    | ここに書いた範囲からの接続に限り、ALB が付ける X-Forwarded-For / X-Forwarded-Proto などを信用する。
    | フレームワークの TrustProxies ミドルウェア(既定で有効)が、bootstrap/app.php で
    | trustProxies(at: ...) を指定していないときにこの設定を読む。
    |
    | 例:TRUSTED_PROXIES=10.0.0.0/16(VPC の範囲。ECS のタスク定義で渡す)
    | 未設定(ローカル開発)の場合はどのプロキシも信用しない。
    */
    
    return [
        'proxies' => env('TRUSTED_PROXIES'),
    ];

    公式ドキュメントでは bootstrap/app.php で trustProxies(at: '*') のように指定する方法が紹介されています。今回は、設定キャッシュ(php artisan optimize)と環境変数で切り替えやすいように設定ファイルにし、値には VPC の IP 範囲を渡しました。web タスクには ALB からしか届かないようにセキュリティグループで絞っていますが、ヘッダーを信用する範囲も VPC 内に限定しておけば、万一ほかの経路から届いた偽のヘッダーは無視されます。

    📰 出典:信頼できるプロキシの設定(Laravel 12.x ドキュメント)

    tests/Feature/TrustedProxyTest.php(抜粋)

    public function test_requests_from_trusted_proxy_are_treated_as_https(): void
    {
        config(['trustedproxy.proxies' => '10.0.0.0/16']);
    
        $response = $this->withServerVariables(['REMOTE_ADDR' => '10.0.1.25'])
            ->withHeaders($this->albHeaders)
            ->get('/dashboard');
    
        $response->assertRedirect();
        $this->assertStringStartsWith('https://', (string) $response->headers->get('Location'));
    }
    
    public function test_forwarded_headers_from_untrusted_address_are_ignored(): void
    {
        config(['trustedproxy.proxies' => '10.0.0.0/16']);
    
        // VPC の外から直接ヘッダーを偽装しても、HTTPS 扱いにはならない
        $response = $this->withServerVariables(['REMOTE_ADDR' => '198.51.100.7'])
            ->withHeaders($this->albHeaders)
            ->get('/dashboard');
    
        $response->assertRedirect();
        $this->assertStringStartsWith('http://', (string) $response->headers->get('Location'));
    }

    未ログインで /dashboard を開いたときのログイン画面へのリダイレクト先で、HTTPS として扱われているかを確かめています。このほか、設定がない場合は信用しないこと、信用した場合はアクセス元 IP が X-Forwarded-For の値になることもテストしています。あわせて、タスク定義で SESSION_SECURE_COOKIE=true を渡し、セッションの Cookie が HTTPS のときだけ送られるようにしました。

    初回のデプロイ手順

    ECR が無いとイメージを push できず、イメージが無いとサービスのタスクが起動できません。初回だけは次の順に進めます(コマンドは infra/terraform で実行する想定。<...> は自分の環境の値)。

    # 1. ECR だけ先に作る
    terraform apply -target='aws_ecr_repository.this'
    
    # 2. イメージを build して push する(app と web は同じタグにする)
    aws ecr get-login-password --region ap-northeast-1 \
      | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
    TAG=$(git rev-parse --short HEAD)
    docker build --target app -t <app のリポジトリ URL>:$TAG .
    docker build --target web -t <web のリポジトリ URL>:$TAG .
    docker push <app のリポジトリ URL>:$TAG
    docker push <web のリポジトリ URL>:$TAG
    
    # 3. SSM に APP_KEY を登録しておく(第8回)
    
    # 4. terraform.tfvars の image_tag に $TAG を書いて、残りをすべて作る
    terraform plan
    terraform apply

    リポジトリの URL は terraform output ecr_repository_urls で確認できます。最後に、app_domain の DNS レコード(CNAME やエイリアス)を terraform output alb_dns_name の ALB に向ければ、https://<ドメイン>/up にアクセスできるようになります。

    ただし、この時点ではまだデータベースにテーブルがありません。/up は DB を使わないので 200 を返しますが、ログイン画面はセッションを DB に保存するため表示できません。テーブルを作るマイグレーションは、次回、専用のタスクで実行します(アプリの起動時に自動で流さない理由も次回説明します)。

    動作確認の方法

    # Laravel のテスト(ローカル、Docker)
    docker compose run --rm --no-deps app php artisan test
    
    # Terraform(infra/terraform で実行)
    docker run --rm -v $PWD:/w -w /w hashicorp/terraform:1.11.4 fmt -check -recursive
    docker run --rm -v $PWD:/w -w /w hashicorp/terraform:1.11.4 init -backend=false
    docker run --rm -v $PWD:/w -w /w hashicorp/terraform:1.11.4 validate

    筆者の環境で確認したことは次のとおりです。

    • php artisan test は 66件すべて成功(今回追加した TrustedProxyTest の4件を含む)
    • terraform fmt -check・validate が成功。AWS に接続しない形での plan は Plan: 58 to add(第8回の42件に、ECR・ライフサイクルポリシー・ロググループ・クラスター・タスク定義・サービス・タスクロール・ALB・ターゲットグループ・リスナーの16件が追加)
    • 本番イメージを compose.prod.yaml で起動し、TRUSTED_PROXIES に Docker のネットワーク範囲を指定したうえで、X-Forwarded-Proto: https を付けて /dashboard にアクセスすると、リダイレクト先が https:// になり、Cookie に secure が付く。ヘッダーなしでは http:// のまま

    AWS 上での確認(ALB の DNS 名への curl -I https://<ドメイン>/up が 200、ECS コンソールでターゲットが healthy、CloudWatch Logs の /ecs/admin-stg/web に nginx のアクセスログが出ること)は、AWS アカウントがないため行っていません。ヘルスチェックの猶予時間(60秒)が十分かどうかも、実際の起動時間を見て調整が必要です。

    つまずきやすい点・セキュリティ上の注意

    • /up は DB の状態を見ない:Laravel の /up はアプリが起動していれば 200 を返し、DB に接続できなくても healthy になります。DB の異常は、第12回のアラームで検知します。
    • 第7回の構成を apply 済みの場合:ALB のセキュリティグループのルールを for_each に変えたため、plan では作り直し(削除と作成)になります。作り直しの間、ALB への通信が一瞬止まる点に注意してください。
    • イメージの CPU アーキテクチャ:タスク定義は X86_64 にしています。Apple Silicon の Mac などでビルドしたイメージは ARM64 になり、そのままでは起動できません。第11回で CI からビルドするようにして、ビルド環境をそろえます。
    • 会員登録画面が開いている:前述のとおり、IP の制限か、会員登録のルートを外す対応が必要です(第12回で本番前の残作業として整理します)。
    • ドメインと証明書:証明書の発行・更新の DNS 検証レコードを誰が管理しているか(社内か開発会社か)を確認しておかないと、更新時に止まることがあります。

    発注者向けメモ:月額の土台は「常に動く台数」で決まる

    Fargate の料金は、タスクに割り当てた CPU とメモリ × 動いている時間で決まります。つまり、画面にアクセスが無い夜間も、desired_count の台数分の費用が発生します。ALB も時間単位の料金がかかる部品です。

    決めること費用・リスクへの影響決めるための問い
    web の台数(最小2台か1台か)2台なら AZ 障害やデプロイ中も止まりにくいが、常に2台分の費用数分止まってよい検証環境か、業務時間中は止められない本番か
    タスクの CPU・メモリ大きいほど費用が増える。小さすぎると遅くなる同時に使う人数、重い画面(一覧・帳票)があるか
    公開範囲(IP 制限)社内・VPN に絞ると攻撃を受ける面が小さくなる社外から使う人(在宅・取引先)はいるか
    ログの保持期間長いほど保管費用が増える障害や不正の調査で何日前まで見返したいか

    開発会社には、次のように確認してみてください。

    • 「検証環境と本番で、タスクの台数と CPU・メモリはどう違いますか?夜間も同じ台数で動かす必要がありますか?」
    • 「管理画面に接続できる IP は制限していますか?社外から使う場合はどうしますか?」
    • 「ドメインと SSL 証明書は誰のアカウントで管理し、更新はどう行いますか?」
    • 「今動いているのがどのバージョンのコードか、どこで確認できますか?前の版に戻すにはどうしますか?」

    まとめと次回予告

    第9回では、管理画面をインターネットに公開するための部品を定義しました。

    • ECR はタグの上書きを禁止し、ライフサイクルポリシーで古いイメージを整理する
    • タスク定義は nginx と php-fpm を1つのタスクにまとめ、秘密情報は ARN で参照して起動時に注入する
    • ECS が使うタスク実行ロールと、アプリが使うタスクロールを分ける
    • サービスで2つの AZ に2台を保ち、ALB は /up で見張り、HTTP は HTTPS にリダイレクトする
    • ALB の裏で HTTPS を正しく扱うため、VPC の範囲だけを信頼するプロキシとして設定する

    次回は「キューワーカーとスケジューラを ECS で動かす」です。SQS のキューとデッドレターキュー、worker・scheduler のサービス、そして今回見送ったマイグレーションを実行する専用タスクを作ります。

    この連載の記事一覧

    この記事は連載「Laravel+Vue.jsで作る管理画面をECSで動かす」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      コメント

      コメント一覧 (1件)

      目次