構成
Terraformで ap-northeast-1
リージョンにVPC、1つのパブリックサブネットおよび2つのプライベートサブネットを作成した。さらにパブリックサブネット内にEC2インスタンスを立て、プライベートサブネットにはRDS for MySQLを設置した。ここまでをTerraformで行った。
EC2インスタンス内にはSSH接続でログインし、Laravelの環境を構築した。
>>
TerraformでEC2、RDSを構築する
IAMユーザーを作成する
AWSコンソールからIAMダッシュボードへ移動する。
ユーザーを追加を選択する。
ユーザー名を入力し、プログラムによるアクセスを選択する。
既存のポリシーを直接アタッチを選択し、AdministratorAccess
を選択する。
内容を確認し、ユーザーの作成を選択する。
アクセスキーID
とシークレットアクセスキー
を控える。
~/.aws/credentials
に aws_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
この状態になる。
[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ファイル用意した。
ファイルの内容
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 }
# Create a VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
tags = { Name = var.app }
}
# 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" }
}
# Provides a resource to create a VPC Internet Gateway.
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = { Name = var.app }
}
# 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" }
}
# 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
}
# 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" }
}
# 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_name
は sample
としている。
後ほど sample
という名前のキーペアを作成する。
# 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" }
}
# 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 }
}
# 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" }
}
変数ファイル
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
ファイルを作って変数に値を渡す。
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_name
、db_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上で下図の状態になる。
あとはSSH接続し、EC2インスタンス内に必要なものをインストールしていく。
もし構築したものをすべて壊すときは terraform destroy -var-file="secret.tfvars"
を実行する。
SHH接続用のキーペアを作成する
AWSコンソールからEC2ダッシュボードへ移動する。
キーペア を選択する。
キーペアを作成 を選択する。
キーペアを作成する。
キーペアが作成される。
キーペアを作成すると秘密鍵がダウンロードされる。
秘密鍵は ~/.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ダッシュボードへ移動する。
インスタンスを選択し、パブリック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
利用可能なパッケージを確認する。
今回はnginx1
とphp7.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 にアクセスすると
このように表示される。
$ 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
を作成し、以下の内容に変更する。
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
の以下の値を変更する。
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
DB_CONNECTION=mysql
DB_HOST=エンドポイント
DB_PORT=3306
DB_DATABASE=データベース名
DB_USERNAME=ユーザー名
DB_PASSWORD=パスワード
エンドポイント
はRDSダッシュボードから確認する。
①
②
③
④
データベース名
、ユーザー名
、およびパスワード
は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を使って下図のような構成にしようと思っていました。
しかし、おそらくタスク定義に問題があり、ローカルでは問題無いもののAWS上ではnginxとphp-fpmの接続がうまくいきませんでした。
どなたかわかる方が居りましたら教えてください。