Amazon ECS で WordPress サイトを構築する

 

本記事は TechHarmony Advent Calendar 2025 12/22付の記事です

こんにちは、稲葉です。
アドベントカレンダーの機会で、普段触っていないコンテナに触れてみようと思いました。
本記事ではコンテナでWordPressサイトを作成し、Amazon ECSにデプロイするところまで試してみようと思います。

構成

本記事で試す構成です。
検証用にローカルPCのDockerでWordPressサイトを構築します。
その後、本番用にECSでWordPressを公開します。

ローカルPCにWordPressサイトを構築する

まずはdocker composeでWordPress環境を作成してみました。

下記のような構成で作成しました。

.
├── docker-compose.yml
├── .env
├── nginx
│ ├── default.conf
│ └── nginx.conf
└── php-fpm
  ├── Dockerfile
  └── php.ini
ファイル名 内容
./docker-compose.yml 複数コンテナをまとめて管理するためのdocker compose用設定ファイル
.env docker-compose.yml用の環境変数を記載するファイル
(コード例は本記事の最後)
./nginx/default.conf nginxのサーバー設定用ファイル、php-fpm コンテナへ転送する設定などを記載
(コードは本記事の最後)
./nginx/nginx.conf nginxの基本設定ファイル(コードは本記事の最後)
./php-fpm/Dockerfile WordPressとphp-fpmの環境を作るためのDockerファイル
※nginx上でWordPressを公開する場合、WordPressはphp-fpmと同時に使用する必要がある
(コードは本記事の最後)
./php-fpm/php.ini phpの設定ファイル(コードは本記事の最後)

docker-compose.yml

下記設定docker-compose.ymlファイルで、ローカルPCのWordPressサイトを構築します。

services:

nginx:
  image:nginx:alpine
  ports:
    - "8080:80"
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    - wordpress_data:/var/www/html
  depends_on:
    - php-fpm
  networks:
    - wordpress-network

php-fpm:
  build:
    context:./php-fpm
    dockerfile:Dockerfile
  volumes:
    - wordpress_data:/var/www/html
  environment:
    WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
    WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
    WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
    WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME}
  depends_on:
    - mysql
  networks:
    - wordpress-network

mysql:
  image:mysql:8.0
  environment:
    MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    MYSQL_DATABASE: ${MYSQL_DATABASE}
    MYSQL_USER: ${MYSQL_USER}
    MYSQL_PASSWORD: ${MYSQL_PASSWORD}
  volumes:
    - mysql_data:/var/lib/mysql
  networks:
    - wordpress-network

volumes:
  wordpress_data:
  mysql_data:
networks:
  wordpress-network:
    driver:bridge

このdocker-compose.ymlでは3つのコンテナを立てる設定が記載されています。

  • nginx
  • php-fpm
  • mysql

それぞれのコンテナの設定について簡単に記載します。

nginxコンテナ

image:nginx:alpineで、nginxのコンテナはnginx:alpineというイメージを元にコンテナを作成する設定をしています。

ports: “8080:80″で、ローカルホストの8080番ポートにアクセスした際にコンテナの80番ポートに転送する設定をしています。

volumes: ./nginx/nginx.conf:/etc/nginx/nginx.confで、ローカルPCの./nginx/nginx.confとコンテナ環境の/etc/nginx/nginx.confを同期しています。
volumes: ./nginx/default.conf:/etc/nginx/conf.d/default.confでも同様です。

volumes: wordpress_data:/var/www/htmlでは、wordpress_data名前付きボリュームでコンテナ内の/var/www/html にマウントする設定をしています。
名前付きボリュームとは、ローカルPCのDockerで名前を付けて管理するボリュームになります。ローカルPC内の他のDockerコンテナからもマウントして使用可能です。

depends_on: php-fpmで、php-fpmのコンテナを作成した後にnginxコンテナを作成する設定をしています。

networks: wordpress-networkで、wordpress-networkというDockerネットワークにnginxコンテナを属させる設定を行っています。
Dockerネットワークに属すことで、属しているコンテナ間でサービス名を使って名前解決可能になります。

php-fpmコンテナ

build: context:./php-fpmで、./php-fpmディレクトリをビルドコンテキストとして使用する設定をしています。

build: dockerfile:Dockerfileで、コンテナを立てるときに使用するDockerfileを指定しています。

volumesは、nginxと同様でコンテナ内の/var/www/html に名前付きボリュームのwordpress_dataをマウントしています。
この設定によって、php-fpmコンテナとnginxコンテナの/var/www/htmlは同期されます。

environment: WORDPRESS_DB_HOST:${WORDPRESS_DB_HOST}で、環境変数WORDPRESS_DB_HOSTの値に.envファイルのWORDPRESS_DB_HOSTで指定している値を使用する設定をしています。
他も同様になります。

mysqlコンテナ

volumes: mysql_data:/var/lib/mysqlで、このデータベースのデータをmysql_dataという名前付きボリュームで管理することで永続化しています。

他の設定は、他のコンテナのところで説明しているため省略します。

ローカルPCのWordpressサイトに接続する

コードができたら下記コマンドで、nginxとphp-fpm、mysqlの3つのコンテナを立てて、ローカルPCのWordPressサイトを構築できます。

docker compose up -d
http://localhost:8080でローカルサーバーにアクセスすると、WordPressの初期設定画面が開きます。
まずは言語設定が聞かれるので、日本語を選択します。

次にデータベースの接続設定画面になりますが、こちらは.envに書いてある下記内容で入力します。

項目名
データベース名 wordpress
ユーザー名 wordpress
パスワード wordpress_password
データベースホスト mysql
テーブル接頭辞 wp_

そのまま初期設定とテスト記事を書くと、このように記事を表示できました。

これにてローカルPCのWordPress環境構築は完了です。

AWS環境にWordPressサイトを構築する

それでは、AWS環境にWordPressサイトを構築していきます。

ECRリポジトリを作成する

まずはECSで実行するためのコンテナイメージファイルを配置するためのECRリポジトリを作成します。

AWSの環境では、MySQLデータベースのところはRDSが担当するので、mysqlコンテナは立てません。

下記のシェルスクリプトを作成し、AWS CLIでECRリポジトリを作成しました。
※下記シェルスクリプトを実行する前に、aws loginなどでAWSの認証を通してください。

. ├── docker-compose.yml
  ├── .env
  ├── nginx
  ├── php-fpm
  └── create-ecr.sh <- new
#!/bin/bash

# 設定
AWS_REGION="ap-northeast-1"
PROJECT_NAME="wordpress"

echo "=== ECRリポジトリを作成 ==="

# Nginx用ECRリポジトリを作成
echo "Nginx用ECRリポジトリを作成中..."
aws ecr create-repository \
--repository-name ${PROJECT_NAME}-nginx \
--region ${AWS_REGION}

# PHP用ECRリポジトリを作成
echo "PHP用ECRリポジトリを作成中..."
aws ecr create-repository \
--repository-name ${PROJECT_NAME}-php \
--region ${AWS_REGION}

# アカウントIDを取得
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

echo "=== ECRリポジトリ作成完了 ==="
echo "Nginx ECR URL: ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${PROJECT_NAME}-nginx"
echo "PHP ECR URL: ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${PROJECT_NAME}-php"
echo ""

echo "次に ./image-push.sh を実行してイメージをプッシュしてください"

ECRにpushするイメージを作成する

ECRにプッシュするために、nginx用とphp-fpm(とwordpress)用のイメージを作成します。

. ├── docker-compose.yml
  ├── .env
  ├── nginx
  ├── php-fpm
  ├── create-ecr.sh
  └── aws-ecs             <- new
    ├── Dockerfile.nginx  <- new
    └── Dockerfile.php    <- new

Dockerfile.nginx

FROM nginx:alpine

# 必要なパッケージをインストール(EFS用)
RUN apk add --no-cache nfs-utils
  # Nginxの設定ファイルをコピー
  COPY nginx/nginx.conf /etc/nginx/nginx.conf
  COPY nginx/default.conf /etc/nginx/conf.d/default.conf
  # WordPressファイル用のディレクトリを作成
  RUN mkdir -p /var/www/html
  # EFS用のマウントポイントを作成
  VOLUME ["/var/www/html"]

# ヘルスチェック用の設定を追加
RUN echo 'server {' > /etc/nginx/conf.d/health.conf && \
  echo '   listen 80;' >> /etc/nginx/conf.d/health.conf && \
  echo '   server_name _;' >> /etc/nginx/conf.d/health.conf && \
  echo '   location /health {' >> /etc/nginx/conf.d/health.conf && \
  echo '     access_log off;' >> /etc/nginx/conf.d/health.conf && \
  echo '     return 200 "healthy\\n";' >> /etc/nginx/conf.d/health.conf && \
  echo '     add_header Content-Type text/plain;' >> /etc/nginx/conf.d/health.conf && \
  echo '  }' >> /etc/nginx/conf.d/health.conf && \
  echo '}' >> /etc/nginx/conf.d/health.conf

EXPOSE 80

# Nginxをフォアグラウンドで実行
CMD ["nginx", "-g", "daemon off;"]

Dockerfile.php

FROM php:8.2-fpm

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
  libfreetype6-dev \
  libjpeg62-turbo-dev \
  libpng-dev \
  libzip-dev \
  unzip \
  nfs-common \
  gettext-base \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install -j$(nproc) gd \
  && docker-php-ext-install pdo_mysql \
  && docker-php-ext-install mysqli \
  && docker-php-ext-install zip \
  && docker-php-ext-install opcache \
  && rm -rf /var/lib/apt/lists/*
  # WordPressをダウンロードしてインストール
  RUN curl -O https://wordpress.org/latest.tar.gz \
  && tar xzf latest.tar.gz \
  && mkdir -p /var/www/html \
  && mkdir -p /tmp/wordpress \
  && cp -R wordpress/* /tmp/wordpress/ \
  && cp -R wordpress/* /var/www/html/ \
  && rm -rf wordpress latest.tar.gz \
  && chown -R www-data:www-data /var/www/html

# wp-config.phpのテンプレートは不要(起動時に作成)

# PHP設定をコピー
COPY php-fpm/php.ini /usr/local/etc/php/

# 起動スクリプトを作成
RUN echo '#!/bin/bash' > /start.sh && \
  echo 'set -e' >> /start.sh && \
  echo 'echo "Starting WordPress PHP-FPM container..."' >> /start.sh && \
  echo 'echo "DB Host: $WORDPRESS_DB_HOST"' >> /start.sh && \
  echo 'echo "DB Name: $WORDPRESS_DB_NAME"' >> /start.sh && \
  echo 'echo "DB User: $WORDPRESS_DB_USER"' >> /start.sh && \
  echo '' >> /start.sh && \
  echo '# WordPressファイルがEFSにない場合はコピー' >> /start.sh && \
  echo 'if [ ! -f /var/www/html/wp-config-sample.php ]; then' >> /start.sh && \
  echo ' echo "Copying WordPress files to EFS..."' >> /start.sh && \
  echo ' cp -R /tmp/wordpress/* /var/www/html/' >> /start.sh && \
  echo 'fi' >> /start.sh && \
  echo '' >> /start.sh && \
  echo '# wp-config.phpを作成' >> /start.sh && \
  echo 'cp /var/www/html/wp-config-sample.php /var/www/html/wp-config.php' >> /start.sh && \
  echo 'sed -i "s/database_name_here/$WORDPRESS_DB_NAME/" /var/www/html/wp-config.php' >> /start.sh && \
  echo 'sed -i "s/username_here/$WORDPRESS_DB_USER/" /var/www/html/wp-config.php' >> /start.sh && \
  echo 'sed -i "s/password_here/$WORDPRESS_DB_PASSWORD/" /var/www/html/wp-config.php' >> /start.sh && \
  echo 'sed -i "s/localhost/$WORDPRESS_DB_HOST/" /var/www/html/wp-config.php' >> /start.sh && \
  echo '' >> /start.sh && \
  echo '# ファイルの権限を設定' >> /start.sh && \
  echo 'chown -R www-data:www-data /var/www/html' >> /start.sh && \
  echo '' >> /start.sh && \
  echo 'echo "Starting PHP-FPM..."' >> /start.sh && \
  echo 'exec php-fpm' >> /start.sh && \

chmod +x /start.sh
WORKDIR /var/www/html

EXPOSE 9000

CMD ["/start.sh"]

作成したイメージをECRにpushする

次は、作成したDockerfileでイメージを作成し、ECRリポジトリにプッシュします。

下記のシェルスクリプトを作成し、AWS CLIでプッシュしました。
※下記シェルスクリプトを実行する前に、aws loginなどでAWSの認証を通してください。

. ├── docker-compose.yml
  ├── .env
  ├── nginx
  ├── php-fpm
  ├── create-ecr.sh
  ├── image-push.sh  <- new 
  └── aws-ecs
#!/bin/bash

# 設定
AWS_REGION="ap-northeast-1"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_REPO_BASE="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
NGINX_REPO="${ECR_REPO_BASE}/wordpress-nginx"
PHP_REPO="${ECR_REPO_BASE}/wordpress-php"

echo "=== DockerイメージをECRにプッシュ ==="

# ECRにログイン
echo "ECRにログイン中..."
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_REPO_BASE}

# Nginxイメージをビルド・プッシュ
echo "Nginxイメージをビルド中..."
docker build -f aws-ecs/Dockerfile.nginx -t ${NGINX_REPO}:latest .
docker push ${NGINX_REPO}:latest

# PHPイメージをビルド・プッシュ
echo "PHPイメージをビルド中..."
docker build -f aws-ecs/Dockerfile.php -t ${PHP_REPO}:latest .
docker push ${PHP_REPO}:latest

echo "イメージプッシュ完了!"
echo "次にTerraformでECS環境を構築してください"

TerraformでAWSリソースを構築する

ECSでWordpressサイトを公開するために必要なAWSリソースを構築します。
量が多いのでTerraformで構築します。

. ├── docker-compose.yml
  ├── .env 
  ├── nginx
  ├── php-fpm
  ├── create-ecr.sh
  ├── image-push.sh
  ├── aws-ecs
  └── terraform            <- new
    ├── main.tf            <- new
    ├── output.tf          <- new
    ├── variables.tf       <- new
    └── terraform.tfvars   <- new

各コードについては、本記事の最後に記載しています。

次のコマンドを叩くことでAWSリソースを構築します。

cd terraform
terraform init
terraform plan
terraform apply

AWS環境のWordPressサイトに接続する

albのエンドポイントにアクセスするとWordPressの初期設定画面が開きます。
ローカルPCのWordPressサイトと同様に設定することで、WordPressサイトを公開できました。

終わりに

Kiroと二人三脚で構築することができました!
Dockerに触れた経験が少なかったので、WordPress環境を作ることで理解が深まったような気がします。

アドベントカレンダー当日の朝まで書いていました。。

コード

./.env

# MySQL設定
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=wordpress_password

# WordPress設定
WORDPRESS_DB_HOST=mysql
WORDPRESS_DB_USER=wordpress
WORDPRESS_DB_PASSWORD=wordpress_password
WORDPRESS_DB_NAME=wordpress

./nginx/default.conf

server {
  listen 80;
  server_name localhost;
  root /var/www/html;
  index index.php index.html index.htm;
  client_max_body_size 100M;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    fastcgi_pass php-fpm:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
  }

  location ~ /\. {
    deny all;
  }

  location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }
}

./nginx/nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  include /etc/nginx/conf.d/*.conf;
}

./php-fpm/Dockerfile

FROM php:8.2-fpm

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
  libfreetype6-dev \
  libjpeg62-turbo-dev \
  libpng-dev \
  libzip-dev \
  unzip \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install -j$(nproc) gd \
  && docker-php-ext-install pdo_mysql \
  && docker-php-ext-install mysqli \
  && docker-php-ext-install zip \
  && docker-php-ext-install opcache

# WordPressをダウンロード
RUN curl -O https://wordpress.org/latest.tar.gz \
  && tar xzf latest.tar.gz \
  && cp -R wordpress/* /var/www/html/ \
  && rm -rf wordpress latest.tar.gz \
  && chown -R www-data:www-data /var/www/html

# PHP設定
COPY php.ini /usr/local/etc/php/

WORKDIR /var/www/html

EXPOSE 9000

./php-fpm/php.ini

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 256M
max_execution_time = 300
max_input_vars = 3000

; OPcache設定
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=2
opcache.fast_shutdown=1

./terraform/output.tf

output "alb_dns_name" {
  description ="DNS name of the load balancer"
  value =aws_lb.main.dns_name
}

output "aurora_cluster_endpoint" {
  description ="Aurora cluster endpoint"
  value =aws_rds_cluster.main.endpoint
}

output "aurora_reader_endpoint" {
  description ="Aurora reader endpoint"
  value =aws_rds_cluster.main.reader_endpoint
}

output "ecs_cluster_name" {
  description ="Name of the ECS cluster"
  value =aws_ecs_cluster.main.name
}

output "efs_file_system_id" {
  description ="EFS file system ID"
  value =aws_efs_file_system.wordpress.id
}

output "efs_dns_name" {
  description ="EFS DNS name"
  value =aws_efs_file_system.wordpress.dns_name
}

./terraform/variables.tf

variable "aws_region" {
  description ="AWS region"
  type =string
  default ="ap-northeast-1"
}

variable "project_name" {
  description ="Project name for resource naming"
  type =string
  default ="wordpress"
}

variable "db_username" {
  description ="Database username"
  type =string
  default ="wordpress"
}

variable "db_password" {
  description ="Database password"
  type =string
  sensitive =true
}

./terraform/terraform.tfvars

aws_region = "ap-northeast-1"
project_name = "wordpress"
db_username = "wordpress"
db_password = "your-secure-password-here"

./terraform/main.tf

terraform {
  required_providers {
    aws ={
      source="hashicorp/aws"
      version="~> 5.57.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
  profile ="inaba"
}

# Data sources
data "aws_availability_zones" "available" {
  state ="available"
}
data "aws_caller_identity" "current" {}

# VPC
resource "aws_vpc" "main" {
  cidr_block ="10.0.0.0/16"
  enable_dns_hostnames =true
  enable_dns_support =true
  tags ={
    Name="${var.project_name}-vpc"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id =aws_vpc.main.id
  tags ={
    Name="${var.project_name}-igw"
  }
}

# Public Subnets
resource "aws_subnet" "public" {
  count =2
  vpc_id =aws_vpc.main.id
  cidr_block ="10.0.${count.index+1}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch =true
  tags ={
    Name="${var.project_name}-public-subnet-${count.index+1}"
  }
}

# Private Subnets
resource "aws_subnet" "private" {
  count =2
  vpc_id =aws_vpc.main.id
  cidr_block ="10.0.${count.index+10}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags ={
    Name="${var.project_name}-private-subnet-${count.index+1}"
  }
}

# Route Table for Public Subnets
resource "aws_route_table" "public" {
  vpc_id =aws_vpc.main.id
  route {
    cidr_block ="0.0.0.0/0"
    gateway_id =aws_internet_gateway.main.id
  }
  tags ={
    Name="${var.project_name}-public-rt"
  }
}

# Route Table Associations for Public Subnets
resource "aws_route_table_association" "public" {
  count =2
  subnet_id =aws_subnet.public[count.index].id
  route_table_id =aws_route_table.public.id
}

# NAT Gateway for Private Subnets (1台のみ)
resource "aws_eip" "nat" {
  domain ="vpc"
  depends_on =[aws_internet_gateway.main]
  tags ={
    Name="${var.project_name}-nat-eip"
  }
}

resource "aws_nat_gateway" "main" {
  allocation_id =aws_eip.nat.id
  subnet_id =aws_subnet.public[0].id
  tags ={
    Name="${var.project_name}-nat-gateway"
  }
  depends_on =[aws_internet_gateway.main]
}

# Route Table for Private Subnets (共通)
resource "aws_route_table" "private" {
  vpc_id =aws_vpc.main.id
  route {
    cidr_block ="0.0.0.0/0"
    nat_gateway_id =aws_nat_gateway.main.id
  }
  tags ={
    Name="${var.project_name}-private-rt"
  }
}

# Route Table Associations for Private Subnets
resource "aws_route_table_association" "private" {
  count =2
  subnet_id =aws_subnet.private[count.index].id
  route_table_id =aws_route_table.private.id
}

# Security Group for ALB
resource "aws_security_group" "alb" {
  name_prefix ="${var.project_name}-alb-"
  vpc_id =aws_vpc.main.id
  ingress {
    description ="HTTP from allowed IP"
    from_port =80
    to_port =80
    protocol ="tcp"
    cidr_blocks =["x.x.x.x7/32"]
  }
  ingress {
    description ="HTTPS from allowed IP"
    from_port =443
    to_port =443
    protocol ="tcp"
    cidr_blocks =["x.x.x.x/32"]
  }
  egress {
    description ="All outbound traffic"
    from_port =0
    to_port =0
    protocol ="-1"
    cidr_blocks =["0.0.0.0/0"]
  }
  tags ={
    Name="${var.project_name}-alb-sg"
  }
  lifecycle {
    create_before_destroy =true
  }
}

# Security Group for ECS
resource "aws_security_group" "ecs" {
  name_prefix ="${var.project_name}-ecs-"
  vpc_id =aws_vpc.main.id
  ingress {
    description ="HTTP from ALB"
    from_port =80
    to_port =80
    protocol ="tcp"
    security_groups =[aws_security_group.alb.id]
  }
  egress {
    description ="All outbound traffic"
    from_port =0
    to_port =0
    protocol ="-1"
    cidr_blocks =["0.0.0.0/0"]
  }
  tags ={
    Name="${var.project_name}-ecs-sg"
  }
  lifecycle {
    create_before_destroy =true
  }
}

# Security Group for EFS
resource "aws_security_group" "efs" {
  name_prefix ="${var.project_name}-efs-"
  vpc_id =aws_vpc.main.id
  ingress {
    description ="NFS from ECS"
    from_port =2049
    to_port =2049
    protocol ="tcp"
    security_groups =[aws_security_group.ecs.id]
  }
  egress {
    description ="All outbound traffic"
    from_port =0
    to_port =0
    protocol ="-1"
    cidr_blocks =["0.0.0.0/0"]
  }
  tags ={
    Name="${var.project_name}-efs-sg"
  }
  lifecycle {
    create_before_destroy =true
  }
}

# Security Group for RDS
resource "aws_security_group" "rds" {
  name_prefix ="${var.project_name}-rds-"
  vpc_id =aws_vpc.main.id
  ingress {
    description ="MySQL from ECS"
    from_port =3306
    to_port =3306
    protocol ="tcp"
    security_groups =[aws_security_group.ecs.id]
  }
  egress {
    description ="All outbound traffic"
    from_port =0
    to_port =0
    protocol ="-1"
    cidr_blocks =["0.0.0.0/0"]
  }
  tags ={
    Name="${var.project_name}-rds-sg"
  }
  lifecycle {
    create_before_destroy =true
  }
}

# EFS File System
resource "aws_efs_file_system" "wordpress" {
  creation_token ="${var.project_name}-efs"
  performance_mode ="generalPurpose"
  throughput_mode ="provisioned"
  provisioned_throughput_in_mibps =10
  encrypted =true
  tags ={
    Name="${var.project_name}-efs"
  }
}

# EFS Mount Targets
resource "aws_efs_mount_target" "wordpress" {
  count =2
  file_system_id =aws_efs_file_system.wordpress.id
  subnet_id =aws_subnet.private[count.index].id
  security_groups =[aws_security_group.efs.id]
}

# Aurora Subnet Group
resource "aws_db_subnet_group" "main" {
  name ="${var.project_name}-aurora-subnet-group"
  subnet_ids =aws_subnet.private[*].id
  tags ={
    Name="${var.project_name}-aurora-subnet-group"
  }
}

# Aurora Cluster
resource "aws_rds_cluster" "main" {
  cluster_identifier ="${var.project_name}-aurora-cluster"
  engine ="aurora-mysql"
  engine_version ="8.0.mysql_aurora.3.04.2"
  database_name ="wordpress"
  master_username = var.db_username
  master_password = var.db_password
  db_subnet_group_name =aws_db_subnet_group.main.name
  vpc_security_group_ids =[aws_security_group.rds.id]
  backup_retention_period =7
  preferred_backup_window ="03:00-04:00"
  skip_final_snapshot =true
  deletion_protection =false
  tags ={
    Name="${var.project_name}-aurora-cluster"
  }
}

# Aurora Instance (1台のみ)
resource "aws_rds_cluster_instance" "main" {
  identifier ="${var.project_name}-aurora-instance"
  cluster_identifier =aws_rds_cluster.main.id
  instance_class ="db.r5.large"
  engine =aws_rds_cluster.main.engine
  engine_version =aws_rds_cluster.main.engine_version
  tags ={
    Name="${var.project_name}-aurora-instance"
  }
}

# CloudWatch Log Group

resource "aws_cloudwatch_log_group" "ecs" {
  name ="/ecs/${var.project_name}"
  retention_in_days =7
  tags ={
    Name="${var.project_name}-log-group"
  }
}

# ECS Cluster
resource "aws_ecs_cluster" "main" {
  name ="${var.project_name}-cluster"
  tags ={
    Name="${var.project_name}-cluster"
  }
}

# IAM Role for ECS Execution
resource "aws_iam_role" "ecs_execution" {
  name ="${var.project_name}-ecs-execution-role"
  assume_role_policy =jsonencode({
    Version="2012-10-17"
      Statement= [
        {
          Action="sts:AssumeRole"
          Effect="Allow"
          Principal= {
            Service="ecs-tasks.amazonaws.com"
          }
        }
      ]
  })
  tags ={
    Name="${var.project_name}-ecs-execution-role"
  }
}

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

# CloudWatch Logs用のIAMポリシー
resource "aws_iam_role_policy" "ecs_logs" {
  name ="${var.project_name}-ecs-logs-policy"
  role =aws_iam_role.ecs_execution.id
  policy =jsonencode({
    Version="2012-10-17"
    Statement= [
      {
        Effect="Allow"
        Action= [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
        Resource="arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"
       }
    ]
  })
}

# ECS Task Definition
resource "aws_ecs_task_definition" "main" {
  family = var.project_name
  network_mode ="awsvpc"
  requires_compatibilities =["FARGATE"]
  cpu ="512"
  memory ="1024"
  execution_role_arn =aws_iam_role.ecs_execution.arn
  volume {
    name ="wordpress-efs"
    efs_volume_configuration {
      file_system_id =aws_efs_file_system.wordpress.id
      root_directory ="/"
    }
  }
  container_definitions =jsonencode([
    {
      name="nginx"
      image="${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/${var.project_name}-nginx:latest"
      portMappings= [
        {
          containerPort=80
          protocol="tcp"
        }
      ]
      essential= true
      dependsOn= [
        {
          containerName="php-fpm"
          condition="START"
        }
      ]
      mountPoints= [ 
        {
          sourceVolume="wordpress-efs"
          containerPath="/var/www/html"
          readOnly= false
        }
      ]
      logConfiguration= {
        logDriver="awslogs"
        options= {
          "awslogs-group" = aws_cloudwatch_log_group.ecs.name
          "awslogs-region" = var.aws_region
          "awslogs-stream-prefix" = "nginx"
        }
      }
    },
    {
      name="php-fpm"
      image="${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/${var.project_name}-php:latest"
      essential= true
      mountPoints= [
        {
          sourceVolume="wordpress-efs"
          containerPath="/var/www/html"
          readOnly= false
        }
      ]
      environment= [
        {
          name="WORDPRESS_DB_HOST"
          value= aws_rds_cluster.main.endpoint
        },
        {
          name="WORDPRESS_DB_NAME"
          value= aws_rds_cluster.main.database_name
        },
        {
          name="WORDPRESS_DB_USER"
          value= aws_rds_cluster.main.master_username
        },
        {
          name="WORDPRESS_DB_PASSWORD"
          value=var.db_password
        }
      ]
      logConfiguration= {
        logDriver="awslogs"
        options= {
          "awslogs-group" = aws_cloudwatch_log_group.ecs.name
          "awslogs-region" = var.aws_region
          "awslogs-stream-prefix" = "php-fpm"
        }
      }
    }
  ])
  tags ={
    Name="${var.project_name}-task"
  }
}

# Application Load Balancer
resource "aws_lb" "main" {
  name ="${var.project_name}-alb"
  internal =false
  load_balancer_type ="application"
  security_groups =[aws_security_group.alb.id]
  subnets =aws_subnet.public[*].id
  enable_deletion_protection =false
  tags ={
    Name="${var.project_name}-alb"
  }
}

# Target Group
resource "aws_lb_target_group" "main" {
name ="${var.project_name}-tg"
  port =80
  protocol ="HTTP"
  vpc_id =aws_vpc.main.id
  target_type ="ip"
  health_check {
    enabled =true
    healthy_threshold =2
    interval =30
    matcher ="200,302"
    path ="/"
    port ="traffic-port"
    protocol ="HTTP"
    timeout =5
    unhealthy_threshold =2
  }
  tags ={
    Name="${var.project_name}-tg"
  }
}

# ALB Listener
resource "aws_lb_listener" "main" {
  load_balancer_arn =aws_lb.main.arn
  port ="80"
  protocol ="HTTP"
  default_action {
  type ="forward"
    target_group_arn =aws_lb_target_group.main.arn
  }
}

# ECS Service
resource "aws_ecs_service" "main" {
  name ="${var.project_name}-service"
  cluster =aws_ecs_cluster.main.id
  task_definition =aws_ecs_task_definition.main.arn
  desired_count =1
  launch_type ="FARGATE"
  network_configuration {
  subnets =aws_subnet.private[*].id
  security_groups =[aws_security_group.ecs.id]
    assign_public_ip =false
  }
  load_balancer {
    target_group_arn =aws_lb_target_group.main.arn
    container_name ="nginx"
    container_port =80
  } 
  depends_on =[
    aws_lb_listener.main,
    aws_efs_mount_target.wordpress
  ]
  tags ={
    Name="${var.project_name}-service"
  }
}
タイトルとURLをコピーしました