LAMP環境をAWS Lightsailで新規に構築する

今回はAWSで気軽にVPSサーバを立てることができるLightsailで、LAMP環境を構築してみます。1台のサーバにWebとDBが同居する形の非常にシンプルな物です。

もともとAnsibleで自動化しようと思ってタスクを洗い出していたのですが、せっかくなのでブログにも残しておきます。

前準備

インスタンスを作成

AWS Lightsailにログインしたら、画面右にある「インスタンスの作成」ボタンをクリック

今回は以下のような構成で作成しています。

  • 東京リージョン
  • OSのみ - AmazonLinux
  • $20/月

ブラウザからシェルにログイン

ひとまずLightsailのダッシュボードから、Webブラウザ上で動作するシェルにログインできますので、Terminalのアイコンっぽいボタンをクリックします。

デフォルトのユーザーであるec2-userでログインします。ec2-userはデフォルトでsudoが利用できます。

ローカルのTerminalから接続

Webブラウザ上で動作するシェルは非常に利用しづらいので、一刻も早くローカルのTerminalに移りたいw というわけでその設定を行います。

~/.ssh/authorized_keysを開き、公開鍵をコピペし保存します。

$ vi ~/.ssh/authorized_keys

ローカル端末で公開鍵/秘密鍵を作成されていない場合は、適宜作成を(macOSやLinuxの場合)。2行目のpbcopyはクリップボードにファイルの内容を入れています。

local: $ ssh-keygen -t rsa -b 4096 -C "katsube@local.macbookair"
local: $ pbcopy < ~/.ssh/id_rsa.pub

あとはローカルのTerminalからIPアドレスやユーザー名を指定してログインします。これで快適に作業できるようになりました。

local: $ ssh ec2-user@111.111.111.111 -p 22 -i ~/.ssh/id_rsa

ここまで正常にできればWebブラウザ上で動作するシェルのウィンドウは閉じて大丈夫です。

OSの初期設定

アップデート

何はなくともOS含め全ソフトウェアのアップデートを実施します。

$ sudo yum update

次に自動的にアップデートがされるようyum-cronの設定を行います。

$ sudo yum install yum-cron
$ sudo vi /etc/yum/yum-cron.conf
update_cmd = security
apply_updates = yes

$ sudo service yum-cron start
$ sudo chkconfig yum-cron on

yum-cronの詳細は過去の記事をご覧ください。

タイムゾーン

デフォルトだとUTCになっているため、日本時間に変更します。

$ sudo cp -p /usr/share/zoneinfo/Japan /etc/localtime
$ sudo vi /etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=false

最後にdateコマンドで日本時間になったか確認してください。詳細は過去の記事をご覧ください。

デフォルトだとntpが動作していますが、お好みでchronyに変更しても良いかと思います。 blog.katsubemakito.net

言語

言語設定を日本語にします。en_US.UTF-8となっている箇所をja_JP.UTF-8に変更して保存します。

$ sudo vi /etc/sysconfig/i18n
ja_JP.UTF-8

SSH

SSHの設定をちょっとだけ厳格にします。22番を狙い撃ちするピンポンダッシュを防止するためにポート番号を22番以外に変更。パスワードによる認証やrootアカウントでのログインは禁止。最後にデーモンを再起動します。

$ sudo vi /etc/ssh/sshd_config
Port 12345
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

$ sudo service sshd restart

この時点でTerminalを閉じたり、サーバとのコネクションを絶対に切断しないでください。もし設定を間違えていた場合、大変面倒なことになります。新しいTerminalのウィンドウやタブを開きそちらで正常に接続できて初めて切断してください。

今回はポート番号を変更したので、Lightsailの「ネットワーク」からファイアウォールの設定に追加をしておきます。22番は削除してしまって問題ありません。

新しいTerminalのウィンドウを開き、正常にログインできれば成功です。

local: $ ssh ec2-user@111.111.111.111 -p 12345 -i ~/.ssh/id_rsa

スワップ領域の作成

この項目は必要なければ飛ばしてOKです。LightsailやEC2では初期状態でスワップ領域が存在しません。コンパイルなどで一時的に大量にメモリを利用する場合や、スケールアップをする気がなく1台をギリギリまで使う場合は作っておいたほうが良いでしょう。

$ sudo mkdir /var/swap/
$ sudo dd if=/dev/zero of=/var/swap/swap0 bs=1M count=1024
$ od -h /var/swap/swap0
$ sudo chmod 600 /var/swap/swap0
$ sudo mkswap /var/swap/swap0
$ sudo swapon /var/swap/swap0

再起動後も利用できるようにfstabへ追記しておきます。

$ sudo vi /etc/fstab
/var/swap/swap0 swap swap defaults 0 0

詳細は過去の記事をご覧ください。

PHP

インストール

今回はPHP7.2を入れます。モジュールは必要に応じて追加/削除してください。

$ sudo yum install php72 php72-cli php72-common php72-devel php72-mbstring php72-mysqlnd php72-pdo php72-opcache
$ php -v
PHP 7.2.17 (cli) (built: Apr 15 2019 23:21:08) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.17, Copyright (c) 1999-2018, by Zend Technologies

php.iniの設定

/etc/php.iniを編集しお好みの設定にします。タイムゾーンと日本語周り、メモリやファイルサイズ、PHPバージョンの出力を行わない指定を行っています。

$ sudo vi /etc/php.ini
date.timezone = Asia/Tokyo
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = auto
mbstring.http_output = UTF-8
mbstring.encoding_translation = On
mbstring.detect_order = auto
mbstring.substitute_character = none
mbstring.func_overload = 0
memory_limit = 32M
upload_max_filesize = 2M
expose_php = Off

Apache

インストール

特別な理由がなければApache 2.4系をインストールします。

$ sudo yum install httpd24 httpd24-devel httpd24-manual httpd24-tools
$ apachectl -version
Server version: Apache/2.4.39 (Amazon)
Server built:   Apr  5 2019 18:55:17

設定

パフォーマンス

環境に合わせてチューニングをしていきます。詳細は割愛しますが、以下はメモリ1G程度の環境で設定している値です。ファイルの末尾などに記載します。

$ sudo vi /etc/httpd/conf/httpd.conf
MaxClients 32
ServerLimit 64
MinSpareServers 5
MaxSpareServers 5
MaxRequestsPerChild 4000
MaxMemFree 1024

セキュリティ

新たに/etc/httpd/conf.d/header.confファイルを作成し、セキュリティ関連の設定をしておきます。

$ sudo vi /etc/httpd/conf.d/header.conf
# サーバー/OS情報を出力しない
ServerTokens Prod
ServerSignature Off
Header unset X-Powered-By

# 他サイトからFRAME/IFRAMEで呼び出し禁止
Header always append X-Frame-Options SAMEORIGIN

# XSS
Header always set X-XSS-Protection "1; mode=block"

# ファイル内容からMIME判定をさせない
Header always set X-Content-Type-Options nosniff

# Traceメソッド禁止
TraceEnable Off

起動

$ sudo service httpd start

自動起動

$ sudo chkconfig httpd on

PHPの動作確認

phpinfo()を実行するだけのスクリプトをドキュメントルート配下に用意します。

$ sudo vi /var/www/html/info.php
<?php
phpinfo();

Webブウウザからアクセスし正常に表示されれば成功です。

ここで表示されている情報がセキュリティホールになる可能性があるため、検証が終わったらスクリプトは必ず削除します。

$ sudo rm /var/www/html/info.php

MySQL

インストール

今回はMySQL5.7を入れます。

$ sudo yum install mysql57 mysql57-common mysql57-devel mysql57-libs mysql57-server
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.25, for Linux (x86_64) using  EditLine wrapper

起動

$ sudo service mysqld start

自動起動

$ sudo chkconfig mysqld on

設定

初期設定

/etc/my.cnfへ文字コードの設定を追記します。

$ sudo vi /etc/my.cnf
[mysqld]
character-set-server = utf8

詳しくは割愛しますが、必要に応じて各種ログの設定やチューニングを行います。

セキュリティ

mysql_secure_installationコマンドを実行すると対話形式でセキュリティ関連の基本的な設定が行なえます。

$ mysql_secure_installation
Would you like to setup VALIDATE PASSWORD plugin?
(パスワードが厳格な物かチェックするプラグインを入れる?)
  ⇛ no

New password: 
Re-enter new password:
(MySQLのrootユーザーのパスワードを設定)
  ⇛ (適当な文字列)

Remove anonymous users? (Press y|Y for Yes, any other key for No) 
(匿名ユーザーを削除する)
  ⇛ yes

Disallow root login remotely?
(MySQLのルートユーザーで外部からのログインを拒否する)
  ⇛ yes

Remove test database and access to it?
(test用のデータベースを削除)
  ⇛ yes

Reload privilege tables now?
(設定を今すぐ反映しても良いか)
  ⇛ yes

PHPからMySQLを触る

実際に利用できるか簡単な検証を行います。

環境準備

先ほど設定したrootのパスワードを利用してMySQLへログインします。

$ mysql -u root -p
Enter password:

検証用のデータベースとユーザー、テーブルを作成します。

CREATE DATABASE foo;
GRANT ALL ON foo.* TO bar@localhost IDENTIFIED BY "UIMMdagMX8TPMa8p";

USE foo;
CREATE TABLE user(
  id   INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(32),
  primary key(id)
);

スクリプトの準備

適当なコードを実行して正常に動作するか確認します。

<?php
try {
  $user = 'bar';
  $pw   = 'UIMMdagMX8TPMa8p';
  $pdo  = new PDO('mysql:host=localhost;dbname=foo;', $user, $pw);

  #-- 挿入  --#
  $sth = $pdo->prepare('INSERT INTO user(name) VALUES(?)');
  $sth->execute([getName()]);

  #-- 参照 --#
  $sth = $pdo->prepare('SELECT * FROM user');
  $sth->execute();
  var_dump( $sth->fetchAll(PDO::FETCH_ASSOC) );
}
catch (PDOException $e) {
 echo '[Error]'.$e->getMessage();
}


function getName(){
  $ls = ['渋谷', '新宿', '東京', '神田', '新橋', '上野', '秋葉原', '高田馬場', '大崎', '有楽町'];
  $i  = rand(0, count($ls)-1);

  return( $ls[$i] );
}

作成したユーザーでログインできているか、権限が正常に付与されているかなど一通りチェックします。PHPから表示されているかはもちろん、MySQLにログインを行い実際のテーブルも念の為確認しておいてください。

お掃除

検証用に作成したデータベースとユーザーを削除しておきます。

$ echo 'DROP DATABASE foo; DROP USER bar@localhost;' | mysql -u root -p

独自ドメイン

静的IPのアタッチ

LightsailやEC2で作成したインスタンスのIPアドレスは一時的な物で、運用中に変更される可能性があります。そこでこちらが指定したタイミング以外で変更されないよう静的なIPアドレスを付与します。

Lightsailのダッシュボードの上にある「ネットワーク」タブをクリックし、「静的IPの作成」ボタンをクリック。 あとは画面の指示に従い、作成するリージョンやIPアドレスを付与するインスタンスを指定するだけです。

なお、どのインスタンスにも付与しないと料金が発生するので注意が必要です。またLightsailでの静的IPは作成できる数に制限があります。無限に作成できるわけではありませんので必要なものにだけ付与します。

ドメインの取得

「AWS Route53」でも良いのですが、お名前.comムームードメインなど好きなところでかまわないので、ドメインを取得しておきます。

DNSゾーンの作成

静的IPの横にあった「DNSゾーンの作成」ボタンをクリックします。ここでは設定したいドメインを入力、ドメインと紐付けたいIPアドレスを指定し、次の画面に移動するだけです。

すると以下のようなDNSサーバのURL一覧が表示されますので、これをドメインを取得したサイトで登録します。

ns-422.awsdns-52.com
ns-1452.awsdns-53.org
ns-744.awsdns-29.net
ns-1555.awsdns-02.co.uk

ムームードメインの場合は実際に登録すると、以下のような表示になります。

これで設定は完了です。whoisなどのコマンドを実行し、DNSの設定が反映されたのが確認できたら実際にアクセスしてみましょう。

独自ドメインでSSLに対応

※後日

バックアップ

DB

Lightsailには最近、RDSのようなマネージドなデータベースサービスが始まりましたが、今回のようにWebサーバと同居している場合はバックアップをこちらで仕掛けておく必要があります。

ぱっと思いつく方法としては以下のようなコマンドをcronで1日1回実行する感じでしょうか。データ量が小規模な場合はこれで十分ですが、莫大な場合には別途作戦を練る必要があります。

$ /usr/bin/mysqldump -uusername -pXXXXXXXXXXXXX -h localhost -x --all-databases --events | gzip > dbdump.sql.gz

ファイル

一般的にAWS環境であれば、S3へコピーしておけば大抵の場合は十分です。以下のようにtarなどで包んでawsコマンドでコピーしてやります。これをcronをトリガーとして日次などでコピーしてやります。

$ tar zcf backup.tar.gz /home/foo /webapp/data /dbdump.sql.gz
$ aws s3 cp backup.tar.gz s3://backup.example.com/files/

AWSのCLIツールの初期設定は以下を参照してください。 blog.katsubemakito.net

もちろんこれも少量の場合は十分に機能しますが、ファイルが多くなってきた場合は別途作戦を練る必要があります。

インスタンス毎まとめて

Lightsailのダッシュボードからインスタンスの項目に入り、「スナップショット」タブをクリック。「スナップショットの作成」ボタンをクリックすれば、その時点でのデータをまるっと記録しておくことができます。

このスナップショットから新たなインスタンスを作成できますので、特定時点のデータや状態を調査したいと言った場合にも役に立ちます。またインスタンスの性能を上げたい、横に増やしたい(スケールアウトしたい)といった際にも便利に使えます。

AWS CLIツールからスナップショットの作成もできますので、この辺りをうまく使えば楽ができます。 docs.aws.amazon.com

監視

CloudWatchでサーバ上の値がいろいろ見れると良いのですが、現状は難しいようなのでこちらも自分で用意してやります。

Zabbixなどの監視サーバを立てて…とやるのは中々面倒なので、クラウドサービスを使ってしまうのが楽ちんです。無料の範囲を超えると個人のお小遣いではちょっと高く感じますが、法人利用であれば問題ない範囲でしょうか。 mackerel.io

その他

各種ツール

バージョン管理ツール

$ sudo yum install git
$ sudo yum install svn