【AWS入門】EC2+NGINX+MySQL環境へLaravelをデプロイする手順

目次

はじめに

初めてAWSを使う方向けの内容となってます。AWSのEC2にLaravelアプリをデプロイするまでの手順です。

前提

  • Laravelアプリは作成済みで、Githubに置いてある。(他から持ってきてもいいです)
  • WEBサーバーにApacheではなくNGINXを使います。
  • データベースはAWSのRDSを使わずにEC2内にMySQLを入れて動かします。

全体の流れ

AWSアカウントを作成して、EC2サービスでインスタンスを立ち上げます。はじめに必要なものをすべてインストールしてからそれぞれの設定を行っていきます。サーバの準備が整ったらGithubからLaravelアプリをCloneします。

全体的には初心者向けの内容ですが、ターミナルなどで簡単なコマンド操作などができる方を対象にしています。

サーバの準備

まずはAWSのアカウントを作るところから、インスタンスの起動までを行いましょう。

AWSアカウント作成

簡単に作れるので詳しくは触れません。以下のページから作成できます。

AWSアカウント作成

EC2インスタンス起動

アカウントにサインインしてEC2インスタンスを起動します。

まずEC2サービスのダッシュボードに行きます。メニューバーの「サービス」から「EC2」を選択するか、検索窓で「EC2」と検索しても良いです。

EC2のダッシュボードに入ったら、「インスタンスの起動」ボタンを押します。

リージョンの確認
右上辺りに表示されているリージョンが「東京」になっているか確認します。なっていなければ「アジアパシフィック (東京)ap-northeast-1」を選択します。(東京でなければいけないことはありません)

ステップ 1: Amazon マシンイメージ (AMI)

特にこだわりがなければ「Amazon Linux 2 AMI (HVM), SSD Volume Type」(無料枠)を選択します。(今回はこの前提での手順です)

ステップ 2: インスタンスタイプの選択

こちらも特にこだわりがなければ「t2.micro」(無料枠)を選択して「確認と作成」ボタンを押します。(ステップ3~6までをデフォルト設定で作成されます)

「次のステップ:インスタンスの詳細の設定」ボタンを押すとステップ3~6までを手動で設定できます。

ステップ 7: インスタンス作成の確認

起動」ボタンを押すとインスタンスを起動できます。が、その前に1つだけ設定しておきます。

セキュリティグループの編集」を押して、「ステップ 6: セキュリティグループの設定」ページに移動します。

以下の図の箇所を編集して新しいセキュリティグループを作成します。

インスタンスウィザードを起動

「セキュリティグループ名」と「説明」に適当なものを入れます。

「ルールの追加」ボタンで図のように「HTTP」を追加します。SSL接続をする場合は「HTTPS」も追加しておきましょう。

確認と作成」ボタンを押すと「ステップ 7: インスタンス作成の確認」ページに戻ってくるので、「起動」ボタンを押します。

キーペアの作成

キーペアのポップアップが出るので、「新しいキーペアを作成」を選択してキーペア名を適当に付けます。キーペアのタイプはRSAでいいです。「キーペアのダウンロード」をしてから「インスタンスの作成」を押します。

するとインスタンスが起動します。(起動には少し時間がかかります)

ダウンロードしたキーペアファイルはSSH接続する時に使います。

各種インストール

起動したら、インスタンスに必要なものをインストールしていきます。

インストールするもの

  • NGINX
  • PHP
  • MySQL
  • Composer
  • Git
  • その他のモジュール

SSHでインスタンスに接続

各種インストールするためにEC2にSSHで接続します。

接続するインスタンスを選択して「接続」ボタンを押すと、各種接続方法が表示されます。

インスタンス I EC2

PCからターミナルで接続する場合

SSH クライアント」を選択すると、接続手順が表示されます。

PCのターミナルから赤線↓のコマンドを打つと接続できます。先程ダウンロードしたキーペアファイルがあるディレクトリ内で実行しましょう。

インスタンスに接続

接続する方法は他にもあります。詳しい方法は長くなるのでここでは省略しますが、機会があったら記事にしたいと思います。

ブラウザから接続する場合

AWSにはブラウザからコマンド入力できる機能があるので、これを使って接続してもいいです。

EC2 Instance Connect」を選択して「接続」ボタンを押す。

インスタンスに接続 I EC2 Management Console

すると、以下のようにコマンド入力画面が開きます。

EC2 Instance Connect

各種インストール

SSH接続できたら、早速インストールしていきます。

まず、インスタンスに初めから入っているパッケージ類をアップデートしましょう。

パッケージ類アップデート

$ sudo yum update

NGINXをインストール

# NGINXをインストール
$ sudo amazon-linux-extras install nginx1

# サーバ起動
$ sudo systemctl start nginx

# 起動しているか確認
$ sudo systemctl status nginx

# インスタンス起動時に自動起動するように設定
$ sudo systemctl enable nginx


↓今ここでは実行しませんが、関連コマンド
----------------------------------
# 再起動
$ sudo systemctl restart nginx
# 設定再読み込み
$ sudo systemctl reload nginx
# 停止
$ sudo systemctl stop nginx

PHPインストール

# PHPインストール
$ sudo amazon-linux-extras install php7.4

# fpm起動
$ sudo systemctl start php-fpm.service

# インスタンス起動時に自動起動するように設定
$ sudo systemctl enable php-fpm

↓今は実行しませんが、後ほど使うコマンド
------------------------------------
# 再起動
$ systemctl restart php-fpm.service

MySQLインストール

データベースをインストールします。今回はMySQLを使いたいので、デフォルトで入っているMariaDBをアンインストールします。

MariaDBが入っているか確認

$ sudo yum list installed | grep mariadb

MariaDBアンインストール

$ sudo yum remove mariadb-libs

MySQL5.7インストール

今回は5.7を使いたいので、リポジトリを追加したら以下の手順で8.0を無効化して5.7を有効化してからインストールします。

# MySQLリポジトリ追加
$ sudo yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm -y

# MySQL8.0の無効化
$ sudo yum-config-manager --disable mysql80-community

# MySQL5.7の有効化
$ sudo yum-config-manager --enable mysql57-community

# MySQLインストール
$ sudo yum install mysql-community-server

# 起動
$ sudo systemctl start mysqld.service

# 起動確認
$ sudo systemctl status mysqld.service

# 自動起動設定
$ sudo systemctl enable mysqld.service

# 確認
$ mysqld --version

バージョン情報が表示されず、エラーが出ている場合は、ログファイルのパーミッションを変更するといいかもしれません。

#パーミッション変更
$ sudo chmod 755 /var/log/mysqld/log

Composerインストール

以下のコマンドでインストールできますが、ハッシュの部分が定期的に変わるのでエラーになる場合があります。その場合は公式を参照します。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

移動&リネーム

$ sudo mv composer.phar /usr/local/bin/composer

Gitインストール

$ sudo yum install git

その他のモジュールをインストール

他にLaravelを動かすのに必要なモジュールをインストールします。

$ sudo yum install php-bcmath

$ sudo yum install php-mbstring

$ sudo yum install php-xml

使ってるライブラリなどによって必要なモジュールが変わる場合があるので、環境に合わせてインストールします。もし足りなかった場合はLaravelデプロイのところでcomposer installした時にエラーで何が足りないか教えてくれるのでそこでインストールしても大丈夫です。

コーヒーブレイク

ここまででインストール関連は終わりです。次に各設定をしていきますが、その前にちょっと休憩がてら、サーバがちゃんと起動しているか確認してみましょう。

EC2のパブリックIPアドレスにブラウザでアクセスしてみます。パブリックIPアドレスは以下の場所で確認できます。パブリックDNSでもアクセスできます。

インスタンス I EC2

NGINXの初期ページが表示されたら正常に起動しています。

SSLの設定をしていないので、「https://」での接続はできないので「http://」でアクセスしてください。

 

各種設定

続いてそれぞれの設定ファイルを編集していきます。

php-fpm

PHPの設定ファイルを一部編集します。

$ sudo vi /etc/php-fpm.d/www.conf

上記コマンドはvimというエディタを使って編集するものです。vimの使い方がわからない方はググってください。

結構たくさんの行がありますが、以下の項目を探して以下のように修正します。(listen.ownerなどは「;」でコメントアウトされているので「;」を消します)

user = nginx
group = nginx

listen = /var/run/php-fpm/php-fpm.sock

listen.owner = nginx
listen.group = nginx
listen.mode = 0660

php-fpm再起動

編集内容を反映するために再起動します。

$ sudo systemctl restart php-fpm.service

NGINX

続いてNGINXの設定をしていきます。

別ファイルを作成して読み込む方法もありますが、今回はこのファイルを直接編集します。

$ sudo vi /etc/nginx/nginx.conf

↓がデフォルトの内容です。

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2;
#        listen       [::]:443 ssl http2;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers PROFILE=SYSTEM;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

}

server{ }の部分(コメントアウトされていない方)を以下のように書き換えます。(Laravel公式で紹介されているものを一部修正しています)内容が理解できなくても最初はコピペでいっちゃいましょう。

server {
    listen 80;
    server_name _;
    root /var/www/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

SSL(ポート443)の設定はまだしていません。現時点での暫定的な内容です。SSLの設定をする場合は後ほど編集します。またドメインの設定をする時にもこのファイルの編集が必要です。

設定反映

# 設定が正しいかテスト
$ sudo nginx -t

# 再起動
$ sudo systemctl restart nginx
or
# 設定再読み込み
$ sudo systemctl reload nginx

エラーが出る場合は、「nginx.conf」の内容に誤りがあります。誤字脱字などないかもう一度確認してみましょう。

ルートディレクトリについて

NGINXのデフォルトのルートディレクトリは「/usr/share/nginx/html」ですが、今回は「/var/www」に変更しています。

ルートディレクトリの権限設定

先程ルートディレクトリを「/var/www」に設定しました。このディレクトリはまだ存在しないので、作成してアクセス権などを設定します。(この権限周りが意外と重要なポイントです

# ルートディレクトリを作る
$ sudo mkdir /var/www

# /var/www の所有者とグループを変更
$ sudo chown ec2-user:nginx /var/www

# /var/www のパーミッションを設定(今後追加されたコンテンツにも適用)
$ sudo chmod 2775 /var/www

# ec2-userを nginx グループに追加
$ sudo usermod -a -G nginx ec2-user

現在ログインしているユーザーは「ec2-user」ですが、外部からのリクエストを実行するのは「nginx」というユーザーです。「ec2-user」を「nginx」のグループに所属させることで同じグループになり、パーミッション77xのファイルならすべての権限を共有することができるようになります。

MySQL

MySQLの初期設定とデーターベースの作成をします。

初期設定の前にrootパスワードを確認しておきましょう。rootパスワードはMySQLのログに出力されています。以下のコマンドでその部分を表示できます。

$ sudo cat /var/log/mysqld.log | grep -e 'A temporary password is generated for root@localhost'

表示された行の最後の文字列がrootパスワードです。

初期設定

$ mysql_secure_installation

以下のように対話形式で設定をしていきます。

Enter password for user root: #初期パスワードを入れる
New password: #新しいパスワードを入れる
Re-enter new password: #もう一度新しいパスワードを入れる
Change the password for root ? ((Press y|Y for Yes, any other key for No) : #変更する場合はYes(y)、しない場合はNo
Remove anonymous users? (Press y|Y for Yes, any other key for No) : #Yes(y)と入力
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : #Yes(y)と入力
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : #Yes(y)と入力
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : #Yes(y)と入力

パスワード入力時、入力した文字は表示されません。入力したらENTERを押しましょう。

新しいパスワードを設定する場合、大文字小文字記号を組み合わせた複雑なものでないと受け付けてくれないので注意。

設定したパスワードはLaravelの.envで使うのでメモっておきます。

データベース作成

# mysqlにログイン
$ mysql -u root -p

# パスワードを聞かれるので入力

# データベース作成
mysql> create database [データベース名];

# 確認
mysql> show databases;

# ログアウト
mysql> exit;

MySQLにログインした状態でのコマンド入力は最後に「;」を付けます。

Laravelをデプロイ

サーバの準備が整ったので、早速Laravelをデプロイしていきます。最初に書いたように、LaravelアプリがGithubに置いてある前提ですが、他から持ってきても構いません。デプロイの方法はいろいろあると思うのでそれぞれの方法で行ってください。

GithubにSSHで接続

AWS側でキーを作成して、出来たパブリックキーをGithubに登録します。

SSHキー作成

# .sshへ移動
$ cd ~/.ssh

# キー作成
$ ssh-keygen

----------------------------------------------------------------------------------
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa): #[Enter]を押す(名前を付けたい場合は入力)
Enter passphrase (empty for no passphrase): #[Enter]を押す(パスフレーズを設定する場合は入力)
Enter same passphrase again: #[Enter]を押す(パスフレーズを設定する場合はもう一度入力)

Githubにパブリックキー登録

以下のコマンドでパブリックキーが表示されるので、コピーしておきます。

$ sudo cat id_rsa.pub

Githubにログインして、Settings > SSH and GPG keys へ進んで「New SSH key」ボタンを押して、コピーしたパブリックキーを登録します。

接続確認

$ ssh -T git@github.com

---------------------------------------------------------------------------------
# 初回接続時は以下のようなことを言われるので、yesと入力
Are you sure you want to continue connecting (yes/no)?

Hi ******! You've successfully authenticated, but GitHub does not provide shell access.
↑こう表示されたら成功

GithubからLaravelアプリをCloneする

# ルートディレクトリに移動
$ cd /var/www

# Cloneする
$ git clone git@github.com:[アカウント名]/[リポジトリ名].git .

cloneコマンドの最後に「.」ドットを付けると、ルートディレクトリ直下にファイルが展開されます。付けないとディレクトリごとcloneされるので、ルートディレクトリの設定を変更する必要があります。

各種設定

.envファイル作成

vi /var/www/.env

.envの内容はアプリによって違うと思いますので、適宜設定してください。

APP_NAME=”アプリ名”
APP_ENV=production
APP_KEY=この後生成
APP_DEBUG=false
APP_URL=”アプリURL”

中略

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=”DB作成時の名前”
DB_USERNAME=root
DB_PASSWORD=”MySQL設定時のパスワード”

中略

パッケージインストール

$ composer install

アプリケーションキー(APP_KEY)生成

$ php artisan key:generate

DBテーブル作成

$ php artisan migrate

確認

先程確認したパブリックIPアドレスを開いて確認してみましょう。

ここまでの手順が問題なくできていれば、Laravelが表示されるはずです。

独自ドメインの設定

パブリックIPアドレスでアプリが表示できたら、そのIPアドレスと独自ドメインを結びつければ、自分のドメインでアプリを公開できるようになります。

ドメインの設定については環境によって大きく変わるので、それぞれのケースに合わせてググってもらった方が良い方法が見つかると思います。大まかにどんなケースがあるのかご紹介したいと思います。

AWSでドメインを取得する場合

AWSで取得して管理する場合は、Amazon Route 53 というサービスを使います。取得から管理まですべてAWSで完結するので、これから取得しようと考えている方はRoute 53を検討しても良いと思います。

他社で取得したドメインをAWSに移管して使用する場合

他社で取得したドメインをAWSに移管する(引っ越す)ことができます。これをすることで、ドメインの管理や支払いなどすべてをAWSに移すことができます。

移管するには、Amazon Route 53 を利用します。移管の方法については割愛しますが、大まかには、携帯のMNP(携帯電話番号ポータビリティ)のように、契約中の会社からキーを受け取って、そのキーを使って移管先で手続きをします。

AWSの場合ではありませんが、ドメイン移管についての流れについて過去にまとめた記事があるので参考にしてください。

サイト引っ越し(ドメイン移管・DNS設定)の流れ

他社で取得済みのドメインをそのまま使用する場合

AWSへドメインを移管しなくても、そのドメインを使ってアプリを表示することができます。他社でドメインを管理したまま、そのドメインの行き先をEC2へ向けるだけで実現します。

AWSでDNSを管理する場合

ドメインを管理している会社のドメイン関連の設定で、DNSの設定にAWSのDNSを設定することで、AWS側でDNSの設定を管理することができるようになります。DNSを管理できるようになると、サブドメインの設定や他の様々なことができるようになるので、可能ならこちらの方法がおすすめです。

これについても具体的な方法は割愛しますが、AWS側ではRoute 53 サービスを利用します。

他社でDNSを管理したままEC2に向ける場合

ドメインの管理もDNSの管理も他社に置いたまま、ドメインの行き先をEC2へ向けることができます。

ドメインを管理している会社のDNS設定で、AレコードというところをEC2のパブリックIPアドレスに設定をするだけです。これも詳細は割愛しますが、設定はドメイン管理会社側のみで、Route 53 サービスは利用しません。

上記の方法すべての場合に言えることですが、パブリックIPアドレスはインスタンスを起動する度に変わってしまいます。なので、ドメインと結びつける場合は必ず固定する必要があります。

EC2のElastic IP というサービスを使ってパブリックIPアドレスを固定しましょう。

SSLの設定

https://~で表示するためにはSSLの設定が必要です。

SSL証明書を無料で発行するには、「Let’s Encrypt」を使う方法と、AWSのELB(Elastic Load Balancing)を使う方法があります。

これについても説明が長くなるので、ここでは省略します。

「Let’s Encrypt」を利用する方法のおすすめ記事↓。

Let’s EncryptによるSSLサーバー証明書の取得、自動更新設定(2021年3月版)

Let’s EncryptによるSSLサーバー証明書の取得、自動更新設定(Snapを使用しない版)

最後に

以上の手順でLaravelアプリをAWSで公開することができたと思います。

AWSを利用する前は得体の知れない難しさを勝手に感じていましたが、使ってみると意外とわかりやすく出来ていて、手順さえわかれば使いやすいサービスだなあと思いました。わからないところはググればAWS関連の記事は多いので、すぐに解決できたりします。とはいえ奥は深いのでいろいろハマると迷子になりますけどね。

サーバのタイムゾーンを日本標準時へ変更

ログなどの記録時間が日本時間の方が都合がいい場合は以下のように変更します。

# タイムゾーンの設定ファイルを所定の場所にコピー
$ sudo cp -p /usr/share/zoneinfo/Japan /etc/localtime

# /etc/sysconfig/clockを編集
$ sudo vi /etc/sysconfig/clock

---------------------
ZONE="Asia/Tokyo"
UTC=false