Amazon AWS Cloud9 で Amazon Linux 2023のLAMP環境を構築する方法(令和最新 2024年進化版)

## はじめに
AWS Cloud9のデフォルトOSがAmazon Linux 2023になりました。
OSが変わったこともあって、改めて公式ドキュメントに従って構築してみたところ、
つまずいたので、備忘録として構築手順を残します。
公式ドキュメントに従ってもうまくいかないのは、AWSあるあるですよね(白目)
## Amazon Linux 2023 の変更点
インスタンスメタデータサービス(IMDS)のバージョンが「IMDSv2」になりました。
これによる作業への影響は、これまで普通にcurlでインスタンスIDやIPアドレスが取得できていたものが、これからはアクセストークンが必要になります。
### Before: これまで(Amazon Linux 2)のメタデータ確認方法
認証されていないという401エラーになる
```
MY_INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) && echo $MY_INSTANCE_ID
MY_PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php

↓

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>401 - Unauthorized</title>
 </head>
 <body>
  <h1>401 - Unauthorized</h1>
 </body>
</html>
```
### After: これから(Amazon Linux 2023)のメタデータ確認方法
予めアクセストークン(6時間有効)を用意して、curlのヘッダーにセットする必要がある
```
TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
MY_INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) && echo $MY_INSTANCE_ID
MY_PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php

↓

i-0eXXX...
http://X.XX.X.XXX/index.php
```

### MySQLサービスの呼び出し方
公式ドキュメントは相変わらず `sudo service mysqld restart` と書いてありますが、
デフォルトでインストールされているのは `mariadb` ですので、
`sudo service mariadb restart` という風に読み換える必要があります。

上記のことを踏まえて、開発環境の構築手順を以下に記します
## 要件
* Amazon Linux 2023
* Apache 2.4以上
* PHP 8.2以上
* MySQL(MariaDB) 10.x
## インスタンスを作成
https://ap-northeast-1.console.aws.amazon.com/cloud9/home?region=ap-northeast-1
* 上記のサイトにアクセス
* 画面右上のリージョン表記が「![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/dac18d2d-6c57-e427-3464-fc5e78b157ab.png)」であることを確認。
* 違う場合は「![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/dac18d2d-6c57-e427-3464-fc5e78b157ab.png)」に変更
    * テストでしか使わない、応答時間が多少遅くても気にしないなら、
    「![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/2f230335-99b9-a775-eccc-8b893f21e2bc.png)」をオススメします。
    * インスタンスの従量課金コストが東京よりも若干安いため
* 「![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/5f4bd6a0-283d-3451-2d05-651faf4bd280.png)」ボタンをクリック
### Step.1 基本設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/063e7610-7b76-722e-50fc-79321224c482.png)

    * 開発環境の名前と説明。
    * 各項目の入力内容を次のとおり設定する
    * 「名前」に開発予定のプロジェクト名を入力する ※必須
        * なんでもいいが、英数字のみで入力するのが無難
    * 「説明」にプロジェクトの説明を入力する ※任意
        * 日本語を入力しても良し
### Step.2 EC2インスタンスの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/b5d307e9-2a1d-b42c-5c1a-8a7ff42c43ed.png)

    * 開発環境の基本的な設定。
    * 各項目の入力内容を次のとおり設定する
        * インスタンスタイプ: t2.micro
            * 従量課金コストで言えば、t3.micro のほうが安いし性能も良いが
              作れる数に限りがあるため、t2.microにする
        * プラットフォーム: Amazon Linux 2023
        * タイムアウト: 30分 (デフォルト)

### Step3. ネットワーク設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/2d2ed07b-233e-ded4-6895-5a949fc8d0ea.png)

    * このあたりは何もいじらずにそのままにしておく

ここまで出来たら「![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/54863b06-f411-541b-1acc-8d4cf8e8f4e0.png)」ボタンをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/5f31d037-5d65-cb33-56f6-8b2091f1ef29.png)

※設定内容の確認画面などはなく、作成ボタンを押した瞬間にインスタンスが作成されるので注意

ネットワーク速度にもよるが、だいたい5分以内に完了する

作成が完了したら、対象インスタンスの「開く」リンクをクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/a578561b-dc70-a51f-2dbd-a6aef42518fd.png)

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)が操作できるようになったらOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/44ee900b-bdfc-60a3-a3e5-1b0b14ffe9bc.png)

## 【初回のみ】環境構築

### 外観のカスタマイズ

デフォルトだと文字サイズが12pxで、老眼にはつらいので、もうちょっち上げます

1. 画面左上 ツールバー Cloud9アイコン -> Preferences (Ctrl-,) をクリック
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/9cabc80d-ca9d-c02b-3825-07eb217a7be7.png)
2. サイドバーから User Settings -> Terminal をクリック
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/5fed4c95-3adc-cc08-4428-b51bcd52e2dd.png)
    *  Font Size を 14 にする
    * Antialiased Fonts を チェック
    * Blinking Cursor を チェック
3. サイドバーから User Settings -> Code Editor (Ace) をクリック
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/1e16deb4-89d9-287b-3070-0306f4438463.png)
    * Font Size を 14 にする
    * Antialiased Fonts を チェック

あとデフォルト背景が白のため、ドライアイにもきついので、黒系の外観にします

1. 画面左上 ツールバー View -> Themes -> UI Themes -> Jett Dark をクリック
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/42894780-3da0-de23-91fe-c23b13b8900a.png)
2. 画面左上 ツールバー View -> Themes -> Monokai をクリック
    * お好みで。私は Sublime Text 3 をよく使ってたので

【重要】隠しファイルを表示する設定にします。「 `.htaccess` 」などを表示するためです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/e2e28b5c-7334-816f-f72f-8fbf85fb0388.png)

1. 画面左上 プロジェクト名の右の歯車アイコンをクリック
2. Show Hidden Files をクリック

あとはPreferencesの設定項目をじっくり眺めてお好みでカスタマイズしてください

## ライブラリのアップデート
公式ドキュメントと同じく、とりあえずyumを更新します

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドをコピペしてEnterキーを押して実行します
```
sudo yum -y update
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/94487dbe-7ed2-d979-d48d-98cb36eabffd.png)
2024.01.31 時点では、特にアップデートはありませんでした。
## LAMP環境のバージョン確認
ターミナル画面で、以下のコマンドを入力して各種ライブラリのインストール状況とバージョンを確認します
### (L) Linux
```
cat /etc/system-release

Amazon Linux release 2023 (Amazon Linux)
```
当然ながら AL2023 がインストールされています
### (A) Apache
```
httpd -v

Server version: Apache/2.4.58 (Amazon Linux)
Server built:   Oct 24 2023 00:00:00
```
最初から Apache2.4 がインストールされていました
### (M) MySQL - MariaDB
```
mysql --version

mysql  Ver 15.1 Distrib 10.5.20-MariaDB, for Linux (x86_64) using  EditLine wrapper
```
最初から MariaDB 10.5.20 がインストールされていました
それと、 `mariadb-server` もプリインストールされているみたいなので、最初から `sudo service mariadb start` とか使えました。
### (P) PHP
```
php -v

PHP 8.2.9 (cli) (built: Aug  3 2023 11:39:08) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.9, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.9, Copyright (c), by Zend Technologies
    with Xdebug v3.2.2, Copyright (c) 2002-2023, by Derick Rethans
```
最初から PHP8.2 が入ってました。手間がかからなくて助かりますね
## ウェブサイトの設定 その1
現時点では、 `sudo service httpd start` としたところでウェブページが表示されません
それを表示されるようにします。書き込み権限割り当てとかそんな感じのことしてます。
一種のおまじないです。

このあたりはAWS公式ドキュメントと同じ

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

単に全行コピペしてCtrl+Vで貼り付ければOK
(貼り付けた瞬間に実行されるので注意)

```
sudo groupadd web-content # Create a group named web-content.
sudo usermod -G web-content -a ec2-user # Add the user ec2-user (your default user for this environment) to the group web-content.
sudo usermod -G web-content -a apache # Add the user apache (Apache HTTP Server) to the group web-content.
sudo chown -R ec2-user:web-content /var/www/html # Change the owner of /var/www/html and its files to user ec2-user and group web-content.
sudo find /var/www/html -type f -exec chmod u=rw,g=rx,o=rx {} \; # Change all file permissions within /var/www/html to user read/write, group read-only, and others read/execute. 
sudo find /var/www/html -type d -exec chmod u=rwx,g=rx,o=rx {} \; # Change /var/www/html directory permissions to user read/write/execute, group read/execute, and others read/execute.

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
```

## ウェブサイトの設定 その2
ここがはじめに書いた通り、メタデータのアクセスにアクセストークンが必要になったので、curlの記述を一部書き換えています。

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

単に全行コピペしてCtrl+Vで貼り付ければOK
(貼り付けた瞬間に実行されるので注意)

```
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

MY_INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id) # Get the ID of the instance for the environment, and store it temporarily.

MY_SECURITY_GROUP_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SecurityGroups[0].GroupId' --output text) # Get the ID of the security group associated with the instance, and store it temporarily.
aws ec2 authorize-security-group-ingress --group-id $MY_SECURITY_GROUP_ID --protocol tcp --cidr 0.0.0.0/0 --port 80 # Add an inbound rule to the security group to allow all incoming IPv4-based traffic over 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 # Add an inbound rule to the security group to allow all incoming IPv6-based traffic over port 80.
MY_SUBNET_ID=$(aws ec2 describe-instances --instance-id $MY_INSTANCE_ID --query 'Reservations[].Instances[0].SubnetId' --output text) # Get the ID of the subnet associated with the instance, and store it temporarily.
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) # Get the ID of the network ACL associated with the subnet, and store it temporarily.
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 # Add an inbound rule to the network ACL to allow all IPv4-based traffic over port 80. Advanced users: change this suggested rule number as desired.
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 # Add an inbound rule to the network ACL to allow all IPv6-based traffic over port 80. Advanced users: change this suggested rule number as desired.

MY_PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php
```
## シンボリックリンクを設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/5dbe5f78-d2d4-1355-10b6-9ca8dd009fc1.png)

Apacheのドキュメントルートは `/var/www/html/index.php` になっています。
ただ、Cloud9のデフォルトディレクトリは `/home/ec2-user/environment/` になっています。
ドキュメントルートをお気に入りのパスに設定してもいいんですが、
私は横着なのでCloud9のデフォルトディレクトリの下にドキュメントルートのシンボリックリンクを貼って、すぐに開発作業が出来るようにしました。

Apacheの設定ファイルの記述的に、
`/home/{{ サイト名 }}/public_html/` みたいな構成にしたほうがよさそうですが、
動けばなんでもええねん。
```bash
sudo ln -s /var/www/html /home/ec2-user/environment/
```
## タイムゾーンを日本時間に変更
Cloud9のデフォルトのタイムゾーン設定はUTC(国際標準時)になっています。
JST(日本時間)のほうが何かと都合が良いので、タイムゾーンを変更します

ローカル時間を日本時間に変更します
```bash
sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
```
また、PHPのデフォルトタイムゾーンが設定されていないので、
現時点では `date()` 関数を使ってもUTC時間が表示されます。
同様に日本時間を設定しましょう
```bash
# php.ini の場所を探す
sudo php -i | grep php.ini
# デフォルトのファイルを残しておく
sudo cp /etc/php.ini /etc/php.ini.org
# php.ini を編集する
sudo vi /etc/php.ini
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/33b80d24-8053-bb51-23ca-73270cd1f080.png)
```php
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =

↓

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =
date.timezone = "Asia/Tokyo"
```
### Apacheのリダイレクト設定を有効にする
Apacheのリダイレクト設定を有効にします。
これをしないと、PHPフレームワークが動かないためです
```bash
sudo cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org
sudo vi /etc/httpd/conf/httpd.conf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/e5822ad3-2220-f8de-b62c-baa21669a993.png)
`/\/var\/www\/html` を入力して、ドキュメントルートへの記述を探し、
`AllowOverride None` を `AllowOverride All` に変更する
```apache
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    #AllowOverride None
    AllowOverride All
```
ここまでできたら再起動します(IPアドレスが変わるので注意)
```bash
sudo reboot
```
再起動後、動作確認
```bash
cat /etc/localtime
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/18a996df-53b9-1453-8e3a-6b02210e317a.png)
```bash
date
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/08af1964-a3e6-17dd-1fe2-1d5db9443601.png)
```bash
php -r 'print(date("Y-m-d H:i:s") . "\n");'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/fb062f2d-a7b7-d9b8-3744-0a330963662c.png)

### ブラウザで表示確認
ターミナル画面(`bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドを上から順に実行する。

```bash
sudo service mariadb start; sudo service httpd start;
TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4 -w "\n"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/8090a982-d39c-9ac2-836f-09c4333e6992.png)

最後のコマンドを実行すると、IPアドレスが表示されます。
それが開発環境のURLです。
そのIPアドレスをコピペして、ブラウザのURL欄に貼り付けてアクセスします。
※ IPアドレスをクリックして、Openリンクをクリックしても別タブで開いてくれますが、たまに開かない時があります。謎。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/eff2afa4-cdde-44a7-4cd5-0d5e54d2dbdb.png)

phpinfoの内容が出てくればOKです。

以上で環境構築は完了です。お疲れ様でした。

## 【次回以降】開発環境を立ち上げるには
### インスタンスの起動

https://ap-northeast-1.console.aws.amazon.com/cloud9/home?region=ap-northeast-1

1. 上記のサイトにアクセス(リージョン確認などの手順は割愛)
2. 任意のプロジェクトをクリック
3. 「Open IDE」ボタンをクリック

### WEBサーバーを立ち上げる

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドを実行する。

```bash
TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`; sudo service mariadb start; sudo service httpd start; curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4 -w "\n"
```

表示されるIPアドレスをコピペして、ブラウザのURL欄に貼り付けてアクセスします。

## 開発環境を終了するには

単にタブを閉じれば良いです。
無操作状態が30分継続すれば、自動でインスタンスを停止してくれます。

## 参考サイト

* https://docs.aws.amazon.com/cloud9/latest/user-guide/sample-lamp.html
* https://oji-cloud.net/2021/12/07/post-6784/