前置きが長いので、Docker, Git, GitHubの設定が終わってる人はここから始められます。
【実際にLaravel開発を始める方へ】
本記事はハンズオンということもあり、初歩的な内容かつ最小限の構成になっております。
実際に開発を始める場合はより実用的なDocker構成の記事を書いてますので、こちらの記事をご参考ください。
- 最強のLaravel開発環境をDockerを使って構築する
-
https://github.com/ucan-lab/docker-laravel
- こちらのテンプレートリポジトリはご自由にご利用ください
概要
DockerとDocker Composeを使ってLEMP環境(PHP/nginx/MySQL)を構築し、Laravelの新規プロジェクト作成、構築した環境を破棄してから環境の再構築までをハンズオン形式で行います。
動作環境
Windows(WSL2), Mac(M1 Mac含む), Linux環境で動作するように書いてます。
筆者の実行環境はM1じゃないMacです。
更新履歴
- 2022/02/23 Laravel9がリリースされたので、合わせて記事をリニューアルしました。
- PHP8.1, Composer2.2, Laravel9
- 2020/09/09 Laravel8(当時はLaravel6が出た頃)がリリースされ、こちらの記事も内容が古くなったので全て書き直しました。
- 2021/04/25 記事内容をアップデートしました
- PHP7.4 => 8.0, Composer1.x => 2.0
- MySQLベースイメージの変更(mysql => mysql/mysql-server)
- 2021/04/29 docker-compose を docker compose へ変更
- 2021/06/06 Chrome 91より
10080
ポートへのアクセスがブロックされるため8080
へ変更
目的
Dockerの細かい概念などはすっ飛ばして、
このハンズオンの通りに進めればDocker×Laravelの環境を構築できます。
習うより慣れろで行ってみましょう!
ハンズオン終了時のGitHub
ハンズオンイベント
【ぺちオブ】ゆーきゃんプレゼンツ!Laravel + Docker 環境構築ハンズオン
あの有名なぺちオブのイベントとしてハンズオン講師をやらせていただきました!
ありがとうございます🙏
DockerでLaravelのローカル環境を構築するハンズオン(オンライン)
2021/05/01 第二回のハンズオンイベントを開催します。
【初心者向け】DockerでLaravel9のローカル環境を構築するハンズオン(オンライン)
2022/02/23 第三回のハンズオンイベントを開催します。
レビュワー
- hiroさん @hirodragon
- かひろくん @kahirokunn
- もっぷ先生 @kobayashi-m42
- こはさん @smspp
スペシャルサンクス🙇♂️🙏
前提条件
- Mac基準ですが、Windowsでも動作することを確認済み
- コマンドは
[環境] $
とprefixを付けてます。 - 所要時間は事前準備部分を除いて20分を目安としてます。
- エディタはファイル編集時にお好みのもので開いて作業ください🙏
構築環境、使用技術
- php: 8.1-fpm-buster
- ピーエッチピー、プログラミング言語
- 公式: https://www.php.net
- PHPとは: https://www.php.net/manual/ja/intro-whatis.php
- Dockerイメージ: https://hub.docker.com/_/php
- composer: 2.2
- コンポサー、PHPのパッケージ管理ツール
- 公式: https://getcomposer.org
- Composerとは: https://getcomposer.org/doc/00-intro.md
- Dockerイメージ: https://hub.docker.com/_/composer
- nginx: 1.20-alpine
- エンジンエックス、ウェブサーバー
- 公式: https://www.nginx.com
- nginxとは: https://nginx.org/en
- Dockerイメージ: https://hub.docker.com/_/nginx
- mysql/mysql-server: 8.0
- マイエスキューエル、データベースサーバー
- 公式: https://www.mysql.com/jp
- MySQLとは: https://ja.wikipedia.org/wiki/MySQL
- Dockerイメージ: https://hub.docker.com/r/mysql/mysql-server
- Laravel: 9.x
- ララベル、PHPのフレームワーク
- 公式: https://laravel.com
- Laravelとは: https://readouble.com/laravel/9.x/ja/installation.html#why-laravel
- Laravel本体: https://github.com/laravel/framework
それぞれ、どういった技術なのか抑えておくと理解がしやすいです。
dockerおすすめ書籍
冒頭で習うより慣れろと言いましたが、習っておくのもそれはそれで良いことなのでオススメの書籍や記事をご紹介します。私はこの辺りの記事や本を読んで勉強したのでオススメしてます。
マンガでわかるdockerシリーズ
@llminatoll さんがDockerについてマンガで分かりやすく説明されてます。
初めての方はこちらのマンガでDockerの概念を学んでから進めると理解しやすいです。
dockerおすすめ記事・サイト
公式系
記事
スライド
- Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
- Dockerfileを改善するためのBest Practice 2019年版
- Dockerfile を書くためのベストプラクティス解説編
事前準備
Dockerのインストール確認
Dockerが使える状態になっていればokです。
[mac] $ docker --version
Docker version 20.10.10, build b485636
【Macユーザー向け】
【Windowsユーザー向け】
- https://docs.docker.com/docker-for-windows/install
- 🐳WindowsでDockerを始める手順🐳
- Git Bashって使ってる?Windowsで動く意外にすごい便利ツール
- Git for Windowsのbashが便利な件
Git Bash を起動した際は下記のコマンドを実行ください。
$ exec winpty bash
【Windowsユーザー(WSL2)向け】
GitHubアカウントの作成
Gitの初期設定
user.email
と user.name
が設定されていればok!
Gitの初期設定を行ってない場合、下記の記事を参考に初期設定をお願いします。
最終的に下記のように表示されればokです。
$ git config --list | grep user
user.email=xxx@yyy.com
user.name=zzz
GitHub SSH接続設定
$ ssh -T github.com
Warning: Permanently added 'github.com,52.69.186.44' (RSA) to the list of known hosts.
Hi ucan-lab! You've successfully authenticated, but GitHub does not provide shell access.
successfully authenticated
の文字が出ればok!
Warningは気にしなくてok!
GitHub SSH接続設定を行ってない場合、下記の記事を参考に初期設定をお願いします。
もしくはハンズオンをHTTPSに置き換えて進めてください。
最終的にgit cloneやgit pushができれば良いのでHTTPSでもokです。
Docker起動確認
インストールしたDockerを起動してください。
running
状態になっていればokです。
ここまでで事前準備終了です。
今回のハンズオンのゴール
- 3層アーキテクチャのコンテナの構築
- ウェブサーバー(web)
- nginxで静的コンテンツ配信サーバを構築
- アプリケーションサーバー(app)
- nginxを経由してPHPを動作させるアプリケーションサーバを構築
- PHPパッケージ管理ツールComposerのインストール
- データベースサーバー(db)
- MySQLデータベースサーバーの構築
- ウェブサーバー(web)
- Laravelをインストールしてwelcome画面の表示
- LaravelとMySQLを連携し、マイグレーションを実行
- GitHubにソースコードをアップロード
- Docker環境の破棄
- Docker環境をGitHubから再構築
🐳【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン🐳
- 所要時間: 20分目安(解説読みながらだと1時間半程度)
作業ディレクトリを作成
[mac] $ mkdir docker-laravel-handson
[mac] $ cd docker-laravel-handson
【補足】最終的なディレクトリ構成
.
├── README.md (この名前にするとGitHubで見た時にHTMLに変換して表示してくれる)
├── infra (*1)
│ ├── mysql (*1)
│ │ ├── Dockerfile
│ │ └── my.cnf (*1)
│ ├── nginx (*1)
│ │ └── default.conf (*1)
│ └── php (*1)
│ ├── Dockerfile (この名前にするとファイル名の指定を省略できる)
│ └── php.ini (*1)
├── docker-compose.yml (この名前にするとファイル名の指定を省略できる)
└── src (*1)
└── Laravelをインストールするディレクトリ
このディレクトリ構成を目指します。
(*1)
任意の名前に変更してもokです。
リモートリポジトリを作成
リポジトリ名 docker-laravel-handson
のGitHubリモートリポジトリを作成する。
初回コミット
[mac] $ echo "# docker-laravel-handson" >> README.md
[mac] $ git init
[mac] $ git add README.md
[mac] $ git commit -m "first commit"
[mac] $ git branch -M main
【補足】master → main デフォルトブランチの変更
※ 2020/10/1 からデフォルトブランチがmasterからmainに変更されました。
【補足】git init 時のメッセージ
$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
下記の設定をしておくと次回以降は main
ブランチがデフォルトブランチとなります。
$ git config --global init.defaultBranch main
リモートリポジトリの登録
リモートリポジトリを登録します。
リモートリポジトリ先は適宜置き換えてください。
[mac] $ git remote add origin git@github.com:ucan-lab/docker-laravel-handson.git
リモートリポジトリ先が正しく設定されていることを確認してください。
[mac] $ git remote -v
origin git@github.com:ucan-lab/docker-laravel-handson.git (fetch)
origin git@github.com:ucan-lab/docker-laravel-handson.git (push)
# もしリモートリポジトリ先を間違えた場合は下記のコマンドから変更できます。
[mac] $ git remote set-url origin <リモートリポジトリ>
リモートリポジトリ先の名前を origin
と付けられていること。
リモートリポジトリ先のURL等に間違いがないことを確認してください。
(このままコピペすると私のリポジトリになってしまいます)
リモートリポジトリへプッシュ
GitHubへpushします。
[mac] $ git push -u origin main
-
-u
オプションは--set-upstream
の省略 - ローカルリポジトリの現在のブランチ(main)をリモートリポジトリ(origin)のmainにpush先を設定
- 以降は
git push
だけでgit push origin main
と同義
GitHubのリモートリポジトリのmainブランチへpushされていることを確認します。
【補足】お好みのGUIエディタで開く
ymlファイルをCUIエディタで書いていくのは大変なのでお好みのGUIエディタ(今回はVSCode)で開きます。
code
コマンドがインストールされていれば、簡単にプロジェクトをVSCodeで開けます。
[mac] $ code .
また、 control
+ shift
+ @
で統合ターミナルをVSCodeで開くことができます。
ファイルの編集もコマンドの実行もVSCodeだけで行えます。
アプリケーションサーバ(app)コンテナを作る
PHPアプリケーションサーバコンテナを作成します。
公式のPHP-FPMイメージをベースイメージとしてカスタマイズします。
【補足】ディレクトリ構成
.
├── infra
│ └── php
│ ├── Dockerfile
│ └── php.ini # PHPの設定ファイル
├── src # Laravelをインストールするディレクトリ
└── docker-compose.yml
【補足】【重要】ファイル編集について
touch
コマンドで空ファイルを作成してます。
作成後はお好みのエディタで編集ください。
タイプミス防止のため空ファイルを先に作成してます。
コマンドを実行する場所を間違えないように cd
コマンドは使用しないように注意してください。
当ハンズオンはこの流れで進めていきます。
あれこれ試したいと思いますが、まずはハンズオンが上手くいってから色々試してみてください。
docker-compose.yml を作成する
[mac] $ touch docker-compose.yml
作成した docker-compose.yml
を下記の通りに編集します。
version: "3.9"
services:
app:
build: ./infra/php
volumes:
- ./src:/data
-
docker-compose.yml
ファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。
docker-compose.yml: 設定値の補足
version: "3.9"
Docker Composeファイルのバージョンを指定しています。
通常はメジャー番号とマイナー番号を両方指定します。
ちなみにマイナーバージョンを指定しない場合はデフォルト0
が使用されます。
# 下記は同じ指定になる
version: "3"
version: "3.0"
services:
app: # => サービス名は任意
build: ./infra/php
volumes:
- ./src:/data
サービス名に app
(アプリケーションサーバー)の名前を付けて定義しています。
サービス名は任意に決められます。
build:
で指定しているのはビルドコンテキストを指定します。
ビルドコンテキストとは、docker build
を実行する際の現在の作業ディレクトリのことをビルドコンテキスト(build context)と呼びます。
Dockerfile
が置かれている ./infra/php
ディレクトリをビルドコンテキストとして指定します。
Dockerビルドの際は Dockerfile
のファイルを探すので、ファイル名の指定は不要です。
volumes:
ではホスト側のディレクトリや名前付きボリュームをコンテナ側へマウントしたい時に指定します。
今回はホスト側の ./src
ディレクトリをappサービスのコンテナ内 /data
へマウントしてます。
./infra/php/Dockerfile を作成する
- Composerコマンドのインストール
- Laravelで必要なPHP拡張機能のインストール
- bcmath, pdo_mysql が不足しているので追加インストール
[mac] $ mkdir -p infra/php
[mac] $ touch infra/php/Dockerfile
-
mkdir -p
必要に応じて親ディレクトリも作成してくれるオプションです。
下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
FROM php:8.1-fpm-buster
ENV COMPOSER_ALLOW_SUPERUSER=1 \
COMPOSER_HOME=/composer
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
RUN apt-get update && \
apt-get -y install --no-install-recommends git unzip libzip-dev libicu-dev libonig-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install intl pdo_mysql zip bcmath
COPY ./php.ini /usr/local/etc/php/php.ini
WORKDIR /data
【補足】./infra/php/Dockerfile
Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに含めることができます。
各種Docker命令を解説します。
FROM php:8.1-fpm-buster
FROM命令はイメージビルドのためのベースイメージを設定します。
FROM イメージ名:タグ名
で指定します。
php | Docker Hub公式イメージをベースイメージとして利用します。
ENV COMPOSER_ALLOW_SUPERUSER=1 \
COMPOSER_HOME=/composer
ENV命令はコンテナ内のサーバー環境変数を設定します。
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
RUN apt-get update && \
apt-get -y install --no-install-recommends git unzip libzip-dev libicu-dev libonig-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install intl pdo_mysql zip bcmath
Debian系のパッケージ管理ツールは apt-get
, apt
とありますが、Dockerfile内で apt
を実行するとCLIインターフェース向けではないと警告が表示されるため、apt-get
を使用します。
apt-get update
インストール可能なパッケージの「一覧」を更新します。
実際のパッケージのインストール、アップグレードなどは行いません。
apt-get -y install --no-install-recommends xxx
Laravelのインストールに必要なパッケージをインストールします。
下記のパッケージをインストールしておけばokだと思います。
- https://packages.debian.org/ja/sid/git
- https://packages.debian.org/ja/sid/unzip
- https://packages.debian.org/ja/sid/libzip-dev
- https://packages.debian.org/ja/sid/libicu-dev
- https://packages.debian.org/ja/sid/libonig-dev
-y
オプションを付けることで問い合わせがあった場合はすべて「y」と答えます。
--no-install-recommends
不要なパッケージのインストールを防止します。
apt-get clean && rm -rf /var/lib/apt/lists/*
ここはパッケージインストールで使用したキャッシュファイルを削除しています。
phpの公式Dockerイメージには、docker-php-ext-install
, docker-php-ext-enable
, docker-php-ext-configure
のPHP拡張ライブラリを簡単に利用するための便利コマンドが予め用意されています。
docker-php-ext-install intl pdo_mysql zip bcmath
PHPの拡張ライブラリをインストールしています。
- https://www.php.net/manual/ja/book.intl.php
- https://www.php.net/manual/ja/ref.pdo-mysql.php
- https://www.php.net/manual/ja/book.zip.php
- https://www.php.net/manual/ja/book.bc.php
COPY ./php.ini /usr/local/etc/php/php.ini
WORKDIR /data
./infra/php/php.ini を作成する
php.ini はPHPの設定ファイル
- PHPエラーメッセージの設定
- PHPエラーログの設定
- メモリ等の設定(お好みで)
- タイムゾーン設定
- 文字コード設定
[mac] $ touch infra/php/php.ini
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8
[Date]
date.timezone = Asia/Tokyo
[mysqlnd]
mysqlnd.collect_memory_statistics = on
[Assertion]
zend.assertions = 1
[mbstring]
mbstring.language = Japanese
src ディレクトリを作成
bindマウントするためのsrcディレクトリを作成します。
[mac] $ mkdir src
build & up
- appコンテナの作成
- PHPのバージョン確認
- Laravelで必要なPHP拡張機能の確認
[mac] $ docker compose build
ビルドは、docker-compose.yml
や Dockerfile
を元にDockerイメージを作成します。
作成したDockerイメージからDockerコンテナを生成します。
[mac] $ docker compose up -d
-
docker compose
コマンドはdocker-compose.yml
があるディレクトリで実行します。 -
docker compose up
はdocker-compose.yml
に定義したサービスを起動します。 -
-d
「デタッチド」モードでコンテナを起動します。- デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示
- 「デタッチド」モードではバックグラウンドで動作
[mac] $ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
docker-laravel-handson-app-1 "docker-php-entrypoi…" app running 9000/tcp
docker-laravel-handson_app_1 コンテナの State が Up になっていたら正常に起動している状態です。
-
docker compose ps
コンテナ一覧を表示します。- Name: コンテナ名
- Command: 最後に実行されたコマンド
- State: 状態(Up)はコンテナが起動している状態
- Ports: 9000/tcp(コンテナのポート)
- 9000番のホストポートは公開されていないので、コンテナの外からはアクセスできない
appコンテナ内ミドルウェアのバージョン確認(コンテナに入ってコマンド実行)
作成したappコンテナの中に入ってPHP, Composerのバージョン、インストール済みの拡張機能を確認します。
Laravel 9.xのサーバ要件に必要な拡張機能が入っていることを確認します。
[mac] $ docker compose exec app bash
ユーザー名@コンテナID:ディレクトリ#
root@927707a27bba:/data#
# PHPのバージョン確認
[app] # php -v
PHP 8.1.3 (cli) (built: Feb 18 2022 20:07:43) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.3, Copyright (c) Zend Technologies
# Composerのバージョン確認
[app] # composer -V
Composer version 2.2.6 2022-02-04 17:00:38
# インストール済みの拡張機能の一覧
[app] # php -m
[PHP Modules]
bcmath
Core
ctype
curl
date
dom
fileinfo
filter
ftp
hash
iconv
intl
json
libxml
mbstring
mysqlnd
openssl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
readline
Reflection
session
SimpleXML
sodium
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
zip
zlib
[Zend Modules]
exit
でコンテナの外に出ます。
[app] $ exit
control
+ d
でもコンテナから出られます。
【補足】docker compose exec
appコンテナ内に入ってphpコマンドを実行しています。
-
docker compose exec
実行中のコンテナ内で、コマンドを実行します。 -
app
サービス名(コンテナ名)を指定します。
PHPのバージョン確認(コンテナの外からコマンド実行)
コンテナの外から php
コマンドを実行することもできます。
docker compose exec [サービス名] [実行したいコマンド]
[mac] $ docker compose exec app php -v
PHP 8.1.3 (cli) (built: Feb 18 2022 20:07:43) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.3, Copyright (c) Zend Technologies
結果にコミット
[mac] $ git status
Untracked files:
(use "git add <file>..." to include in what will be committed)
docker-compose.yml
infra/php/Dockerfile
infra/php/php.ini
[mac] $ git add .
[mac] $ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: docker-compose.yml
new file: infra/php/Dockerfile
new file: infra/php/php.ini
[mac] $ git commit -m "feat create docker app container"
[main e1e6eeb] feat create docker app container
3 files changed, 48 insertions(+)
create mode 100644 docker-compose.yml
create mode 100644 infra/php/Dockerfile
create mode 100644 infra/php/php.ini
[mac] $ git log
コンテナを破棄する
docker-compose.yml を変更するので、一度コンテナを破棄しておきます。
[mac] $ docker compose down
ウェブサーバー(web)コンテナを作る
nginxウェブサーバーコンテナを作成します。
nginxのベースイメージをそのまま利用します。
【補足】ディレクトリ構成
.
├── infra
│ └── nginx
│ └── default.conf # nginxの設定ファイル
├── src
│ └── public # 動作確認用に作成
│ ├── index.html # HTML動作確認用
│ └── phpinfo.php # PHP動作確認用
└─── docker-compose.yml
docker-compose.yml へ追記する
- ポート転送の設定(今回は8080ポートにする)
- タイムゾーンの設定
version: "3.9"
services:
app:
build: ./infra/php
volumes:
- ./src:/data
# 追記
web:
image: nginx:1.20-alpine
ports:
- 8080:80
volumes:
- ./src:/data
- ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
working_dir: /data
-
docker-compose.yml
ファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。 -
app
コンテナの設定と同じインデントレベルで貼り付けます。
【補足】docker-compose.yml
image: nginx:1.20-alpine
コンテナを起動させるイメージを指定します。
nginx | Docker Hubを指定してます。
今回は公式のnginxイメージをそのまま利用しています。(Dockerfileは不要)
ちなみにnginxは1.10
, 1.12
等の偶数のバージョンが安定バージョンになります。
特に理由がなければ偶数バージョンをご利用ください。
ports:
- 8080:80
nginxへ外(ホスト側)からコンテナ内へアクセスさせるため公開用のポートを設定します。
ホスト側:コンテナ側
と設定します。
volumes:
- ./src:/data
- ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
ホスト側にあるディレクトリ、ファイルをコンテナ内へマウントさせています。
infra/nginx/default.conf を作成する
[mac] $ mkdir infra/nginx
[mac] $ touch infra/nginx/default.conf
Laravel公式にnginxの設定例が用意されているので、こちらを流用します。
https://readouble.com/laravel/8.x/ja/deployment.html
server {
listen 80;
server_name example.com;
root /data/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
root
, fastcgi_pass
ドキュメントルート設定を書き換えてます。
nginxの設定を詳しく知りたい方は下記の記事がオススメです。
build & up
[mac] $ docker compose up -d
-
docker-compose
コマンドはdocker-compose.yml
があるディレクトリで実行します。
[mac] $ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
docker-laravel-handson-app-1 "docker-php-entrypoi…" app running 9000/tcp
docker-laravel-handson-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8080->80/tcp
docker-laravel-handson_web_1 コンテナの State が Up になっていたら正常に起動している状態です。
また、Ports
の項目が、appコンテナは9000/tcp
でwebコンテナは 0.0.0.0:8080->80/tcp
と表示形式が異なってます。
これはホスト上の8080番ポートをコンテナの80番ポートへ割り当てています。
nginxのバージョン確認
[mac] $ docker compose exec web nginx -v
nginx version: nginx/1.20.2
webコンテナの確認
- webコンテナの動作確認
- HTMLとPHPが表示されるか
[mac] $ mkdir src/public
[mac] $ echo "Hello World" > src/public/index.html
[mac] $ echo "<?php phpinfo();" > src/public/phpinfo.php
「Hello World」が表示されることを確認する。
webサーバーが正しく動作することを確認できました。
phpinfoの情報が表示されることを確認する。
webサーバーがappサーバーへphpを実行させ結果を返してくれることを確認できました。
[mac] $ rm -rf src/*
確認用に作成したHTML, PHPファイルは不要なので削除します。
結果にコミット
[mac] $ git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: docker-compose.yml
Untracked files:
(use "git add <file>..." to include in what will be committed)
infra/nginx/default.conf
[mac] $ git add .
[mac] $ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: docker-compose.yml
new file: infra/nginx/default.conf
[mac] $ git commit -m "feat create docker web container"
[main 1e26387] feat create docker web container
2 files changed, 41 insertions(+)
create mode 100644 infra/nginx/default.conf
[mac] $ git log
Laravelをインストールする
- app コンテナに入り、Laravelをインストール
- welcomeページが表示されるか
[mac] $ docker compose exec app bash
[app] $ composer create-project --prefer-dist "laravel/laravel=9.*" .
[app] $ chmod -R 777 storage bootstrap/cache
[app] $ php artisan -V
Laravel Framework 9.1.0
[app] $ exit
-
composer create-project --prefer-dist
-
--prefer-dist
zipでダウンロードするため高速 - composer の–prefer-distってよく使うけど何してる?
-
-
composer create-project
コマンドでメモリ足りない問題の対策 -
artisan
について- Laravelが用意しているコマンドラインインターフェイス
- https://readouble.com/laravel/9.x/ja/artisan.html
-
php artisan list
使用可能なコマンド一覧を表示
Laravel ウェルカム画面の表示
LaravelのWelcome画面が表示されることを確認する。ライトモードとダークモードで色合いが変わります。
結果にコミット
[mac] $ git status
[mac] $ git add .
[mac] $ git status
[mac] $ git commit -m "feat laravel install"
[mac] $ git log
今回は大量のファイルがインストールで生成されているので、差分は省略してます。
コンテナを破棄する
docker-compose.yml を変更するので、一度コンテナを破棄しておきます。
[mac] $ docker compose down
データベース(db)コンテナを作る
MySQLデータベースコンテナを作成します。
【補足】mysql/mysql-serverイメージ
Docker公式がメンテしている mysql
イメージと OracleのMySQLチームがメンテしている mysql/mysql-server
の2種類あります。
オススメは mysql/mysql-server
の方です。
mysql
の方だとM1 Macが動作しないといった報告があります。
【補足】ディレクトリ構成
.
├── infra
│ └── mysql
│ ├── Dockerfile
│ └── my.cnf # MySQLの設定ファイル
└── docker-compose.yml
docker-compose.yml へ追記する
- データベース名やユーザー名等の接続情報とタイムゾーンの設定は環境変数で渡す
- トップレベルvolumeを使用してデータの永続化
version: "3.9"
services:
app:
build: ./infra/php
volumes:
- ./src:/data
web:
image: nginx:1.20-alpine
ports:
- 8080:80
volumes:
- ./src:/data
- ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
working_dir: /data
# 追記
db:
build: ./infra/mysql
volumes:
- db-store:/var/lib/mysql
volumes:
db-store:
-
docker-compose.yml
ファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。 -
web
コンテナの設定と同じインデントレベルでdb
コンテナの設定を貼り付けます。 -
volumes
はトップレベル(servicesと同じレベル)に貼り付けます。 - DockerのMySQLイメージ起動時に渡す環境変数
- Docker for Macのmount遅い問題まとめ
- Performance tuning for volume mounts (shared filesystems)
./infra/mysql/Dockerfile を作成する
[mac] $ mkdir infra/mysql
[mac] $ touch infra/mysql/Dockerfile
下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
FROM mysql/mysql-server:8.0
ENV MYSQL_DATABASE=laravel \
MYSQL_USER=phper \
MYSQL_PASSWORD=secret \
MYSQL_ROOT_PASSWORD=secret \
TZ=Asia/Tokyo
COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf
コメントでいただいたのですが、Windows環境でボリュームマウントを行うと、ファイルパーミッションが777となるようです。
my.cnf
に書き込み権限が付いてるとMySQLの起動時にエラーが発生します。
その対策としてボリュームマウントではなくDockerfileを作成して my.cnf
ファイルコピー、読み取り専用に権限変更してます。
infra/mysql/my.cnf を作成する
細かい解説はしませんが、主に下記の設定をしています。
ハンズオンでただ動かす程度ですから設定ファイル自体必要ないと思います。
- 文字コード、照合順序の設定
- タイムゾーンの設定
- ログ設定
[mac] $ touch infra/mysql/my.cnf
[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
secure-file-priv = /var/lib/mysql-files
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks
# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM
# Error Log
log-error = mysql-error.log
# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0
# General Log
general_log = 1
general_log_file = mysql-general.log
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
default
コメント部分はコンテナに元々入っていた設定を踏襲しています。
文字コードと照合順序は、 utf8mb4
, utf8mb4_ja_0900_as_cs_ks
を選択するのが現状良いとされています。
タイムゾーンでSYSTEMを設定するとOSのタイムゾーンが設定されます。
build & up
[mac] $ docker compose build
[mac] $ docker compose up -d
-
docker compose
コマンドはdocker-compose.yml
があるディレクトリで実行します。
[mac] $ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
docker-laravel-handson-app-1 "docker-php-entrypoi…" app running 9000/tcp
docker-laravel-handson-db-1 "/entrypoint.sh mysq…" db running (starting) 33060-33061/tcp
docker-laravel-handson-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8080->80/tcp
docker-laravel-handson_db_1 コンテナの State が Up になっていたら正常に起動している状態です。
[mac] $ docker compose exec db mysql -V
mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)
マイグレーション実行(エラーが発生します)
[mac] $ docker compose exec app bash
[app] $ php artisan migrate
Illuminate\Database\QueryException
SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = laravel and table_name = migrations and table_type = 'BASE TABLE')
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:712
708▕ // If an exception occurs when attempting to run a query, we'll format the error
709▕ // message to include the bindings with SQL, which will make this exception a
710▕ // lot more helpful to the developer instead of just the database's errors.
711▕ catch (Exception $e) {
➜ 712▕ throw new QueryException(
713▕ $query, $this->prepareBindings($bindings), $e
714▕ );
715▕ }
716▕ }
+36 vendor frames
37 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
[app] $ exit
SQLSTATE[HY000] [2002] Connection refused
このエラーはよく見るMySQLのエラーです。
MySQLに接続拒否されたエラーなので、この場合は大体MySQLへの接続設定に誤りがあります。
src/.env
のDB接続設定を修正する。
[mac] $ code src/.env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=phper
DB_PASSWORD=secret
src/.env.example
も同様にDB接続設定を修正しておきます。
[mac] $ vim src/.env.example
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=phper
DB_PASSWORD=secret
【補足】DB_HOST=db
php artisan migrate
は app
サービスのコンテナが php
を実行しています。
app
から db
サービスのコンテナへ通信できることが必要です。
DB_HOST=127.0.0.1
これだと app コンテナ内のデータベースに接続しようと試みます。
appコンテナ内にデータベースはないので、もちろんエラーになります。
$ docker network list
NETWORK ID NAME DRIVER SCOPE
be6ba90aeade bridge bridge local
98a3fc98010d docker-laravel-handson_default bridge local
be953fa99c81 host host local
a8e65b8280d4 none null local
docker-laravel-handson_default
のデフォルトネットワークが構築されてます。
docker network inspect
コマンドでネットワークの詳細を見れます。
$ docker network inspect docker-laravel-handson_default
[
{
"Name": "docker-laravel-handson_default",
"Id": "98a3fc98010ddb61798d623c544d9a2a79762e85f94fc7c1660f2a76c83da8ed",
"Created": "2022-02-21T16:02:09.872079294Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"1be61fbec051bd6f995a78ed3fe39b088a864f5eea01448e4fada84896a8cfee": {
"Name": "docker-laravel-handson-app-1",
"EndpointID": "1a920f7a384c0b53578c2b1f66cc80501d4879e0d9edac0e97dbbdb5c8b964bf",
"MacAddress": "02:42:ac:15:00:03",
"IPv4Address": "172.21.0.3/16",
"IPv6Address": ""
},
"2485795092e86232ea8cbe91373f7cf71c568f25382fcde6a0e9252dd5b0a95b": {
"Name": "docker-laravel-handson-db-1",
"EndpointID": "4073d7ee440ba55e167e319e85ec05e9681a100e2505abd2debbfab09144daee",
"MacAddress": "02:42:ac:15:00:02",
"IPv4Address": "172.21.0.2/16",
"IPv6Address": ""
},
"e1fa6a28aff5b6de5045e88e116e8ef1f978e0b7327b977de3771a4100b41c75": {
"Name": "docker-laravel-handson-web-1",
"EndpointID": "ff568293928f6fbf7c017548c3aff80bd91108a023dfc291d4eaf035c08dc44e",
"MacAddress": "02:42:ac:15:00:04",
"IPv4Address": "172.21.0.4/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "docker-laravel-handson",
"com.docker.compose.version": "2.1.1"
}
}
]
Containers
を見ると app
, web
, db
コンテナがデフォルトネットワークに所属していることが分かります。
コンテナはネットワークを共有している場合のみ相互に通信できます。
各コンテナはホスト名を検索し、app
, web
, db
のサービス名から適切なコンテナのIPアドレスを取得できます。
DB_HOST=db
まとめるとLaravelのartisanコマンドはappのコンテナで実行され、dbコンテナのIPを解決してdbコンテナと通信できます。
- https://docs.docker.com/compose/networking
- https://docs.docker.com/compose/compose-file/compose-file-v3/#network-configuration-reference
- https://knowledge.sakura.ad.jp/23899
マイグレーション実行(再実行)
[mac] $ docker compose exec app bash
[app] $ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (58.09ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (42.39ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (70.60ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (62.23ms)
新しいターミナルを開いて、下記のコマンドを実行します。
[mac] $ docker compose exec db bash
[db] $ mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE
[mysql] > show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| laravel |
+--------------------+
2 rows in set (0.00 sec)
[mysql] > show tables;
+------------------------+
| Tables_in_laravel |
+------------------------+
| failed_jobs |
| migrations |
| password_resets |
| personal_access_tokens |
| users |
+------------------------+
5 rows in set (0.00 sec)
[mysql] > desc users;
+-------------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | UNI | NULL | |
| email_verified_at | timestamp | YES | | NULL | |
| password | varchar(255) | NO | | NULL | |
| remember_token | varchar(100) | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+-------------------+-----------------+------+-----+---------+----------------+
8 rows in set (0.01 sec)
[mysql] > SELECT * FROM users;
Empty set (0.01 sec)
MySQLを開いているターミナルは閉じずに残しておきましょう。
試しにデータを作ってみる
[mac] $ docker compose exec app bash
[app] $ php artisan tinker
# 下記のコードはコピペして実行できます。
$user = new App\Models\User();
$user->name = 'phper';
$user->email = 'phper@example.com';
$user->password = Hash::make('secret');
$user->save();
MySQLを開いているターミナルに戻って下記のSQLを実行します。
[mysql] > SELECT * FROM users\G
*************************** 1. row ***************************
id: 1
name: phper
email: phper@example.com
email_verified_at: NULL
password: $2y$10$/es6/J88KaxbbwKElD4qveMz/W5HdEb2T.r8loJdkLsqlGvHakUp6
remember_token: NULL
created_at: 2022-02-21 16:10:17
updated_at: 2022-02-21 16:10:17
1 row in set (0.00 sec)
結果にコミット
[mac] $ git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: docker-compose.yml
modified: src/.env.example
Untracked files:
(use "git add <file>..." to include in what will be committed)
infra/mysql/Dockerfile
infra/mysql/my.cnf
[mac] $ git add .
[mac] $ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: docker-compose.yml
new file: infra/mysql/Dockerfile
new file: infra/mysql/my.cnf
modified: src/.env.example
[mac] $ git commit -m "feat create docker db container"
[mac] $ git log
GitHubにpush
[mac] $ git push
リモートリポジトリへpushされていることを確認します。
Docker環境の再構築
Docker環境の破棄
コンテナの停止、ネットワーク・名前付きボリューム・コンテナイメージ、未定義コンテナを削除
[mac] $ docker compose down --rmi all --volumes --remove-orphans
作業ディレクトリの削除
プロジェクトを削除するので、GUIエディタは閉じておきましょう。
[mac] $ cd ..
[mac] $ rm -rf docker-laravel-handson
Visual Studio Code等のGUIエディタで開いている場合は、一度エディタを終了しましょう。
環境の再構築
GitHubからリポジトリをクローン
** 自身のリポジトリ先に適宜変更してください **
[mac] $ git clone git@github.com:ucan-lab/docker-laravel-handson.git
[mac] $ cd docker-laravel-handson
[mac] $ docker compose up -d
/data/public/../vendor/autoload.php
を開くのに失敗してエラーになっていることを確認します。
git cloneが終わった状態では app
コンテナ内に /data/vendor
ディレクトリが存在しないためです。
Laravelインストール
app コンテナに入ります。
[mac] $ docker compose exec app bash
書き込み権限がないとキャッシュやログにエラーを書き込めないので、権限を付与しておきます。
[app] $ chmod -R 777 storage bootstrap/cache
vendor
ディレクトリへライブラリ群をインストールします。
composer.lock
ファイルを参照します。
[app] $ composer install
画面を開いて確認します。
500 SERVER ERROR だと何が原因なのか分かりません。
ログファイルを見てエラーを確認します。
[app] $ cat storage/logs/laravel.log
...
[2022-02-15 15:07:29] production.ERROR: No application encryption key has been specified. {"exception":"[object] (Illuminate\\Encryption\\MissingAppKeyException(code: 0): No application encryption key has been specified. at /data/vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php:79)
[stacktrace]
#0 /data/vendor/laravel/framework/src/Illuminate/Support/helpers.php(302): Illuminate\\Encryption\\EncryptionServiceProvider->Illuminate\\Encryption\\{closure}(NULL)
#1 /data/vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php(81): tap(NULL, Object(Closure))
#2 /data/vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php(60): Illuminate\\Encryption\\EncryptionServiceProvider->key(Array)
#3 /data/vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php(32): Illuminate\\Encryption\\EncryptionServiceProvider->parseKey(Array)
#4 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(873): Illuminate\\Encryption\\EncryptionServiceProvider->Illuminate\\Encryption\\{closure}(Object(Illuminate\\Foundation\\Application), Array)
#5 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(758): Illuminate\\Container\\Container->build(Object(Closure))
#6 /data/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(855): Illuminate\\Container\\Container->resolve('encrypter', Array, true)
#7 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(694): Illuminate\\Foundation\\Application->resolve('encrypter', Array)
#8 /data/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(840): Illuminate\\Container\\Container->make('encrypter', Array)
#9 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(1027): Illuminate\\Foundation\\Application->make('encrypter')
#10 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(947): Illuminate\\Container\\Container->resolveClass(Object(ReflectionParameter))
#11 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(908): Illuminate\\Container\\Container->resolveDependencies(Array)
#12 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(758): Illuminate\\Container\\Container->build('App\\\\Http\\\\Middle...')
#13 /data/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(855): Illuminate\\Container\\Container->resolve('App\\\\Http\\\\Middle...', Array, true)
#14 /data/vendor/laravel/framework/src/Illuminate/Container/Container.php(694): Illuminate\\Foundation\\Application->resolve('App\\\\Http\\\\Middle...', Array)
#15 /data/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(840): Illuminate\\Container\\Container->make('App\\\\Http\\\\Middle...', Array)
#16 /data/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(206): Illuminate\\Foundation\\Application->make('App\\\\Http\\\\Middle...')
#17 /data/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(180): Illuminate\\Foundation\\Http\\Kernel->terminateMiddleware(Object(Illuminate\\Http\\Request), Object(Illuminate\\Http\\Response))
#18 /data/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->terminate(Object(Illuminate\\Http\\Request), Object(Illuminate\\Http\\Response))
#19 {main}
"}
例外が発生した時のログは下記のフォーマットで出力されます。
[YYYY-MM-DD HH:II:SS] 環境.ログレベル: ログメッセージ
トレースログ
トレースログは例外が発生した場所から呼び出し元の場所を遡って表示してくれます。
一番大事なのは最初のログメッセージです。
1[2022-02-15 15:07:29] production.ERROR: No application encryption key has been specified.
「アプリケーション暗号化キーが指定されていません。」というエラーになります。
.env
の APP_KEY
に値が設定されてないと表示されるエラーです。
[app] $ ls -l
total 328
drwxr-xr-x 25 root root 800 Feb 15 15:05 .
drwxr-xr-x 1 root root 4096 Feb 15 15:05 ..
-rw-r--r-- 1 root root 258 Feb 15 15:03 .editorconfig
-rw-r--r-- 1 root root 897 Feb 15 15:03 .env.example
-rw-r--r-- 1 root root 152 Feb 15 15:03 .gitattributes
-rw-r--r-- 1 root root 207 Feb 15 15:03 .gitignore
-rw-r--r-- 1 root root 175 Feb 15 15:03 .styleci.yml
-rw-r--r-- 1 root root 3958 Feb 15 15:03 README.md
drwxr-xr-x 7 root root 224 Feb 15 15:03 app
-rwxr-xr-x 1 root root 1686 Feb 15 15:03 artisan
drwxr-xr-x 4 root root 128 Feb 15 15:03 bootstrap
-rw-r--r-- 1 root root 1746 Feb 15 15:03 composer.json
-rw-r--r-- 1 root root 285701 Feb 15 15:03 composer.lock
drwxr-xr-x 17 www-data www-data 544 Feb 15 15:03 config
drwxr-xr-x 6 root root 192 Feb 15 15:03 database
drwxr-xr-x 4 www-data www-data 128 Feb 15 15:03 lang
-rw-r--r-- 1 root root 473 Feb 15 15:03 package.json
-rw-r--r-- 1 root root 1175 Feb 15 15:03 phpunit.xml
drwxr-xr-x 6 101 ssh 192 Feb 15 15:03 public
drwxr-xr-x 5 root root 160 Feb 15 15:03 resources
drwxr-xr-x 6 root root 192 Feb 15 15:03 routes
drwxr-xr-x 5 root root 160 Feb 15 15:03 storage
drwxr-xr-x 6 root root 192 Feb 15 15:03 tests
drwxr-xr-x 44 root root 1408 Feb 15 15:06 vendor
-rw-r--r-- 1 root root 559 Feb 15 15:03 webpack.mix.js
.env.example
はありますが、 .env
ファイルが存在しません。
理由は .env
はGit管理対象外に設定されているからです。
$ cat .gitignore
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
/.idea
/.vscode
そもそも .env
ファイルがないのが問題なので、.env.example
を元にコピーして作成します。
(composer create-project
時は .env
は作成されますが、 composer install
時は .env
ファイルは作成されません。)
[app] $ cp .env.example .env
エラー画面の見た目が変わりました。
これは .env
で APP_DEBUG=true
が設定されているためです。
エラーメッセージはログと同じく .env
に APP_KEY=
の値がないと出ています。
アプリケーションキーはこのコマンドで生成できます。
[app] $ php artisan key:generate
Application key set successfully.
とりあえず、Welcome画面が表示されました。
続いて、 public/storage
から storage/app/public
へのシンボリックリンクを張ります。
システムで生成したファイル等をブラウザからアクセスできるよう公開するためにシンボリックリンクを張ってます。
[app] $ php artisan storage:link
最後にマイグレーションを実行して適用されればOKです。
[app] $ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (49.19ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (84.15ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (39.74ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (54.08ms)
お疲れ様でした🙌🙌 コンテナを停止して終了してください。
[app] $ exit
[mac] $ docker compose down
オマケ
GitHub Actions を使ってビルドテストを書く
$ git switch -c laravel-testing
プリリクエストを作りたいので、ブランチを切ります。
$ mkdir -p .github/workflows
$ touch .github/workflows/laravel-testing.yml
GitHub Actions のワークフロー設定ファイルを追加します。
name: Laravel Testing
on:
pull_request:
jobs:
laravel-testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Docker Version
run: docker version
- name: Build Docker Images
run: docker-compose build
- name: Create & Start Docker Containers
run: docker-compose up -d
- name: OS Version
run: |
docker-compose exec -T app cat /etc/os-release
docker-compose exec -T app cat /etc/debian_version
- name: PHP Version
run: docker-compose exec -T app php --version
- name: Composer Version
run: docker-compose exec -T app composer --version
- name: Install Dependencies
run: docker-compose exec -T app composer install
- name: Laravel Version
run: docker-compose exec -T app php artisan --version
- name: Laravel Setting
run: |
docker-compose exec -T app cp .env.example .env
docker-compose exec -T app php artisan key:generate
- name: Laravel Migrate Testing
run: docker-compose exec -T app php artisan migrate
- name: Laravel Rollback Testing
run: docker-compose exec -T app php artisan migrate:refresh
- name: Laravel Seeding Testing
run: docker-compose exec -T app php artisan db:seed
- name: Laravel PHPUnit Testing
run: docker-compose exec -T app php artisan test
$ git add .
$ git commit -m "feat laravel testing on github actions"
$ git push -u origin HEAD
...
remote: https://github.com/ucan-lab/docker-laravel-handson/pull/new/laravel-testing
ターミナルに表示されるURLを開いてプルリクエスト作成します。
Detailsからワークフローの詳細ログを確認できます。
【補足】GitHub Actionsの無料枠
Public Repositoryの場合はActionsを無料で使用できます。
Privateの場合は無料枠を超えると追加で課金が必要なので注意してください。
【補足】Laravelのログをコンテナログに表示する
src/.env
を修正する。
LOG_CHANNEL=stderr
src/routes/web.php
Route::get('/', function () {
logger('welcome route.');
return view('welcome');
});
$ docker compose logs
# -f でログウォッチ
$ docker compose logs -f
# サービス名を指定してログを表示
$ docker compose logs -f app
【補足】MySQLクライアントツールで接続したい
docker-compose.yml
の db
サービスに下記設定を追記して、コンテナを再起動して設定を反映してください。
ports:
- 33060:3306
dbコンテナへのポート公開設定がないとホストからアクセスが行えません。
MySQLのデフォルトポートは3306
ですが、ポートが被らないように設定例では33060
ポートを公開してます。
MySQLクライアントツールは「Sequel Ace」がオススメです。
Host: localhost
Username: phper
Password: secret
Database: laravel
Port: 33060
【補足】Windowsエラーメモ
Error response from daemon
$ docker compose up -d --build
docker: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).
解決策をいくつか
- Docker For Windowsを再起動する
- Dockerでpullやrunを行ったときイメージダウンロードができなくなった問題の解決法
- Proxy環境下のDockerトラブルシューティング
- さわって理解する Docker 入門
【補足】マイグレーションエラーの補足
Host '172.27.0.2' is not allowed to connect to this MySQL server
$ php artisan migrate
Illuminate\Database\QueryException : SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations and table_type = 'BASE TABLE')
at /work/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664
660| // If an exception occurs when attempting to run a query, we'll format the error
661| // message to include the bindings with SQL, which will make this exception a
662| // lot more helpful to the developer instead of just the database's errors.
663| catch (Exception $e) {
> 664| throw new QueryException(
665| $query, $this->prepareBindings($bindings), $e
666| );
667| }
668|
Exception trace:
1 PDOException::("SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server")
/work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70
2 PDO::__construct("mysql:host=db;port=3306;dbname=homestead", "homestead", "secret", [])
/work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70
Please use the argument -v to see more details.
このエラーが発生した場合は my.cnf
を作成する前に docker compose up -d
でビルドしてしまった可能性が高いです。
$ docker compose down --volumes --rmi all
$ docker compose up -d --build
設定ファイルがない状態でMySQLの初期化が行われたでデータが永続化されてしまってるので一度ボリューム毎削除してビルドし直せばokです。
豆知識
【補足】Makefileを使おう
install:
@make clean
@make build
@make up
docker compose exec app composer install
docker compose exec app cp .env.example .env
docker compose exec app php artisan key:generate
docker compose exec app php artisan storage:link
docker compose exec app chmod -R 777 storage bootstrap/cache
@make fresh
clean:
docker compose down --rmi all --volumes --remove-orphans
build:
docker compose build --no-cache --force-rm
up:
docker compose up -d
down:
docker compose down
fresh:
docker compose exec app php artisan migrate:fresh --seed
app:
docker compose exec app bash
sql:
docker compose exec db bash -c 'mysql -u $$MYSQL_USER -p$$MYSQL_PASSWORD $$MYSQL_DATABASE'
- コマンドの前の空白はスペースではなく、タブで書く必要があります。
-
@make
を使うとエコーバックされなくなります。 - MakefileはC/C++のコンパイル定義に使われます。
- エイリアス的に使うのは正しい使い方ではないです。
$ make install
とコマンドを打つとこれ一発で環境構築をやってくれます。
README.mdにコマンドの手順を書いてもいいですが、Makefileに書いてあった方が楽かなと思ってこっちを採用してます。
使用頻度の高いコマンドを定義しておくと良いです。
【補足】docker compose と docker-compose コマンドの違い
2021/03/10 Compose CLIが技術プレビュー版として利用可能になりました。(Docker Desktop for Mac, for Windows 3.2.1以降で利用可能です)
技術プレビュー版では本番環境で用いることは推奨されていませんが、今度は docker compose
コマンドに移行になります。(docker-compose
のほぼすべてのコマンドがリプレース済みです。)
- https://github.com/docker/compose-cli
- https://docs.docker.com/compose/cli-command
- https://docs.docker.com/compose/cli-command-compatibility
- https://matsuand.github.io/docs.docker.jp.onthefly/compose/cli-command/
- https://docs.docker.com/docker-for-mac/release-notes/#docker-desktop-321
【補足】depends_onの使い所
他の解説記事で登場する depends_on のご紹介です。
depends_on
はサービス間の依存関係を表現します。
簡単な使い方の例をご紹介します。
version: "3.9"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
-
docker compose up
依存関係の順序でサービスを開始します。- この例では、
db
とredis
のサービス開始後にweb
がサービス開始します。
- この例では、
-
docker compose stop
依存関係の順序でサービスを停止します。- この例では、
web
のサービス停止後にdb
,redis
がサービス停止します。
- この例では、
-
注意として、サービスの起動、停止順序を制御するだけで稼働順を制御するわけではないです。
- 起動: 動き始めること(例: PC の電源ボタンを押すこと)
- 稼働: 働きはじめること(例: PC が利用可能になること)
-
https://docs.docker.com/compose/compose-file/compose-file-v3/#depends_on
-
https://kotaroooo0-dev.hatenablog.com/entry/2020/07/25/000000
管理するサービスが増えて来た時に、どのサービスがどのサービスに依存してるのか明示して分かりやすくするために導入するのが良いと思います。
ハンズオンで取り入れると説明がめんどくさいので省略しています。(depends_on は実務でも特に使用してません)
【補足】linksは非推奨
links
は コンテナ間の通信をするための設定です。
また、 depends_on
と同じくサービスの起動順序を決定します。
links
は古い記述方法で現在は非推奨となっています。
web:
links:
- "db"
- "db:database"
- "redis"
【補足】Composer公式のインストール方法は非推奨
Composer公式のインストール方法をそのまま利用すると下記のようになります。
FROM php:8.1-fpm-buster
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
問題となるのが4行目のハッシュ値チェックの箇所です。
常に最新版を取得するので、インストーラが更新されるたびにハッシュ値の修正が必要になります。
常に最新版を取得したい時は下記の指定でokです。
COPY --from=composer /usr/bin/composer /usr/bin/composer
それでも勝手にバージョンが上がると困るので下記のようにマイナーバージョンまで指定してバージョン止めするのがベターです。
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
【補足】ADD命令の使い所
COPY
命令と似た命令で ADD
命令があります。
ADD
命令の特徴として、下記の内容があります。
- リモートからもファイル追加ができる。
- 圧縮ファイルが自動解凍される。
https://docs.docker.com/engine/reference/builder/#add
https://docs.docker.com/engine/reference/builder/#copy
COPY
に比べて ADD
は複雑な挙動になっています。
利用者が知らずに使うとセキュリティリスクが生じてしまいます。
ADD
命令を使う場合は注意して利用してください。
【補足】MySQL8.0の認証方式
MySQL8.0.4以降はデフォルトのログイン認証方式が mysql_native_password
から caching_sha2_password
に変更されています。
PHP 7.1.16, 7.2.4 以降は caching_sha2_password
に対応しています。
- https://www.php.net/manual/ja/ref.pdo-mysql.php
- https://qiita.com/ucan-lab/items/3ae911b7e13287a5b917
古い記事だと mysql_native_password
の認証方式に戻す手順の名残があるので注意してください。
【補足】Sequel ProでMySQL8.0に接続できない
Sequel ProはMySQL8.0に対応してません。
-
https://github.com/sequelpro/sequelpro/issues/2699
- 5年以上Issueが残っています。
Sequel Aceという後継アプリが登場しているのでこちらを使いましょう。
【補足】コンテナOSのalpineは上級者向け
DockerHub公式のphpイメージのコンテナOSとしては buster, stretch, alpine の3種類が用意されています。
各Dockerイメージの容量は下記の通りでした。
- buster (Debian 10) (405MB)
- stretch (Debian 9) (493MB)
- alpine (Alpine Linux) (83.5MB)
容量が小さいとデプロイやテストが高速化したりメリットはあります。
しかし、タイムゾーンを設定するだけでも一手間かかったり保守性が著しく下がります。
メリットよりデメリットの方が大きいので、よほどの理由がない限りコンテナOSはDebianもしくはUbuntuを利用しましょう。
【補足】container_nameは指定しない
コンテナ名ですが、重複したコンテナ名を複数立ち上げることができません。
下記のように container_name
を明示的に指定して重複を避ける方法があります。
version: "3.9"
services:
app: # ここはサービス名(コンテナ名ではない)
image: php:8.1-fpm-buster
container_name: myapp
container_name
は被らない名前を付ける必要があるので、管理するコンテナが増えるほど名付けがめんどくさくなります。
そもそも、Docker Composeで立ち上げたコンテナは、 COMPOSE_PROJECT_NAME 変数 + _ + サービス名 + _ + 連番
とコンテナ名が自動的に命名されます。
COMPOSE_PROJECT_NAME
に入るデフォルト値は docker-compose.yml
があるディレクトリ名になります。
そのため他の階層で同じディレクトリ名でDocker Composeを使って立ち上げない限り被ることはありません。
その場合も .env
ファイルで COMPOSE_PROJECT_NAME
の環境変数を設定してあげればokです。
【補足】Node.js のコンテナは作らない
本番デプロイ用にNode.js用のコンテナを用意するのはアリだと思います。
しかし、ローカル環境ではディスクアクセスが多すぎるため開発効率が著しく下がります。
現時点ではローカル用のためにNode.jsコンテナは用意しない方が良いと思われます。
各PCにNode.jsをインストールしてもらって、バージョン固定化するためには .node-version
や packages.json
に engines
を指定して対策しましょう。
【補足】RUN命令はワンライナーで実行する
Dockerfileで命令を実行するたびにイメージレイヤーが作成されます。
命令が増えれば増えるほどイメージレイヤーも増えていき、最終的なDockerイメージの容量が肥大化します。
これを防ぐために &&
でコマンドを繋げてワンライナーで実行します。
FROM php:8.1-fpm-buster
RUN apt-get update && \
apt-get -y install --no-install-recommends git unzip libzip-dev libicu-dev libonig-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
また、下記のように中間ファイルやキャッシュファイルを削除するRUN命令を書いてもイメージレイヤーが増えるのでむしろ容量は増えてしまいます。
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
【補足】Dockerfileは変更頻度が低い順に書く
Dockerfileの上から順に命令を実行し、イメージレイヤーを生成します。
イメージビルド時にイメージレイヤーのキャッシュがあれば、新しくイメージレイヤーを使うのではなく再利用します。
更新頻度の高い設定ファイル系などはなるべく後の方に記述します。
【補足】実務で使う場合はさらなる考慮が必要
- 設定値はイメージに含めず、環境変数で実行時に外から注入
- データベースはコンテナを使わない(Amazon RDS等)
- ログファイルは書かずに、標準出力(Amazon CloudWatch等)
- Dockerイメージをコンテナレジストリに登録(Amazon ECR等)
- コンテナの実行場所、コンテナ管理(Amazon ECS等)
- サーバー環境変数の管理(Amazon Secrets Manage等)