Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【前編:サーバーサイド】
Webアプリ開発に慣れない人にとって環境構築は大きなハードルです。 この記事ではサーバーサイドはDockerとLaravel、フロントエンドではReactとTypeScriptを使ったフルスタックパッケージを、ひとつづつ手順を確認しながら構築していきます。 前編ではサーバーサイドのDockerとLaravelの構築までを行います。
構成の確認
- Docker(Nginx+PHP+MySQL)
- Laravel(Composer)
- React
- TypeScript
GitHubで手順ごとにソースを公開しています。
git clone https://github.com/mariebell/fullstack-project.git
1. Docker環境構築
仮想環境上にアプリケーションを構築します。Dockerの環境構築から始めましょう。
Docker+Laravel環境構築(前編での最終形)
1fullstack-project/
2 ├ src/
3 | └ (Laravelのコード)
4 ├ docker/
5 │ ├ nginx/
6 │ │ ├ Dockerfile
7 │ │ └ default.conf
8 │ ├ php/
9 │ │ ├ Dockerfile
10 │ │ └ php.ini
11 │ └ mysql/
12 │ ├ Dockerfile
13 │ └ my.conf
14 └ docker-compose.yml
Docker Desktopをインストールし、起動していることを確認したら、まずは新しく作ったディレクトリの中にdocker-compose.ymlを作成します。
1mkdir fullstack-project
2cd ./fullstack-project
3touch docker-compose.yml
1-1. Nginx
まずはHTTPリクエストを捌くリバースプロキシ用のNginxを準備します。
docker-compose.yml
アプリ開発では複数のコンテナを運用するのが一般的なので、まずdocker-compose.ymlから編集していきます。
1version: “3.9”
2services:
3 (これから記述していきます)
docker-compose.ymlファイルを新規作成し、まずはversionとservicesを記述します。
versionは最新版の3.9としていますが、3と書いても大丈夫です。 servicesの中に動かしたいサービス名を書きます。
ここではNginxコンテナを動かすサービスをproxyとします。
1version: “3.9”
2services:
3 proxy:
4 container_name: fullstack-proxy
5 build: ./docker/nginx
6 ports:
7 - “8000:80"
8 volumes:
9 - ./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の中身を記述します。
1FROM 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を引っ張ってきたものです。 必要に応じて編集できるようにしておきます。
1server {
2 listen 80;
3 listen [::]:80;
4 server_name localhost;
5
6 #access_log /var/log/nginx/host.access.log main;
7
8 location / {
9 root /usr/share/nginx/html;
10 index index.html index.htm;
11 }
12
13 #error_page 404 /404.html;
14
15 # redirect server error pages to the static page /50x.html
16 #
17 error_page 500 502 503 504 /50x.html;
18 location = /50x.html {
19 root /usr/share/nginx/html;
20 }
21
22 # proxy the PHP scripts to Apache listening on 127.0.0.1:80
23 #
24 #location ~ \\.php$ {
25 # proxy_pass http://127.0.0.1;
26 #}
27
28 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
29 #
30 #location ~ \\.php$ {
31 # root html;
32 # fastcgi_pass 127.0.0.1:9000;
33 # fastcgi_index index.php;
34 # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
35 # include fastcgi_params;
36 #}
37
38 # deny access to .htaccess files, if Apache's document root
39 # concurs with nginx's one
40 #
41 #location ~ /\\.ht {
42 # deny all;
43 #}
44}
docker-compose.ymlで指定したports:
とvolumes:
のゲスト側の値は、
default.confの2行目のポート番号(80)と、9行目のrootのパス(/usr/share/nginx/html)にそれぞれ対応しています。
確認用のHTMLを配置
volumes:
のホスト側に指定したsrcディレクトリを作成し、確認用のHTML(index.html)を配置します。
1<!DOCTYPE html>
2<html lang="ja">
3<head>
4 <meta charset="UTF-8">
5 <title>Hello</title>
6</head>
7<body>
8 Hello, World.
9</body>
10</html>
docker-compose.ymlのvolumesに1行追加し、ローカルマシンのdefault.confを仮想コンテナ(ホスト)のdefault.confと同期させます。
1services:
2 proxy:
3 container_name: fullstack-proxy
4 build: ./docker/nginx
5 ports:
6 - “8000:80"
7 volumes:
8 - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
9 - ./src:/usr/share/nginx/html
Dockerコンテナを起動して確認してみる
プロジェクトルートにいる状態でdocker-compose up -d
コマンドで起動してみましょう。
1docker-compose up -d
2Creating network "fullstack-project_default" with the default driver
3Building proxy
4[+] Building 8.4s (6/6) FINISHED
5
6...省略...
7
8Creating fullstack-proxy ... done
1docker ps
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3e5045092c12e 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
コマンドで、コンテナを停止+削除することができます。
1docker-compose down
2Stopping fullstack-proxy ... done
3Removing fullstack-proxy ... done
4Removing network fullstack-project_default
1-2. PHP
次にLaravelのアプリケーションを動かすために、PHPのDockerイメージを導入していきます。
docker-comopse.yml
services:
に新しくLaravelを動かすためのサービスapp
を追加します。
1version: “3.9”
2services:
3 proxy:
4 container_name: fullstack-proxy
5 build: ./docker/nginx
6 ports:
7 - “8000:80"
8 volumes:
9 - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
10 - ./src:/usr/share/nginx/html
11 app:
12 container_name: fullstack-app
13 build: ./docker/php
14 volumes:
15 - ./src:/var/www/html
container_name
,build
,volumes
をnginxの時と同様に指定します。
ゲスト上のnginx(proxy)からゲストのphp(app)にリクエストを転送するので、ports
を指定する(ローカルマシンとphpコンテナのポートを紐づける)必要はありません。
docker/php/Dockerfile
1FROM php:8.1-fpm-alpine
php:8.1-fpm-alpine
PHPのバージョンは8.1で、サーバー機能を持たせるfpmと、Alpine Linuxのバージョン11であるalpineをタグに指定します。
default.confの編集(phpへのリクエストを転送)
nginxが受けたリクエストのうち、拡張子がphpのファイルをapp(phpのコンテナ)に転送する設定を行います。
location ~ \.php$
ディレクティブのコメントアウトを外し、値を編集します。
1...
2 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
3 #
4 location ~ \\.php$ {
5 root /var/www/html;
6 fastcgi_pass app:9000;
7 fastcgi_index index.php;
8 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
9 include fastcgi_params;
10 }
11...}
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を引っ張ってきて、ローカルマシンで編集できるようにします。
docker cp fullstack-app:/usr/local/etc/php/php.ini-development ./docker/php/php.ini
1services:
2 ...
3 app:
4 container_name: fullstack-app
5 build: ./docker/php
6 volumes:
7 - ./docker/php/php.ini:/usr/local/php/php.ini
8 - ./src:/var/www/html
確認用のPHPを配置
/srcディレクトリに確認用のPHP(info.php)を配置します。
1<?php
2phpinfo();
Dockerコンテナを再起動して確認してみる
Dockerfileやdefault.confを編集したのでコンテナの再起動が必要です。
docker-compose restart
コマンドで再起動してみましょう。
1docker-compose restart
2Restarting fullstack-proxy ... done
3Restarting 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
ボリュームを作成します。
1version: “3.9”
2services:
3 proxy:
4 container_name: fullstack-proxy
5 build: ./docker/nginx
6 ports:
7 - “8000:80"
8 volumes:
9 - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
10 - ./src:/usr/share/nginx/html
11 app:
12 container_name: fullstack-app
13 build: ./docker/php
14 volumes:
15 - ./docker/php/php.ini:/usr/local/php/php.ini
16 - ./src:/var/www/html
17 db:
18 container_name: fullstack-db
19 build: ./docker/mysql
20 ports:
21 - 3306:3306
22 volumes:
23 - fullstack-db-volume:/var/lib/mysql
24volumes:
25 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に指定しました。
1FROM mysql:8.0
M1チップ搭載のMacを利用している場合Dockerイメージがpullできずに失敗することがあります。
その場合は--platform linux/amd64
オプションをつけると実行できるようです。
参考:https://github.com/docker-library/mysql/issues/778
1FROM mysql:8.0
環境変数の設定
MySQLから参照される環境変数をdocker-compose.ymlに設定しておきます。
1...
2 db:
3 container_name: fullstack-db
4 build: ./docker/mysql
5 ports:
6 - 3306:3306
7 volumes:
8 - fullstack-db-volume:/var/lib/mysql
9 environment:
10 MYSQL_DATABASE: laravel_db
11 MYSQL_ROOT_PASSWORD: rootpassword
12 MYSQL_USER: user
13 MYSQL_PASSWORD: password
14volumes:
15 fullstack-db-volume:
dbサービスのenvironment:にMySQLの初期データベース名(MYSQL_DATABASE
)とルートユーザのパスワード(MYSQL_ROOT_PASSWORD
)を設定します。
任意でMYSQL_USER
、MYSQL_PASSWORD
も設定できます。
1services:
2 ...
3 db:
4 container_name: fullstack-db
5 build: ./docker/mysql
6 ports:
7 - 3306:3306
8 volumes:
9 - ./docker/mysql/my.cnf:/etc/mysql/my.cnf
10 - fullstack-db-volume:/var/lib/mysql
コピーしたmy.cnfのファイルの一番下に追加で2行の設定をしておきます。
タイムゾーン(defualt_time_zone
)を日本時間であるAsia/Tokyoに、
デフォルト文字コード(character-set-server
)をutf8mb4に設定します。
1...
2[mysqld]
3pid-file = /var/run/mysqld/mysqld.pid
4socket = /var/run/mysqld/mysqld.sock
5datadir = /var/lib/mysql
6secure-file-priv= NULL
7
8# Custom config should go here
9!includedir /etc/mysql/conf.d/
10
11# Settting for the application
12default_time_zone=Asia/Tokyo
13character-set-server=utf8mb4
Dockerコンテナを再起動して確認してみる
1docker-compose up -d
2...
3Building db
docker-compose upコマンドで再度コンテナを起動します。dbサービスのDockerイメージがビルドされます。
1docker-compose exec db mysql -u root -p
2Enter Password: (rootユーザのパスワードを入力)
起動したMySQLのコンテナにログインしてインストールできたか確認してみます。
docker-compose exec (サービス名) (コンテナ内で実行したいコマンド)
または
docker exec -it (コンテナ名) (コンテナ内で実行したいコマンド)
コマンドによって起動しているコンテナに対してコマンドを実行することができます。
mysqlコマンドで設定したパスワードを入力して、mysqlにログインできれば成功です。
phpMyAdmin(任意)
phpMyAdminを利用すると、MySQLのデータをブラウザ上で参照・編集することができます。
services:
の中に、phpmyadmin
サービスを追加します。
1...
2 phpmyadmin:
3 container_name: fullstack-phpmyadmin
4 image: phpmyadmin/phpmyadmin
5 ports:
6 - 8001:80
7 environment:
8 - PMA_ARBITRARY=1
9 - PMA_HOST=db
10 - 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を導入します。
Composer 公式ではインストール方法がくどくどと説明されていますが、結局のところphpが動く環境で以下の1行を実行すればインストールできます。(ただし後述の理由により、以下のコマンドの実行する必要はありません。)
curl -sS https://getcomposer.org/installer | php
composer.pharというファイルが生成され、php composer.phar
でComposerを実行できるようになります。
ところで、DockerコンテナでLaravelを動かすためには、Composerインストールする手順が毎回含まれてきます。 このインストール手順はDockerfile内にあらかじめ書いてしまうことで、コンテナを起動するたびに実行する手間を省けます。 Dockerイメージをビルドする段階でComposerをインストールしてしまいましょう。
1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer
RUN (ビルド時に実行させたいコマンド)
docker/php配下のDockerfileにコマンドを2行追記します。前述のComposerインストールコマンドの行と、
php composer.phar
ではなくcomposer
コマンドでComposerを使えるようにするための行を追加しています。
Dockerfileを編集した場合は、変更をイメージに反映するためにdocker-compose build
コマンドでDockerイメージを再ビルドします。
1docker-compose build app
2Building app
3[+] Building 1.9s (7/7) FINISHED
4 => [internal] load build definition from Dockerfile 0.0s
5 => => transferring dockerfile: 187B 0.0s
6 => [internal] load .dockerignore 0.0s
7 => => transferring context: 2B 0.0s
8 => [internal] load metadata for docker.io/library/php:8.1-fpm-alpine 1.8s
9 => [1/3] FROM docker.io/library/php:8.1-fpm-alpine@sha256:58508918c6d212da25d38b4b422af86588b0146f1509256b38a3d1203932f512 0.0s
10 => CACHED [2/3] RUN curl -sS https://getcomposer.org/installer | php 0.0s
11 => CACHED [3/3] RUN mv composer.phar /usr/local/bin/composer 0.0s
12 => exporting to image 0.0s
13 => => exporting layers 0.0s
14 => => writing image sha256:125ddc758e99ce8aed432fea227069e8180ba737f2b812632edcb0ae17723eb0 0.0s
15 => => naming to docker.io/library/fullstack-project_app 0.0s
出力を確認するとComposerインストールのコマンドが実行されていることがわかります。
1docker-compose up -d
2docker-compose exec app composer -v
docker-compose upコマンドで再度コンテナを起動して、Composerがインストールされたか確認してみましょう。
コンテナ内でcomposer -v
コマンドを実行してComposerのロゴが表示されれば成功です。
![fig7]/images/app/204/fig7.png)
2-2. Laravel
Installation Via Composer - Laravel
ComposerでLaravelプロジェクトを作成します。 Laravelアプリケーションの保存先をローカルディレクトリのsrc/配下にします。 src/配下にあったindex.htmlとinfo.phpは不要なので削除した後、以下のコマンドを実行します。
1docker-compose exec app composer create-project laravel/laravel .
2
3...
4In GitDownloader.php line 77:
5 git was not found in your PATH, skipping source download
gitコマンドが使えないようなので、インストールします。phpのDockerfileに追記して、再度ビルドしましょう。
1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer
6
7# install packages
8RUN apk update
9RUN apk add git
apk
コマンドをupdateした後、gitをインストールします。
docker-compose build app
1docker-compose exec app composer create-project laravel/laravel .
2
3Creating a "laravel/laravel" project at "./"
4Info from https://repo.packagist.org: #StandWithUkraine
5Installing laravel/laravel (v9.1.10)
6...
7> @php artisan key:generate --ansi
8Application key set successfully.
default.confの編集
ブラウザでlocalhost:8000にアクセスしてみると、残念ながらNginxの403エラーが表示されます(src/index.htmlを削除したため)。
そこで、Nginxのdefault.confに設定の変更を行います。
1...
2 #access_log /var/log/nginx/host.access.log main;
3
4 location / {
5 root /usr/share/nginx/html/public;
6 index index.html index.htm index.php;
7
8 try_files $uri $uri/ /index.php$is_args$args;
9 }
10
11 ...
12
13 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
14 #
15 location ~ \\.php$ {
16 root /var/www/html/public;
17 fastcgi_pass app:9000;
18 fastcgi_index index.php;
19 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
20 include fastcgi_params;
21 }
22...
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
をインストールしています。
1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer
6
7# install packages
8RUN apk update
9RUN apk add git
10
11# install php extensions
12RUN docker-php-ext-install pdo pdo_mysql
データベースの接続設定とマイグレーション
Laravelの環境変数.env
に、Dockerのデータベースの情報を設定します。
1...
2DB_CONNECTION=mysql
3DB_HOST=db
4DB_PORT=3306
5DB_DATABASE=laravel_db
6DB_USERNAME=root
7DB_PASSWORD=rootpassword
8...
DB_HOST
はDockerのサービス名db
、DB_DATABASE
、DB_USERNAME
、DB_PASSWORD
もDockerで設定した値にします。
1docker-compose exec app php artisan migrate
2...
3Migration table created successfully.
4Migrating: 2014_10_12_000000_create_users_table
5Migrated: 2014_10_12_000000_create_users_table (135.82ms)
6Migrating: 2014_10_12_100000_create_password_resets_table
7Migrated: 2014_10_12_100000_create_password_resets_table (68.97ms)
8Migrating: 2019_08_19_000000_create_failed_jobs_table
9Migrated: 2019_08_19_000000_create_failed_jobs_table (66.53ms)
10Migrating: 2019_12_14_000001_create_personal_access_tokens_table
11Migrated: 2019_12_14_000001_create_personal_access_tokens_table (116.13ms)
Dockerコンテナを再起動して確認してみる
ブラウザで<Command>localhost:8000</Command>にアクセスし、Laravelの初期画面が表示されれば成功です。
ここまででLaravelが動く環境が用意できました。
後編に続く
後編に続きます。
Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【後編:フロントエンド】