

# EC2 인스턴스에 웹 서버 설치
<a name="CHAP_Tutorials.WebServerDB.CreateWebServer"></a>

[EC2 인스턴스를 시작하여 DB 클러스터에 연결](CHAP_Tutorials.WebServerDB.LaunchEC2.md)에서 생성한 EC2 인스턴스에 웹 서버를 설치합니다. 웹 서버는 [Amazon Aurora DB 클러스터 생성](CHAP_Tutorials.WebServerDB.CreateDBCluster.md)에서 생성한 Amazon Aurora DB 클러스터에 연결됩니다.

## PHP 및 MariaDB와 함께 Apache 웹 서버 설치
<a name="CHAP_Tutorials.WebServerDB.CreateWebServer.Apache"></a>

EC2 인스턴스에 연결하고 서버를 설치합니다.

**EC2 인스턴스에 연결하고 PHP가 포함된 Apache 웹 서버를 설치하는 방법**

1. **Amazon EC2 사용 설명서에 있는 [Linux 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstances.html)의 단계를 따라 앞에서 만든 EC2 인스턴스에 연결합니다.

   SSH를 사용하여 EC2 인스턴스에 연결하는 것이 좋습니다. Windows, Linux 또는 Mac에 SSH 클라이언트 유틸리티가 설치된 경우 다음 명령 형식을 사용하여 인스턴스에 연결할 수 있습니다.

   ```
   ssh -i location_of_pem_file ec2-user@ec2-instance-public-dns-name
   ```

   예를 들어 `ec2-database-connect-key-pair.pem`이 Linux의 `/dir1`에 저장되어 있고, EC2 인스턴스의 퍼블릭 IPv4 DNS가 `ec2-12-345-678-90.compute-1.amazonaws.com`이라고 가정해 보겠습니다. SSH 명령은 다음과 같이 표시됩니다.

   ```
   ssh -i /dir1/ec2-database-connect-key-pair.pem ec2-user@ec2-12-345-678-90.compute-1.amazonaws.com
   ```

1. EC2 인스턴스에서 소프트웨어를 업데이트하여 최신 버그 수정 및 보안 업데이트를 받습니다. 이렇게 하려면 다음 명령을 사용하십시오.
**참고**  
`-y` 옵션을 사용하면 확인 여부를 묻지 않고 업데이트를 설치합니다. 설치 전에 업데이트 정보를 확인하려면 이 옵션을 생략합니다.

   ```
   sudo dnf update -y
   ```

1. 업데이트가 완료되면 다음 명령을 사용하여 Apache 웹 서버, PHP, MariaDB 또는 PostgreSQL 소프트웨어를 설치합니다. 이 명령은 여러 소프트웨어 패키지와 관련 종속 프로그램을 동시에 설치합니다.

------
#### [ MariaDB & MySQL ]

   ```
   sudo dnf install -y httpd php php-mysqli mariadb105
   ```

------
#### [ PostgreSQL ]

   ```
   sudo dnf install -y httpd php php-pgsql postgresql15
   ```

------

   오류가 발생하면 인스턴스가 Amazon Linux 2023 AMI로 실행되지 않은 것입니다. Amazon Linux 2 AMI를 사용하고 있는 것일 수 있습니다. 다음 명령을 사용하여 Amazon Linux 버전을 볼 수 있습니다.

   ```
   cat /etc/system-release
   ```

   자세한 내용은 [인스턴스 소프트웨어 업데이트](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/install-updates.html) 단원을 참조하십시오.

1. 다음 명령을 사용하여 웹 서버를 시작합니다.

   ```
   sudo systemctl start httpd
   ```

   웹 서버가 제대로 설치되고 시작되었는지 테스트할 수 있습니다. 이렇게 하려면 웹 브라우저의 주소 표시줄에 EC2 인스턴스의 퍼블릭 Domain Name System(DNS) 이름을 입력합니다(예: `http://ec2-42-8-168-21.us-west-1.compute.amazonaws.com`). 웹 서버가 실행되고 있으면 Apache 테스트 페이지가 표시됩니다.

   Apache 테스트 페이지가 표시되지 않으면 [자습서: DB 클러스터에 사용할 Amazon VPC 생성(IPv4 전용)](CHAP_Tutorials.WebServerDB.CreateVPC.md)에서 생성한 VPC 보안 그룹에 대한 인바운드 규칙을 확인합니다. 인바운드 규칙에 웹 서버에 연결할 IP 주소에 대한 HTTP(포트 80) 액세스를 허용하는 규칙이 포함되어 있는지 확인합니다.
**참고**  
Apache 테스트 페이지는 문서의 루트 디렉터리 `/var/www/html`에 콘텐츠가 없는 경우에만 표시됩니다. 문서의 루트 디렉터리에 콘텐츠를 추가한 후에는 콘텐츠가 EC2 인스턴스의 퍼블릭 DNS 주소에 나타납니다. 이 시점 이전에는 Apache 테스트 페이지에 나타납니다.

1. `systemctl` 명령을 사용하여 웹 서버가 시스템 부팅 때마다 시작되도록 구성합니다.

   ```
   sudo systemctl enable httpd
   ```

`ec2-user`가 Apache 웹 서버의 기본 루트 디렉터리에 있는 파일을 관리할 수 있도록 하려면 `/var/www` 디렉터리의 소유권 및 권한을 변경합니다. 이 작업을 수행하는 방법에는 여러 가지가 있습니다. 본 자습서에서는 `ec2-user`를 `apache` 그룹에 추가하여 `apache` 그룹에 `/var/www` 디렉터리의 소유권을 부여하고 쓰기 권한을 할당합니다.

**Apache 웹 서버에 대한 파일 권한 설정 방법**

1. `ec2-user` 사용자를 `apache` 그룹에 추가합니다.

   ```
   sudo usermod -a -G apache ec2-user
   ```

1. 권한을 새로 고치고 새 `apache` 그룹을 포함하려면 로그아웃합니다.

   ```
   exit
   ```

1. 다시 로그인한 다음, `apache` 명령을 사용하여 `groups` 그룹이 있는지 확인합니다.

   ```
   groups
   ```

   출력 결과는 다음과 비슷합니다.

   ```
   ec2-user adm wheel apache systemd-journal
   ```

1. `/var/www` 디렉터리 및 해당 콘텐츠의 그룹 소유권을 `apache` 그룹으로 변경합니다.

   ```
   sudo chown -R ec2-user:apache /var/www
   ```

1. `/var/www` 및 그 하위 디렉터리의 디렉터리 권한을 변경해서 그룹 쓰기 권한을 추가하고 나중에 생성될 하위 디렉터리에서 그룹 ID를 설정합니다.

   ```
   sudo chmod 2775 /var/www
   find /var/www -type d -exec sudo chmod 2775 {} \;
   ```

1. `/var/www` 디렉터리 및 하위 디렉터리의 파일 권한을 계속 변경해서 그룹 쓰기 권한을 추가합니다.

   ```
   find /var/www -type f -exec sudo chmod 0664 {} \;
   ```

이제 `ec2-user`(및 `apache` 그룹의 향후 멤버)는 Apache 문서 루트에서 파일을 추가, 삭제, 편집할 수 있습니다. 따라서 정적 웹 사이트 또는 PHP 애플리케이션과 같은 콘텐츠를 추가할 수 있습니다.

**참고**  
HTTP 프로토콜을 실행하는 웹 서버는 송신하거나 수신하는 데이터에 대해 아무런 전송 보안 기능도 제공하지 않습니다. 웹 브라우저를 사용하여 HTTP 서버에 연결하면 네트워크 경로를 따라 어디서든 엿보려는 사람들이 많은 정보를 볼 수 있습니다. 이 정보에는 방문하는 URL, 수신하는 웹 페이지의 내용, HTML 양식의 내용(암호 포함)이 포함됩니다.  
웹 서버를 안전하게 보호하기 위한 최선의 방법은 HTTPS(HTTP Secure) 지원 기능을 설치하는 것입니다. 이 프로토콜은 SSL/TLS 암호화로 데이터를 보호합니다. 자세한 내용은 *Amazon EC2 사용 설명서*에서 [자습서: Amazon Linux AMI를 사용하여 SSL/TLS 구성](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-ami.html)을 참조하세요.

## DB 클러스터에 Apache 웹 서버 연결
<a name="CHAP_Tutorials.WebServerDB.CreateWebServer.PHPContent"></a>

이제 Amazon Aurora DB 클러스터에 연결되는 Apache 웹 서버에 콘텐츠를 추가합니다.

**DB 클러스터에 연결되는 Apache 웹 서버에 콘텐츠를 추가하는 방법**

1. EC2 인스턴스에 계속 연결되어 있을 때 디렉터리를 `/var/www`로 변경하고 `inc`라는 새로운 하위 디렉터리를 생성합니다.

   ```
   cd /var/www
   mkdir inc
   cd inc
   ```

1. `inc`라는 `dbinfo.inc` 디렉터리에서 새 파일을 생성한 다음 nano 또는 선택한 편집기를 호출하여 파일을 편집합니다.

   ```
   >dbinfo.inc
   nano dbinfo.inc
   ```

1. 다음 내용을 `dbinfo.inc` 파일에 추가합니다. 여기서 *db\$1instance\$1endpoint*는 DB 클러스터에 대해 포트가 없는 DB 클러스터 라이터 엔드포인트입니다.
**참고**  
웹 서버의 문서 루트에 속하지 않은 폴더에 사용자 이름과 암호 정보를 두는 것이 좋습니다. 이렇게 하면 보안 정보가 노출될 가능성이 줄어듭니다.  
애플리케이션에서 적절한 암호로 `master password`를 변경해야 합니다.

   ```
   <?php
   
   define('DB_SERVER', 'db_cluster_writer_endpoint');
   define('DB_USERNAME', 'tutorial_user');
   define('DB_PASSWORD', 'master password');
   define('DB_DATABASE', 'sample');
   ?>
   ```

1. `dbinfo.inc` 파일을 저장하고 닫습니다. nano를 사용하는 경우 Ctrl\$1S 및 Ctrl\$1X를 사용하여 파일을 저장하고 닫습니다.

1. 디렉터리를 `/var/www/html`로 변경합니다.

   ```
   cd /var/www/html
   ```

1. `html`라는 `SamplePage.php` 디렉터리에서 새 파일을 생성한 다음 nano 또는 선택한 편집기를 호출하여 파일을 편집합니다.

   ```
   >SamplePage.php
   nano SamplePage.php
   ```

1. 다음 콘텐츠를 `SamplePage.php` 파일에 추가합니다.

------
#### [ MariaDB & MySQL ]

   ```
   <?php include "../inc/dbinfo.inc"; ?>
   <html>
   <body>
   <h1>Sample page</h1>
   <?php
   
     /* Connect to MySQL and select the database. */
     $connection = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD);
   
     if (mysqli_connect_errno()) echo "Failed to connect to MySQL: " . mysqli_connect_error();
   
     $database = mysqli_select_db($connection, DB_DATABASE);
   
     /* Ensure that the EMPLOYEES table exists. */
     VerifyEmployeesTable($connection, DB_DATABASE);
   
     /* If input fields are populated, add a row to the EMPLOYEES table. */
     $employee_name = htmlentities($_POST['NAME']);
     $employee_address = htmlentities($_POST['ADDRESS']);
   
     if (strlen($employee_name) || strlen($employee_address)) {
       AddEmployee($connection, $employee_name, $employee_address);
     }
   ?>
   
   <!-- Input form -->
   <form action="<?PHP echo $_SERVER['SCRIPT_NAME'] ?>" method="POST">
     <table border="0">
       <tr>
         <td>NAME</td>
         <td>ADDRESS</td>
       </tr>
       <tr>
         <td>
           <input type="text" name="NAME" maxlength="45" size="30" />
         </td>
         <td>
           <input type="text" name="ADDRESS" maxlength="90" size="60" />
         </td>
         <td>
           <input type="submit" value="Add Data" />
         </td>
       </tr>
     </table>
   </form>
   
   <!-- Display table data. -->
   <table border="1" cellpadding="2" cellspacing="2">
     <tr>
       <td>ID</td>
       <td>NAME</td>
       <td>ADDRESS</td>
     </tr>
   
   <?php
   
   $result = mysqli_query($connection, "SELECT * FROM EMPLOYEES");
   
   while($query_data = mysqli_fetch_row($result)) {
     echo "<tr>";
     echo "<td>",$query_data[0], "</td>",
          "<td>",$query_data[1], "</td>",
          "<td>",$query_data[2], "</td>";
     echo "</tr>";
   }
   ?>
   
   </table>
   
   <!-- Clean up. -->
   <?php
   
     mysqli_free_result($result);
     mysqli_close($connection);
   
   ?>
   
   </body>
   </html>
   
   
   <?php
   
   /* Add an employee to the table. */
   function AddEmployee($connection, $name, $address) {
      $n = mysqli_real_escape_string($connection, $name);
      $a = mysqli_real_escape_string($connection, $address);
   
      $query = "INSERT INTO EMPLOYEES (NAME, ADDRESS) VALUES ('$n', '$a');";
   
      if(!mysqli_query($connection, $query)) echo("<p>Error adding employee data.</p>");
   }
   
   /* Check whether the table exists and, if not, create it. */
   function VerifyEmployeesTable($connection, $dbName) {
     if(!TableExists("EMPLOYEES", $connection, $dbName))
     {
        $query = "CREATE TABLE EMPLOYEES (
            ID int(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            NAME VARCHAR(45),
            ADDRESS VARCHAR(90)
          )";
   
        if(!mysqli_query($connection, $query)) echo("<p>Error creating table.</p>");
     }
   }
   
   /* Check for the existence of a table. */
   function TableExists($tableName, $connection, $dbName) {
     $t = mysqli_real_escape_string($connection, $tableName);
     $d = mysqli_real_escape_string($connection, $dbName);
   
     $checktable = mysqli_query($connection,
         "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = '$t' AND TABLE_SCHEMA = '$d'");
   
     if(mysqli_num_rows($checktable) > 0) return true;
   
     return false;
   }
   ?>
   ```

------
#### [ PostgreSQL ]

   ```
   <?php include "../inc/dbinfo.inc"; ?>
   
   <html>
   <body>
   <h1>Sample page</h1>
   <?php
   
   /* Connect to PostgreSQL and select the database. */
   $constring = "host=" . DB_SERVER . " dbname=" . DB_DATABASE . " user=" . DB_USERNAME . " password=" . DB_PASSWORD ;
   $connection = pg_connect($constring);
   
   if (!$connection){
    echo "Failed to connect to PostgreSQL";
    exit;
   }
   
   /* Ensure that the EMPLOYEES table exists. */
   VerifyEmployeesTable($connection, DB_DATABASE);
   
   /* If input fields are populated, add a row to the EMPLOYEES table. */
   $employee_name = htmlentities($_POST['NAME']);
   $employee_address = htmlentities($_POST['ADDRESS']);
   
   if (strlen($employee_name) || strlen($employee_address)) {
     AddEmployee($connection, $employee_name, $employee_address);
   }
   
   ?>
   
   <!-- Input form -->
   <form action="<?PHP echo $_SERVER['SCRIPT_NAME'] ?>" method="POST">
     <table border="0">
       <tr>
         <td>NAME</td>
         <td>ADDRESS</td>
       </tr>
       <tr>
         <td>
       <input type="text" name="NAME" maxlength="45" size="30" />
         </td>
         <td>
       <input type="text" name="ADDRESS" maxlength="90" size="60" />
         </td>
         <td>
       <input type="submit" value="Add Data" />
         </td>
       </tr>
     </table>
   </form>
   <!-- Display table data. -->
   <table border="1" cellpadding="2" cellspacing="2">
     <tr>
       <td>ID</td>
       <td>NAME</td>
       <td>ADDRESS</td>
     </tr>
   
   <?php
   
   $result = pg_query($connection, "SELECT * FROM EMPLOYEES");
   
   while($query_data = pg_fetch_row($result)) {
     echo "<tr>";
     echo "<td>",$query_data[0], "</td>",
          "<td>",$query_data[1], "</td>",
          "<td>",$query_data[2], "</td>";
     echo "</tr>";
   }
   ?>
   </table>
   
   <!-- Clean up. -->
   <?php
   
     pg_free_result($result);
     pg_close($connection);
   ?>
   </body>
   </html>
   
   
   <?php
   
   /* Add an employee to the table. */
   function AddEmployee($connection, $name, $address) {
      $n = pg_escape_string($name);
      $a = pg_escape_string($address);
      echo "Forming Query";
      $query = "INSERT INTO EMPLOYEES (NAME, ADDRESS) VALUES ('$n', '$a');";
   
      if(!pg_query($connection, $query)) echo("<p>Error adding employee data.</p>"); 
   }
   
   /* Check whether the table exists and, if not, create it. */
   function VerifyEmployeesTable($connection, $dbName) {
     if(!TableExists("EMPLOYEES", $connection, $dbName))
     {
        $query = "CREATE TABLE EMPLOYEES (
            ID serial PRIMARY KEY,
            NAME VARCHAR(45),
            ADDRESS VARCHAR(90)
          )";
   
        if(!pg_query($connection, $query)) echo("<p>Error creating table.</p>"); 
     }
   }
   /* Check for the existence of a table. */
   function TableExists($tableName, $connection, $dbName) {
     $t = strtolower(pg_escape_string($tableName)); //table name is case sensitive
     $d = pg_escape_string($dbName); //schema is 'public' instead of 'sample' db name so not using that
   
     $query = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = '$t';";
     $checktable = pg_query($connection, $query);
   
     if (pg_num_rows($checktable) >0) return true;
     return false;
   
   }
   ?>
   ```

------

1. `SamplePage.php` 파일을 저장하고 닫습니다.

1. 웹 브라우저를 열고 `http://EC2 instance endpoint/SamplePage.php`(예: `http://ec2-12-345-67-890.us-west-2.compute.amazonaws.com/SamplePage.php`)를 검색하여 웹 서버에서 DB 클러스터에 제대로 연결되는지 확인합니다.

`SamplePage.php`를 사용하여 DB 클러스터에 데이터를 추가할 수 있습니다. 그러면 추가한 데이터가 페이지에 표시됩니다. 데이터가 테이블에 삽입되었는지 확인하려면 Amazon EC2 인스턴스에 MySQL을 설치합니다. 그런 다음 DB 클러스터에 연결하여 테이블을 쿼리합니다.

DB 클러스터 연결에 대한 자세한 내용은 [Amazon Aurora DB 클러스터에 연결](Aurora.Connecting.md) 섹션을 참조하세요.

DB 클러스터를 최대한 보호하려면 VPC 외부의 소스가 DB 클러스터에 연결할 수 없는지 확인합니다.

웹 서버 및 데이터베이스 테스트를 완료한 후에는 DB 클러스터와 Amazon EC2 인스턴스를 삭제해야 합니다.
+ DB 클러스터를 생성하려면 [Aurora DB 클러스터 및 DB 인스턴스 삭제](USER_DeleteCluster.md)의 지침을 따릅니다. 최종 스냅샷을 생성할 필요가 없습니다.
+ Amazon EC2 인스턴스를 종료하려면 *Amazon EC2 사용 설명서*에서 [인스턴스 종료](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html) 섹션의 지침을 따르세요.