Published: 2022-06-18
Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【前編:サーバーサイド】
Webアプリ開発に慣れない人にとって環境構築は大きなハードルです。 この記事ではサーバーサイドはDockerとLaravel、フロントエンドではReactとTypeScriptを使ったフルスタックパッケージを、ひとつづつ手順を確認しながら構築していきます。 前編ではサーバーサイドのDockerとLaravelの構築までを行います。
構成の確認
- Docker(Nginx+PHP+MySQL)
- Laravel(Composer)
- React
- TypeScript
GitHubで手順ごとにソースをこちらで公開しています。
consolegit clone https://github.com/mariebell/fullstack-project.git
1. Docker環境構築
仮想環境上にアプリケーションを構築します。Dockerの環境構築から始めましょう。
Docker+Laravel環境構築(前編での最終形)fullstack-project/
├ src/
| └ (Laravelのコード)
├ docker/
│ ├ nginx/
│ │ ├ Dockerfile
│ │ └ default.conf
│ ├ php/
│ │ ├ Dockerfile
│ │ └ php.ini
│ └ mysql/
│ ├ Dockerfile
│ └ my.conf
└ docker-compose.yml
Docker Desktopをインストールし、起動していることを確認したら、 まずは新しく作ったディレクトリの中にdocker-compose.ymlを作成します。
consolemkdir fullstack-project
cd ./fullstack-project
touch docker-compose.yml
1-1. Nginx
まずはHTTPリクエストを捌くリバースプロキシ用のNginxを準備します。
docker-compose.yml
Docker Composeアプリ開発では複数のコンテナを運用するのが一般的なので、まずdocker-compose.ymlから編集していきます。
/docker-compose.ymlversion: “3.9”
services:
(これから記述)
docker-compose.ymlファイルを新規作成し、まずはversionとservicesを記述します。
versionは最新版の3.9としていますが、3と書いても大丈夫です。 servicesの中に動かしたいサービス名を書きます。
ここではNginxコンテナを動かすサービスをproxyとします。
/docker-compose.ymlversion: “3.9”
services:
proxy:
container_name: fullstack-proxy
build: ./docker/nginx
ports:
- “8000:80"
volumes:
- ./src:/usr/share/nginx/html
container_name: (コンテナ名)
docker psコマンドで起動中のコンテナを確認する際に便利なのでcontainer_nameを指定します。
build: (Dockerfileがあるディレクトリ)
buildにDockerfileがあるディレクトリを指定します。 DockerHubにあるDockerイメージをそのまま利用する場合は、build: ディレクトリ名の代わりにimage: イメージ名とします。
ports: "(ホストのポート番号):(ゲストのポート番号)"
ports:に、解放するローカルマシン(ホスト)のポート番号と仮想コンテナ(ゲスト)のポート番号を指定します。 ホスト側のポート番号を8000とすると、ブラウザからlocalhost:8000でアクセスできます。 ウェルノウンポートや他のコンテナやアプリケーションと被らない値を設定します。 ゲスト側のポート番号はHTTPのデフォルトである80で問題ありません。
volumes: "(ホストのディレクトリ):(ゲストのディレクトリ)"
volumesに、ローカルマシン(ホスト)のディレクトリと仮想コンテナ(ゲスト)のディレクトリを同期(マウント)することができます。 ホストのディレクトリで指定しておいた場所にあるファイルを編集すると、自動的にコンテナのファイルも上書きされます。
ゲスト側では、nginxのデフォルトWebルートディレクトリである/usr/share/nginx/htmlを指定しています。
docker/nginx/Dockerfile
docker-compose.ymlのbuild:で指定したDockerfileの中身を記述します。
/docker/nginx/DockerfileFROM nginx:1.21.1-alpine
FROM (ベースとなるイメージ名:タグ)
ベースとなるDockerイメージを指定します。タグによってバージョンを指定することができます。 ここで指定するイメージ名はDockerHub上で探すことができます。
ここでは「nginx」イメージの「1.21.1-alpine」タグがついたイメージを指定しています。 常に最新版にしたい場合はnginx:latestでも良いでしょう。
タグ末尾についているalpineとはAlpine Linuxのことで、軽量なLinuxディストリビューションイメージがくっついています。 そのためDockerコンテナをLinuxサーバライクに扱うことができます。Alpine LinuxはUbuntuよりもかなり軽量なため、Dockerイメージのサイズを抑えることができます。
default.conf
以下はnginx(1.21.1)にデフォルトで入っているdefault.confを引っ張ってきたものです。 必要に応じて編集できるようにしておきます。
/docker/nginx/default.confserver {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
docker-compose.ymlで指定したports:とvolumes:のゲスト側の値は、 default.confの2行目のポート番号(80)と、9行目のrootのパス(/usr/share/nginx/html)にそれぞれ対応しています。
確認用のHTMLを配置
volumes:のホスト側に指定したsrcディレクトリを作成し、確認用のHTML(index.html)を配置します。
src/index.html<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
Hello, World.
</body>
</html>
docker-compose.ymlのvolumesに1行追加し、ローカルマシンのdefault.confを仮想コンテナ(ホスト)のdefault.confと同期させます。
/docker-compose.ymlservices:
proxy:
container_name: fullstack-proxy
build: ./docker/nginx
ports:
- “8000:80"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./src:/usr/share/nginx/html
Dockerコンテナを起動して確認してみる
プロジェクトルートにいる状態でdocker-compose up -dコマンドで起動してみましょう。
consoledocker-compose up -d
Creating network "fullstack-project_default" with the default driver
Building proxy
[+] Building 8.4s (6/6) FINISHED
...省略...
Creating fullstack-proxy ... done
docker psコマンドで、コンテナが起動していることがわかります。
consoledocker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e5045092c12e fullstack-project_proxy "/docker-entrypoint.…" 21 minutes ago Up 21 minutes 0.0.0.0:8000->80/tcp fullstack-proxy
ブラウザでlocalhost:8000にアクセスし、「Hello, World.」が表示されれば成功です。

ここでローカルマシンのsrc/index.htmlを編集して保存し、ブラウザをリロードすると変更が反映されることがわかります。

docker-compose downコマンドで、コンテナを停止+削除することができます。
consoledocker-compose down
Stopping fullstack-proxy ... done
Removing fullstack-proxy ... done
Removing network fullstack-project_default
1-2. PHP
次にLaravelのアプリケーションを動かすために、PHPのDockerイメージを導入していきます。
docker-comopse.yml
services:に新しくLaravelを動かすためのサービスappを追加します。
/docker-compose.ymlversion: “3.9”
services:
proxy:
container_name: fullstack-proxy
build: ./docker/nginx
ports:
- “8000:80"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./src:/usr/share/nginx/html
app:
container_name: fullstack-app
build: ./docker/php
volumes:
- ./src:/var/www/html
container_name,build,volumesをnginxの時と同様に指定します。
ゲスト上のnginx(proxy)からゲストのphp(app)にリクエストを転送するので、portsを指定する(ローカルマシンとphpコンテナのポートを紐づける)必要はありません。
docker/php/Dockerfile
/docker/php/DockerfileFROM php:8.1-fpm-alpine
php:8.1-fpm-alpinePHPのバージョンは8.1で、サーバー機能を持たせるfpmと、Alpine Linuxのバージョン11であるalpineをタグに指定します。
default.confの編集(phpへのリクエストを転送)
nginxが受けたリクエストのうち、拡張子がphpのファイルをapp(phpのコンテナ)に転送する設定を行います。
location ~ \.php$ディレクティブのコメントアウトを外し、値を編集します。
/docker/nginx/default.conf...
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /var/www/html;
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
...}
root (phpコンテナ内のドキュメントルート);
htmlとなっている値を/var/www/htmlに変更します。
fastcgi_pass (docker-compose.ymlのサービス名:ポート番号);
Dockerで運用している場合は、fastcgi_passにIPアドレスではなくサービス名(app)を指定することができます。
fastcgi_param SCRIPT_FILENAME (phpコンテナ内のファイルパス);
/scripts$fastcgi_script_nameとなっている値を$document_root$fastcgi_script_nameに変更します。
php.ini
docker cpコマンドを利用して、php-fpmにデフォルトで入っているphp.iniを引っ張ってきて、ローカルマシンで編集できるようにします。
consoledocker cp fullstack-app:/usr/local/etc/php/php.ini-development ./docker/php/php.ini
docker-compose.ymlのvolumesに1行追加し、ローカルマシンのphp.iniを仮想コンテナ(ホスト)に同期させます。
/docker-compose.ymlservices:
...
app:
container_name: fullstack-app
build: ./docker/php
volumes:
- ./docker/php/php.ini:/usr/local/php/php.ini
- ./src:/var/www/html
確認用のPHPを配置
/srcディレクトリに確認用のPHP(info.php)を配置します。
src/info.php<?php
phpinfo();
Dockerコンテナを再起動して確認してみる
Dockerfileやdefault.confを編集したのでコンテナの再起動が必要です。
docker-compose restartコマンドで再起動してみましょう。
consoledocker-compose restart
Restarting fullstack-proxy ... done
Restarting fullstack-app ... done
localhost:8000/info.phpにアクセスしてphpinfoが表示されたら成功です。

ここまででPHPが動く環境が用意できました。
1-3. MySQL
次に、Laravelのアプリケーションを動かすための、MySQLのDockerイメージを導入していきます。
docker-compose.yml
services:に新しくLaravelを動かすためのサービスdbを追加します。
今回もvolumes:の設定でローカルマシンのMySQLデータと仮想コンテナのデータを同期設定しますが、 MySQLの実データはコードで管理する必要がないので、プロジェクトの中にデータを置く代わりにDockerの機能であるボリュームを使用します。 MySQLの実データを保持する場所としてfullstack-db-volumeボリュームを作成します。
/docker-compose.ymlversion: “3.9”
services:
proxy:
container_name: fullstack-proxy
build: ./docker/nginx
ports:
- “8000:80"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./src:/usr/share/nginx/html
app:
container_name: fullstack-app
build: ./docker/php
volumes:
- ./docker/php/php.ini:/usr/local/php/php.ini
- ./src:/var/www/html
db:
container_name: fullstack-db
build: ./docker/mysql
ports:
- 3306:3306
volumes:
- fullstack-db-volume:/var/lib/mysql
volumes:
fullstack-db-volume:
Dockerのボリュームの定義であるvolumes:はdocker-compose.ymlのトップレベルに書き、services:の配下に書くのではないことに注意してください。
一方、dbサービスのvolumes:については、ローカルマシン側はfullstack-db-volume、仮想コンテナ(ゲスト)側は/var/lib/mysqlを指定します。
ポート番号ports:は3306:3306とある通り、ローカルマシン(ホスト)も仮想コンテナ(ゲスト)もデフォルトポートの3306を設定しています。
docker/mysql/Dockerfile
NginxやPHPと同様に、MySQL8.0のイメージをDockerfileに指定しました。
/docker/mysql/DockerfileFROM mysql:8.0
- M1チップ搭載のMacを利用している場合Dockerイメージがpullできずに失敗することがあります。
その場合は--platform linux/amd64オプションをつけると実行できるようです。
参考:https://github.com/docker-library/mysql/issues/778
FROM --platform=linux/amd64 mysql:8.0
環境変数の設定
MySQLから参照される環境変数をdocker-compose.ymlに設定しておきます。
/docker-compose.yml...
db:
container_name: fullstack-db
build: ./docker/mysql
ports:
- 3306:3306
volumes:
- fullstack-db-volume:/var/lib/mysql
environment:
MYSQL_DATABASE: laravel_db
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
fullstack-db-volume:
dbサービスのenvironment:にMySQLの初期データベース名(MYSQL_DATABASE)とルートユーザのパスワード(MYSQL_ROOT_PASSWORD)を設定します。 任意でMYSQL_USER、MYSQL_PASSWORDも設定できます。
my.cnf
docker cpコマンドを利用して、mysqlにデフォルトで入っているmy.cnfを引っ張ってきて、ローカルマシンで編集できるようにします。
consoledocker cp fullstack-db:/etc/mysql/my.cnf ./docker/mysql/my.cnf
docker-compose.ymlのvolumesに1行追加し、ローカルマシンのmy.cnfを仮想コンテナ(ホスト)に同期させます。
/docker-compose.ymlservices:
...
db:
container_name: fullstack-db
build: ./docker/mysql
ports:
- 3306:3306
volumes:
- ./docker/mysql/my.cnf:/etc/mysql/my.cnf
- fullstack-db-volume:/var/lib/mysql
コピーしたmy.cnfのファイルの一番下に追加で2行の設定をしておきます。 タイムゾーン(defualt_time_zone)を日本時間であるAsia/Tokyoに、 デフォルト文字コード(character-set-server)をutf8mb4に設定します。
/docker/mysql/my.cnf...
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
secure-file-priv= NULL
# Custom config should go here
!includedir /etc/mysql/conf.d/
# Settting for the application
default_time_zone=Asia/Tokyo
character-set-server=utf8mb4
Dockerコンテナを再起動して確認してみる
consoledocker-compose up -d
...
Building db
docker-compose upコマンドで再度コンテナを起動します。dbサービスのDockerイメージがビルドされます。
consoledocker-compose exec db mysql -u root -p
Enter Password: (rootユーザのパスワードを入力)
起動したMySQLのコンテナにログインしてインストールできたか確認してみます。
docker-compose exec (サービス名) (コンテナ内で実行したいコマンド)または
docker exec -it (コンテナ名) (コンテナ内で実行したいコマンド)
コマンドによって起動しているコンテナに対してコマンドを実行することができます。

mysqlコマンドで設定したパスワードを入力して、mysqlにログインできれば成功です。
phpMyAdmin(任意)
phpMyAdminを利用すると、MySQLのデータをブラウザ上で参照・編集することができます。services:の中に、phpmyadminサービスを追加します。
/docker-compose.yml...
phpmyadmin:
container_name: fullstack-phpmyadmin
image: phpmyadmin/phpmyadmin
ports:
- 8001:80
environment:
- PMA_ARBITRARY=1
- PMA_HOST=db
- PMA_ROOT_PASSWORD=rootpassword
environment:にはPMA_HOSTにdbサービスのサービス名(db)、PMA_ROOT_PASSWORDにルートパスワードを設定します。PMA_ARBITARYは任意のサーバーに接続するかどうかの設定で、ここでは接続する(1)設定にしています。

ブラウザでlocalhost:8001にアクセスすると、ログイン画面が表示されます。 先ほど設定した値でログインすることができます。

Dockerfileとdocker-compose.ymlのどちらに設定を書くべきか
docker-compose.ymlの各サービスに記述する設定はDockerfileにも記述できます。
Dockerfileに設定を記述する基準は「永続化したいかどうか」です。 永続化したい場合や再配布したい設定はDockerfileに記述し、 環境によって変更したい・されそうな設定はdocker-compose.ymlに記述します。
2. Laravel環境構築
2-1. Composer
LaravelはphpパッケージマネージャであるComposerでインストールできます。 そのため、まずはComposerを導入します。
公式ではインストール方法がくどくどと説明されていますが、結局のところphpが動く環境で以下の1行を実行すればインストールできます。(ただし後述の理由により、以下のコマンドの実行する必要はありません。)
consolecurl -sS https://getcomposer.org/installer | php
composer.pharというファイルが生成され、php composer.pharでComposerを実行できるようになります。
ところで、DockerコンテナでLaravelを動かすためには、Composerインストールする手順が毎回含まれてきます。 このインストール手順はDockerfile内にあらかじめ書いてしまうことで、コンテナを起動するたびに実行する手間を省けます。 Dockerイメージをビルドする段階でComposerをインストールしてしまいましょう。
/docker/php/DockerfileFROM php:8.1-fpm-alpine
# install composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN (ビルド時に実行させたいコマンド)
docker/php配下のDockerfileにコマンドを2行追記します。前述のComposerインストールコマンドの行と、php composer.pharではなくcomposerコマンドでComposerを使えるようにするための行を追加しています。
Dockerfileを編集した場合は、変更をイメージに反映するためにdocker-compose buildコマンドでDockerイメージを再ビルドします。
consoledocker-compose build app
Building app
[+] Building 1.9s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 187B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/php:8.1-fpm-alpine 1.8s
=> [1/3] FROM docker.io/library/php:8.1-fpm-alpine@sha256:58508918c6d212da25d38b4b422af86588b0146f1509256b38a3d1203932f512 0.0s
=> CACHED [2/3] RUN curl -sS https://getcomposer.org/installer | php 0.0s
=> CACHED [3/3] RUN mv composer.phar /usr/local/bin/composer 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:125ddc758e99ce8aed432fea227069e8180ba737f2b812632edcb0ae17723eb0 0.0s
=> => naming to docker.io/library/fullstack-project_app 0.0s
出力を確認するとComposerインストールのコマンドが実行されていることがわかります。
consoledocker-compose up -d
docker-compose exec app composer -v
docker-compose upコマンドで再度コンテナを起動して、Composerがインストールされたか確認してみましょう。
コンテナ内でcomposer -vコマンドを実行してComposerのロゴが表示されれば成功です。

2-2. Laravel
Installation Via Composer - Laravel
ComposerでLaravelプロジェクトを作成します。 Laravelアプリケーションの保存先をローカルディレクトリのsrc/配下にします。 src/配下にあったindex.htmlとinfo.phpは不要なので削除した後、以下のコマンドを実行します。
consoledocker-compose exec app composer create-project laravel/laravel .
...
In GitDownloader.php line 77:
git was not found in your PATH, skipping source download
gitコマンドが使えないようなので、インストールします。phpのDockerfileに追記して、再度ビルドしましょう。
/docker/php/DockerfileFROM php:8.1-fpm-alpine
# install composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
# install packages
RUN apk update
RUN apk add git
apkコマンドをupdateした後、gitをインストールします。
consoledocker-compose build app
Laravelをインストールします。
consoledocker-compose exec app composer create-project laravel/laravel .
Creating a "laravel/laravel" project at "./"
Info from https://repo.packagist.org: #StandWithUkraine
Installing laravel/laravel (v9.1.10)
...
> @php artisan key:generate --ansi
Application key set successfully.
default.confの編集
ブラウザでlocalhost:8000にアクセスしてみると、残念ながらNginxの403エラーが表示されます(src/index.htmlを削除したため)。

そこで、Nginxのdefault.confに設定の変更を行います。
/docker/nginx/default.conf...
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html/public;
index index.html index.htm index.php;
try_files $uri $uri/ /index.php$is_args$args;
}
...
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /var/www/html/public;
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
...
location / ディレクティブの編集
ルートディレクトリを/usr/share/nginx/html/publicに変更します。
また、ブラウザでルートパス(/)を指定した場合にindex.phpを参照させるように、indexにindex.phpを追加します。
さらに、URLに含まれるパスやパラメータをLaravelアプリケーションに転送できるよう、以下の行を追加します。
try_files $uri $uri/ /index.php$is_args$args;
location ~ \.php$ ディレクティブの編集
Laravelアプリケーションの開始点となるindex.phpはpublicディレクトリの中にあります。 末尾にphpファイルが付く場合のルートも/var/www/html/publicに変更します。
default.confの編集が完了したらコンテナを再起動します。
Laravelのサーバー要件を満たす
Laravelを動かすためにはPHP以外にもいくつかの拡張モジュールが必要です。
Dockerfileではdocker-php-ext-installコマンドでPHPの拡張モジュールをインストールすることができます。 ここではLaravelでMySQLを扱うためにpdo、pdo_mysqlをインストールしています。
/docker/php/DockerfileFROM php:8.1-fpm-alpine
# install composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
# install packages
RUN apk update
RUN apk add git
# install php extensions
RUN docker-php-ext-install pdo pdo_mysql
データベースの接続設定とマイグレーション
Laravelの環境変数.envに、Dockerのデータベースの情報を設定します。
/src/.env...
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=root
DB_PASSWORD=rootpassword
...
DB_HOSTはDockerのサービス名db、DB_DATABASE、DB_USERNAME、DB_PASSWORDもDockerで設定した値にします。
consoledocker-compose exec app php artisan migrate
...
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (135.82ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (68.97ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (66.53ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (116.13ms)
Dockerコンテナを再起動して確認してみる
ブラウザでlocalhost:8000にアクセスし、Laravelの初期画面が表示されれば成功です。

ここまででLaravelが動く環境が用意できました。
後編に続く
後編に続きます。
Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【後編:フロントエンド】