こんにちは。松戸市でエンジニアの幸福を目指す企業、Joolenです。
現在、弊社では新規Webサービスを絶賛構築中です!
いきなりですが、新規サービスでは主に下記のフレームワークやクラウドサービスを利用しています。
- Laravel(-mix)
- React
- Cloud Run
- Cloud Storage(CDNとして利用)
この組み合わせ、驚くほどに良いです。何が良いかというと
- 安い →
Cloud Run
のコスパが半端じゃない。(動いた分だけしか課金されない。無料枠も大きい) - 早い → 動的サイトとは思えないほど早い。開発チームメンバー全員が感動するレベル。
- うまい → LaravelとReactどちらも、拡張性が高い。ライブラリも豊富!特盛!熱盛り!(「うまい」だけこじ付け)
ただ、この良さを引き出すためには色々と工夫が必要で、情報探しはかなり苦労しました。
(ありがとう、Github。ありがとう、StackOverflow。その他、有益な情報を公開されている世界中の皆さんに感謝です。)
ということで、Qiita読者の皆さんにもこの良さを伝えるべく、我々が何をしたのか惜しげなく公開していきたいと思います!(施されたら施し返す。恩返しです!)
ただ、ここではCloud Runとは何か?ということまでは説明しません。それについては公式サイトにお任せして、我々はこのサービスをどうやって活用しているのかという点に絞って説明をしていきたいと思います。
-はじめに- どのような設計なのか
システム構成としては下記のようなイメージです。(一部抜粋)
基本的に、クラウドサービスについてはGCP内で完結するようにしています。
この構成を実現するために
次にStep by Stepでこのアーキテクチャをどうやって実現するかこの記事では、最適化したLaravelのデプロイまでに絞って説明していきます。
では早速、具体的な実現方法について説明していきましょう。
Cloud RunでLaravelを動かす。
最新のソースコードはGithubで公開されています。こちらも、是非、参考にしてください。
動作に最低限必要なファイルを作成する
1.Laravelのプロジェクトを作成する
任意の場所にディレクトリ を作り、作成したディレクトリの配下で
composer create-project laravel/laravel app
を実行します。これにより、Laravelのプロジェクトが作成されます。
2.Apacheの設定ファイルを記述する
作成したディレクトリの配下に、apache/000-default.conf
という名前でファイルを作りましょう。
ファイルの内容は以下を記載しておきます。
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/public
<Directory /var/www/html/>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
3.php.iniを用意する
同じ階層の配下にphp/php.ini
というファイルを用意します。本来であれば、要件によって細かく記述を変えるところではありますが、デモなので最低限の記述に止めます。
[php]
max_execution_time = 30
memory_limit = 512M
upload_max_filesize = 1M
post_max_size = 1M
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.revalidate_freq=60
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
;***** Added by go-pear
include_path=".:/lib/php"
;*****
4.Dockerfileを記述する
最後にDockerfile
を作って下記のようにします。
FROM php:7.4-apache
WORKDIR /var/www/html
# 必要最低限のPHP拡張とNode.jsのインストール
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get update \
&& apt-get install -y libzip-dev libpq-dev mariadb-client unzip git libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ \
&& docker-php-ext-install zip pdo_mysql mysqli gd exif opcache \
&& a2enmod rewrite \
&& apt-get -y install vim \
&& apt-get install -y nodejs \
&& git clone https://github.com/phpredis/phpredis.git /usr/src/php/ext/redis \
&& docker-php-ext-install redis
EXPOSE 8080
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
ENV APACHE_LOG_DIR /var/log/apache2
# composerをインストール
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY ./apache/*.conf /etc/apache2/sites-enabled/
COPY ./php/php.ini /usr/local/etc/php/php.ini
COPY ./app /var/www/html
# composer install を高速化するためのライブラリをグローバルインストール
RUN composer global require hirak/prestissimo
RUN composer install --optimize-autoloader --no-dev
RUN php artisan key:generate \
&& php artisan config:cache \
&& php artisan view:cache
RUN chmod 777 -R /var/www/html/storage/framework/ \
&& echo "Listen 8080" >> /etc/apache2/ports.conf
5.念のためにローカルで動かす
下記のコマンドを実行して、http://localhost:8080
で画面がみられれば成功です。
# buildして
docker build ./ -t cloud-run-demo
# 実行
docker run -p 8080:80 -d cloud-run-demo
6. Cloud Runへデプロイするための設定ファイルを作成する
GCP
にアカウントがある前提で進めます。
まずは、.gcloudignore
というファイルをディレクトリ 直下に作成します。ファイル内容は下記の通りです。これは、composer
で作られるvendor
やnpm
コマンドで作られるnode_modules
を除外することによりDockerコンテナのイメージサイズを小さくし、デプロイの速度を早めるために使っています。
*/node_modules/
*/vendor/
次に、cloudbuild.json
というファイルを同じ階層に作ります。{your-project-id}
の箇所は、GCPで作成したプロジェクトのIDとしてください。
ポイントは--cache-from
の記述で、これがあることで前回のビルドのキャッシュを取得できるため、次回からのデプロイが高速化されます
ただし、1ステップ目は最初のビルドに限りイメージが見つからない、というエラーが出ます。ご注意ください
{
"steps": [
{
"name": "gcr.io/cloud-builders/docker",
"entrypoint": "bash",
"args": [
"-c",
"docker pull gcr.io/{your-project-id}/cloud-run-demo:latest || exit 0"
]
},
{
"name": "gcr.io/cloud-builders/docker",
"args": [
"build",
"-t",
"gcr.io/{your-project-id}/cloud-run-demo:latest",
"--cache-from",
"gcr.io/{your-project-id}/cloud-run-demo:latest",
"-f",
"Dockerfile",
"."
]
}
],
"images": [
"gcr.io/{your-project-id}/cloud-run-demo:latest"
]
}
7. いよいよCloud Runへデプロイする
下記のコマンドを実行すると、GCP
のイメージリポジトリにビルドしたコンテナがpushされます。とても便利ですね。
gcloud builds submit --config cloudbuild.json --timeout="30m" --ignore-file ".gcloudignore"
次にGCP
のコンソールからサービスを作成します。Webサービスなので、「未認証の呼び出しを許可」しましょう。
「次へ」を押した後、既存のコンテナ イメージから 1 つのリビジョンをデプロイする、を選択しましょう。先ほど、pushしたイメージが候補になっていると思います。
次に「詳細設定を表示」して、コンテナポートを80
にしておきます。
作成ボタンを押して、しばらくしたら完了ですローカルと同様に、Laravelのデフォルトのトップページが表示さればです。
Laravelを高速に動作させるためのコツ
ここで、上記設定を振り返りながらLaravelを高速に動かすためのポイントをいくつかご紹介します。
OPcacheを有効にする
かなりメジャーな手法であり、ある意味必須とも言える対応ですね。弊社では開発中に無効にしていたのですが、OPcacheを入れたことでレスポンスに100ms以上改善して驚きました。コードを変更しなくても、かなり効果がありますね。Dokcerfile
の9行目の記述がポイントです。
&& docker-php-ext-install zip pdo_mysql mysqli gd exif opcache \
php.ini
にも記述が必要ですので注意しましょう。
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.revalidate_freq=60
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
Laravelのマニュアル(デプロイ)を読み、忠実に従う
こちらにきちんと記述があります。これに従いましょう。
Dockerfile
では31行目以降が、該当します。
RUN composer install --optimize-autoloader --no-dev
RUN php artisan key:generate \
&& php artisan config:cache \
&& php artisan view:cache
cache
の流れではroute:cache
もパフォーマンス改善に有効です。しかし、デフォルトのままではroutes/web.php
に、コールバックメソッドがあるため、失敗します。そのため、デモのDockerfile
の記述からは除外しています。もちろん、この記述を削除すれば問題なく動きますので、こちらも忘れずに入れると良いでしょう。
デフォルトのweb.php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
//下記のcallbackが原因です。例えば
//Route::get('/','WelcomeController@index');
//などとすると、問題なく動きます。
Route::get('/', function () {
return view('welcome');
});
上記の対策を全て行った場合、何も対策をしない場合を比べて200ms近くのパフォーマンスアップにつながりました。
人が体感できるレベルで早くなるので是非とも、上記を意識することをお勧めします。
ここまでのソースコードはGithubにPushされています。こちらも、是非、参考にしてください。