サイトアイコン 協栄情報ブログ

【CloudFormation】AWSマルチAZ3層アーキテクチャの構築_EC2

皆様、お世話になっております。鈴木と申します。
今回はAWS CloudFormationを使用してEC2を作成していきたいと思います。

1.目次

目次はこちら

2.要件

3.EC2について

EC2

EC2(Amazon Elastic Compute Cloud)は、Amazon Web Services(AWS)が提供するクラウドコンピューティングサービスで、仮想サーバー(インスタンス)を必要に応じて起動し、管理することができます。

ユースケース

EC2は、AWSの強力なコンピューティングリソースを提供するサービスで、スケーラブルで柔軟な仮想サーバーの管理が可能です。多様なインスタンスの選択肢や価格モデルにより、さまざまなニーズに応じたクラウドコンピューティング環境を構築できます。

4.構成図

【インバウンド】

【アウトバウンド】

4-2.IAMインスタンスプロファイル

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
      (&#039;Alice&#039;, &#039;Smith&#039;, &#039;alice.smith@example.com&#039;, &#039;2023-01-15&#039;, 60000.00, 1),
      (&#039;Bob&#039;, &#039;Johnson&#039;, &#039;bob.johnson@example.com&#039;, &#039;2023-02-20&#039;, 55000.00, 2),
      (&#039;Charlie&#039;, &#039;Williams&#039;, &#039;charlie.williams@example.com&#039;, &#039;2023-03-25&#039;, 65000.00, 1),
      (&#039;David&#039;, &#039;Brown&#039;, &#039;david.brown@example.com&#039;, &#039;2023-04-30&#039;, 70000.00, 2),
      (&#039;Eva&#039;, &#039;Garcia&#039;, &#039;eva.garcia@example.com&#039;, &#039;2023-05-05&#039;, 58000.00, 1);

  UPDATE employees
  SET salary = 55000.00
  WHERE employee_id = 1;
  EOF

簡単にやっていることを下記にてご説明致します。

以上になります。
これで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

簡単にやっていることを下記にてご説明致します。

上記で作成した2号機は下記のように表示されます。

また1号機で作成作成済みのEFSの中にあるファイルを共有して使用することが出来ます。

8.感想

ユーザーデータを使用することでヒューマンエラーを減らしたうえで、WEBサーバーとして素早く機能させることが出来ると実感できました。
1からやる場合は設定ファイルのバックアップはしっかりとったうえで作業しましょう。。

モバイルバージョンを終了