Laravel10環境をECS Fargate上に構築する


概要

  • 本記事では CI/CD は実装してないです。すべて手動。
    • 手動でやる部分は全て構成図のように CodePipeline で自動化できると思います。
  • ALB も省略しています。Fargate に直接アクセスさせます。
C:.
├─docker
│  │  .env
│  │  docker-compose.yml
│  │
│  └─php
│          Dockerfile

├─laravel
├─Dockerile.deploy
└─terraform
    │  main.tf
    └─ task-definitions.json

ローカルで Docker コンテナ作成 ↓ コンテナ内で Laravel プロジェクト作成&ローカル PC のディレクトリにマウント ↓ Fargate にプッシュする際に、ローカル PC のディレクトリにマウントした Laravel プロジェクトをコピーしてプッシュする

ローカル開発環境作成

php/Dockerfile

FROM php:8.2-apache
RUN a2enmod rewrite
RUN docker-php-ext-install pdo_mysql mysqli
RUN apt-get update && apt-get install -y unzip

# Apacheのドキュメントルートを設定
ENV APACHE_DOCUMENT_ROOT /var/www/html/public

# Apacheの各設定ファイルのドキュメントルートを変更
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

# composerをDockerHub経由でインストール
COPY --from=composer:2.5.4 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

docker-compose.yml

version: "3.8"
services:

  php:
    container_name: sphp
    build:
      context: ./php/
      dockerfile: Dockerfile
    ports:
      - 8080:80
    volumes:
      - ../laravel:/var/www/html

.env

COMPOSE_PROJECT_NAME=laravel

Docker イメージビルド&コンテナ立ち上げ

イメージビルド&コンテナ立ち上げ

docker-compose up -d

立ち上がったコンテナ内に入り、Laravel プロジェクトを作成する

composer create-project --prefer-dist laravel/laravel .

laravel ディレクトリにプロジェクトが作成されていることを確認 ![image.png](https://image.docbase.io/uploads/2627f237-1940-4baf-b0a4-f95f8af2563e.png =600x400)

バージョン確認

# php artisan -V
Laravel Framework 10.1.5

AWS デプロイ(デベロッパー)

コードの更新。ECR を更新 ※ローカル PC の AWS CLI はセッティング済みとします ※ECR リポジトリは作成済みとします

Dockerfile.deploy

FROM php:8.2-apache
RUN a2enmod rewrite
RUN docker-php-ext-install pdo_mysql mysqli
RUN apt-get update && apt-get install -y unzip

# Apacheのドキュメントルートを設定
ENV APACHE_DOCUMENT_ROOT /var/www/html/public

# Apacheの各設定ファイルのドキュメントルートを変更
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

# composerをDockerHub経由でインストール
COPY --from=composer:2.5.4 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

# ローカルのlaravelディレクトリをコンテナ内にコピー
COPY laravel/ /var/www/html/

# アクセス権限エラーの回避
RUN chmod -R 775 /var/www/html/storage && \
         chown -R www-data:www-data /var/www/html/storage

ECR にログイン&プッシュ

※AWS コンソールの ECR の画面に以下で行うプッシュコマンドが表示されています

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

# イメージをビルド(Dockerfile.deployのあるディレクトリまで移動しておいてください)
docker build -t laravel -f Dockerfile.deploy .

# ECRにプッシュする用にタグ付け
docker tag laravel:latest 828642921232.dkr.ecr.ap-northeast-1.amazonaws.com/smatag-laravel:latest

# ECRにプッシュ
docker push 828642921232.dkr.ecr.ap-northeast-1.amazonaws.com/laravel:latest

AWS デプロイ(インフラ)

Fargate とネットワーク周りのデプロイ ※Terraform インストール済み

terraform/main.tf

####################################
## プロバイダー設定
####################################

# Terraform のバージョン指定
terraform {
  required_version = "~> 1.3.0"
}
# プロバイダーを指定
provider "aws" {
  region = "ap-northeast-1"
}

####################################
## 環境変数の設定
####################################

## プレフィックス
variable "project_name" {
  type    = string
  default = "laravel"
}

####################################
## リソースの定義
####################################

# VPCの作成(Terraform Registryの使用)
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.10.0"

  name = "${var.project_name}-vpc"
  cidr                 = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  azs             = ["ap-northeast-1a", "ap-northeast-1c"]
  public_subnets  = ["10.0.11.0/24", "10.0.12.0/24"]

  #デフォルトセキュリティグループのルール削除
  manage_default_security_group  = true
  default_security_group_ingress = []
  default_security_group_egress  = []
}

# ECSタスクがECRリポジトリを見れる用のIAMロール作成
data "aws_iam_policy_document" "default" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "default" {
  name               = "MyEcsTaskRole"
  assume_role_policy = data.aws_iam_policy_document.default.json
}

resource "aws_iam_role_policy_attachment" "default" {
  role       = aws_iam_role.default.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# タスク定義
resource "aws_ecs_task_definition" "default" {
  family                   = "${var.project_name}-task"
  #0.25vCPU
  cpu                      = "256"
  #0.5GB
  memory                   = "512"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  container_definitions    = file("./task-definitions.json")
  execution_role_arn = aws_iam_role.default.arn
}

# クラスター
resource "aws_ecs_cluster" "default" {
  name = "${var.project_name}-cluster"
}

# サービス
resource "aws_ecs_service" "default" {
  name                              = "${var.project_name}-service"
  cluster                           = aws_ecs_cluster.default.arn
  task_definition                   = aws_ecs_task_definition.default.arn
  desired_count                     = 1
  launch_type                       = "FARGATE"
  platform_version                  = "1.4.0"

  network_configuration {
    assign_public_ip = true
    security_groups  = [aws_security_group.default.id]
    subnets = module.vpc.public_subnets
  }

  ## デプロイ毎にタスク定義が更新されるため、リソース初回作成時を除き変更を無視
  lifecycle {
    ignore_changes = [task_definition]
  }
}

# セキュリティグループ
resource "aws_security_group" "default" {
  name        = "${var.project_name}-sg"
  description = "sg"
  vpc_id      = module.vpc.vpc_id
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.project_name}-sg"
  }
}

terraform/task-definitions.json

※事前に作成した EMR の URI を指定していることに注意

[
    {
      "name": "laravel",
      "image": "828642921232.dkr.ecr.ap-northeast-1.amazonaws.com/laravel:latest",
      "essential": true,
      "memory": 128,
      "portMappings": [
        {
          "protocol": "tcp",
          "containerPort": 80
        }
      ]
    }
  ]

デプロイ

※terraform ディレクトリに移動しておく

# 初回実行時のみ
terraform init

# 事前確認
terraform plan

# デプロイ
terraform apply

デプロイ後の確認

ECS のコンソール画面からパブリック IP を確認できます。 ECS -> クラスター名 -> タスクタブ -> タスク名

パブリック IP にアクセス