皆様、お世話になっております。鈴木と申します。
今回はAWS CloudFormationを使用してEC2を作成していきたいと思います。
1.目次
2.要件
- プライベートサブネットに配置
- Amazon Linux2023を使用
- EBSはgp3を使用
- キーペアを付与
- SSMより操作
- Webサーバーとして機能させる(Apacheサーバー)
- RDSへのログインを可能とする
- ユーザーデータにてインストールやEFSへのマウントを行う
3.EC2について
EC2
EC2(Amazon Elastic Compute Cloud)は、Amazon Web Services(AWS)が提供するクラウドコンピューティングサービスで、仮想サーバー(インスタンス)を必要に応じて起動し、管理することができます。
-
仮想サーバー:
EC2では仮想サーバーを「インスタンス」と呼びます。これにより、物理サーバーに依存せずにスケーラブルなコンピューティングリソースを提供します。 -
オンデマンドスケーリング:
必要に応じてインスタンスを追加・削除でき、リソースのスケーリングが簡単です。 -
柔軟なインスタンス:
異なる種類のインスタンスがあり、計算能力、メモリ、ストレージのニーズに応じて選択できます。 -
価格モデル:
- オンデマンドインスタンス: 使用した分だけの課金。
- リザーブドインスタンス: 長期間の利用を前提に割引価格で提供。
- スポットインスタンス: 空き容量に応じて安価に提供される一時的なインスタンス。
-
セキュリティ:
- 仮想プライベートクラウド(VPC)を使用してネットワークを分離し、セキュリティグループやネットワークACLでアクセス制御が可能。
-
自動化:
- Auto ScalingやElastic Load Balancingを利用してトラフィックに応じてインスタンスの数を自動で調整。
ユースケース
- ウェブアプリケーションのホスティング: 高い可用性とスケーラビリティを提供。
- データ処理: 大量のデータを処理するための計算リソース。
- 開発・テスト環境の構築: 短期間でインスタンスを立ち上げ、すぐに利用開始。
EC2は、AWSの強力なコンピューティングリソースを提供するサービスで、スケーラブルで柔軟な仮想サーバーの管理が可能です。多様なインスタンスの選択肢や価格モデルにより、さまざまなニーズに応じたクラウドコンピューティング環境を構築できます。
4.構成図
- SSM経由でEC2にアクセス(HTTPS)できるようにすること。
- RDSへのログインも可能にすること。
- EFSにファイルを格納して共有できるようにすること。
事前にキーペア・セキュリティグループ、IAMインスタンスプロファイルは作成済みです。
セキュリティグループとIAMインスタンスプロファイルの設定は下記です。4-1.セキュリティグループ
【インバウンド】
- ALB ↔ EC2(80・HTTP)
- SSM ↔ EC2(443・HTTPS)
- EFS ↔ EC2(2049・EFS)
- RDS ↔ EC2(3306・MYSQL)
【アウトバウンド】
- 全許可
4-2.IAMインスタンスプロファイル
- AmazonSSMManagedInstanceCore(SSM用のポリシー)
- CloudWatchAgentAdminPolicy(Cloudwatch用のポリシー)
→下記ではCloudwatchの設定は割愛しております。
5.ソースコード全体
YAML形式にて記述しております。
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to create an EC2 instance with specified configurations
Parameters:
ImageId:
Description: The ID of the Amazon Machine Image (AMI) to use
Type: String
Default:
InstanceType:
Description: The EC2 instance type
Type: String
Default:
KeyName:
Description: The EC2 Key Pair to allow SSH access to the instance
Type: AWS::EC2::KeyPair::KeyName
Default:
SubnetId:
Description: The Subnet ID for the instance
Type: AWS::EC2::Subnet::Id
Default:
SecurityGroupId:
Description: The Security Group ID for the instance
Type: AWS::EC2::SecurityGroup::Id
Default:
InstanceProfile:
Description: The IAM instance profile for the instance
Type: String
Default:
Resources:
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
SubnetId: !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroupId
ImageId: !Ref ImageId
IamInstanceProfile: !Ref InstanceProfile
DisableApiTermination: true
InstanceInitiatedShutdownBehavior: stop
Monitoring: true
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 8
VolumeType: gp3
Iops: 3000
DeleteOnTermination: true
Tags:
- Key: Name
Value: naoki-ec2-2a
UserData:
#ユーザーデータを記載
Outputs:
InstanceId:
Description: The instance ID
Value: !Ref EC2Instance
InstancePrivateIp:
Description: The private IP address of the instance
Value: !GetAtt EC2Instance.PrivateIpAddress
6.ソースコード詳細
EC2の基本的なソースコードになります。
ユーザーデータは別でご説明致します。
使用するオプション | 設定値 | 説明 |
---|---|---|
Type | AWS::EC2::Instance | デフォルトの定義 |
InstanceType | !Ref KeyName | キーペアを指定 |
SubnetIds | !Ref SubnetIds | サブネットを指定 |
SecurityGroupIds | !Ref SecurityGroupId | セキュリティグループを指定 |
ImageId | !Ref ImageId | amiのイメージIDを指定 |
IamInstanceProfile | !Ref InstanceProfile | IAMインスタンスプロファイルをコンソールを指定 |
DisableApiTermination | true | 削除保護を有効化 |
InstanceInitiatedShutdownBehavior | stop | シャットダウン動作をストップに指定 |
Monitoring | true | Cloudwatchのモニタリングを有効化 |
Ebs | 下記ソースコード参照 | EBS詳細を記載(gp3) |
Tags | Key: Name Value: naoki-ec2-2a |
タグの値とキーを設定 |
UserData | 下記ユーザーデータを参照 | ユーザーデータを記載 |
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
SubnetId: !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroupId
ImageId: !Ref ImageId
IamInstanceProfile: !Ref InstanceProfile
DisableApiTermination: true
InstanceInitiatedShutdownBehavior: stop
Monitoring: true
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 8
VolumeType: gp3
Iops: 3000
DeleteOnTermination: true
Tags:
- Key: Name
Value: naoki-ec2-2a
UserData:
#ユーザーデータを記載
7.ユーザーデータ
7-1.ユーザーデータについて
ユーザーデータは、EC2インスタンス起動時に自動的に実行されるスクリプトやコマンドを指定するための機能です。主に初期設定やソフトウェアのインストール、自動構成などに利用されます。ユーザーデータはインスタンスの初回起動時のみ実行され、再起動時には実行されません。
7-2.EC2(1号機用)
今回EC2を2台使用します。
1号機と2号機の別々のphpファイルを表示してタイトルを表示させます。
#!/bin/bash
# 環境変数の設定(変更してください)
export EFS_DNS="EFSのDNS名"
export MYSQL_HOST="RDSのARN"
export MYSQL_USER="ユーザー名"
export MYSQL_PASSWORD="RDSパスワード"
# 更新を行う
dnf update -y
# Apache HTTPサーバーをインストール
dnf install -y httpd
# EFSファイルシステム用のディレクトリを作成
mkdir -p /mnt/efs
# EFSファイルシステムをマウント
mount -t nfs4 "${EFS_DNS}:/" /mnt/efs
# EFSのマウントを永続化
grep -q "${EFS_DNS}:/ /mnt/efs nfs4 defaults,_netdev 0 0" /etc/fstab || echo "${EFS_DNS}:/ /mnt/efs nfs4 defaults,_netdev 0 0" >> /etc/fstab
# MySQLクライアントのインストール
dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
dnf -y install mysql-community-client
# MySQLドライバーのインストール
dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
# PHPとApacheのPHPモジュールのインストール
dnf install -y php php-cli php-fpm php-mysqlnd php-json php-opcache php-gd php-xml php-mbstring
sudo dnf update -y
# Apache MPMをpreforkに設定
echo "LoadModule mpm_prefork_module modules/mod_mpm_prefork.so" | sudo tee /etc/httpd/conf.modules.d/00-mpm.conf
# Apacheの設定変更
sed -i 's|DocumentRoot "/var/www/html"|DocumentRoot "/mnt/efs"|g' /etc/httpd/conf/httpd.conf
sed -i 's|||g' /etc/httpd/conf/httpd.conf
sed -i 's/index.html/index01.php/g' /etc/httpd/conf/httpd.conf
# PHP-FPMの設定
systemctl start php-fpm
systemctl enable php-fpm
# Apache設定にPHP-FPMの設定を追加
cat < /etc/httpd/conf.d/php.conf
SetHandler "proxy:unix:/var/run/php-fpm/www.sock|fcgi://localhost/"
EOF
# Apacheの起動と自動起動の設定
systemctl start httpd
systemctl enable httpd
# データベース設定ファイルの作成(DB名だけ変更)
cat < /mnt/efs/db_config.php
<?php
\$db_config = [
'servername' => '$MYSQL_HOST',
'username' => '$MYSQL_USER',
'password' => '$MYSQL_PASSWORD',
'dbname' => 'naoki_rds'
];
?>
EOF
# PHPファイルの作成
cat < /mnt/efs/index01.php
<?php
include 'db_config.php';
// データベース接続の設定
\$servername = \$db_config['servername'];
\$username = \$db_config['username'];
\$password = \$db_config['password'];
\$dbname = \$db_config['dbname'];
// MySQLデータベースに接続
\$conn = new mysqli(\$servername, \$username, \$password, \$dbname);
// UTF-8でエンコーディングを設定
\$conn->set_charset("utf8");
// 接続をチェックする
if (\$conn->connect_error) {
die("接続に失敗しました: " . \$conn->connect_error);
}
// employeesテーブルからデータを取得するクエリ
\$sql = "SELECT * FROM employees";
\$result = \$conn->query(\$sql);
// クエリの実行をチェックする
if (!\$result) {
die("クエリの実行に失敗しました: " . \$conn->error);
}
// HTMLの文字コードを指定
echo "";
// タイトルを表示
echo " <h1>WEBサーバー 1号機 </h1> ";
// データが取得できた場合、HTMLテーブルとして表示する
if (\$result->num_rows > 0) {
echo "<table border='1'>";
echo "<tr><th>Employee ID</th><th>First Name</th><th>Last Name</th><th>Email</th><th>Hire Date</th><th>Salary</th><th>Department ID</th></tr>";
while(\$row = \$result->fetch_assoc()) {
echo "<tr>";
echo "<td>".\$row["employee_id"]."</td>";
echo "<td>".\$row["first_name"]."</td>";
echo "<td>".\$row["last_name"]."</td>";
echo "<td>".\$row["email"]."</td>";
echo "<td>".\$row["hire_date"]."</td>";
echo "<td>".\$row["salary"]."</td>";
echo "<td>".\$row["department_id"]."</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "データが見つかりませんでした";
}
// MySQL接続を閉じる
\$conn->close();
?>
EOF
# ALBのヘルスチェック用のHTMLファイルの作成
cat < /mnt/efs/healthcheck.php
<title>Health Check</title>
<h1>ALB Health Check Passed</h1>
EOF
# ファイアウォールの設定を変更し、HTTPトラフィックを許可
if systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-service=http
firewall-cmd --reload
else
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
service iptables save
fi
# MySQLデータベースの設定(CREATEとUSEのデータベース名変える)
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD <<EOF
CREATE DATABASE IF NOT EXISTS naoki_rds;
USE naoki_rds;
CREATE TABLE IF NOT EXISTS employees (
employee_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100),
hire_date DATE,
salary DECIMAL(10, 2),
department_id INT
);
INSERT INTO employees (first_name, last_name, email, hire_date, salary, department_id)
VALUES
('Alice', 'Smith', 'alice.smith@example.com', '2023-01-15', 60000.00, 1),
('Bob', 'Johnson', 'bob.johnson@example.com', '2023-02-20', 55000.00, 2),
('Charlie', 'Williams', 'charlie.williams@example.com', '2023-03-25', 65000.00, 1),
('David', 'Brown', 'david.brown@example.com', '2023-04-30', 70000.00, 2),
('Eva', 'Garcia', 'eva.garcia@example.com', '2023-05-05', 58000.00, 1);
UPDATE employees
SET salary = 55000.00
WHERE employee_id = 1;
EOF
簡単にやっていることを下記にてご説明致します。
- ApacheやPHP、MYSQLドライバーなどのRDSへのログインやWEBサーバーとして機能させるために必要なものをインストール
- PHPのモジュールの設定
- Apache設定ファイルの編集
- EFS用のファイルを作成して、マウントするように指定
- 1,2号機共通のRDS情報のあるPHPファイルとALBヘルスチェック用のPHPファイルの作成
- 1号機用のPHPファイルの作成
- RDSにログインしてデータベースとテーブルの作成
※Cloudwatchエージェント系のインストールも実施しておりますが、説明は割愛致します。
以上になります。
これで1号機のWEBサーバーとしての準備は整いました。
下記はALB→EC2→NATの構成にして1号機の内容を表示した結果です。
またEc2にログインした中身は下記の通りです。
7-3.EC2(2号機用)
続いて2号機です。
すでにEFSにDBのパスワード等のPHPファイルとALBヘルスチェック用のPHPファイルは存在するため2号機用のPHPファイルの作成のみとEFSへのマウントを行います。
#!/bin/bash
# 環境変数の設定(変更してください)
export EFS_DNS="EFSのDNS名"
export MYSQL_HOST="RDSのARN"
export MYSQL_USER="ユーザー名"
export MYSQL_PASSWORD="RDSパスワード"
# 更新を行う
dnf update -y
# Apache HTTPサーバーをインストール
dnf install -y httpd
# EFSファイルシステム用のディレクトリを作成
mkdir -p /mnt/efs
# EFSファイルシステムをマウント
mount -t nfs4 "${EFS_DNS}:/" /mnt/efs
# EFSのマウントを永続化
grep -q "${EFS_DNS}:/ /mnt/efs nfs4 defaults,_netdev 0 0" /etc/fstab || echo "${EFS_DNS}:/ /mnt/efs nfs4 defaults,_netdev 0 0" >> /etc/fstab
# MySQLクライアントのインストール
dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
dnf -y install mysql-community-client
# PHPとApacheのPHPモジュールのインストール
dnf install -y php php-cli php-fpm php-mysqlnd php-json php-opcache php-gd php-xml php-mbstring
sudo dnf update -y
# Apache MPMをpreforkに設定
echo "LoadModule mpm_prefork_module modules/mod_mpm_prefork.so" | sudo tee /etc/httpd/conf.modules.d/00-mpm.conf
# Apacheの設定変更
sed -i 's|DocumentRoot "/var/www/html"|DocumentRoot "/mnt/efs"|g' /etc/httpd/conf/httpd.conf
sed -i 's|||g' /etc/httpd/conf/httpd.conf
sed -i 's/index.html/index02.php/g' /etc/httpd/conf/httpd.conf
# PHP-FPMの設定
systemctl start php-fpm
systemctl enable php-fpm
# Apache設定にPHP-FPMの設定を追加
cat < /etc/httpd/conf.d/php.conf
SetHandler "proxy:unix:/var/run/php-fpm/www.sock|fcgi://localhost/"
EOF
# Apacheの起動と自動起動の設定
systemctl start httpd
systemctl enable httpd
# PHPファイルの作成
cat < /mnt/efs/index02.php
set_charset("utf8");
// 接続をチェックする
if (\$conn->connect_error) {
die("接続に失敗しました: " . \$conn->connect_error);
}
// employeesテーブルからデータを取得するクエリ
\$sql = "SELECT * FROM employees";
\$result = \$conn->query(\$sql);
// クエリの実行をチェックする
if (!\$result) {
die("クエリの実行に失敗しました: " . \$conn->error);
}
// HTMLの文字コードを指定
echo "";
// タイトルを表示
echo " <h1>WEBサーバー 2号機 </h1> ";
// データが取得できた場合、HTMLテーブルとして表示する
if (\$result->num_rows > 0) {
echo "<table border='1'>";
echo "<tr><th>Employee ID</th><th>First Name</th><th>Last Name</th><th>Email</th><th>Hire Date</th><th>Salary</th><th>Department ID</th></tr>";
while(\$row = \$result->fetch_assoc()) {
echo "<tr>";
echo "<td>".\$row["employee_id"]."</td>";
echo "<td>".\$row["first_name"]."</td>";
echo "<td>".\$row["last_name"]."</td>";
echo "<td>".\$row["email"]."</td>";
echo "<td>".\$row["hire_date"]."</td>";
echo "<td>".\$row["salary"]."</td>";
echo "<td>".\$row["department_id"]."</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "データが見つかりませんでした";
}
// MySQL接続を閉じる
\$conn->close();
?>
EOF
# ALBのヘルスチェック用のHTMLファイルの作成
cat < /mnt/efs/healthcheck.php
<title>Health Check</title>
<h1>ALB Health Check Passed</h1>
EOF
# ファイアウォールの設定を変更し、HTTPトラフィックを許可
if systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-service=http
firewall-cmd --reload
else
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
service iptables save
fi
簡単にやっていることを下記にてご説明致します。
- ApacheやPHP、MYSQLドライバーなどのRDSへのログインやWEBサーバーとして機能させるために必要なものをインストール
- PHPのモジュールの設定
- Apache設定ファイルの編集
- EFS用のファイルを作成して、マウントするように指定
- 2号機用のPHPファイルの作成
上記で作成した2号機は下記のように表示されます。
また1号機で作成作成済みのEFSの中にあるファイルを共有して使用することが出来ます。
8.感想
ユーザーデータを使用することでヒューマンエラーを減らしたうえで、WEBサーバーとして素早く機能させることが出来ると実感できました。
1からやる場合は設定ファイルのバックアップはしっかりとったうえで作業しましょう。。