Docker + Apache + Laravel で Web アプリケーションつくる
やりたいこと
Laravel 製の Web アプリケーションを作成し, 自宅サーバ PC にデプロイしたい. とりあえず, 自宅のプライベートネットワーク内だけの運用を想定しているがそのうち VPN などで外部からもアクセスできるようにしたいかも.
デプロイ作業を楽にしたい&環境構築とかでカオスになりたくないため Web サーバーやデータベースは Docker コンテナを利用し, 各コンテナを連携させる構成とする.
Docker コンテナを利用した Laravel 開発には Sail があるが, Apache への載せ替え方がわからなかったのと, Docker の勉強も兼ねてコンテナ連携の構築から自前で作成する.
また, 自身の勉強も兼ねているため各技術要素についてなるべく詳細に残していく予定.
要件
- Web サーバーとデータベースはそれぞれ Docker コンテナ化し連携する
- Web サーバーは Apache を使用する
- データベースは MySQL を使用する
- Web アプリケーションは PHP のフレームワーク Laravel を使用し作成する
本記事の内容
本記事では要件のうち基盤づくりまで, つまり Docker で Apache と MySQL のコンテナ を作成・連携, Apache コンテナ上に構築した Laravel から簡単なレスポンスをクライアント返すところまでを作る.
また, 作成したプロジェクトのリポジトリは以下.
https://github.com/kita127/docker-apache-example
環境
ホストPCで使用する各種ツールのバージョンは以下. 開発は Mac PC で行う.
- macOS Monterey
- 12.5.1
- Intel Mac
- PHP 8.1.10
- サーバーサイドのプログラミング言語
- Composer 2.4.1
- PHP のパッケージマネージャ
- docker desktop 4.5.0
- Engine 20.10.12
- コンテナ内環境
サーバ PC は準備中. おそらく ubuntu を採用する見込み. 現段階ではホストPCのみで開発をすすめる.
環境構築
必要なアプリケーションを Homebrew を使ってインストールする.
# brew install --cask docker # brew install php # brew install composer
構成
今回作成するプロジェクトの構成は以下の通り.
プロジェクトトップ ├── docker/ │ ├── apache/ │ │ ├── Dockerfile │ │ ├── config/ │ │ │ └── 000-default.conf │ │ └── php.ini │ └── db/ │ ├── Dockerfile │ └── initdb.d/ │ └── master.sql ├── docker-compose.yaml └── webapp/
- docker/
- 各コンテナの Dockerfile やコンフィグ系のファイルを格納する
- docker/apache/
- Apache コンテナの設定や Dockerfile など
- Web サーバの設定である
.conf
ファイルもここで管理する php.ini
もここで管理
- docker/db/
- docker-compose.yaml
- コンテナの連携のための Docker Compose 設定ファイル
- webapp/
- Web アプリケーション(Laravel プロジェクト)
手順
Docker の構築
docker-compose.yaml の作成
Webサーバと DB のコンテナを作成する設定をつくる. 構成に記載の docker-compose.yaml
を以下の通り作成.
version: '3' services: apache: container_name: apache build: context: . dockerfile: ./docker/apache/Dockerfile ports: - 80:80 environment: COMPOSER_ALLOW_SUPERUSER: 1 volumes: - ./webapp:/var/www/html - ./docker/apache/php.ini:/usr/local/etc/php/php.ini depends_on: - db networks: - net1 db: container_name: db build: context: . dockerfile: ./docker/db/Dockerfile environment: MYSQL_ROOT_PASSWORD: secret MYSQL_USER: docker MYSQL_PASSWORD: docker TZ: 'Asia/Tokyo' networks: - net1 ports: - 3306:3306 networks: net1:
services
以下にapache
コンテナとdb
コンテナを作成services.apache
Apache
のコンテナ- image の指定やコンテナ独自の設定は Dockerfile で行う
container_name
- コンテナの名前
- 各コンテナはこの名前で IP の名前解決がされるためコンテナ名での通信が互いに可能となる
- 例えば
apache
コンテナ内で$ ping db
でdb
コンテナに対して ping を投げたりもできる
- 例えば
build
dockerfile
- ビルド時の Dockerfile ファイルを指定
ports
environment
- コンテナに環境変数
COMPOSER_ALLOW_SUPERUSER
を 1 で設定- root ユーザへの Composer インストールを許可するとのこと
- Do not run xxx のような警告が出るらしくそれを抑えるため設定
- コンテナに環境変数
volumes
- Laravel プロジェクト(
webapp/
)をapache
コンテナの/var/www/html
にマウント - ローカルの
php.ini
をコンテナにマウントする- ./docker/apache/php.ini:/usr/local/etc/php/php.ini
- Laravel プロジェクト(
depends_on
- Laravel から DB にアクセスするため
db
コンテナの起動後にapache
コンテナを起動する - なくても大丈夫な気もする・・・
- Laravel から DB にアクセスするため
net1
apache
コンテナとdb
コンテナは互いにやりとりする必要があるため同一ネットワークnet1
に属させる
services.db
networks.net1
apache
コンテナとdb
コンテナで通信するためのネットワークを定義
apache コンテナの Dockerfile の作成
プロジェクトトップ/docker/apache/
に以下の apache
コンテナ用の Dockerfile を作成する.
FROM php:8.1-apache-bullseye # apt install iputils-ping net-tools で ping を導入 RUN apt-get update \ && apt-get install -y zlib1g-dev libzip-dev unzip vim iputils-ping net-tools sudo\ && docker-php-ext-install zip # node と npm をインストール RUN apt-get install -y gnupg RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -\ && apt-get install -y nodejs\ && npm install npm@8.12.1 --global # a2emod rewrite をして apache に rewrite モジュールを追加 # これをしないと Laravel でルート以外にアクセスできない RUN a2enmod rewrite # docker php には mysql 用のドライバが未インストールのため追加する RUN docker-php-ext-install pdo_mysql COPY --from=composer:2.4.1 /usr/bin/composer /usr/bin/composer ADD docker/apache/php.ini /usr/local/etc/php/ # Apache の conf は seites-available に作成し # a2ensite コマンドでシンボリックリンクを sites-enabled に作成する ADD docker/apache/config/000-default.conf /etc/apache2/sites-available/ RUN a2ensite 000-default WORKDIR /var/www/html COPY ./webapp /var/www/html RUN chown www-data storage/ -R \ && composer install\ && npm install
FROM
RUN apt-get update ....
- node と npm のインストールの詳細についてはnode/npmのインストール詳細を参照
RUN a2enmod rewrite
- Apache に
rewrite
モジュールを追加する - Laravel でのルーティングにはこのモジュールの有効化が必要
- Apache に
RUN docker-php-ext-install pdo_mysql
COPY --from=composer:2.4.1 /usr/bin/composer /usr/bin/composer
- composer:2.4.1 イメージをビルドし作成した
composer
の実行形式をコンテナの/usr/bin/composer
にコピーしているぽい - Composer のバージョンはホスト環境と合わせる
COPY --from=name src dest
FROM <image> as <name>
として名前をつけて構築したステージをコピー元として指定できるcomposer:2.4.1
を指定しているので名前つけをしたステージ以外にも image を直接指定もできるぽい?- 詳細は Docker Hub の Composer イメージのページや Dockerfile リファレンスの
COPY
を参照
- composer:2.4.1 イメージをビルドし作成した
ADD docker/apache/php.ini /usr/local/etc/php/
- PHP の設定ファイル(
php.ini
)をコンテナの然るべき場所に置く
- PHP の設定ファイル(
ADD docker/apache/config/000-default.conf /etc/apache2/sites-available/
RUN a2ensite 000-default
docker/apache/config
にある Apache のコンフィグファイル(000-default.conf
)をsites-available
に置く- 大元のコンフィグファイルである
apache2.conf
ではsites-enabled/
のみIncludeOptional
ディレクティブにより有効化される.sites-available/
は有効化されない sites-available
に置いたコンフィグファイルのシンボリックリンクをsites-enabled
に置くことによりsites-available/
内の任意のコンフィグを有効にする- シンボリックリンクの作成は直接作成して
sites-enabled
においても構わないが,a2ensite
コマンドで作成可能 - https://nanbu.marune205.net/2021/12/debian-apache2-dir.html
WORKDIR /var/www/html
COPY ./webapp /var/www/html
- Web アプリケーション(Laravelプロジェクト)をコンテナの
/var/www/html
にコピーする
- Web アプリケーション(Laravelプロジェクト)をコンテナの
RUN chown www-data storage/ -R \
composer install
composer.lock
の内容でパッケージをインストール
node/npmのインストール詳細
Laravel でアプリケーションを作成する場合, 使用するパッケージによっては Node および npm を使用するため Docker 環境内に導入しておく. 導入に際してDockerでphpコンテナとかにnpmをインストールするときのメモを参考にさせていただきました.
Docker コンテナ上に構築される Debian のパッケージマネージャでは Node のバージョンが古かったり, npm が同梱されていなかったり等あるらしいので NodeSource Node.js Binary Distributionsからインストールする.
リンク先の記載内容を元に任意の Node をインストールする.
今回は Node 本体としては Debian 向けのバージョンは 18 を選択(curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
).
Node インストール後, 任意のバージョンの npm を取得するため npm でバージョン 8.12.1 の npm をインストール(npm install npm@8.12.1 --global
).
# node と npm をインストール RUN apt-get install -y gnupg RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -\ && apt-get install -y nodejs\ && npm install npm@8.12.1 --global
Apache のコンフィグファイルの作成
Apache サーバのコンフィグファイル
プロジェクトトップ/docker/apache/config/000-default.conf
を以下の通り作成する.
<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html/public ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
VirtualHost
- バーチャルホストを実現するディレクティブ
- ひとつのサーバで複数のウェブサイトを提供する機能
- IP ベース, 名前ベース, ポートベースといくつかの実現方法がある
- 今回は複数のウェブサイトを提供したいわけではないため, 80 番ポートで受けるひとつの
VirtualHost
ディレクティブのみ - 詳細は以下あたりを参照
ServerAdmin
- サーバがクライアントに送るエラーメッセージに含めるアドレス
- プライベートに使用するウェブサイトのため適当に設定
ErrorLog
- エラーログファイルの指定
APACHE_LOG_DIR
の環境変数はコンテナの/etc/apache2/envvars
に定義されている
CustomLog
- クライアントのアクセスログを記録するファイルとフォーマットを指定する
- 第2引数の
combined
はLogFormat
ディレクティブで名前つけされたフォーマットcombined
はapache2.conf
に以下の通り定義されているLogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
- 本ディレクティブは
mod_log_config
モジュールの機能
php.ini の作成
PHP の設定ファイル プロジェクトトップ/docker/apache/php.ini
を以下の通り作成.
とりあえず, タイムゾーンと言語に関する設定だけ.
[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.language = "Japanese"
db コンテナの Dockerfile の作成
プロジェクトトップ/docker/db/
に以下の db
コンテナ用の Dockerfile を作成する.
FROM mysql:8.0.30 # docker-entrypoint-initdb.d にある SQL ファイルがコンテナ起動時に実行される COPY ./docker/db/initdb.d /docker-entrypoint-initdb.d
FROM mysql:8.0.30
- MySQL イメージの
8.0.30
タグを使用する
- MySQL イメージの
COPY ./docker/db/initdb.d /docker-entrypoint-initdb.d
docker/db/initdb.d
フォルダをコンテナの/docker-entrypoint-initdb.d
にコピーする/docker-entrypoint-initdb.d
フォルダにある SQL ファイルがコンテナ起動時に実行される
プロジェクトトップ/docker/db/initdb.d/
に以下の db
コンテナ起動時に実行される master.sql
を格納する.
キャラクタ設定をして master
スキーマを作成している.
SET CHARACTER_SET_CLIENT = utf8; SET CHARACTER_SET_CONNECTION = utf8; CREATE DATABASE `master`;
以上で Apache と MySQL のコンテナ作成のための準備は完了.
Laravel プロジェクトの作成
それでは Laravel プロジェクトを作成する.
今回は Docker 環境内で Laravel を動かすため, Docker 側から Laravel プロジェクトをインストールする.
プロジェクト直下で docker-compose up -d --build
を実行しコンテナを立ち上げる.
その際 Dockerfile 内の RUN chown www-data storage/ -R
によってまだ作成していない Laravel プロジェクト内の
storage
ディレクトリにアクセスしようとしエラーとなるので一旦 docker/apache/Dockerfile
の以下部分の記述はコメントアウトしておく.
#RUN chown www-data storage/ -R \ # && composer install
Docker コンテナが問題なく立ち上がったら docker-compose exec apache bash
で apache コンテナ内に入る.
/var/www/
に移動し以下コマンドを実行し html ディレクトリに Laravel プロジェクトを作成する.
$ composer create-project laravel/laravel html
問題なく作成されたら exit しコンテナから出る.
その後, コメントアウトした Dockerfile の記述を元に戻し, 一旦 docker-compose をダウン(docker-compose down --rmi all --volumes --remove-orphans
),
再度アップ(docker-compose up -d --build
) し再構築する.
この状態でブラウザから http://localhost:80
にアクセスし, Laravel のトップページが表示されれば問題なし.
Laravel から DB にアクセスする準備
とりあえず, db コンテナと連携できるかの確認のため捨てテーブルを作る. テーブル生成用のマイグレーションファイルを作成する. 以下のコマンドを実行.
$ php artisan make:migration create_hoges_table
database/migrations/yyyy_mm_dd_xxxx_create_hoges_table.php
が生成される.
確認用なのでとりあえずデフォルトのままでOK.
Eloqent モデルを作成する. 以下のコマンドを実行.
$ php artisan make:model Hoge
webapp/app/Models/Hoge.php
が作成される.
次にコントローラを以下のコマンドで作成.
$ php artisan make:controller HogeDir/HogeController --invokable
webapp/app/Http/Controllers/HogeDir/HogeController.php
が作成される.
とりあえず動作確認のため, hoges テーブルからレコードを取得し先頭要素の id をビューに渡すだけの処理を実装.
<?php namespace App\Http\Controllers\HogeDir; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use App\Models\Hoge; class HogeController extends Controller { /** * Handle the incoming request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function __invoke(Request $request) { $ls = Hoge::all(); return view('hoge.index', ['hoge' => $ls[0]->id]); } }
webapp/resources/views/hoge/index.blade.php
を作成する. これも確認のためだけなので $hoge を表示する簡易な表示のみ.
<!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>アプリタイトル</title> </head> <body> <h1>アプリボディ</h1> <p>{{ $hoge }}</p> </body> </html>
.env の変更
.env
は DB に関する設定だけ以下の通り変更する.
DB_HOST
にはデータベースサーバの IP を設定するが, Docker のネットワーク内であれば
コンテナ名で名前解決されるため, db
で OK. ただし, ホストからはコンテナ名では IP の名前解決はできないため
ホストで動かした Laravel からは DB にはアクセスできない. docker-compose.yaml
で別途コンテナに IP アドレスを
付与してやり, そちらを DB_HOST
に設定すればホストで動かした Laravel からでも DB にアクセスできる気がするが,
基本的に apache コンテナで動かすつもりなので今の所やらない.
... DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=master DB_USERNAME=root DB_PASSWORD=secret ...
Web サーバ(Apache)の確認
ここまでで準備が整ったのでそれぞれのコンテナの動作確認をする.
まずはコンテナを生成するためプロジェクトトップで以下のコマンドを実行.
-d
でデーモン起動(detachの略らしいけど).
$ docker-compose up -d
ブラウザで http://localhost:80/
にアクセスし Laravel のページが表示されれば問題なく
apache
コンテナが起動していることを確認できる.
DB(MySQL)の確認
Laravel から DB にアクセスできるか確認する.
そのまえに, DB アクセスのための下準備が完了していないためそちらを終わらせる.
まず, master
スキーマが生成されていることを確認する. db
コンテナに入る.
$ docker-compose exec db bash
以下のコマンドを実行. docker-compose.yaml
に設定している MySQL のパスワードを入力する.
# mysql -u root -p # パスワードを入力
show databases;
で master
スキーマが生成されていることを確認する.
+--------------------+ | Database | +--------------------+ | information_schema | | master | | mysql | | ...... | | ...... |
exit;
し mysql を終了, さらに exit
しコンテナからも出る.
次に apache
コンテナに入る.
$ docker-compose exec apache bash
まずは apache
コンテナで MySQL 用のドライバがインストールされているか確認する.
以下のコマンドを実行し pdo_mysql
が確認できれば OK.
$ php -m | grep mysql mysqlnd pdo_mysql
Laravel プロジェクトをマウントしたディレクトリ(/var/www/html)に移動する(おそらくコンテナに入った時点でそのディレクトリのはず).
$ pwd /var/www/html
マイグレーションを実行し hoges
テーブルを作成する.
$ php artisan migrate
exit
し apache
コンテナを出る. 再度, db
コンテナに入る.
$ docker-compose exec db bash
hoges
テーブルが作成されていることを確認. 表示用のダミーデータを適当に追加する.
$ mysql -u root -p パスワード入力 mysql> use master; mysql> show tables; +--------------------+ | Tables_in_master | +--------------------+ | ...... | | hoges | | ...... | | ...... | | ...... | mysql> insert into hoges (id) values (1); mysql> select * from hoges; +----+------------+------------+ | id | created_at | updated_at | +----+------------+------------+ | 1 | NULL | NULL | +----+------------+------------+
db
コンテナから抜ける.
ブラウザから http://localhost:80/hoge
にアクセスしテーブルに追加したレコードの id が表示されていることを確認できれば
問題なく apache
コンテナ上の Laravel から db
コンテナの MySQL にアクセスできている.
以上で Docker + Apache + Laravel での最低限の環境が完成.
その他
- Docker コンテナの停止と削除は以下のコマンドで実施
$ docker-compose down --rmi all --volumes --remove-orphans
--rmi all
- 使用したイメージも全て削除する
--volumes
- volume を全削除
--remove-orphans
- Compose ファイルで定義されていないコンテナも削除する
apache
コンテナ内でのphp artisan migrate
はホストからdocker exec
コマンドでも OK$ docker exec apache php artisan migrate
- データベースへのアクセスをコマンドでやるとめんどくさいので GUI アプリを使用すると吉
出典
作成にあたり以下の記事を参考にさせていただきました.