Blogical

AWS/Salesforceを中心に様々な情報を配信していきます(/・ω・)/

【AWS入門】スケーラブルな構築をしてみよう(4)

こんにちは、ロジカル・アーツの井川です。

第4回記事ですね。いよいよ佳境に入ってまいりました。ここまで来ればあと少しです。いつものように全体の大まかな流れを振り返っておきましょう。

今回の作業を終えると、次のような構成が完成します。

f:id:logicalarts:20191029090413p:plain

AWSのリソースとしては、新しく作成するのはELBだけですが、ついでにLAMP環境の構築も行いたいと思います。LAMP環境についての詳しい説明は本文中で述べます。それでは始めていきましょう。

ELBとは

ELB(Elastic Load Balancing)とは、受信したトラフィックを複数のターゲット(例えばEC2インスタンス)に自動的に分散させるサービスのことです。いわゆる負荷分散ができます。ELBは次の三種類のロードバランサーをサポートしていますが、今回はClassic Load Balancerを使用します。

  • Application Load Balancer
  • Network Load Balancer
  • Classic Load Balancer

EC2インスタンスのセキュリティグループの設定追加(80ポート開放)

今回の設定では、ELBはHTTPプロトコルで通信するようにします。そのため、EC2に割り当てたセキュリティーグループ(ec2-sg)に80ポートでの通信を許可するように設定する必要があります。

EC2管理ページに移動し、左側の「セキュリティグループ」を選択します。「グループ名」「ec2-sg」のセキュリティグループを選択し、「インバウンド」タブを選択した状態で「編集」をクリックします。

f:id:logicalarts:20191125172557p:plain

「ルールの追加」をクリックし、「タイプ」「HTTP」を選択します。ソースについては、今の時点では任意のトラフックを許可するように「0.0.0.0/0」と入力します。最後に「保存」をクリックします。

f:id:logicalarts:20191021174404p:plain

これでセキュリティグループの設定は終わりました。

LAMP環境の構築

さて、ここではLAMP環境というものを構築し、ブラウザ上でRDSインスタンスにデータを送信できるようにしたいと思います。

LAMP環境とは

OSをLinux、ウェブサーバをApache HTTP Server、データベースをMySQLプログラミング言語PHPとしたサーバ環境のことをいいます*1。これらの頭文字を取ってLAMPと呼ばれます。

今回は次のような機能を実装していきます。

  • サーバ側
    以下の画面を表示させます。(簡単なものですが、、、) この画面でMySQLにログインするのに必要な情報と、データベース名とひと言を入力して送信ボタンをクリックすれば、入力したひと言がMySQLのテーブルに保存されるようにします。なお、画面にあるアベイラビリティゾーンは、アクセスしたEC2インスタンスのあるアベイラビリティゾーンを表示しています。ロードバランサーによりトラフィックが分散されていることを、この表示が切り替わることによって確認することができます。

f:id:logicalarts:20200106111201p:plain

  • データベース側
    「sample」という名前でデータベースを作成します。次にsampleデータベースにwordというカラムをもつwordsというテーブルを作成します。

では実際に作っていきましょう!

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

まずはEC2インスタンスSSH接続し、次のコマンドを実行します:

sudo yum install -y httpd24 php70 mysql56 php70-mysqlnd

このコマンドでApache HTTP Server、MySQLPHP ソフトウェアパッケージとMySQL Native Driverがインストールされます。SSH接続の方法は第2回記事を参考にしてください。

f:id:logicalarts:20191125172537p:plain

サーバ側の準備

ファイルをふたつ準備します。ひとつは先程のHello Wordページ用のファイル(hello_word.php)で、もうひとつは送信された情報をMySQLに送る用のファイル(test.php)です。

次のコードをコピーして、 hello_word.php という名前で保存します。

<!DOCTYPE html>
<html>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<head>
  <meta charset="utf-8">
  <title>Top Page</title>
  <style type="text/css">
  .jumbotron-extend {
      position: relative;
      height: 225px;
  }
  </style>
</head>
<body>
  <header>
    <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
      <a class="navbar-brand" href="hello_word.php">Hello Word</a>
    </nav>
  </header>

  <div class="jumbotron text-center jumbotron-extend">
    <div class="container-fluid">
      <div>
      <h1 class="display-4">Hello, word!</h1>
      <h4>
      <?php
      $ch = curl_init("http://169.254.169.254/latest/meta-data/placement/availability-zone");
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, True);
      $resp = curl_exec($ch);
      echo "アベイラビリティーゾーン:" . $resp;
      curl_close($ch);
      ?>
      </h4>
      <p class="lead">以下の項目を入力して送信ボタンをクリックすれば、入力したひと言がデータベースに登録されます。</p>
      </div>
    </div>
  </div>

  <form action="test.php" method="POST">
    <div class="form-row justify-content-center">
      <div class="form-group col-md-6">
        <label>ホスト名</label>
        <input type="text" class="form-control" name="host" required>
        <label>ユーザ名</label>
        <input type="text" class="form-control" name="username" required>
        <label>パスワード</label>
        <input type="password" class="form-control" name="passwd" required>
        <label>データベース名</label>
        <input type="text" class="form-control" name="dbname" >
        <label>ひと言</label>
        <input type="text" class="form-control" name="word">
      </div>
    </div>
    <div class="row justify-content-center">
      <input class="btn btn-primary" type="submit" value="送信">
    </div>
  </form>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
</body>
</html>

こちらのコードは test.phpという名前で保存します。

<!DOCTYPE html>
<html>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<head>
    <meta charset="utf-8">
    <title>Access Page</title>
</head>
<body class="text-center">
  <header>
    <nav class="navbar navbar-expand-md navbar-dark bg-dark">
      <a class="navbar-brand" href="hello_word.php">Hello Word</a>
    </nav>
  </header>

<?php

$host = $_POST["host"];

$username = $_POST["username"];

$passwd = $_POST["passwd"];

$dbname = $_POST["dbname"];

$word = $_POST["word"];

?>

  <main role="main">
    <div class="jumbotron text-center">
    <h1 class="jumbotron-heading">
    <?php
    $link = mysqli_connect($host, $username, $passwd, $dbname);
    if(!$link) {
        die("接続失敗 php" . mysqli_connect_error() );
    }
    echo "接続成功";
    ?>
    </h1>
    <p>
    <?php
    $word = mysqli_real_escape_string($link, $word);
    $result = mysqli_query($link, "INSERT INTO words VALUES (\"$word\")");
    if(!$result){
        die("クエリ失敗");
    }
    echo $word . "がデータベースに保存されました";
    mysqli_close($link);
    ?>
    </p>
    <a href="hello_word.php" class="btn btn-primary my-2">戻る</a>
  </div>
  </main>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
</body>
</html>

保存したら、これらをEC2インスタンスに転送します。第2回に引き続いてTera Termを使って説明します。Macでの転送方法は、例えば次を参考にしてください。

Tera Termを起動し、EC2インスタンスに接続した状態で、画面上部の「ファイル」からSSH SCP」を選択します。1ファイルずつしか送れないことに注意してください。

f:id:logicalarts:20191125173956p:plain

下の画像の左側の赤枠の部分をクリックし、先程作成したファイルを保存したディレクトリから選択して、「Send」ボタンをクリックします。

f:id:logicalarts:20191021174353p:plain

ふたつとも送り終わったら、ls コマンドでファイルが送られていることを確認し、/var/www/html/ ディレクトリにこれらのファイルを移します。ファイルは次のコマンドで移動させることができます:

sudo mv hello_word.php test.php /var/www/html/

f:id:logicalarts:20200106111158p:plain

Apacheサーバを起動しましょう。これでインターネット経由でアクセスできるようになります。コマンドはsudo service httpd startです。

続けてsudo chkconfig httpdでシステム起動時にApacheサーバも起動するようにします。また、chkconfig --list httpdhttpdの設定を確認できます。実行レベル2,3,4,5でonであればうまくいっています。

f:id:logicalarts:20200106111150p:plain

ブラウザのアドレスバーにEC2インスタンスに割り当てたElastic IPアドレスを入力します。サーバが起動していれば、次のような画面になるはずです。

f:id:logicalarts:20191021174311p:plain

入力したアドレスに続いて /hello_word.php と入力してみてください。以下の画面になれば成功です。 f:id:logicalarts:20200106111201p:plain

これでサーバの準備が出来ました!

データベース側の準備

次にデータベース側の準備をします。

EC2インスタンス経由でMySQLに接続し、次のコマンドたちを実行してください。接続方法は例によって、第3回記事を参考にしてください。

実行順序 コマンド 内容
1 create database sample; sampleという名前のデータベースを作成
2 use sample; 使用するデータベース(sample)を選択
3 create table words (word varchar(50)); sampleデータベース内にカラムwordをもつwordsテーブルを作成する
4 select * from words; wordsテーブルの中身を確認(この時点では空)

f:id:logicalarts:20191125174343p:plain

機能確認

先程のウェブページに戻りましょう。

f:id:logicalarts:20200106111201p:plain

以下の情報を入力し、送信ボタンをクリックします。うまくいけば、次の画面になるはずです。

項目 内容
ホスト名 RDSインスタンスのエンドポイント
ユーザ名 admin
パスワード RDSインスタンス作成時に設定したもの
データベース名 sample
ひと言 test(何でも可*2

f:id:logicalarts:20200106112316p:plain

実際にデータが保存されていることを確認しましょう。もう一度 select * from words; を実行してください。次のようになっていれば成功です!

f:id:logicalarts:20191125174348p:plain

2つめのEC2インスタンスの作成

さて、もう一度同じ設定のEC2インスタンスを作るのは大変ですよね。既存のEC2インスタンスのイメージを作成すれば同じ設定のEC2インスタンスが簡単に作れるようになります。

イメージの作成

EC2管理ページに移動し、左側のインスタンスを選択します。 「sample」と名前を付けたインスタンスを選択した状態で、「アクション」プルダウンメニューから「イメージ」 -> 「イメージの作成」をクリックします。

f:id:logicalarts:20191125172532p:plain

「イメージ名」「simple web server」と入力し、「イメージの作成」をクリックします。

f:id:logicalarts:20191021174235p:plain

しばらく待つとイメージが作成されるので、EC2インスタンスを作成しましょう。

EC2インスタンスの作成

基本的な流れは以前EC2インスタンスを作成した時と同じなので、変更点だけ説明します。詳細は第2回記事を参照してください。最初の「AMIの選択」で、左側の「マイAMI」を選択し、作成したイメージ(simple web server)を選択します。

f:id:logicalarts:20191021174240p:plain

インスタンスの詳細設定」で、「サブネット」「sample-public-1c」と名前の付いているものを選択します。

f:id:logicalarts:20191021174245p:plain

「タグの追加」では、「キー」「Name」とし、「値」を「sample2」にします。

f:id:logicalarts:20191125174938p:plain

「セキュリティグループの設定」では、既にEC2インスタンスに割り当てているセキュリティグループ(ec2-sg)を選択します。

f:id:logicalarts:20191021174255p:plain

これで2つめのEC2インスタンスが作れました!

ELBの作成

それではいよいよELBの作成に取り掛かりましょう。

再びEC2管理ページから、左側のロードバランサーを選択し、ロードバランサーの作成」をクリックします。

f:id:logicalarts:20191021174222p:plain

Classic Load Balancerの「作成」をクリックします。

f:id:logicalarts:20191021174414p:plain

ロードバランサー名」「elb」と入力し、ロードバランサー作成する場所」「sample」と名前の付いたVPCを選択します。

f:id:logicalarts:20191021174419p:plain

ページ下部に移動し、「サブネットの選択」で、名前に「public」と付けたサブネットふたつを選択します。その後「セキュリティグループの割り当て」をクリックします。

f:id:logicalarts:20191021174424p:plain

「新しいセキュリティグループを作成する」を選択し、「セキュリティグループ名」「elb-sg」とし、「説明」「Security group for ELB」と入力します。「タイプ」「HTTP」を選択し、「セキュリティ設定の構成」をクリックします。

f:id:logicalarts:20191125180155p:plain

そのまま「ヘルスチェックの設定」をクリックします。

f:id:logicalarts:20191021174435p:plain

pingパス」「/hello_word.phpに変更してください。これにより、ELBを経由したトラフィックがこのPHPファイルに向かいます。「EC2インスタンスの追加」をクリックして次の画面に行きます。

f:id:logicalarts:20200106112320p:plain

「sample」「sample2」と名前を付けたEC2インスタンスを選択します。それから「タグの追加」をクリックします。

f:id:logicalarts:20191125180413p:plain

タグは付けずに、「確認と作成」をクリックします。

f:id:logicalarts:20191021174452p:plain

そのまま「作成」をクリックします。

f:id:logicalarts:20191021174457p:plain

「閉じる」をクリックします。

f:id:logicalarts:20200106111154p:plain

「名前」「elb」ロードバランサーを選択し、インスタンスタブを選択します。「ステータス」「InService」になるまでしばらく待ちます。

f:id:logicalarts:20191125183142p:plain

「説明」タブを選択し、DNS名」をコピーします。これがElastic IPアドレスの代わりになります。

f:id:logicalarts:20191125183147p:plain

ブラウザのアドレスバーに{DNS名}/hello_word.phpと入力すれば(波括弧は不要)、おなじみの画面になります。ブラウザのリロードボタンを何度か押してみてください。アベイラビリティゾーンが切り替わることが確認できると思います。

f:id:logicalarts:20200106111201p:plain f:id:logicalarts:20200106111206p:plain

最後にEC2インスタンスたちに割り当てたセキュリティグループ(ec2-sg)を編集し、作成したELBからの通信のみを許可するように設定します。

EC2管理ページで、左側の「セキュリティグループ」を選択します。「インバウンド」タグを選択し、「編集」をクリックします。

f:id:logicalarts:20191125172557p:plain

「タイプ」「HTTP」「ソース」を、ELBに割り当てたセキュリティグループ(elb-sgと入力すれば出てくるかと思います)にして「保存」をクリックします。

f:id:logicalarts:20191125183231p:plain

これでELBを経由しないHTTP通信はできなくなります。はじめにしたように、Elastic IPアドレスでアクセスしようとしても繋がりません。

AMIの削除方法

不要なAMIは次のように削除できます。

「EC2管理ページ」から左側の「AMI」を選択した状態で、削除したいAMIを選択します(下の例では「AMI 名」「simple web server」のもの)。その後、「アクション」プルダウンメニューをクリックし、「登録解除」をクリックします。

f:id:logicalarts:20200106142204p:plain

次の画面で「次へ」をクリックすれば、AMIが削除されます。

f:id:logicalarts:20200106142159p:plain

ELBの削除方法

不要なELBは次のように削除できます。

「EC2管理ページ」から左側のロードバランサーを選択した状態で、削除したいロードバランサーを選択します(下の例では「名前」「elb」のもの)。その後、「アクション」プルダウンメニューをクリックし、「削除」をクリックします。

f:id:logicalarts:20200106115116p:plain

次の画面で「はい、削除する」をクリックすれば、ロードバランサーが削除されます。

f:id:logicalarts:20200106115120p:plain

おわりに

お疲れ様でした!ELBの作成自体はそれほど大変でもなかったと思います。LAMP環境のほうは動作確認レベルの簡単なものを作りましたが、興味のある方はいろいろ試してみてください。ただ、今回作ったものはセキュリティ的な考慮は特にしていないので、おわったら削除するようお願いします。次回はRDSをMulti-AZ構成にして、いよいよスケーラブルな構成が完成します。

参考サイト

*1:MとPにはMariaDBPythonなどの別バージョンもあります

*2:ただし、日本語だと文字化けする可能性があります