これまでCloud9上でのPHPプログラムの開発には、PHPに付属しているビルトインWebサーバを利用してきました。PHPのビルトインWebサーバは手軽に利用できるので開発時にはとても便利です。一方でビルトインWebサーバはシングルスレッドプロセスにしか対応していないので、同時に複数のクライアントからのリクエストを処理することはできません。そのため、完成したPHPアプリケーションを実行する場合は、ビルトインWebサーバを使うのではなく、ApacheなどのWebサーバをインストールして動作させることが一般的です。

注意:以下の作業に従ってApacheを使ってPHPアプリケーションを公開することで、インターネットを通じての不特定多数のユーザからアプリケーションにアクセスできるようになります。具体的にはポートフォリオを公開するなどの用途に利用できますが、脆弱性のあるアプリケーションを公開してしまうリスクもあるので注意してください。

Apache

Apacheは世界中で広く使われているWebサーバです。Apacheを利用することでネットワーク上にコンテンツを公開できます。ここではApacheを使用する上で最低限必要となる以下の3つの項目について取り上げます。

  1. Apacheの起動方法
  2. ドキュメントルート
  3. Apacheの設定ファイル

2022.01月時点でCloud9にはデフォルトでApacheはインストールされています。そのため特別なインストール作業は不要です。

1. Apacheの起動方法

ApacheもMySQL(MariaDB)と同様に systemctl コマンドで起動や停止、ステータスの確認ができるようになっています。ここではApacheの起動方法、停止方法、ステータスの確認方法について学習しておきましょう。

Apacheを起動するには以下のように入力します。

$ sudo systemctl start httpd

画面に何も表示されなければ起動が完了しています。

次にApacheのステータスを確認してみましょう。

$ sudo systemctl status httpd                                                                    
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/httpd.service.d
           └─php-fpm.conf
   Active: active (running) since Wed 2022-01-26 03:38:33 UTC; 52s ago
     Docs: man:httpd.service(8)
 Main PID: 9413 (httpd)
   Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec:   0 B/sec"
    Tasks: 47
   Memory: 13.5M
   CGroup: /system.slice/httpd.service
           ├─9413 /usr/sbin/httpd -DFOREGROUND
           ├─9474 /usr/sbin/httpd -DFOREGROUND
           ├─9476 /usr/sbin/httpd -DFOREGROUND
           ├─9477 /usr/sbin/httpd -DFOREGROUND
           ├─9478 /usr/sbin/httpd -DFOREGROUND
           └─9479 /usr/sbin/httpd -DFOREGROUND

Jan 26 03:38:32 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server...
Jan 26 03:38:33 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server.

上記のように出力されればApacheは正常に起動しています。

さいごにApacheの停止方法についても確認しておきましょう。

$ sudo systemctl stop httpd

画面に何も表示されなければ停止が完了しています。

停止中のApacheのステータスも確認してみましょう。

$ sudo systemctl status httpd                                                                    
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/httpd.service.d
           └─php-fpm.conf
   Active: inactive (dead)
     Docs: man:httpd.service(8)

Jan 26 03:38:32 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server...
Jan 26 03:38:33 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server.
Jan 26 03:40:08 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Stopping The Apache HTTP Server...
Jan 26 03:40:09 ip-172-31-22-192.ap-northeast-1.compute.internal systemd[1]: Stopped The Apache HTTP Server.

Apacheが停止している場合は上記のように出力されます。

完成したアプリケーションを本番環境(プロダクション環境)で動作させる場合は、ApacheやMySQLなど「サーバ」と呼ばれるソフトウェアは一度起動したらそのまま使いづづけることが一般的です。ただし、システムのメンテナンス時にはサーバを停止したり、再起動したりすることもあります。

以降の作業を進めるために再度、Apacheを起動しておきましょう。

$ sudo systemctl start httpd

2. ドキュメントルート

続いてApacheのドキュメントルートについても確認しておきましょう。ApacheのようなWebサーバはコンピュータ上の指定のフォルダをネットワーク上に公開するようになっています。この公開フォルダのことをドキュメントルートと呼びます。ドキュメントルート上に配置したファイルやフォルダはHTTPによってアクセスできるので、ブラウザなどを通じてアクセス可能です。

Cloud9では以下のフォルダがデフォルトのドキュメントルートとなります。

/var/www/html

次のようにコマンドを入力して /var/www/html フォルダが作成されていることを確認しておきましょう。

$ ls -l /var/www/
total 0
drwxr-xr-x 2 root root 6 Dec 30 21:40 cgi-bin
drwxr-xr-x 2 root root 6 Dec 30 21:40 html

上記の出力結果から /var/www/ フォルダの中に cgi-bin フォルダと html フォルダの2つが存在していることがわかります。この html フォルダに配置したコンテンツがネットワーク上に公開されることになります。

またターミナル上で上記のフォルダ( /var/www/html )にアクセスするにはLinuxのアクセス権限が必要になります。以降の作業では sudo コマンドを使って管理者権限を付与して /var/www/html フォルダ下のファイルやフォルダにアクセスします。

3. Apacheの設定ファイル

Apacheの設定をカスタマイズするためにはApacheの設定ファイルを編集します。Apacheのインストール方法にも依存しますが、一般的には httpd.conf という名前の設定ファイルが用意されています。Cloud9(Amazon Linux)の場合は、デフォルトで以下の場所に httpd.conf ファイルが用意されています。

/etc/httpd/conf/httpd.conf

ターミナルから cat コマンドを入力すると httpd.conf ファイルの中身を閲覧できます。cat コマンドはファイルの中身を閲覧するためのコマンドです。

$ cat /etc/httpd/conf/httpd.conf 
#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
...省略...
# Supplemental configuration
#
# Load config files in the "/etc/httpd/conf.d" directory, if any.
IncludeOptional conf.d/*.conf

httpd.conf ファイルにはたくさんの記述がありますが、そのほとんどは先頭が # で始まるコメント(説明書き)です。現時点ではApacheの設定を変更する必要ありませんが httpd.conf ファイルを編集することで、Apacheのドキュメントルートを変更したり、起動時のポート番号を変更したり、様々なカスタマイズができるようになっています。

Cloud9(EC2)のネットワーク設定について

ローカル(自分のパソコン)にApacheをインストールして、デフォルトの設定(80番ポート)でApacheを起動した場合は、ブラウザから以下のURLにアクセスしてApacheにアクセスできます。

http://localhost

http通信の場合、80番ポートの指定は省略できます。

ただし、本講座ではローカルではなくCloud9(EC2)上でApacheを起動させているので、手元のブラウザからApacheにアクセスするためには、EC2のネットワーク設定をカスタマイズする必要があります。AWSではApacheやMySQLの実行ガイドを以下のページで公開しています。

https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-lamp.html

以降は上記のページにある「ステップ 3: ウェブサイトの設定」の部分を参考にして作業を進めます。

ネットワーク設定の変更

ここでは以下の作業を順に行います。

  1. アクセス権限の変更
  2. サンプルプログラム index.php の作成
  3. ネットワークの設定変更
  4. ブラウザから動作確認
  5. (参考)ネットワークの設定変更を元に戻す場合

以下、Cloud9のターミナルにコマンドをコピーアンドペーストで実行します。手入力しないように注意してください。

実際の作業に着手する前にAWSのIAMポリシーについて確認しておきましょう。

事前確認: IAMポリシーの確認

これからEC2のネットワーク設定を変更するので、AWSにログインしているユーザに対してEC2にアクセスする権限が必要です。具体的にはIAMグループ(あるいはIAMユーザ)に対して AmazonEC2FullAccess ポリシーを付与します。

AWSアカウント( ルートユーザ )でマネジメントコンソールにサインインしてIAMサービスにアクセスします。サイドバーのメニューから 個のユーザーグループ を選択します。

注意:通常の my-user アカウントではなくAWSアカウント作成時のルートユーザで作業する必要があります。AWSに my-user アカウントでサインインしている場合は、一旦サインアウトしてください。

現在、利用しているIAMアカウント( my-user )は developer グループに所属しているので developer グループを選択します。

developerグループの概要画面が表示されるので、画面中央にある「許可」タブを選択して、以下のように AmazonEC2FullAccess ポリシーを付与します。

以降はIAMユーザ( my-user )でEC2に対する操作が許可されます。

1. アクセス権限の変更

まずは Apacheのドキュメントルートである /var/www/html フォルダへのアクセス権限を変更します。以下の一連のコマンドをコピーして、Cloud9のターミナルに貼り付けて実行します。

sudo groupadd web-content
sudo usermod -G web-content -a ec2-user
sudo usermod -G web-content -a apache
sudo chown -R ec2-user:web-content /var/www/html
sudo find /var/www/html -type f -exec chmod u=rw,g=rx,o=rx {} \;
sudo find /var/www/html -type d -exec chmod u=rwx,g=rx,o=rx {} \;

各コマンドの詳細な説明については後述します。

以上でアクセス権限の変更は完了です。

2. サンプルプログラム index.php の作成

次にターミナルから /var/www/html/index.php というPHPファイルを作成します。以下の一連のコマンドをコピーして、Cloud9のターミナルに貼り付けて実行します。

sudo touch /var/www/html/index.php 
sudo chown -R ec2-user:web-content /var/www/html/index.php
sudo chmod u=rw,g=rx,o=rx /var/www/html/index.php
sudo printf '%s\n%s\n%s' '<?php' '  phpinfo();' '?>' >> /var/www/html/index.php

/var/www/html/index.php ファイルが作成されます。

以上でサンプルプログラムの作成は完了です。

3. ネットワークの設定変更

次にEC2の設定を変更して80番ポートへのアクセスを許可します。ここではVPC(Virtual Private Cloud)のネットワークACL(Access Control List)とEC2のセキュリティグループの設定を変更します。以下の一連のコマンドをコピーして、Cloud9のターミナルに貼り付けて実行します。

MY_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text) 
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80 
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --ip-permissions IpProtocol=tcp,Ipv6Ranges='[{CidrIpv6=::/0}]',FromPort=80,ToPort=80
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text) 
MY_NETWORK_ACL_ID=$(aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=$MY_SUBNET_ID --query 'NetworkAcls[].Associations[0].NetworkAclId' --output text)
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10000 --cidr-block 0.0.0.0/0 --port-range From=80,To=80
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10100 --ipv6-cidr-block ::/0 --port-range From=80,To=80


以上でネットワークの設定変更は完了です。

4. ブラウザから動作確認

さいごにブラウザからApacheにアクセスしてみましょう。ターミナルから以下のコマンドを入力してURLを取得します。

MY_PUBLIC_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php

次に表示されたURLをコピーして、ブラウザのアドレスバーに貼り付けてアクセスします。

上記のように画面が表示されればApacheへのアクセスは完了です。

5. (参考)ネットワークの設定変更を元に戻す場合

「3. ネットワークの設定変更」でEC2のネットワーク設定を変更しています。Apacheを使用しない場合は、以下のコードを実行してネットワークの設定を元に戻す(80番ポートへのアクセスを禁止)ことができます。

MY_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text)
aws ec2 revoke-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80
aws ec2 revoke-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --ip-permissions IpProtocol=tcp,Ipv6Ranges='[{CidrIpv6=::/0}]',FromPort=80,ToPort=80
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text)
MY_NETWORK_ACL_ID=$(aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=$MY_SUBNET_ID --query 'NetworkAcls[].Associations[0].NetworkAclId' --output text) 
aws ec2 delete-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --rule-number 10000
aws ec2 delete-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --rule-number 10100

セキュリティの観点からは、不要なポートへのアクセスを許可することは好ましくありません。Apacheを使う必要がないのであれば、ネットワーク設定も元に戻しておくと良いでしょう。

作成したPHPアプリケーションをWebサーバで公開する方法

これまでに作成してきたPHPプログラムをApacheのドキュメントルート( /var/www/html )に配備すればWeb上に公開できるようになります。ここではPHPプログラムを公開する方法を見てみましょう。

Cloud9上で以下のファイル( sample.php )を作成したとします。

<?php
echo "Hello PHP";

次にターミナル上で cp コマンドを入力して作成したファイル /var/www/html フォルダにコピーします。

$ cp sample.php /var/www/html

cp コマンドはファイルやフォルダをコピーするコマンドです。 -r オプションを指定してフォルダの中身を再起的にコピーすることもできます。

それから「4. ブラウザから動作確認」に取得したURLを参考にして、以下のようにアクセスします。URLの一部にファイル名が含まれている点に注意してください。

http://IPアドレス/sample.php

そうすると次のような結果を確認できるでしょう。

参考:Cloud9の自動停止設定(Cost-saving setting)の変更

Apacheを起動したとしても、Cloud9(EC2)が停止してしまうとブラウザからアクセスできなくなってしまいます。

デフォルト設定だとアクセスがなくなった30分後に停止するようになっています。

Cloud9の自動停止設定(Cost-saving setting)を変更する場合は以下のようにします。

Cloud9の画面右上の設定アイコンをクリックします。

表示されたPreferences 画面でサイドバーのメニューから EC2 Instance を選択して、Stop my environment 項目を変更します。

参考:コマンドの解説

参考までにターミナルでの操作内容について解説しておきます。

各コマンドの詳細を理解するためにはLinuxやAWSの知識が必要です。

1. アクセス権限の変更

# web-content グループを作成
sudo groupadd web-content
# ec2-user ユーザを web-content グループに追加
sudo usermod -G web-content -a ec2-user
# apache ユーザを web-content グループに追加
sudo usermod -G web-content -a apache
# /var/www/html フォルダ下の所有者を ec2-user ユーザ(web-content グループ)に変更
sudo chown -R ec2-user:web-content /var/www/html
# /var/www/html フォルダ下のファイルのパーミッションを655に変更
sudo find /var/www/html -type f -exec chmod u=rw,g=rx,o=rx {} \;
# /var/www/html フォルダ下のフォルダのパーミッションを755に変更
sudo find /var/www/html -type d -exec chmod u=rwx,g=rx,o=rx {} \;

2. サンプルプログラム index.php の作成

# /var/www/html/index.php ファイルを作成
sudo touch /var/www/html/index.php 
# /var/www/html/index.php ファイルの所有者を ec2-user ユーザ(web-content グループ)に変更
sudo chown -R ec2-user:web-content /var/www/html/index.php
# /var/www/html/index.php ファイルのパーミッションを655に変更
sudo chmod u=rw,g=rx,o=rx /var/www/html/index.php
# PHPコード(phpinfo関数呼び出し)を /var/www/html/index.php ファイルに出力
sudo printf '%s\n%s\n%s' '<?php' '  phpinfo();' '?>' >> /var/www/html/index.php

3. ネットワークの設定変更

# EC2インスタンスIDを取得
MY_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
# セキュリティグループIDを取得
MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text) 
# 対象セキュリティグループの tcp 80番ポート CIDR 0.0.0.0 を許可(IPv4)
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80 
# 対象セキュリティグループの tcp 80番ポート CIDR ::/0 を許可(IPv6)
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --ip-permissions IpProtocol=tcp,Ipv6Ranges='[{CidrIpv6=::/0}]',FromPort=80,ToPort=80
# 対象インスタンスのサブネットIDを取得
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text) 
# サブネットに関連付けられたネットワークACLのIDを取得
MY_NETWORK_ACL_ID=$(aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=$MY_SUBNET_ID --query 'NetworkAcls[].Associations[0].NetworkAclId' --output text)
# ネットワークACLに80番ポート上のすべてのトラフィックを許可(IPv4)
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10000 --cidr-block 0.0.0.0/0 --port-range From=80,To=80
# ネットワークACLに80番ポート上のすべてのトラフィックを許可(IPv6)
aws ec2 create-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --protocol tcp --rule-action allow --rule-number 10100 --ipv6-cidr-block ::/0 --port-range From=80,To=80

4. ブラウザから動作確認

# パブリックIPアドレスを取得して出力
MY_PUBLIC_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php

5. (参考)ネットワークの設定変更を元に戻す場合

# EC2インスタンスIDを取得
MY_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
# セキュリティグループIDを取得
MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text)
# 対象セキュリティグループの tcp 80番ポート CIDR 0.0.0.0 の許可(IPv4)を削除
aws ec2 revoke-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80
# 対象セキュリティグループの tcp 80番ポート CIDR ::/0 の許可(IPv6)を削除
aws ec2 revoke-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --ip-permissions IpProtocol=tcp,Ipv6Ranges='[{CidrIpv6=::/0}]',FromPort=80,ToPort=80
# 対象インスタンスのサブネットIDを取得
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text)
# サブネットに関連付けられたネットワークACLのIDを取得
MY_NETWORK_ACL_ID=$(aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=$MY_SUBNET_ID --query 'NetworkAcls[].Associations[0].NetworkAclId' --output text) 
# ネットワークACLに80番ポート上のすべてのトラフィックの許可(IPv4)を削除
aws ec2 delete-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --rule-number 10000
# ネットワークACLに80番ポート上のすべてのトラフィックの許可(IPv6)を削除
aws ec2 delete-network-acl-entry --network-acl-id $MY_NETWORK_ACL_ID --ingress --rule-number 10100