2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Terraformを用いてEC2およびRDSを立ち上げ、Laravelの環境を構築する

Posted at

構成

Terraformで ap-northeast-1リージョンにVPC、1つのパブリックサブネットおよび2つのプライベートサブネットを作成した。さらにパブリックサブネット内にEC2インスタンスを立て、プライベートサブネットにはRDS for MySQLを設置した。ここまでをTerraformで行った。
EC2インスタンス内にはSSH接続でログインし、Laravelの環境を構築した。
ec2-rds-nginx-laravel.jpg
>>

TerraformでEC2、RDSを構築する

IAMユーザーを作成する

AWSコンソールからIAMダッシュボードへ移動する。
1
ユーザーを追加を選択する。
2
ユーザー名を入力し、プログラムによるアクセスを選択する。
3
既存のポリシーを直接アタッチを選択し、AdministratorAccess を選択する。
4
内容を確認し、ユーザーの作成を選択する。
5
アクセスキーIDシークレットアクセスキーを控える。
6

~/.aws/credentialsaws_access_key_id および aws_secret_access_key を記述する。

% mkdir ~/.aws
% (echo "[default]"; echo "aws_access_key_id = xxxxxxxxxxxxxxxxxxxx"; echo "aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") >> ~/.aws/credentials

この状態になる。

~/.aws/credentials
[default]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Terraformのファイルを用意する

ファイルの構成

sample
├── aws_db_instance.tf
├── aws_db_parameter_group.tf
├── aws_db_subnet_group.tf
├── aws_instance.tf
├── aws_internet_gateway.tf
├── aws_route_table.tf
├── aws_route_table_association.tf
├── aws_security_group.tf
├── aws_subnet.tf
├── aws_vpc.tf
├── main.tf
├── secret.tfvars
└── terraform.tfvars

リソースごとにファイルを分けた。
今回は13ファイル用意した。

ファイルの内容

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}
# Configure the AWS Provider
provider "aws" {
  region = "ap-northeast-1"
}
# variables
variable "app" { type = string }
variable "region" { type = string }
variable "vpc_cidr_block" { type = string }
variable "subnet_cidr_block1" { type = string }
variable "subnet_cidr_block10" { type = string }
variable "subnet_cidr_block11" { type = string }
variable "ssh_cidr_blocks" {
  type = list
  description = "allowed SSH connection"
  default = ["0.0.0.0/0"]
}
variable "db_name" { type = string }
variable "db_username" { type = string }
variable "db_password" { type = string }
aws_vpc.tf
# Create a VPC
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr_block
  tags = { Name = var.app }
}
aws_subnet.tf
# Create a subnet
resource "aws_subnet" "public" {
  vpc_id = aws_vpc.main.id
  cidr_block = var.subnet_cidr_block1
  availability_zone = "${var.region}a"
  tags = { Name = "${var.app}-public-subnet" }
}
resource "aws_subnet" "private1" {
  vpc_id = aws_vpc.main.id
  cidr_block = var.subnet_cidr_block10
  availability_zone = "${var.region}a"
  tags = { Name = "${var.app}-private1-subnet" }
}
resource "aws_subnet" "private2" {
  vpc_id = aws_vpc.main.id
  cidr_block = var.subnet_cidr_block11
  availability_zone = "${var.region}c"
  tags = { Name = "${var.app}-private2-subnet" }
}
aws_internet_gateway.tf
# Provides a resource to create a VPC Internet Gateway.
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id
  tags = { Name = var.app }
}
aws_route_table.tf
# Provides a resource to create a VPC routing table.
resource "aws_route_table" "public_route" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
  tags = { Name = "${var.app}-public-route" }
}
aws_route_table_association.tf
# Provides a resource to create an association between a route table and a subnet or a route table and an internet gateway.
resource "aws_route_table_association" "public_route" {
  subnet_id = aws_subnet.public.id
  route_table_id = aws_route_table.public_route.id
}
aws_security_group.tf
# Provides a security group resource.
resource "aws_security_group" "instance" {
  name = var.app
  description = "secutity group for instanse of ${var.app}"
  vpc_id = aws_vpc.main.id
  ingress {
    from_port = 80
    to_port = 80
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = var.ssh_cidr_blocks
  }
  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "${var.app}-instance-security-group" }
}

resource "aws_security_group" "db_instance" {
  name = "${var.app}-db"
  description = "security group for db instance of ${var.app}"
  vpc_id = aws_vpc.main.id
  ingress {
    from_port = 3306
    to_port = 3306
    protocol = "tcp"
    security_groups = [aws_security_group.instance.id]
  }
  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
  }
  tags = { Name = "${var.app}-db-instance-security-group" }
}
aws_instance.tf
# Provides an EC2 instance resource.
resource "aws_instance" "web" {
  ami = data.aws_ssm_parameter.amzn2_ami.value
  instance_type = "t2.micro"
  associate_public_ip_address = true
  subnet_id = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.instance.id]
  key_name = "sample"
  tags = { Name = var.app }
}
# Provides an SSM Parameter data source.
data "aws_ssm_parameter" "amzn2_ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

aws_ssm_parameter のData SourceでAmazon Linux 2のAMI IDを取得している。
この記事が参考になった。
本記事内では key_namesample としている。
後ほど sample という名前のキーペアを作成する

aws_db_subnet_group.tf
# Provides an RDS DB subnet group resource.
resource "aws_db_subnet_group" "default" {
  name = "${var.app}-db-subnet-group"
  description = "${var.app}-db-subnet-group"
  subnet_ids = [ aws_subnet.private1.id, aws_subnet.private2.id ]
  tags = { Name = "${var.app}-db-subnet-group" }
}
aws_db_parameter_group.tf
# Provides an RDS DB parameter group resource.
resource "aws_db_parameter_group" "default" {
  name_prefix = var.app
  family = "mysql8.0"
  description = "${var.app} parameter group for mysql8.0"
  parameter {
    name = "time_zone"
    value = "Asia/Tokyo"
  }
  tags = { Name = var.app }
}
aws_db_instance.tf
# Provides an RDS instance resource.
resource "aws_db_instance" "default" {
  allocated_storage = 20
  storage_type = "gp2"
  engine = "mysql"
  engine_version = "8.0"
  instance_class = "db.t2.micro"
  db_subnet_group_name = aws_db_subnet_group.default.id
  vpc_security_group_ids  = [ aws_security_group.db_instance.id ]
  parameter_group_name = aws_db_parameter_group.default.id
  name = var.db_name
  username = var.db_username
  password = var.db_password
  skip_final_snapshot = true
  tags = { Name = "${var.app}-rds" }
}
変数ファイル
terraform.tfvars
app = "sample"
region = "ap-northeast-1"
vpc_cidr_block = "10.1.0.0/16"
subnet_cidr_block1 = "10.1.1.0/24"
subnet_cidr_block10 = "10.1.10.0/24"
subnet_cidr_block11 = "10.1.11.0/24"

terraform.tfvars ファイルを作って変数に値を渡す。

secret.tfvars
ssh_cidr_blocks = ["x.x.x.x/32"] // ← allowed SSH connection
db_name = "sample"
db_username = "xxxxxx"
db_password = "xxxxxx"

secret.tfvars などのように terraform.tfvars 以外の名前を使う場合は -var-file を使う。
クレデンシャル情報を書く場合は、同ファイルをバージョン管理の対象外としておく。
今回は secret.tfvars にクレデンシャル情報を書いた。
db_namedb_username および db_password の値は Laravelの環境変数を設定する際に使用する

terraform を実行する

% terraform init

terraformの作業ディレクトリを初期化している。

% terraform plan -var-file="secret.tfvars"

terraform plan 差分を確認する。
-var-file="secret.tfvars" で変数を渡している。
terraform.tfvars に書いた変数は -var-file 無しで渡せる。

% terraform apply -var-file="secret.tfvars"

terraform apply でAWSにサービスを構築する。
これでAWS上で下図の状態になる。
ec2-rds.jpg
あとはSSH接続し、EC2インスタンス内に必要なものをインストールしていく。

もし構築したものをすべて壊すときは terraform destroy -var-file="secret.tfvars" を実行する。

SHH接続用のキーペアを作成する

AWSコンソールからEC2ダッシュボードへ移動する。
1
キーペア を選択する。
2
キーペアを作成 を選択する。
3
キーペアを作成する。
4
キーペアが作成される。
5
キーペアを作成すると秘密鍵がダウンロードされる。
秘密鍵は ~/.ssh へ移動させて、権限を変更してユーザーのみ読み込み可能にしておく。

% mv ~/Downloads/sample.pem ~/.ssh/
% chmod 400 ~/.ssh/sample.pem 

Amazon EC2 キーペアと Linux インスタンス | オプション 1: Amazon EC2 を使用してキーペアを作成する - Amazon Elastic Compute Cloud

EC2インスタンス内にLaravelの環境を構築する

AWSコンソールからEC2ダッシュボードへ移動する。
1
インスタンスを選択し、パブリックIPアドレスをコピーする。

SSHクライアントでEC2インスタンスに接続する

% ssh -i ~/.ssh/sample.pem ec2-user@x.x.x.x

SSH接続でEC2にログインする。

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
No packages needed for security; 3 packages available
Run "sudo yum update" to apply all updates.

nginxとphpをインストールする

$ amazon-linux-extras 

利用可能なパッケージを確認する。
今回はnginx1php7.4を利用する。

$ sudo amazon-linux-extras install -y php7.4 nginx1

インストールする。
必要なパッケージも同時にインストールされる。

$ sudo systemctl start nginx php-fpm

nginxおよびphp-fpmを起動させる。
nginxを起動させてから http://x.x.x.x にアクセスすると
screenshot
このように表示される。

$ systemctl status nginx php-fpm

で起動出来ているかどうかの確認ができる。

必要なパッケージのインストール

Laravel 8.x インストールによると、

以下の要件を満たす必要があります。

PHP >= 7.3
BCMath PHP拡張
Ctype PHP拡張
Fileinfo PHP拡張
JSON PHP拡張
Mbstring PHP拡張
OpenSSL PHP拡張
PDO PHP拡張
Tokenizer PHP拡張
XML PHP拡張

従って、必要なパッケージをインストールする。

$ sudo yum install -y php-bcmath php-ctype php-fileinfo php-json php-mbstring php-openssl php-pdo php-tokenizer php-xml

Composerをインストールする

$ curl -sS https://getcomposer.org/installer | php

Composerをインストールする。

$ sudo mv composer.phar /usr/local/bin/composer

移動させてパスを通す。

nginxとphpの設定

nginxの設定

$ sudo vi /etc/nginx/conf.d/default.conf 

/etc/nginx/conf.d/default.conf を作成し、以下の内容に変更する。

/etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name _;
    root /var/www/sample/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

内容はデプロイ 8.x Laravelを参考にしている。

php-fpmの設定

$ sudo vi /etc/php-fpm.d/www.conf

/etc/php-fpm.d/www.conf の以下の値を変更する。

/etc/php-fpm.d/www.conf
user = nginx
group = nginx

listen = /run/php-fpm/php-fpm.sock

listen.owner = nginx
listen.group = nginx
listen.mode = 0660
$ sudo systemctl restart nginx php-fpm

再起動する。

$ sudo mkdir /var/www && sudo chmod 777 /var/www

/var/www ディレクトリを作成し、全権限を与える。

Laravelをインストールする

$ composer create-project --prefer-dist laravel/laravel /var/www/sample

インストールする。

$ sudo chmod -R 777 /var/www/sample/{storage,bootstrap/cache/}

/var/www/sample/storage/および/var/www/sample/bootstrap/cache/の権限を変更し、全権限を与える。

データベースの環境変数を設定する

$ vi /var/www/sample/.env
/var/www/sample/.env
DB_CONNECTION=mysql
DB_HOST=エンドポイント
DB_PORT=3306
DB_DATABASE=データベース名
DB_USERNAME=ユーザー名
DB_PASSWORD=パスワード

エンドポイントはRDSダッシュボードから確認する。

1
2
3
4
データベース名ユーザー名、およびパスワードterraformに変数で渡した値

マイグレーション

$ cd /var/www/laravel
$ php artisan migrate:fresh --seed

成功すれば、LaravelとMySQLの接続も問題ない。
EC2インスタンス内にも必要なものが揃い、目的の構成の状態になった。

npmを使うためには

$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - && sudo yum install -y nodejs

インストールする。

gitが必要な場合

$ sudo yum install -y git

インストールする。

mysqlにログインする場合

$ sudo yum install -y mysql

インストールする。

$ mysql -h エンドポイント -u ユーザー名 -p

ログインする。

あとがき

もともと ECS、ECRを使って下図のような構成にしようと思っていました。

screenshot

しかし、おそらくタスク定義に問題があり、ローカルでは問題無いもののAWS上ではnginxとphp-fpmの接続がうまくいきませんでした。
どなたかわかる方が居りましたら教えてください。

参考

2
3
2

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
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?