2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TerraformでWebアプリ開発 - 4. Terraformでインフラ構築.ver3

Last updated at Posted at 2025-06-25

前回の記事に続き、第4回は Fargate Deploy を GitHub Actions と連携して行えるようにします。

目次

  1. 環境構築
  2. Terraformでインフラ構築.ver1
  3. Terraformでインフラ構築.ver2
  4. Terraformでインフラ構築.ver3 ←ココ
  5. おまけ

4.Terraformでインフラ構築.ver3

このセクションでは以下を行います。

  1. フロンエンド構築
  2. Fargate Deploy with GitHubActions

実装範囲はこの辺です。

image.png

4-1. フロントエンド構築

これまで、https://subdomain.tfapp.netに接続すると、laravelのapiを返して画面を表示させていました。
laravelだけのアプリケーションであれば、あとはweb.phpapi.phpを編集することでlaravelを元にしたアプリケーションを作ることができます。
これ以降の紹介は、laravelをバックエンドAPIとして運用し、フロントエンドは別で管理し運用することを想定した実装を紹介します。

フロントエンド構築
/backendと同じ階層に/frontendを構築し、こちらでフロントエンドアプリケーションを管理することにします。

$ cd /workspace/frontend
$ npm create vite@latest .
Need to install the following packages:
create-vite@6.5.0
Ok to proceed? (y) y


> npx
> create-vite .

│
◇  Select a framework:
│  React
│
◇  Select a variant:
│  TypeScript + SWC
│
◇  Scaffolding project in /workspace/frontend...
│
└  Done. Now run:

  npm install
  npm run dev

portを編集するため、frontend/vite.config.jsを編集します。

frontend/vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    host: true,
  },
});

また、バックエンドと疎通確認するためにApp.tsxを編集します。

/frontend/src/App.tsx
import { useEffect, useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState<string>("");
  const [request, setRequest] = useState<{ data: number } | undefined>(
    undefined
  );

  const apiUrl = `${import.meta.env.VITE_BACKEND_API_BASE_URL}/api`;

  useEffect(() => {
    (async () => {
      const data = await fetch(`${apiUrl}/hoge`).then((res) => {
        return res.json();
      });
      setMessage(data.message);

      const fuga = await fetch(`${apiUrl}/fuga`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          data: 12345,
        }),
      }).then((res) => {
        return res.json();
      });
      setRequest(fuga.received);
    })();
  }, [apiUrl]);

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>{message}</h1>
      <h2>{JSON.stringify(request?.data)}</h2>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

localhost/apiでバックエンドAPIと疎通確認が取れるようにします。
.env.local.env.productionをそれぞれ作成し、以下のように修正します。
(viteはnpm run devでは.env.localを、npm run prodでは.env.productionをデフォルトで参照します)

frontend/.env.local
VITE_BACKEND_API_BASE_URL=http://localhost
frontend/.env.production
VITE_BACKEND_API_BASE_URL=https://api.tfapp.net

もし、.envがgitignoreの対象外であれば追加してください。

それぞれの.envApp.tsxにおいて、const apiUrl = `${import.meta.env.VITE_BACKEND_API_BASE_URL}/api`;とすることで、ビルドに応じてバックエンドAPIの参照先を変更しています。

バックエンドAPIの編集
現在、laravelはlocalhostで表示させています。
localhost/apiでバックエンドAPIとして機能させるように修正します。

/backend/app/Providers/RouteServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    .
    .
    .// prefix("api")を追加
        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));
        });
    }
}
/backend/routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::get('/hoge', function (Request $request) {
    return response()->json(['message' => 'hello world']);
});
Route::post('/fuga', function (Request $request) {
    return response()->json(['received' => $request->all(), 'status' => 200]);
});
/backend/config/cors.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => [env('FRONTEND_APP_DOMAIN')],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

laravelでprefix("api")を追加することで、localhost/apiでリクエストを捌くように修正します。
疎通確認用に、今回はroute.phpGETPOSTを追加しました。
また、初期設定ではオリジンの設定が"*"なので、'allowed_origins' => [env('FRONTEND_APP_DOMAIN')]を加えて制限しています。

環境変数FRONTEND_APP_DOMAINはあとで、ECRの環境変数にも設定しますが、まずはlaravelの.envに設定してローカルで疎通確認ができるように修正します。

/workspace/backend/.env
.
.
.
FRONTEND_APP_DOMAIN=http://localhost:3000
/workspace/backend/.env.example
.
.
.
FRONTEND_APP_DOMAIN=

疎通確認
npm run installnpm run devを実行して、localhost:3000を表示します。ローカル環境で hello world などバックエンドからのレスポンスが届いているかを確認します。
また、frontendのvite.config.jsでportを3000に修正しましたが、適当にportを変えてオリジンの制御ができているかを確認しておきます。

image.png

ここまでで、一度githubのmainにプッシュしておきます。
(Laravelの変更をgithubActionsでコンテナに、フロントエンドはAmplifyに適用)

AWS Amplifyでデプロイ
今回は簡略化して、AWS Amplifyを使ってフロントエンドアプリをデプロイすることにします。

作成にあたってGitHubとのOAuth認証許可が必要になります。
認証許可と作成したリポジトリを指定し、yamlを以下のように修正します。

Amplifyコンソール
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - cd frontend
        - npm install
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: frontend/dist
    files:
      - "**/*"
  cache:
    paths:
      - frontend/node_modules/**/*

laravelもあとで修正するので、一旦バックエンドAPIとして以下を設定します。

Key: VITE_BACKEND_API_BASE_URL
Value: https://api.tfapp.net

設定後、デプロイを実行し、払い出されたURLにアクセスしてフロントエンドアプリが表示できるか確認します。

サブドメインの設定
フロントエンドアプリは例としてサブドメインをfrontendと設定します。
以降はアプリケーションの構造として、
フロントエンド:frontend.tfapp.net
バックエンド:api.tfapp.net
とします。

ドメインを追加を選択し、フロントエンドアプリケーションにfrontendを設定し、カスタムドメインを設定してください。設定完了後、数分経てば設定したドメインからアプリを確認できます。

Laravelを再定義
現在Laravelはsubdomain.tfapp.netでアプリが公開されています。
本番環境ではフロントエンドのドメインを参照する必要があるので、SSMで設定します。
そのあと、Laravelアプリを再デプロイして、本番環境で疎通確認が取れるかを確認します。

SSMでフロントエンドドメインを設定
マネコンから以下の値を設定します。

キー:/test/prod/tfapp/FRONTEND_APP_DOMAIN
タイプ:安全な文字列
値:https://frontend.tfapp.net

Nginxのcontainer.confを編集します。

infra/nginx/container.conf
.
.
.
server {
    server_name api.tfapp.net; # subdomain.tfapp.net → api.tfapp.netに変更
    sendfile on;

ecs.tfのコンテナタスクを以下のように修正します。
具体的には各コンテナのimage参照方法とsecrets(FRONTEND_APP_DOMAIN)を1つ追加します。

/terraform/aws/envs/prod/app/ecs.tf
resource "aws_ecs_task_definition" "this" {
.
.
.
container_definitions = jsonencode(
        [
            {
            name = "nginx"
            // 編集
            image = "${module.nginx.ecr_repository_this_repository_url}@${data.aws_ecr_image.nginx_latest.image_digest}"

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

            environment = []

            secrets = []

            dependsOn = [
                {
                    containerName = "php"
                    condition = "START"
                }
            ]

            mountPoints = [
                {
                    containerPath = "/var/run/php-fpm"
                    sourceVolume = "php-fpm-socket"
                }
            ]

            logConfiguration = {
                logDriver = "awslogs"
                options = {
                    awslogs-group = "/ecs/${local.name_prefix}-${local.service_name}/nginx"
                    awslogs-region = data.aws_region.current.id
                    awslogs-stream-prefix = "ecs"
                }
            }
        },
        {
            name = "php"
            // 編集
            image = "${module.php.ecr_repository_this_repository_url}@${data.aws_ecr_image.php_latest.image_digest}"
            portMappings = []
            environment = []
            secrets = [
                {
                    name = "APP_KEY"
                    valueFrom = "/${local.system_name}/${local.env_name}/${local.service_name}/APP_KEY"
                },
                // 追加
                {
                    name = "FRONTEND_APP_DOMAIN" 
                    valueFrom = "/${local.system_name}/${local.env_name}/${local.service_name}/FRONTEND_APP_DOMAIN"
                }
            ]

            mountPoints = [
                {
                    containerPath = "/var/run/php-fpm"
                    sourceVolume = "php-fpm-socket"
                }
            ]

            logConfiguration = {
                logDriver = "awslogs"
                options = {
                    awslogs-group = "/ecs/${local.name_prefix}-${local.service_name}/php"
                    awslogs-region = data.aws_region.current.id
                    awslogs-stream-prefix = "ecs"
                }
            }
        }
        ]
    )
.
.
.
}

resource "aws_ecs_service" "this" {
    .
    .
    .
    enable_execute_command = true
    force_new_deployment = true // 追加
}

/app以下でmoduleの参照を追加します。

/terraform/aws/envs/prod/app/data.tf
data "aws_ecr_image" "nginx_latest" {
  repository_name = module.nginx.ecr_repository_this_name
  image_tag       = "latest"
}

data "aws_ecr_image" "php_latest" {
  repository_name = module.php.ecr_repository_this_name
  image_tag       = "latest"
}

modules/ecr/outputs.tfに以下を追加します。

aws/modules/ecr/outputs.tf
output "ecr_repository_this_name" {
  value = aws_ecr_repository.this.name
}

バックエンドAPIのサブドメインを変更
これまでLaravelアプリをsubdomain.tfapp.netとしましたが、バックエンドAPIとしてアプリを変更するため、サブドメインをapiとすることにします。
こちらのサブドメインはterraform上で管理しているため、該当箇所を修正します。

envs/prod/routing/tfapp_net/route53.tf
resource "aws_route53_record" "alb_record" {
    count = var.enable_alb ? 1 : 0
    zone_id = data.aws_route53_zone.this.zone_id
    name    = "api" // subdomain -> api に変更
    type    = "A"

    alias {
        name                   = aws_lb.this[0].dns_name
        zone_id                = aws_lb.this[0].zone_id
        evaluate_target_health = true
    }
}

修正が完了したら、それぞれの機構でapplyします。

ALB

$ cd envs/prod/routing/tfapp_net
$ terraform apply

NATゲートウェイ

$ cd envs/prod/network/main
$ terraform apply

ECS

$ cd envs/prod/app/
$ terraform apply

タスクがRUNするまで少々時間がかかりますが、Amplifyで設定したfrontend.tfapp.netに接続後、バックエンドAPIと疎通確認が取れており、localhostで立ち上げた時のように表示できていればOKです。

確認後、余計な課金を防ぐのであれば、以下のコマンドを各ディレクトリで実行してリソースの稼働を切っておきます。

ALB

$ cd envs/prod/routing/tfapp_net
$ terraform apply -var="enable_alb=false"

NATゲートウェイ

$ cd envs/prod/network/main
$ terraform apply -var='enable_nat_gateway=false'

ECRタスク

$ cd envs/prod/app
$ terraform apply -var="desired_count=0"

4-2. GitHub ActionsでFargateデプロイ

ecspressoの導入
ecspresso は ECS のサービス定義とタスク定義を管理することにフォーカスしたツールです。
デプロイのたびに更新の必要があるリソース(ECSサービスとタスク定義などのアプリケーション管理側)はecspressoで管理し、それ以外のインフラの管理を Terraform に分けることができます。

これを導入することで、GithubActionsのワークフロー上でコンテナをECR登録した後、ecspressoを実行してECSを自動でデプロイさせることができます。

ecspressoをローカルにインストールし、トップレベルにecspressoディレクトリを作成、配下にいくつかの定義ファイルを作成します。
以下のDockerfileに追記されていなければ追記します。

.devcontainer/docker/app/Dockerfile
    # AWS CLI v2 install
    RUN curl -sS "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m).zip" -o "awscliv2.zip" \
        && unzip awscliv2.zip \
        && ./aws/install \
        && rm -rf aws awscliv2.zip
    .
    .
    . 追加していなければ追記
    # ecspresso instakk
    RUN curl -Lo ecspresso.tar.gz "https://github.com/kayac/ecspresso/releases/download/v2.5.0/ecspresso_2.5.0_linux_amd64.tar.gz" \
        && tar -xzf ecspresso.tar.gz \
        && chmod +x ecspresso \
        && mv ecspresso /usr/local/bin/ \
        && rm ecspresso.tar.gz

追加した後はコンテナをリビルドします。
リビルト後はインストールできたか確認します。

$ ecspresso version
ecspresso v2.5.0

ecspressoのセットアップを行います。
/terraformと同階層にecspressoディレクトリを作成します。
その後、設定したクラスター名・サービス名を指定してconfigファイルを作成します。

ecspressoの挙動
内部的には、ecspresso initを行うことで、指定された cluster と service に対して AWS API を叩いて現在の ECS の構成を取得している。
その情報を基に以下の定義ファイルを作成している。

$ mkdir ecspresso
$ cd ecspresso
$ ecspresso init --config config.yaml --region ap-northeast-1 --cluster test-prod-tfapp --service test-prod-tfapp

各種ファイルが生成できれば、ecspressoによる手動デプロイを試します。

dry run
ecspressoでデプロイするときに、以下のように--dry-runオプションを付けることで、実際にはデプロイせずに、デプロイの事前検証ができます。実行後にDRY RUN OKと出ればOKです。

$ ecspresso deploy --config config.yaml --dry-run
2025-06-24T13:38:37.390+09:00 [INFO] [test-prod-tfapp/test-prod-tfapp] service attributes will not change
2025-06-24T13:38:37.390+09:00 [INFO] [test-prod-tfapp/test-prod-tfapp] desired count: 0
2025-06-24T13:38:37.390+09:00 [INFO] [test-prod-tfapp/test-prod-tfapp] DRY RUN OK

ecspressoによるデプロイ
実際にecspressoでデプロイするには以下のコマンドを叩きます。
Completed!となればデプロイ成功です。
(ロードバランサー / NATゲートウェイが有効であること)

$ ecspresso deploy --config config.yaml

ブラウザでアプリケーションにアクセスして問題なくECSタスクが起動していればOKです。

ECSタスクを止める
--tasks 0を付与することで、すべてのECSタスクが停止できます。
Completed!となれば停止成功です。

$ ecspresso deploy --config config.yaml --tasks 0

tfstate共有とデプロイ環境対応
設定したtfstateを直接参照できるように設定します。
ecspressoは参照できるtfstateが1つしかできないので、ecspresso用のterraformファイルを作成します。
実装のイメージとしては、

  • ecspresso用にtfファイルを作成 & 参照設定
  • githubActionsからecspressoを叩けるようにiam_policyを追加
  • workflow/deploy.yaml修正
  • espresso/以下のconfigファイル修正

で行います。

まずは 「ecspresso用にtfファイルを作成 & 参照設定」 を行います。
以下のディレクトリにファイルを作成します。

/envs/prod/cicd/app/ecspresso.tf
data "aws_ecr_repository" "nginx" {
  name = "${local.name_prefix}-${local.service_name}-nginx"
}

data "aws_ecr_repository" "php" {
  name = "${local.name_prefix}-${local.service_name}-php"
}

data "aws_iam_role" "ecs_task_execution" {
  name = "${local.name_prefix}-${local.service_name}-ecs-task-execution"
}

data "aws_iam_role" "ecs_task" {
  name = "${local.name_prefix}-${local.service_name}-ecs-task"
}

data "aws_lb_target_group" "this" {
    name = "${local.name_prefix}-${local.service_name}"
}

data "aws_security_group" "vpc" {
    name = "${local.name_prefix}-main-vpc"
}

data "aws_subnet" "private" {
  for_each = var.azs

  tags = {
    Name = "${local.name_prefix}-main-private-${each.key}"
  }
}

variable "azs" {
  type = map(object({
    public_cidr  = string
    private_cidr = string
  }))

  default = {
    a = {
      public_cidr  = "172.31.0.0/20"
      private_cidr = "172.31.48.0/20"
    },

    c = {
      public_cidr  = "172.31.16.0/20"
      private_cidr = "172.31.64.0/20"
    }
  }
}

data "aws_cloudwatch_log_group" "nginx" {
    name = "/ecs/${local.name_prefix}-${local.service_name}/nginx"
}

data "aws_cloudwatch_log_group" "php" {
    name = "/ecs/${local.name_prefix}-${local.service_name}/php"
}
/envs/prod/cicd/app/data.tf
data "aws_ecs_service" "this" {
    cluster_arn = "${local.name_prefix}-${local.service_name}"
    service_name = "${local.name_prefix}-${local.service_name}"
}

追加したtfstate(S3)をecspressoから読み込めるようにiam.tfを編集します。

/envs/prod/cicd/app/iam.tf
.
.
.
# ecspresso s3読取権限
resource "aws_iam_role_policy" "s3" {
  name = "s3"
  role = aws_iam_role.deployer.id

  policy = jsonencode(
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject"
                ],
                "Resource": "arn:aws:s3:::tfapp-tfstate/${local.system_name}/${local.env_name}/cicd/app/${local.service_name}_*.tfstate"
            }
        ]
    }
  )
}

# ecspresso ecsデプロイ権限
resource "aws_iam_role_policy" "ecs" {
    name = "ecs"
    role = aws_iam_role.deployer.id

    policy = jsonencode(
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "RegisterTaskDefinition",
                    "Effect": "Allow",
                    "Action": [
                        "ecs:RegisterTaskDefinition",
                        "ecs:TagResource"
                    ],
                    "Resource": "*"
                },
                {
                    "Sid": "PassRoleInTaskDefinition",
                    "Effect": "Allow",
                    "Action": [
                        "iam:PassRole"
                    ],
                    "Resource": [
                        data.aws_iam_role.ecs_task.arn,
                        data.aws_iam_role.ecs_task_execution.arn
                    ]
                },
                {
                    "Sid": "DeployService",
                    "Effect": "Allow",
                    "Action": [
                        "ecs:UpdateService",
                        "ecs:DescribeServices",
                    ],
                    "Resource": [
                        data.aws_ecs_service.this.arn
                    ]
                }
            ]
        }
    )
}

作成後、applyを実行しecspressoでtfstateを読み込めるようにします。

$ cd /envs/prod/cicd/app
$ terraform apply

workflow/deploy.yamlを修正します。
コンテナをECRにプッシュした後、ecspressoをセットアップ→ECSにデプロイするステップを追加します。

.github/workflows/deploy.yml
.
.
. 以下追記
      - name: Set up ecspresso
        uses: kayac/ecspresso@v0
        with:
          version: v2.5.0

      - name: Deploy to ECS
        run: ecspresso deploy --config config_$ENV_NAME.yaml
        working-directory: ./ecspresso

run: ecspresso deploy --config config_$ENV_NAME.yamlにもあるように、各環境によって、configの参照を変えるようにしています。そのため、ecspresso initをしたときに作成された、config.yamlconfig_prod.yamlにリネームした上で以下の編集を続けます。

ecspressoから実行したtfstateを読み込みめるようにpluginをconfig.yamlに追記します。

/ecspresso/config_prod.yaml
.
.
.
timeout: "10m0s"
image_digest: true // 追記
plugins: // 追記
  - name: tfstate
    config:
      url: s3://tfapp-tfstate/test/prod/cicd/app/tfapp_v1.5.0.tfstate

/ecspressoのjsonをそれぞれ修正します。

/ecspresso/ecs-service-def.json
{
  "availabilityZoneRebalancing": "DISABLED",
  "capacityProviderStrategy": [
    {
      "base": 0,
      "capacityProvider": "FARGATE_SPOT",
      "weight": 1
    }
  ],
  "deploymentConfiguration": {
    "deploymentCircuitBreaker": {
      "enable": false,
      "rollback": false
    },
    "maximumPercent": 200,
    "minimumHealthyPercent": 100
  },
  "deploymentController": {
    "type": "ECS"
  },
  "desiredCount": 1,
  "enableECSManagedTags": false,
  "enableExecuteCommand": true,
  "healthCheckGracePeriodSeconds": 60,
  "launchType": "",
  "loadBalancers": [
    {
      "containerName": "nginx",
      "containerPort": 80,
      "targetGroupArn": "{{ tfstate `data.aws_lb_target_group.this.arn` }}"
    }
  ],
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "assignPublicIp": "DISABLED",
      "securityGroups": [
        "{{ tfstate `data.aws_security_group.vpc.id` }}"
      ],
      "subnets": [
        "{{ tfstate `data.aws_subnet.private['a'].id` }}",
        "{{ tfstate `data.aws_subnet.private['c'].id` }}"
      ]
    }
  },
  "platformFamily": "Linux",
  "platformVersion": "1.4.0",
  "propagateTags": "NONE",
  "schedulingStrategy": "REPLICA",
  "tags": [
    {
      "key": "Env",
      "value": "{{ must_env `ENV_NAME` }}"
    },
    {
      "key": "System",
      "value": "{{ must_env `SYSTEM_NAME` }}"
    },
    {
      "key": "Name",
      "value": "{{ must_env `SYSTEM_NAME` }}-{{ must_env `ENV_NAME` }}-{{ must_env `SERVICE_NAME` }}"
    }
  ]
}
/ecspresso/ecs-task-def.json
{
  "containerDefinitions": [
    {
      "cpu": 0,
      "dependsOn": [
        {
          "condition": "START",
          "containerName": "php"
        }
      ],
      "essential": true,
      "image": "{{ tfstate `data.aws_ecr_repository.nginx.repository_url` }}:{{ must_env `IMAGE_TAG` }}",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {

          "awslogs-group": "{{ tfstate `data.aws_cloudwatch_log_group.nginx.name` }}",
          "awslogs-region": "{{ must_env `AWS_REGION` }}",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "mountPoints": [
        {
          "containerPath": "/var/run/php-fpm",
          "sourceVolume": "php-fpm-socket"
        }
      ],
      "name": "nginx",
      "portMappings": [
        {
          "appProtocol": "",
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ],
      "versionConsistency": ""
    },
    {
      "cpu": 0,
      "essential": true,
      "image": "{{ tfstate `data.aws_ecr_repository.php.repository_url` }}:{{ must_env `IMAGE_TAG` }}",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "{{ tfstate `data.aws_cloudwatch_log_group.php.name` }}",
          "awslogs-region": "{{ must_env `AWS_REGION` }}",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "mountPoints": [
        {
          "containerPath": "/var/run/php-fpm",
          "sourceVolume": "php-fpm-socket"
        }
      ],
      "name": "php",
      "secrets": [
        {
          "name": "APP_KEY",
          "valueFrom": "/{{ must_env `SYSTEM_NAME` }}/{{ must_env `ENV_NAME` }}/{{ must_env `SERVICE_NAME` }}/APP_KEY"
        },
        {
          "name": "FRONTEND_APP_DOMAIN",
          "valueFrom": "/{{ must_env `SYSTEM_NAME` }}/{{ must_env `ENV_NAME` }}/{{ must_env `SERVICE_NAME` }}/FRONTEND_APP_DOMAIN"
        }
      ],
      "versionConsistency": ""
    }
  ],
  "cpu": "256",
  "executionRoleArn": "{{ tfstate `data.aws_iam_role.ecs_task_execution.arn` }}",
  "family": "{{ must_env `SYSTEM_NAME` }}-{{ must_env `ENV_NAME` }}-{{ must_env `SERVICE_NAME` }}",
  "ipcMode": "",
  "memory": "512",
  "networkMode": "awsvpc",
  "pidMode": "",
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "tags": [
    {
      "key": "Env",
      "value": "{{ must_env `ENV_NAME` }}"
    },
    {
      "key": "System",
      "value": "{{ must_env `SYSTEM_NAME` }}"
    },
    {
      "key": "Name",
      "value": "{{ must_env `SYSTEM_NAME` }}-{{ must_env `ENV_NAME` }}-{{ must_env `SERVICE_NAME` }}"
    }
  ],
  "taskRoleArn": "{{ tfstate `data.aws_iam_role.ecs_task.arn` }}",
  "volumes": [
    {
      "host": {},
      "name": "php-fpm-socket"
    }
  ]
}

ここまで設定した後はmainブランチにコードをプッシュすると自動でECR登録→ECSデプロイが実行されます。
なお、/ecspresso以下のファイルはtfstateやENV_NAMEなど注入された値を参照するように編集しているため、ローカルでECSデプロイを行う場合は

# 起動
$ IMAGE_TAG=latest  AWS_REGION=ap-northeast-1 \
SYSTEM_NAME=test ENV_NAME=prod SERVICE_NAME=tfapp \
ecspresso deploy --config=config_prod.yaml --skip-task-definition

# 停止
$ IMAGE_TAG=latest  AWS_REGION=ap-northeast-1 \
SYSTEM_NAME=test ENV_NAME=prod SERVICE_NAME=tfapp \
ecspresso deploy --config=config_prod.yaml --skip-task-definition --tasks 0

のように環境変数などを指定する必要があります。

次回

ここまでで4.Terraformでインフラ構築.ver3が終了しました。
1~4回までで一通りWebアプリケーション実装ができました。
次回おまけセクションでは、MFA設定とSwitchRole設定を紹介できればと思います。

他の記事

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?