Amazon AWS Cloud9 開発環境を速攻で作る方法(令和最新版)

## 2023.07.24 追記
PHP8.2版も作成しましたので良ければご確認ください。

https://qiita.com/qwe001/items/6855beee4d2cc9fc3e32

## 2024.01.31 追記
Amazon Linux2023版も作成しましたので良ければご確認ください

https://qiita.com/qwe001/items/825debff92c52def6481

## はじめに

[以前、記事を書いた時は](https://qiita.com/qwe001/items/5922027fe50aacae10ad
)、公式ドキュメントに従うことで開発環境が速攻で作れました


ですが、デフォルトのプラットフォームが
Amazon LinuxからAmazon Linux 2 に変更になったからなのか、
公式ドキュメント通りにしても開発環境が作れなくなっていました。

今回、開発環境をまた用意する必要があったので、改めて書くことにしました。

2021年1月時点でのやり方なので、
将来的には同じように実行しても、
開発環境が作れなくなっている可能性はあります。

予めご了承ください。

## 要件

* Apache 2.4以上
* PHP 7.4以上
* MySQL(MariaDB) 10.2.10-MariaDB

PHPはデフォルトで7.2がインストールされていました。

ですが、今回用意しなければいけない環境はPHP7.4です。

yumにある最新のPHPは7.2までなので、
7.4を入れるためには、epelでリポジトリを追加する必要があります。

また、MySQLもデフォルトでmariadbがインストールされてました。

これにより、今まで `sudo service mysqld restart` とか書いてた部分は
`sudo service mariadb restart` という風に読み換える必要があります。

## インスタンスを作成

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

1. 上記のサイトにアクセス
2. 画面右上のリージョン表記が「東京」であることを確認。違う場合は「東京」に変更
 * テストでしか使わない、応答時間が多少遅くても気にしないなら、「us-east-1(バージニア北部)」をオススメします。EC2インスタグラム
3. 「Create environment」ボタンをクリック
4. Step 1 Name environment
 * 開発環境の名前と説明。
 * 各項目の入力内容を次のとおり設定する
 * 「Name」に開発予定のプロジェクト名を入力する ※必須
     * なんでもいいが、英数字のみで入力するのが無難
 * 「Description」にプロジェクトの説明を入力する ※任意
     * 日本語を入力しても良し
5. Step 2 Configure settings
 * 開発環境の基本的な設定。
 * 各項目の入力内容を次のとおり設定する
 * Environment type - Create a new instance for environment (EC2)
 * Instance type - t2.micro (1 GiB RAM + 1 vCPU)
 * Platform - Amazon Linux 2 (recommended)
 * Cost-saving setting - After 30 minutes (default)
 * IAM role - AWSServiceRoleForAWESCloud9 ※変更できない
 * Network settings (advanced) - 何も変更しない
 * Add new tag - 何も設定しない
 * ここまで出来たら「Next step」ボタンをクリック
6. Step 3 Review
 * 設定内容の確認。
 * 「Create environment」ボタンをクリック
7. 開発環境の構築が完了するまで待つ
 * ネットワーク速度にもよるが、だいたい5分以内に完了する
 * ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)が操作できるようになったらOK

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

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

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

1. 画面左上 ツールバー Cloud9アイコン -> Preferences (Ctrl-,) をクリック
2. サイドバーから User Settings -> Terminal をクリック
 * Font Size を 14 にする
 * Antialiased Fonts を チェック
 * Blinking Cursor を チェック
3. サイドバーから User Settings -> Code Editor (Ace) をクリック
 * Font Size を 14 にする
 * Antialiased Fonts を チェック

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

1. 画面左上 ツールバー View -> Themes -> UI Themes -> Jett Dark をクリック
2. 画面左上 ツールバー View -> Themes -> Monokai をクリック
 * お好みで。私は Sublime Text 3 をよく使ってたので

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

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

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

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

ターミナル画面( `bash - "ip-***-**-**-***"` みたいなのが書いてあるタブ)で、
以下のコマンドをコピペしてEnterキーを押して実行します
```bash
sudo yum -y update
```

2021.01.22 時点では、特に何も更新されませんでした

### Apache が入っているかを確認

```bash
httpd -V
```

Apache/2.4.46 が最初からインストールされてました。
このままでいいので作業なし。

### PHP が入っているかを確認

```bash
php -v
```

```bash
PHP 7.2.24 (cli) (built: Oct 31 2019 18:27:08) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
```

入ってますが、7.2なので、要件より古いですね。
そこで、PHP7.4にアップデートします

#### PHP 7.4 のインストール

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

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

```bash
# Amazon Linux でepel-releaseパッケージをインストール出来るようにする
sudo amazon-linux-extras install epel
# epel-releaseパッケージをインストール
sudo yum install epel-release
# remiリポジトリを使えるようにする
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
# PHP7.4をインストールする
sudo yum install -y --enablerepo=epel,remi,remi-php74 install php74 php74-php
# PHP7.4でよく使うライブラリをインストールする
sudo yum install -y --enablerepo=remi-php74 php74-php-fpm php74-php-mbstring php74-php-mysqlnd php74-php-xml php74-php-xdebug php74-php-intl
# 現時点ではphpコマンドはPHP7.2を使用しているので、リンクをつけなおす
sudo unlink /usr/bin/php
# これでPHP7.4が使用される
sudo ln -s /usr/bin/php74 /usr/bin/php
```

※2021.07.15 修正 php74-php-zipがなんか壊れてたので必要最低限のライブラリに絞りました

改めて、PHPのバージョンを確認

```bash
php -v
```

```bash
PHP 7.4.21 (cli) (built: Jun 29 2021 15:17:15) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v3.0.4, Copyright (c) 2002-2021, by Derick Rethans
```

これで要件を満たしました

### MySQL(MariaDB) が入っているかを確認

```bash
mysql -V
```

```bash
mysql  Ver 15.1 Distrib 10.2.10-MariaDB, for Linux (x86_64) using  EditLine wrapper
```

入ってますが、これだけではダメで、mariadb-serverもインストールする必要があります
`sudo service mariadb start` とかを使えるようにするためです

### MariaDB-Server をインストールする

```bash
sudo yum install -y mariadb-server
```

また注意があって、Amazon Linux2ではmysqldではなく、mariadbが使用されます。

そのため、公式ドキュメントに書いてある
`sudo service mysqld start && sudo service mysqld status` みたいなコマンドをそのまま打っても、
`Unit mysqld.service could not be found.` というエラーが出て実行されません。

`sudo service mariadb start && sudo service mariadb status` と言う風に、
`mysqld` は `mariadb` に読み換える必要があります

### ウェブサイトの設定
現時点では、 `sudo service httpd start` としたところでウェブページが表示されません
それを表示されるようにします。書き込み権限割り当てとかそんな感じのことしてます。
一種のおまじないです。

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

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

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

MY_INSTANCE_ID=$(curl 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 http://169.254.169.254/latest/meta-data/public-ipv4) && echo http://$MY_PUBLIC_IP/index.php # Get the URL to the index.php file within the web server root.
```
### シンボリックリンクを設定
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/opt/remi/php74/php.ini /etc/opt/remi/php74/php.ini.org
# php.ini を編集する
sudo vi /etc/opt/remi/php74/php.ini
```
```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
```

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

### 日本語化対応する

これは好みですが、システムメッセージを日本語で表示したいなら以下のコマンドで日本語化できます

```bash
sudo localectl set-locale LANG=ja_JP.utf8
```

ここまでできたら再起動します(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; curl -q 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リンクをクリックしても別タブで開いてくれますが、たまに開かない時があります。謎。

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
sudo service mariadb start; sudo service httpd start; curl -q http://169.254.169.254/latest/meta-data/public-ipv4 -w "\n"
```

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

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

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

## 参考サイト

* https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-lamp.html
* https://qiita.com/nagahama/items/2fdc820791bee5d564ca

## 追記
### Node.jsのバージョンアップを行う

※この作業はLambdaを使わない場合は無視して構いません

私のCloud9環境は、デフォルトで入ってるnode.jsのバージョンが10系でした。

```bash
# node.js v10.24.0 が入ってました
node -v
```

本番環境のLambdaは12系のランタイムを使ってるので、
12系の、最新バージョンにアップデートします

```bash
# node.js v12系の最新バージョン(と直近2つのバージョン)を確認する
nvm ls-remote | grep v12 | tail -n 3
# 現時点では、v12.22.0が最新みたいなので、それをインストールする
nvm install v12.22.0
# nodeコマンドのデフォルトランタイムをv12.22.0にする
nvm alias default v12.22.0
# npmの更新
npm update -g npm
npm i -g npm
# インストールしたランタイムが使用されていることを確認
nvm ls
# nodeコマンドもインストールしたバージョンが使用されていることを確認
node -v
```

## 2022.05.28 追記
### MySQL(MariaDB)が起動しなくなった場合

なぜかは不明ですが、一部の開発環境でMySQL(MariaDB)が起動しなくなりました。

```bash
Owner:~/environment $ systemctl status mariadb.service
● mariadb.service - MariaDB 10.2 database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/mariadb.service.d
           └─tokudb.conf
   Active: failed (Result: exit-code) since Sun 2022-05-08 15:10:41 JST; 13s ago
  Process: 8382 ExecStart=/usr/libexec/mysqld --basedir=/usr $MYSQLD_OPTS $_WSREP_NEW_CLUSTER (code=exited, status=1/FAILURE)
  Process: 8343 ExecStartPre=/usr/libexec/mysql-prepare-db-dir %n (code=exited, status=0/SUCCESS)
  Process: 8287 ExecStartPre=/usr/libexec/mysql-check-socket (code=exited, status=0/SUCCESS)
 Main PID: 8382 (code=exited, status=1/FAILURE)
   Status: "MariaDB server is down"

May 08 15:10:36 ip-172-31-17-106.ec2.internal mysql-prepare-db-dir[8343]: ERROR: ld.so: object '/usr/lib64/libjemalloc.so.1' from LD_PRELOAD cannot be preloaded (cannot open shared object ...: ignored.
May 08 15:10:36 ip-172-31-17-106.ec2.internal mysql-prepare-db-dir[8343]: If this is not the case, make sure the /var/lib/mysql is empty before running mysql-prepare-db-dir.
May 08 15:10:36 ip-172-31-17-106.ec2.internal mysqld[8382]: ERROR: ld.so: object '/usr/lib64/libjemalloc.so.1' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
May 08 15:10:36 ip-172-31-17-106.ec2.internal mysqld[8382]: 2022-05-08 15:10:36 139848248385344 [Note] /usr/libexec/mysqld (mysqld 10.2.38-MariaDB) starting as process 8382 ...
May 08 15:10:36 ip-172-31-17-106.ec2.internal mysqld[8382]: 2022-05-08 15:10:36 139848248385344 [Warning] Could not increase number of max_open_files to more than 1024 (request: 32183)
May 08 15:10:36 ip-172-31-17-106.ec2.internal mysqld[8382]: 2022-05-08 15:10:36 139848248385344 [Warning] Changed limits: max_open_files: 1024  max_connections: 151 (was 151)  table_cach...1 (was 2000)
May 08 15:10:41 ip-172-31-17-106.ec2.internal systemd[1]: mariadb.service: main process exited, code=exited, status=1/FAILURE
May 08 15:10:41 ip-172-31-17-106.ec2.internal systemd[1]: Failed to start MariaDB 10.2 database server.
May 08 15:10:41 ip-172-31-17-106.ec2.internal systemd[1]: Unit mariadb.service entered failed state.
May 08 15:10:41 ip-172-31-17-106.ec2.internal systemd[1]: mariadb.service failed.
Hint: Some lines were ellipsized, use -l to show in full.
```

MariaDBを再インストールして、DBを再構築することで解決しました。
本番環境で同じことが起きたらどうしたらいいんでしょうね。まあ、本番環境はRDS使うだろうから考えなくて良いのかな。知らんけど

```bash
sudo yum erase mariadb-server
sudo rm -rf /var/lib/mysql
sudo yum install mariadb-server
```

## 2024.03.04 追記

Cloud9でWordPressを構築してる人向けの便利スクリプトを共有します。
WordPressはデータベース内のwp_optionsテーブルにあるsiteurlとhomeの値を自サイトのドメインに変えないと動きませんが、Cloud9はIPアドレスが変動するので、インスタンスを再起動するたびにSQL文を発行する必要があり、手間です。

ですので、DB起動、Apache起動、SQL文の設定を自動で行うシェルスクリプトを作成しました。

以下のシェルスクリプトを`/home/ec2-user/environment/` などに置いて、
実行権限を与えてから実行することで、上記の作業を自動化できます。

実行にはwp-cliが必要ですので、入れてなければコメントアウトしている行を参考にインストールしてから実行してください。

```bash:./updateWpHomeAndSiteUrl.sh
#!/bin/bash

# [NOTE] you MUST install wp-cli before run this.
#cd ~/environment/
#curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
#chmod +x wp-cli.phar
#sudo mv wp-cli.phar /usr/local/bin/wp

# you MUST set wordpress project path to here.
MY_WORDPRESS_DIR=/var/www/html/
MY_PUBLIC_IP=$(curl --silent http://169.254.169.254/latest/meta-data/public-ipv4)

sudo service mariadb start
sudo service httpd start
cd $MY_WORDPRESS_DIR
wp option update siteurl "http://$MY_PUBLIC_IP/blog/"
wp option update home "http://$MY_PUBLIC_IP/"
echo $MY_PUBLIC_IP
```

```bash
cd ~/environment/
# 実行権限を付与
chmod 755 updateWpHomeAndSiteUrl.sh
# シェルスクリプトを実行
./updateWpHomeAndSiteUrl.sh
```