この記事はOluyemi Olususiがこちらで公開した記事(英語)を日本語化したものです。
あなたが所属する開発チームは最近アプリをリリースし、そのアプリが短期間で大ヒットしていることが分かりました。利用者からの需要は収まらず、開発チームは「スケールアップ」について検討を始めました。
かつて、このスケールアップ作業は「垂直スケーリング」と呼ばれ、高機能のサーバーに投資することを意味していましたが、最近のコンテナ技術の台頭により、低コストで迅速にコンテナを追加できるようになり、アプリケーションの水平スケーリングが可能になりました。
これにより、2つの大きなメリットがもたらされます。1つ目は、高機能のサーバーに追加コストをかける必要がないことです。2つ目は、現在の顧客の需要に基づいて、アプリケーションのスケールアップとスケールダウンが可能になることです。
本稿では、LaravelプロジェクトでDockerを使用する方法について紹介します。Dockerを使えば、アプリケーションの使用量の急増と急減の両方に対応してスケーリング可能なアプリケーションを開発できます。
以下のチュートリアルでは、ApacheをWebサーバーとして使用し、PostgreSQLでデータベースエンジンを提供します。著名な歴史家による有名な名言を表示するアプリケーションを作成します。
必要条件
このチュートリアルを進めるには、以下の項目が必要です。
- PHPとLaravelの基本的な知識。
- コンテナ、イメージ、ネットワーク、サービスなど、基本的なDocker用語の知識。Jeff Hale氏は、これらの用語について、連載記事で分かりやすく解説しています。これらの用語についてあまり知らない場合は、ぜひ読んでみてください。
- Docker Desktop
はじめに
最初に、laravel_docker
という名前の新しいディレクトリを作成します。
mkdir laravel_docker
cd laravel_docker
このアプリケーションは、相互に通信する必要のあるさまざまなコンポーネントで構成されているため、Docker Composeを使用してサービスを定義します。laravel_dockerディレクトリのルートに、docker-compose.ymlという名前の新しいファイルを作成します。
touch docker-compose.yml
このファイルには、コンテナのビルド方法から、コンテナにアクセス可能なネットワークやボリュームまで、アプリケーションの設定で作成するコンテナのすべての設定が保持されます。
docker-compose.ymlに以下を追加します。
version: '3.8'
services:
version
はスキーマバージョンを参照し、services
は、アプリケーションスタックを構成するコンテナのリストを定義します。services
は、実際は単なる「本番環境のコンテナ」です。
次の各セクションでは、データベース、Apache Webサーバー、PHPのコンテナについて説明します。
データベースコンテナをビルドする
docker-compose.ymlで、services
エントリを次のように更新します。
services:
database:
image: postgres
container_name: database
restart: unless-stopped
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: laravel_docker
volumes:
- ./postgres-data:/var/lib/postgresql/data
ports:
- '5432:5432'
container_name
は、Docker Composeでコンテナを自動的に生成するのではなく、実行時にコンテナの名前を設定します。
image
は、コンテナのビルド元のブループリントをDockerに認識させることができます。今回は、データベースエンジンとしてPostgreSQLを使用するため、postgres
を指定します。
environment
キーは、デフォルトのデータベースの名前やパスワードなど、環境変数のリストを指定します。ここでは、ユーザー名を指定していないため、データベースのユーザー名は「postgres」になります。
ports
キーを使用して、ローカル開発マシンのポートをコンテナのポートにマップし、GUIツールを使用してデータベースに接続できるようにします。コロン(:
)の左側に指定されているポートは、コンピューターのポートです。右側に指定されているポートは、コンテナのポートです。
PostgreSQLインスタンスを実行している場合、またはポート4306が占有されている場合は、コンピューターの別のポートを指定できます。
次に、volume
キーを使用してボリュームを宣言します。Docker公式ドキュメントによると:
ボリュームは、Dockerコンテナが生成、使用するデータを保持するための推奨メカニズムです。
コンテナの破棄または再ビルド時にデータベースが失われないようにするために、ボリュームを宣言します。
PHPとApacheコンテナをビルドする
データベースコンテナとは異なり、PHP/Apacheコンテナを設定するには、追加の指示を指定する必要があります。これを行うには、DockerfileからPHPコンテナをビルドします。ルートディレクトリlaravel_dockerに、phpという名前のディレクトリを作成します。次に、laravel_docker/phpに、Dockerfileという名前のファイルを作成します。
Dockerfileには拡張子がありません。
mkdir php
touch php/Dockerfile
laravel_docker/php/Dockerfileに、以下を追加します。
FROM php:8.0-apache
RUN apt update \
&& apt install -y \
g++ \
libicu-dev \
libpq-dev \
libzip-dev \
zip \
zlib1g-dev \
&& docker-php-ext-install \
intl \
opcache \
pdo \
pdo_pgsql \
pgsql \
WORKDIR /var/www/laravel_docker
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
公式のPHP Dockerイメージには、Apacheサーバーに付属するバリエーションが用意されています。これをFROM
で指定すると、ベースコンテナにApacheがインストールされます。
PHPイメージからコンテナのための骨組みを作成することに加えて、以下を実行します。
- Laravelが依存するPHP拡張機能をインストールします。
- コンテナの作業ディレクトリを/var/www/laravel_dockerに設定します。
- Composerをインストールします。
次に、docker-compose.ymlを次のように更新します。
services:
# The existing database container configuration...
php-apache:
container_name: php-apache
build:
context: ./php
ports:
- '8080:80'
volumes:
- ./src:/var/www/laravel_docker
- ./apache/default.conf:/etc/apache2/sites-enabled/000-default.conf
depends_on:
- database
php-apache
コンテナは、docker-compose.ymlにdatabase
コンテナとは異なる定義にします。イメージを指定する代わりに、ビルドコンテキストを指定します。このように、docker-compose
コマンドを実行すると、phpディレクトリに宣言されたDockerfileがコンテナのビルドに使用されます。
次に、データベースコンテナの場合と同様に、ローカル開発マシンのポート8080がコンテナのポート80にマップされます。./apache/default.confに指定された仮想ホストの設定はこのポートでリッスン(待機)するため、ポート80が使用されます。
コンテナで生成されたデータを保持するため、ボリュームを宣言します。この場合、Laravelアプリケーションはphp-apache
コンテナの/var/www/laravel_dockerディレクトリに作成されます。ただし、これはローカル開発マシンのプロジェクトディレクトリのsrcディレクトリに保持されます。
Apacheによりデフォルトで有効化されている000-default.conf
ホストとアプリケーションの仮想ホストの設定をリンクできるように、追加のボリュームが宣言されています。これまでは、デフォルトの設定を無効にしてから独自の設定を有効にし、コンテナがビルドまたは再ビルドされるたびにApacheサーバーをリロードする必要がありましたが、これにより、そのストレスが軽減されます。
その後、新しい設定キー(depends_on
)を導入します。これにより、php-apache
コンテナの前にまずデータベースコンテナをビルドする必要があることをDockerに認識させることができます。
プロジェクトのルートディレクトリにsrcディレクトリを作成します。
mkdir src
php-apache
コンテナからLaravelプロジェクトをスキャフォールドすると、プロジェクトファイルがここに保持されます。次に、apache
という名前のディレクトリを作成します。その中に、default.confというファイルを作成します。
mkdir apache
touch apache/default.conf
以下をapache/default.confに追加します。
<VirtualHost *:80>
ServerName laravel_docker
DocumentRoot /var/www/laravel_docker/public
<Directory /var/www/laravel_docker>
AllowOverride All
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
これで、ようやくコンテナをビルドできます。次のコマンドを実行します。
docker-compose up -d --build
Docker Desktopアプリケーションを開くと、次のスクリーンショットで示すように、新しく作成されたコンテナが表示されます。
Laravelアプリケーションを設定する
Laravelアプリケーションを設定するには、次のコマンドを使用し、php-apache
コンテナでコンテナを開始します。
docker-compose exec php-apache /bin/bash
これにより、php-apache
コンテナでCLIが開きます。次のコマンドを使用し、Laravelアプリケーションを作成します。
composer create-project laravel/laravel .
完了後に、http://localhost:8080/に移動します。デフォルトのLaravelのウェルカムページが表示されます。
また、srcディレクトリを見ると、Laravelプロジェクトファイルもそこに追加されていることが分かります。
src/.envで、以下のようにデータベースパラメーターを編集します。
DB_CONNECTION=pgsql
DB_HOST=database
DB_PORT=5432
DB_DATABASE=laravel_docker
DB_USERNAME=postgres
DB_PASSWORD=secret
アプリケーションを構築する
本稿では名言アプリケーションを作成します。まずはアプリケーションのモデルを作成してみましょう。php-apache
コンテナで、次のコマンドを実行します。
php artisan make:model Quote -fms
-f
引数により、名言のファクトリーを作成することをartisanに認識させることができます。同様に、-m
は移行用、-s
はデータベースシーダー用です。
src/database/migrations/YYYY_MM_DD_HHMMSS_create_quotes_table.phpで、以下のようにup
関数を更新します。
public function up() {
Schema::create(
'quotes',
function (Blueprint $table) {
$table->id();
$table->text('quote');
$table->string('historian');
$table->string('year');
$table->timestamps();
}
);
}
次に、src/database/seeders/QuoteSeeder.phpを開き、run
関数に以下を追加します。
Quote::factory()->times(50)->create();
これにより、QuoteFactoryから名言が50回作成され、毎回データベースに保存されます。
注:次のインポートを追加することを忘れないでください:
use App\Models\Quote;
src/database/seeders/DatabaseSeeder.phpで、run
関数に以下を追加します。
$this->call(
[
QuoteSeeder::class
]
);
これにより、db:seed
コマンドが実行されたときにQuoteSeeder
が実行されます。次に、src/database/factories/QuoteFactory.phpで、definition
関数に以下を追加します。
return [
'quote' => $this->faker->sentence(),
'historian' => $this->faker->name(),
'year' => $this->faker->year(),
];
移行を実行してデータベースをシードするには、php-apache
コンテナで次のコマンドを実行します。
php artisan migrate:fresh --seed
データベースに何が追加されたかを確認するには、次のコマンドを実行し、データベースコンテナへのコマンドラインを開きます。
docker-compose exec database /bin/bash
次に、次のコマンドを実行し、PostgreSQLサービスにログインします。
psql -U postgres laravel_docker
SQLクエリを実行し、名言テーブルのすべての項目を取得します。
SELECT * FROM quotes;
次のスクリーンショットのように出力されます。
準備ができたら、次のコマンドを使用して名言のリクエストを処理するコントローラーを作成します。
php artisan make:controller QuoteController
src/app/Http/Controllers/QuoteController.phpを開き、以下のように更新します。
<?php
namespace App\Http\Controllers;
use App\Models\Quote;
use Illuminate\Http\Request;
class QuoteController extends Controller
{
public function index(){
return view('quotes.index', ['quotes' => Quote::all()]);
}
}
index
関数では、データベースからすべての名言を取得し、ビュー(quotes/index.blade.php)に渡して、名言を表示します。現時点ではビューが存在しないため、次のコマンドを実行してビューを作成します。
mkdir resources/views/quotes
touch resources/views/quotes/index.blade.php
src/resources/views/quotes/index.blade.phpを開き、以下を追加します。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Top Quotes</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6"
crossorigin="anonymous">
<style>
.wrapper {
margin: 1em auto;
width: 95%;
}
</style>
</head>
<body>
<div class="wrapper">
<h1>Top Quotes</h1>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Quote</th>
<th scope="col">Historian</th>
<th scope="col">Year</th>
</tr>
</thead>
<tbody>
@foreach ($quotes as $quote)
<tr>
<td>{{ $loop->index + 1 }}</td>
<td>{{ $quote->quote }}</td>
<td>{{ $quote->historian }}</td>
<td>{{ $quote->year }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
crossorigin="anonymous">
</script>
</body>
</html>
このテンプレートでは、Bootstrapを使用して名言テーブルのスタイルを設定します。次に、foreach loopディレクティブを使用して、QuoteController
から受信した名言をループ処理し、名言、歴史家、年をそれぞれ表示します。
最後に、ルーティングを更新し、インデックスページに名言を読み込みます。これを行うには、src/routes/web.phpを開き、以下に一致するようにルート宣言を更新します。
Route::get('/', [App\Http\Controllers\QuoteController::class, 'index']);
これにより、index
ページにアクセスしたときに、ウェルカムページを返す代わりに、QuoteController
のindex
関数が呼び出されます。
変更をテストする
変更をテストするには、http://localhost:8080/に移動します。次のスクリーンショットのようなページレンダリングが表示されます。
すべて破棄する
先程、docker-compose up -d –build
コマンドを使用して、コンテナを作成してビルドしました。ハードディスクの空き容量が少ない、アプリケーションが冗長であるなどの理由で、システムのクリーンアップが必要になる場合がありますが、Dockerでは、このための機能がすでに用意されています。コンテナと関連するすべてのネットワークを停止して削除するには、次のコマンドを実行します。
docker-compose down
次のように出力されます。
アプリケーションをスケーリングする
サービスの1つのインスタンスでは、アプリケーションへのすべてのトラフィックを十分に処理できない場合があります。このような場合に対応できるように、Dockerでは、–scale
フラグを使用したサービスのスケーリング(サービスの複数のインスタンスの作成)をサポートしています。
たとえば、php-apach
eサービスをスケールアップするには、docker-compose.ymlを開きます。ここでは、2つの変更を行います。最初に、コンテナ名の設定を削除します。これは、コンテナ名が一意である必要があるためです。同じ名前で複数のサービスを作成しようとすると、Dockerはエラーを出力します。
同様に、サービスのポート設定を変更します。これにより、新しく作成された各サービスにDockerがポートを自動的に割り当てることができます。コンテナ名と同様に、2つのサービスを同じポートにバインドすることはできません。
これらの変更を行うには、以下のようにphp-apache
サービス設定を更新します。
php-apache:
build:
context: ./php
ports:
- '8080'
volumes:
- ./app:/var/www/laravel_docker
- ./apache/default.conf:/etc/apache2/sites-enabled/000-default.conf
depends_on:
- database
設定の更新後に、次のコマンドを実行し、php-apache
サービスのインスタンスを10個作成します。
docker-compose up --scale php-apache=10 -d
これが完了すると、次のスクリーンショットのような出力がターミナルに表示されます。
database is up-to-date
Recreating php-apache ... done
Creating laravel_docker_php-apache_2 ... done
Creating laravel_docker_php-apache_3 ... done
Creating laravel_docker_php-apache_4 ... done
Creating laravel_docker_php-apache_5 ... done
Creating laravel_docker_php-apache_6 ... done
Creating laravel_docker_php-apache_7 ... done
Creating laravel_docker_php-apache_8 ... done
Creating laravel_docker_php-apache_9 ... done
Creating laravel_docker_php-apache_10 ... done
すべてが機能していることをテストするには、docker-compose ps
を実行します。次の例のような出力が表示されるはずです。
Name Command State Ports
-------------------------------------------------------------------------------------------------------
database docker-entrypoint.sh postgres Up 0.0.0.0:5432->5432/tcp
laravel_docker_php-apache_1 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55966->8080/tcp
laravel_docker_php-apache_10 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55970->8080/tcp
laravel_docker_php-apache_2 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55972->8080/tcp
laravel_docker_php-apache_3 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55968->8080/tcp
laravel_docker_php-apache_4 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55974->8080/tcp
laravel_docker_php-apache_5 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55973->8080/tcp
laravel_docker_php-apache_6 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55969->8080/tcp
laravel_docker_php-apache_7 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55967->8080/tcp
laravel_docker_php-apache_8 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55971->8080/tcp
laravel_docker_php-apache_9 docker-php-entrypoint apac ... Up 80/tcp, 0.0.0.0:55975->8080/tcp
DockerとLaravelの使用を開始する方法は以上です
イメージとDockerfileからコンテナをビルドできるだけでなく、コンテナを相互に通信させることもできるため、Laravelアプリケーションとデータベースを個別のコンテナで実行できます。
これは、水平スケーリングが可能なアプリケーションスタックを作成するための第一歩です。コンテナオーケストレーションのインフラストラクチャを使用することにより、サーバーで処理されるリクエストの数に合わせてコンテナを作成および破棄できます。
このチュートリアルのコードベース全体は、GitHubで入手できます。ぜひご確認ください。コーディングを楽しみましょう!
Oluyemiは、電気通信工学のバックグラウンドを持つテクノロジー愛好家です。ユーザーが直面する日々の問題を解決することに強い関心を持ち、プログラミングの道に進んで以来、Webとモバイルの両方のソフトウェア開発で問題解決能力を磨いてきました。
Oluyemiは、知識の共有に情熱を注ぐフルスタックのソフトウェアエンジニアであり、ブログで多数の技術記事とコンテンツをインターネットに公開しています。テクノロジーにも精通しており、趣味は新しいプログラミング言語とフレームワークを試すことです。
- Twitter: https://twitter.com/yemiwebby
- GitHub: https://github.com/yemiwebby
- Webサイト: https://yemiwebby.com.ng/