- 投稿日:2022-01-31T19:32:03+09:00
PHPファウンデーションの最新情報 2022年1月
先日PHPファウンデーションが発足しましたが、その後どうなったでしょうか。 以下は、2021年1月の経過報告、The PHP Foundation Update, January 2022の日本語訳です。 著者のRoman Pronskiy( Twitter / GitHub / Webサイト )はJetBrainsのえらい人です。 The PHP Foundation Update, January 2022 <?php echo 'Hello world!'; 2021年11月に、PHPファウンデーションが正式に発足し、幸いなことにコミュニティから多大な支持を得ることができました。 この活動を支えてくださっている全ての方に感謝します。 我々は毎月活動を報告していく予定ですが、今回がその最初の記事となります。 The first round of applications is now closed スポンサード開発者枠の一次募集を締め切りました。 募集開始以降、多くの開発者から寄付の申し込みがありましたが、中にはぜひともスポンサード開発者になりたいという者も多数ありました。 現在、応募者の方々と詳細を検討していますが、正しく運用するためには適切な契約書類が必要になります。 JetBrainsの法務部とOpenCollectiveのスタッフが、必要な書類作成を手伝ってくれています。 1月中には準備を完了し、書類にサインを終える予定です。 その後、第一次スポンサード開発者のリストを発表します。 How we're making decisions いままでにミーティングを3回実施し、喫緊の課題について議論しました。 各会議と意思決定プロセスは全てGitHubに収められています。 今のところ詳細は伏せていますが、タイミングが来たときには公開するつもりです。 今後数か月は隔週でミーティングを続け、ファウンデーションのガバナンス、ルール、規則などの作成に取り組んでいく予定です。 Shout out to our sponsors スポンサーの皆さまへの感謝。 PHPファウンデーションに寄付してくださった皆様、本当にありがとうございました。 設立当初からのスポンサーであるJetBrains・Automattic・Acquia・Zend・Craft CMS・Laravel・Symfony・Tideways・PrestaShop・Private Packagistに加え、さらに多くのスポンサーが参加してくれました。 10000ドル以上の支援をいただいた、以下の企業には特に感謝します。 ・RAKUS ・Livesport s.r.o. ・OP.GG ・Polcode ・Shopware ・BASE, Inc. ・Aternos GmbH ・Digital Scholar ・Ardennes-étape PHPファウンデーションは、みんなのものです。 本日の報告は以上です。 最新情報を得たいならば@ThePHPFをフォローしてください。 ?? 感想 PHPファウンデーションは、これまで50万ドル近くを調達することに成功しました。 青息吐息なプロジェクトも多い中、幸先の良いスタートを切れたようです。 これが今後も継続していければ、PHPはますます良いサイクルに入れるでしょう。 そして成功したモデルとして、他のOSSプロジェクトのフラグシップとなれるとよいですね。 逆に言うとたった5千万なんで、フルタイム開発者をほんの数人しか雇えないとも言えます。 GAFAあたりが全く参加してないのもアレですね。 本文で募集を締め切ったと言っているのは開発者であり、通常の寄付についてはいつでも受け付けています。 日頃お世話になっているあなたも、ぜひ参加してみてはいかがでしょうか。 私も参加しようと思ったのですが、何故かカードが拒否されるんですよね。 なんで。 わからん(限度額ではない)
- 投稿日:2022-01-31T19:00:34+09:00
【Laravel 8】ユーザー真偽をして表示/非表示
はじめに Laravel 初学者の私が学習した内容をアウトプットしていく記事になります。 冗長している点や軽い書き方などあれば御指南をお願いいたします。。。? ユーザー真偽をして表示/非表示 実装する内容 今回は、ユーザー自身のポートフォリオをBootstrap tabを使用して、ユーザー自身のマイページならタブを表示 / 他ユーザーのページに行ったらタブを消す仕様です。 詰まったこと 条件をどう分岐させるか的を得てない。 今回の解決策はこれ!URL の id と ログインユーザーが一緒なら ture URL に注目していなかった。根本的にロジックが立てられていない状態なので、そこに着眼点を置くことができていませんでした。 if (Auth::id() === $user->id) 「訪れているページが自分か他人か」 このロジックが今回は正解でした。 今思えば、なに詰まることが事があるんだと恥ずかしくなります。 該当コードが以下のものになります。 UserController.php public function show(User $user, Request $request) { $user->load('portfolios.technologies', 'portfolios.user'); $introduction = ReplaceClickableUrl($user->user_self_introduction); $currentUser = Auth::user(); $isPublished = $request->input('is_published'); $isPublished = (bool)$isPublished; $portfolios = Portfolio::where('is_published', $isPublished)->orderBy('created_at', 'desc')->where('user_id', $user->id)->get(); $portfolios->load('portfolioLikes'); if (Auth::guard('company')->check()) { return view('user.profiles.show', compact('user', 'portfolios', 'introduction', 'currentUser')); } return view('user.profiles.show', compact('user', 'portfolios', 'introduction', 'currentUser','isPublished')); } 最初は、$portfolios に ->get() していたので first() に変更してとか試してました。しかし、下の条件分岐で count メソッドがあるので、複数ある場合はエラーを吐かれるそう。 今回は、 controller 側で完結だったのでだいぶ蛇足を踏みました。。。 show.blade.php <div class="row justify-content-center mt-5"> <div class="col-md-10"> @if (Auth::id() === $user->id) <nav> <div class="nav nav-tabs" id="nav-tab" role="tablist"> <a class="nav-item nav-link inline-block text-gray-500 hover:text-gray-600 hover:border-gray-300 rounded-t-lg px-4 text-sm font-medium text-center border-transparent border-b-2 dark:text-gray-400 dark:hover:text-gray-300 {{ $isPublished ? 'active' : '' }}" id="nav-published-tab" data-tab-item="published" href="?is_published=1"> 公開 </a> <a class="nav-item nav-link inline-block text-gray-500 hover:text-gray-600 hover:border-gray-300 rounded-t-lg px-4 text-sm font-medium text-center border-transparent border-b-2 dark:text-gray-400 dark:hover:text-gray-300 {{ !$isPublished ? 'active' : '' }}" id="nav-draft-tab" data-tab-item="draft" href="?is_published=0"> 下書き </a> </div> </nav> @endif @include('components.mypageTab') </div> </div> mypageTab.php div class="tab-content" id="nav-tabContent"> <div class="tab-pane fade show active" id="nav-published" role="tabpanel" aria-labelledby="nav-published-tab"> <div class="row text-center pt-3"> @if (count($portfolios) !== 0) @foreach ($portfolios as $portfolio) @include('components.portfolio-card') @endforeach @else <div class="col-12 mb-3 mt-5"> <p>現在、まだポートフォリオの投稿はありません。</p> </div> @endif </div> </div> </div> こんなことで躓くのか。。ハぁ?と悔しかったです。しかし、これからもいろんな事象を解決できるようにインプットをしていきたいと思います。 これからも、アウトプットを続けていきます!! 最後まで、読んでいてだきありがとうございました??♂️
- 投稿日:2022-01-31T19:00:16+09:00
recoverメソッドに感動した話(CakePHP2のTreeビヘイビアについて)
この記事では、CakePHP2のTreeビヘイビアに搭載されているrecoverメソッドの実装例とすごさを記述します。 はじめに 世間的にはもう役目を終えつつあるCakePHP2ですが、最近実務で触る機会がありました。 Treeメソッドを使用したテーブルにレコードを追加するという業務をこなす中で、recoverメソッドに出会い感動しました。 もしかしてまだ使用したことがない人もいるかも、と思いましたので共有させていただきます。 Treeビヘイビアって何? データベースのテーブル内のレコードに対し、階層構造を作る仕組みです。 下記のような構造を同一テーブル内で実現できます。 詳細は公式ドキュメントを参照ください。 https://book.cakephp.org/2/ja/core-libraries/behaviors/tree.html recoverメソッドって何? 何に感動したの? recoverメソッドは、階層構造の座標指定を自動で調整してくれる仕組みです。 これを知らない時の私は、以下のようにモヤモヤを抱えていました。 lftとrghtって、どうやって指定すればいいんだろう? もし階層をずらす場合、前レコードのlftとrghtを再計算しないといけないのだろうか? もしそうなら、非常にめんどくさい……。 ご安心ください! recoverメソッドを使えば、lftとrghtの調整が1行で行えます。 当時の私は こんなに簡単に調整できるのか!! CakePHP2やるやん!! と、感動しました。 (なおサポート切れの模様) どうやって使うの? 簡単です。 使用したい場所に下記の1行を追加してください。 $this->モデル名->recover(); マイグレーションファイル内で使用する場合は、afterメソッド内から呼び出すと良いと思います。 public function after($direction) { App::import('Model','モデル名'); $モデル名 = new モデル名(); // recoverメソッド呼び出し! $モデル名->recover(); return true; } これだけ。 なんてシンプルで素敵な記述なんでしょう。 最後に ドキュメントは隅々まで目を通すべきだな、と実感しました。 誰かのお役に立てれば幸いです。
- 投稿日:2022-01-31T18:30:49+09:00
PHP forの多重ループ
繰り返し処理の際にforを使用していて処理が分かっていなかったため調べたときの備忘録です。 for($i = 0; $i < 3; $i++){ for ($j = 1; $j < 4; $j++) { echo $i . $j . " "; } } //出力:01 02 03 11 12 13 21 22 23 1.1番大きなfor分の処理が始まる。$i = 0 2.中のfor分の処理が始まり、この処理が終わるまで表示が繰り返される。$j = 1から3まで 3.2までが終わると1の$iに1が足され同じ処理が繰り返される。$i = 1 一番大きなfor文の中の処理が終わりきると一番大きなfor分が次の繰り返しに移るってことなんですが、わかりやすく書けなかったです。 語彙力もつけたいです。
- 投稿日:2022-01-31T15:42:02+09:00
paiza問題集:5以上の整数の合計
「5以上の整数の合計」の私なりの回答です。 <?php //$tmpに一行目を格納 while ($line = fgets(STDIN)) { $tmp[] = trim($line); } //変数初期化 $result = 0; //5以上であれば足し算 for($i = 1; $i<=$tmp[0]; $i++){ if($tmp[$i] >= 5){ $result += $tmp[$i]; } } echo $result; ?>
- 投稿日:2022-01-31T14:37:34+09:00
March文系3年次編入生のエンジニア就活
こんにちは!初投稿です。 プログラミングを勉強するにあたって、Qiitaで記録をつけていくことにしました。 自分の経歴は、大学二年生で英語の勉強のみしてきました。(TOEIC 745点) プログラミングの勉強の初めは、web制作の勉強をしていましたが、エンジニアの方々に相談した結果、サーバーサイドエンジニアを目指そうと方向転換しました。 現在は、ProgateのPHPを学習しているところです。かなり難しい内容ですが、慣れるまで食らいつきます。 これから、同じ境遇の方々や、エンジニアの方たちをみて学習していきたいです。よろしくお願いいたします。
- 投稿日:2022-01-31T13:59:41+09:00
SQLSTATE[HY000] [2002] Connection refused が出た時の対処方法
エラー内容 このような内容でした 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)) 対処法 php artisan tinkerで.envファイルの内容がちゃんと反映されてるか確かて見たところ、反映されていなかったためこれが原因だったようです。 root@8dae87ded68a:/var/www/app# php artisan tinker Psy Shell v0.11.1 (PHP 8.0.15 — cli) by Justin Hileman >>> config('database') => [ ../ "mysql" => [ "driver" => "mysql", "url" => null, "host" => "127.0.0.1", #要注意 "port" => "3306", "database" => "laravel",#要注意 "username" => "root",#要注意 "password" => "",#要注意 "unix_socket" => "", "charset" => "utf8mb4", "collation" => "utf8mb4_unicode_ci", "prefix" => "", "prefix_indexes" => true, "strict" => true, "engine" => null, "options" => [], ], ../ なので.envファイルを適用させればいいのですが、php artisan config:clearコマンドで内容を適用させればいらしいんですが、私の場合これではうまく適用されずrm app/.envでファイルを削除させもう一度復元させたら成功しました。 まとめ ファイルを消さないと適用されないところを治したい
- 投稿日:2022-01-31T08:28:14+09:00
Dockerでnginx + PHP(Laravel)+ MySQLのLEMP環境を構築する
概要 Docker, Docker Compose を使って、nginx + PHP(Laravel)+ MySQLのLEMP環境を構築する記事です。 検索するとDockerfileやdocker-compose.ymlの書き方については色んな方が記事を書いてくださっていて、動く環境を作ること自体は難しくありませんでした。 ですが、筆者はひとつコンテナを作っては動作確認していくという過程を経てすごく理解が深まったなと思うので、ファイルの書き方だけでなく動作確認したことやその結果を含めて記事に残しておきたいと思います。 全体像 最終的なディレクトリ構成は以下の通りです。 全コンテナを管理するdocker-compose.ymlがトップレベルにあり、同じくトップレベルにあるdockerディレクトリ配下に各コンテナのDockerfileや設定ファイルを置いています。 tree docker_sample/ ├── src // Laravelプロジェクトのソースコード ├── docker │ ├── app // PHPコンテナ │ │ ├── Dockerfile │ │ └── php.ini │ ├── db // MySQLコンテナ │ │ ├── Dockerfile │ │ └── my.cnf │ └── web // nginxコンテナ │ ├── Dockerfile │ └── default.conf └── docker-compose.yml // 全コンテナの管理 早速一つずつコンテナを作っていきます。 PHPのコンテナ(Laravelの開発環境) まずはLaravelの開発環境を構築するための、PHPのコンテナを作成します。 docker-compose.ymlへの記述 PHPコンテナ用のDockerfile PHPコンテナ用の設定ファイル(php.ini) について順に説明します。 docker-compose.yml(PHP) docker-compose.ymlのうち、PHPコンテナについての記述は以下の通りです。 ※docker-compose.ymlはインデントが意味を持つので注意。 docker-compose.yml version: "3.9" services: app: // サービス名 build: context: . dockerfile: ./docker/app/Dockerfile volumes: - ./src/:/app version: これはPHPコンテナについてではなく docker-compose.yml の先頭に書く設定ですが、Composeファイルのバージョンを表しています。 今回は最新の 3.9 を使います。(最新のバージョンは下記の公式サイトを参照) Compose file build: ビルドコンテキストを指定します。 context:ビルドコンテキスト(buildを実行する場所)の設定 dockerfile:buildするDockerfileまでのパス(docker-compose.ymlから見た相対パス) 参考記事: docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話 - Qiita volumes: ホスト側の ./src/をコンテナ側の /app にマウントするという意味になります。 ※ここで services: の中に書いているこの volumes: は バインドマウント を行っています。 参考記事:Dockerのマウント3種類についてわかったことをまとめる - Qiita 何が起きているかは実際の挙動を見てみた方が分かりやすいと思うので、後ほどまた動作確認します。 Dockerfile(PHP) Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに書きます。 まず作成するDockerfileの全文がこちらです。 /docker/app/Dockerfile FROM php:8.0-fpm ENV TZ Asia/Tokyo RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./docker/app/php.ini /usr/local/etc/php/php.ini COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer WORKDIR /app 一つずつ説明します。 FROM php:8.0-fpm FROMではイメージをビルドするためのベースイメージを設定します。 書き方は FROM イメージ名:タグ名 です。 ここではDocker HubからPHP公式のイメージをベースとして指定しています。 Php - Official Image | Docker Hub ENV TZ Asia/Tokyo ENVはコンテナ内のサーバー環境変数を設定します。 RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath RUN にはコンテナビルド時に実行するコマンドを書きます。 && で複数のコマンドをつなぎ、 \ で改行します。 ※ && と \ を使うことで複数コマンドを1レイヤーにまとめることができ、公式でもベストプラクティスのTipsとして挙げられています。 Intro Guide to Dockerfile Best Practices - Docker Blog apt-get update :インストール可能なパッケージの「一覧」を更新 apt-get -y install:パッケージをインストール docker-php-ext-install:PHPの拡張ライブラリをインストール ※Laravelのインストールや開発に必要な(もしくは便利な)パッケージや拡張ライブラリをインストールしています。 ※試しにこれらを全くインストールせず手順を進めてみると、コンテナを起動することはできますが、その後のLaravelプロジェクトを作成する時に大量のエラーに遭遇しました(笑) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini ローカルで(後ほど)作成する php.ini (PHPの設定ファイル)をDockerコンテナ内にコピーします。 書き方は COPY [ローカル側のパス] [コンテナ側のパス]です。 ※ローカル側のパスは、Dockerfileから見てではなくbuildコマンドを実行するディレクトリから見た相対パスです。今回はDocker composeを使ってイメージビルドを行うので、docker-compose.ymlから見た相対パスになっています。 COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer Laravelを使うためComposerをインストールします。 この書き方によりマルチステージビルドという方法でインストールされます。 ※マルチステージビルドという方法はイメージの軽量化に役立つようです。 公式ドキュメント:Use multi-stage builds WORKDIR /app コンテナを起動している時に $ docker-compose exec コンテナ名 bash というコマンドを実行すると、コンテナの中でbashを実行することができるのですが、 WORKDIRはその時のカレントディレクトリを指定しています。 設定ファイル(php.ini) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini で出てきたPHPの設定ファイル php.iniを作成します。 作成したファイルの中身がこちらです。 /docker//app/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 = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanese こちらの記事の開発用php.iniをまるっとお借りしました。 開発用 / 本番用の設定例と、項目ごとの説明も載っていて面白かったので、ぜひご覧ください。 参考記事:PHP7.4 ぼくのかんがえたさいきょうのphp.ini - Qiita PHPコンテナを起動する ここまででPHPのコンテナの準備が出来たので、実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build docker compose up は docker-compose.yml に定義したサービスを起動します。 -d 「デタッチド」モードでコンテナを起動します。 (デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示する。「デタッチド」モードではバックグラウンドで動作する。) -build コンテナの開始前にイメージをビルドします。 (特に変更がない場合はキャッシュが使用される。) ※ $ docker-compose build → $ docker-compose up -d を順に行うのと同じです。 起動したコンテナを確認する 以下のコマンドで起動中のコンテナを一覧で確認することができます。 $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp STATUSがrunningになっていれば正常に起動しています。 今はPHPのコンテナしか作っていないので1つだけ表示されていますが、この後nginx, MySQLのコンテナを作り、最終的に3つのコンテナが表示されるようにします。 ここで今作業しているディレクトリの構成を確認してみます。 $ tree . ├── src // 作成された! ├── docker │ ├── app │ │ ├── Dockerfile │ │ └── php.ini └── docker-compose.yml するとdocker-compose.ymlに書いたバインドマウントの以下の部分を受けて、コンテナを起動したときに自動でsrcディレクトリが作成されたことが確認できます。(同様にコンテナ内にもappディレクトリが作成されています。) volumes: - ./src/:/app コンテナの中に入ってみる コンテナを起動中に以下のコマンドを実行すると、コンテナの中に入ってbashを実行することができます。 $ docker-compose exec app bash // appの部分はサービス名を指定する これで今コンテナの中に入れたので、Dockerfileの記述通りにコンテナが作られているか、またPHP・Composer・インストールした拡張機能が使えるか確認していきます。 // Dockerfileの「WORKDIR /app」で指定したカレントディレクトリ通りか確認 [app]:/app$ pwd /app // PHPのバージョン確認 [app]:/app$ php -v PHP 8.0.15 (cli) (built: Jan 26 2022 17:38:36) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.15, Copyright (c) Zend Technologies // Composerのバージョン確認 [app]:/app$ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.0.14 2021-05-21 17:03:37 // gitのバージョン確認 [app]:/app$ git --version git version 2.30.2 // インストール済の拡張機能の一覧 [app]:/app$ php -m [PHP Modules] bcmath intl pdo_mysql zip // たくさん出てくるので他は省略 // php.iniがコピー出来ているか確認 [app]:/app$ cat /usr/local/etc/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 = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japaneseroot@0e3ba825df88 確認できたのでコンテナを抜けます。 $ exit // もしくは ctrl + d コンテナの外からコマンドを実行する 上記のコンテナの中で実行したコマンドは、 $ docker-compose exec サービス名 実行したいコマンド でコンテナの外から実行することもできます。(コンテナが起動中に限る) バインドマウントの挙動を確認する コンテナの外からコマンドが実行できるという確認も兼ねて、バインドマウントの動きを見る為、試しに以下のコマンドを実行してみます。 // コンテナ内の/app配下にファイルを作ってみる $ docker-compose exec app touch sample.php $ docker-compose exec app pwd /app $ docker-compose exec app ls sample.php コンテナ内の/app配下にファイルを作成しました。 この/appという場所は、docker-compose.ymlに書いた以下の記述の通りにバインドマウントされています。 volumes: - ./src/:/app これにより、プロジェクトディレクトリ配下の/srcにもsample.phpが作成されています。 $ ls src sample.php 以上より、コンテナ内の /app に対して行ったことが、プロジェクトの /src に反映していることが分かりました。 では次に /src への変更がコンテナ内の /app に対しても反映するか試してみます。 /src/sample.php をエディタで開いてファイルに hello と書きこみ、以下のコマンドを実行します。 $ docker-compose exec app cat sample.php hello // この時permission errorが出たら権限を変更する $ sudo chmod -R 777 ./src するとコンテナ内の ./app/sample.php も編集されていることがわかります。 このようにバインドマウントにより、ホスト側のディレクトリがコンテナ内へマウント出来ていることも確認できました。 試しに作成したファイルは不要なので消しておきます。 $ rm src/sample.php nginxのコンテナ(webサーバー) webサーバーとなるnginxのコンテナを作成します。 PHPコンテナのときと同じく、 docker-compose.ymlへの記述 nginxコンテナ用のDockerfile nginxコンテナ用の設定ファイル(default.conf) の流れで説明します。 docker-compose.yml(nginx) 既に作成しているdocker-compose.ymlに、nginxのコンテナについての部分を追記します。 docker-compose.yml services: api: // 中略 web: // サービス名 build: context: . dockerfile: ./docker/web/Dockerfile ports: - 8081:80 depends_on: - app volumes: - ./src/:/app 解説していきます。 build: ・volumes: ここはPHPコンテナと同様なので説明は省きます。 ports: ホスト側とコンテナ間のポート番号の対応付けを設定します。 書き方は ホスト側のポート番号 : コンテナのポート番号です。 ※今回ホスト側(自分のPC)は既に他の開発で使っているポートとの兼ね合いで 8081 を使いました。コンテナ側はnginxのデフォルトのポート番号である 80 にしています。 depends_on: サービスの起動順序を制御します。 web の depends_on に app と書いているので、 app → web の順に起動するように指定しています。 ※但しこの記述なしで $ docker-compose up -d --build をしてみても私の環境では全く問題なく動きました。が、サービス同士の依存関係を明示的に記すという意味でも書いておくに越したことはないという判断で書いています。 ※ nginxとphp間でTCPによるfpm接続についてや、depends_on オプションについては以下の記事がすごく勉強になったので是非読んでみてください。 参考記事:【docker-compose】depends_onとサービス名解決にまつわるエトセトラ - Qiita Dockerfile(nginx) 作成するDockerfileの全文はこちらです。 /docker/web/Dockerfile FROM nginx:1.20-alpine ENV TZ Asia/Tokyo COPY ./docker/web/default.conf /etc/nginx/conf.d/default.conf FROM , ENV , COPY の意味はPHPコンテナのDockerfileと同様なので詳しい説明は省略し、このファイルで設定していることを簡単にまとめます。 nginx公式のイメージ(Alpineベース)をベースイメージに使用 Nginx - Official Image | Docker Hub 参考記事:Dockerでよく利用されているAlpineは他のLinuxディストリビューションと比べて、どれだけ軽量なのか - プログラミングは芸術だ! 環境変数のタイムゾーンを設定する nginxの設定ファイル(default.conf)をコンテナ内にバインドマウント ※ちなみにnginxは1.18, 1.20などの偶数バージョンが安定バージョンであり、安定バージョンの使用を推奨されています。 公式ドキュメント:Installing NGINX Open Source 設定ファイル(default.conf) ./docker/web/default.confを作成します。 Laravel公式に用意されているnginxの設定例をべースに使います。 Laravel 8.x デプロイ root とfastcgi_passの設定のみ、このプロジェクトに合わせて書き換えています。 /docker/web/default.conf server { listen 80; server_name example.com; root /app/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 リクエストのルートディレクトリです。 root /app/public; と書いたので、 localhost:8081(docker-compose.ymlで設定したポート番号)にアクセスすると /app/publicを見に行きます。 fastcgi_pass FastCGIサーバーのアドレスです。 ※FastCGI:Webサーバ上で動くプログラムを一度起動したらしばらく待機させることによって、プログラムの開始と終了にかかる手間を減らし、動きを速くしたりWebサーバの負荷を軽減することができる仕組み。 app:9000; と書いたので、appコンテナの9000番ポートを指定しています。 nginxコンテナを起動する nginxのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)の2つが起動できました。 またdocker_sample-web-1の PORTS が 0.0.0.0:8081->80/tcp となっており、ホスト上の8081番ポートをコンテナの80番ポートへ割り当てられていることも確認できます。 コンテナの動作確認 nginxのバージョンを確認します。 $ docker-compose exec web nginx -v nginx version: nginx/1.20.2 コンテナの中に入ってみます。 $ docker-compose exec web bash OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH: unknown するとエラーになりました。 Alpineをベースとすると bash は使えず、 ash や sh は使えるようです。 $ docker-compose exec web ash [web]:/ $ pwd / 無事コンテナの中に入れました。 webサーバーとしての動作確認 リクエストに対してファイルを返しブラウザで表示できる nginxのコンテナからPHPのコンテナへphpを実行させることができる 上記2点を確認します。 $ mkdir src/public $ touch src/public/test.php test.phpを以下のように編集します。 /src/public/test.php <?php echo 'test.phpです'; phpinfo(); http://localhost:8081/test.php にアクセスすると以下のように表示され、webサーバーが正しく動作していることが確認できます。 試しに作成したファイルは不要なので消しておきます。 $ rm -rf src/* MySQLのコンテナ(データベース) データベースのMySQLコンテナを作成します。 以下の流れで説明します。 docker-compose.ymlへの記述 MySQLコンテナ用のDockerfile MySQLコンテナ用の設定ファイル(my.conf) docker-compose.yml(MySQL) 既に作成しているdocker-compose.ymlに、MySQLのコンテナについての部分を追記します。 docker-compose.yml services: // 中略 db: // サービス名 build: context: . dockerfile: ./docker/db/Dockerfile ports: - 3306:3306 environment: MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password TZ: 'Asia/Tokyo' volumes: - mysql-volume:/var/lib/mysql volumes: mysql-volume: build: ・ ports: 既出の通りです。 enviroment: 環境変数の設定です。 ※名前やDB名・ユーザー名・パスワードは好きなものを設定します。 ※実際のプロジェクト管理では、環境変数は .env に書いて .gitignore にするなどして、重要な情報が公開されないようにします。 MYSQL_DATABASE:DB名 MYSQL_USER:ユーザー名 MYSQL_PASSWORD:パスワード MYSQL_ROOT_PASSWORD:ルート権限のパスワード TZ:時間設定(Time Zone) volumes: 考え方はPHPやnginxのコンテナ同様ですが、先ほどまではホスト側のディレクトリを書いていたところに mysql-volume と書いています。 これにより mysql-colume という名前で作成した名前付きボリュームとコンテナ内を紐づけています。 Docker Volume Volumeとは、コンテナを破棄してもデータを永続的に保存できるように、コンテナ外に提供されているデータの保存領域です。 Dockerの管理下でホスト上にストレージ領域を確保しており、Linux なら /var/lib/docker/volumes/以下にあります。 参考記事:Docker、ボリューム(Volume)について真面目に調べた - Qiita なぜVolumeが必要なのか? コンテナが起動している間はDBのレコードは保存された状態が続きますが、例えば以下のようなコマンドでコンテナを新しく作り直したとするとデータベースの情報はゼロに戻ってしまいます。 // コンテナ削除 $ docker-compose down // コンテナ起動 $ docker-compose up -d これでは困るのでコンテナを破棄してもデータを残したい、、、というときにVolumesを使ってデータの永続化を行います。 ※ボリュームには名前付きボリュームと匿名ボリュームがありますが、通常は管理しやすい名前付きボリュームを使うと良いかと思います。 名前付きボリュームを作成する場合は、データの永続化対象のコンテナに対して volumes: オプションでバインドマウントを行うだけでなく、以下のようにdocker-compose.ymlのトップレベルでボリューム名を定義します。 docker-compose.yml volumes: - mysql-volume: Dockerfile(MySQL) 作成するDockerfileの全文はこちらです。 /docker/db/Dockerfile FROM mysql:8.0 COPY ./docker/db/my.cnf /etc/my.cnf 内容を簡単にまとめると、 公式のMySQLイメージをベースイメージに使用 Mysql - Official Image | Docker Hub MySQLの設定ファイル(my.cnf)をコンテナ内にバインドマウント ※ここで使用しているイメージではM1 Macでは動作しないという情報が見られました。どうやらOracleのMySQLチームがメンテしている mysql/mysql-server のイメージだと動作するようです。 mysql-server | Docker Hub 設定ファイル(my.conf) ./dicker/db/my.cnfを作成します。 色んな記事で作成されているmy.cnfを参考にさせていただきながら書きました。 参考記事:【Docker】docker-composeでmysqlのコンテナを立てる 文字コード タイムゾーン ログ の設定を行っています。 /docker/db/my.conf [mysqld] # character character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # 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 MySQLコンテナを起動する MySQLのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-db-1 "docker-entrypoint.s…" db running 0.0.0.0:3306->3306/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)、db(MySQLのコンテナ)の3つが起動できました。 コンテナの動作確認 MySQLのバージョンを確認します。 $ docker compose exec db mysql -V mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL) ※この後Laravelのプロジェクトを作成したら、このDBとLaravelを接続します。 そしてマイグレーションを行ってDBにテーブルを作成してから、MySQLにログインしてDBを使う動作確認をしたいと思います。 Laravelのインストール Laravelプロジェクトの作成 LEMP環境が構築できたので、Laravelのアプリケーションを作っていきます。 appコンテナに入り、Laravelをインストールします。 $ docker compose exec app bash [app]:/app$ composer create-project --prefer-dist "laravel/laravel=8.*" . [app]:/app$ php artisan -v Laravel Framework 8.81.0 コンテナ内の/app配下にLaravelのプロジェクトが新規作成され、ホスト側の/src配下にも同じくLaravelのプロジェクトが出来ました。 localhost:8081にアクセスして、ブラウザでもLaravelのウェルカムページが表示できることを確認します。 DB接続 appコンテナ(Laravel)からdbコンテナ(MySQL)へ接続する設定を行います。 Laravelではデータベースへの接続設定を .env ファイルに定義しているので、 /src/.env のDBの部分を以下のように修正します。 /src/.env DB_CONNECTION=mysql DB_HOST=db // MySQLコンテナのサービス名 DB_PORT=3306 DB_DATABASE=database DB_USERNAME=root DB_PASSWORD=passwor DB_HOST にはMySQLコンテナのサービス名を指定します。 その他の項目もMySQLコンテナで設定した値(今回はdocker-compose.ymlのenviromentで定義)と同じ値を指定します。 DBに接続出来ているか確認する為、以下のコマンドを実行してマイグレーションを行います。 $ docker compose exec app bash [app]:/app$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (55.42ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (51.11ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (45.12ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (75.74ms) MySQLを使ってみる MySQLのコンテナに入ってDBを確認してみます。 $ docker-compose exec db bash [db]:/$ mysql -u root -p // パスワードを求められるので入力 [db] mysql> use database; [db] mysql> show tables; +------------------------+ | Tables_in_database | +------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | users | +------------------------+ 5 rows in set (0.00 sec) 先ほどマイグレーションを実行したので、Laravelのデフォルトで用意されているマイグレーションファイル通りにテーブルが作成されていることが確認できました。 最後に これでDocker(Docker Compose)を使ったLEMP環境の構築が完了です。 今回作成した環境は非常にシンプルなものなので、業務で使うとなるともっと設定を細やかに行ったり、開発環境・ステージング環境・本番環境それぞれの設定ファイルを用意して設定を切り替えたりといった作業が必要になるかと思いますが、仕組みが分かればあとは全てこの延長にあるのかなと思います。 またいずれ環境ごとの設定ファイル切り替えだったり、あとはNodeのコンテナを使ってNext.jsの環境構築もやってみたいです。 参考記事
- 投稿日:2022-01-31T08:28:14+09:00
DockerでPHP(Laravel)+ nginx + MySQLのLEMP環境を構築する
概要 Docker, Docker Compose を使って、PHP(Laravel) + nginx + MySQLのLEMP環境を構築する記事です。 検索するとDockerfileやdocker-compose.ymlの書き方については色んな方が記事を書いてくださっていて、動く環境を作ること自体は難しくありませんでした。 ですが、筆者はひとつコンテナを作っては動作確認していくという過程を経てすごく理解が深まったなと思うので、ファイルの書き方だけでなく動作確認したことやその結果を含めて記事に残しておきたいと思います。 ↓ 完成後のリポジトリはこちらです。 全体像 最終的なディレクトリ構成は以下の通りです。 全コンテナを管理するdocker-compose.ymlがトップレベルにあり、同じくトップレベルにあるdockerディレクトリ配下に各コンテナのDockerfileや設定ファイルを置いています。 tree docker_sample/ ├── src // Laravelプロジェクトのソースコード ├── docker │ ├── app // PHPコンテナ │ │ ├── Dockerfile │ │ └── php.ini │ ├── db // MySQLコンテナ │ │ ├── Dockerfile │ │ └── my.cnf │ └── web // nginxコンテナ │ ├── Dockerfile │ └── default.conf └── docker-compose.yml // 全コンテナの管理 早速一つずつコンテナを作っていきます。 PHPのコンテナ(Laravelの開発環境) まずはLaravelの開発環境を構築するための、PHPのコンテナを作成します。 docker-compose.ymlへの記述 PHPコンテナ用のDockerfile PHPコンテナ用の設定ファイル(php.ini) について順に説明します。 docker-compose.yml(PHP) docker-compose.ymlのうち、PHPコンテナについての記述は以下の通りです。 ※docker-compose.ymlはインデントが意味を持つので注意。 docker-compose.yml version: "3.9" services: app: // サービス名 build: context: . dockerfile: ./docker/app/Dockerfile volumes: - ./src/:/app version: これはPHPコンテナについてではなく docker-compose.yml の先頭に書く設定ですが、Composeファイルのバージョンを表しています。 今回は最新の 3.9 を使います。(最新のバージョンは下記の公式サイトを参照) Compose file build: ビルドコンテキストを指定します。 context:ビルドコンテキスト(buildを実行する場所)の設定 dockerfile:buildするDockerfileまでのパス(docker-compose.ymlから見た相対パス) 参考記事: docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話 - Qiita volumes: ホスト側の ./src/をコンテナ側の /app にマウントするという意味になります。 ※ここで services: の中に書いているこの volumes: は バインドマウント を行っています。 参考記事:Dockerのマウント3種類についてわかったことをまとめる - Qiita 何が起きているかは実際の挙動を見てみた方が分かりやすいと思うので、後ほどまた動作確認します。 Dockerfile(PHP) Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに書きます。 まず作成するDockerfileの全文がこちらです。 /docker/app/Dockerfile FROM php:8.0-fpm ENV TZ Asia/Tokyo RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./docker/app/php.ini /usr/local/etc/php/php.ini COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer WORKDIR /app 一つずつ説明します。 FROM php:8.0-fpm FROMではイメージをビルドするためのベースイメージを設定します。 書き方は FROM イメージ名:タグ名 です。 ここではDocker HubからPHP公式のイメージをベースとして指定しています。 Php - Official Image | Docker Hub ENV TZ Asia/Tokyo ENVはコンテナ内のサーバー環境変数を設定します。 RUN apt-get update && \ apt-get install -y git unzip libzip-dev libicu-dev libonig-dev && \ docker-php-ext-install intl pdo_mysql zip bcmath RUN にはコンテナビルド時に実行するコマンドを書きます。 && で複数のコマンドをつなぎ、 \ で改行します。 ※ && と \ を使うことで複数コマンドを1レイヤーにまとめることができ、公式でもベストプラクティスのTipsとして挙げられています。 Intro Guide to Dockerfile Best Practices - Docker Blog apt-get update :インストール可能なパッケージの「一覧」を更新 apt-get -y install:パッケージをインストール docker-php-ext-install:PHPの拡張ライブラリをインストール ※Laravelのインストールや開発に必要な(もしくは便利な)パッケージや拡張ライブラリをインストールしています。 ※試しにこれらを全くインストールせず手順を進めてみると、コンテナを起動することはできますが、その後のLaravelプロジェクトを作成する時に大量のエラーに遭遇しました(笑) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini ローカルで(後ほど)作成する php.ini (PHPの設定ファイル)をDockerコンテナ内にコピーします。 書き方は COPY [ローカル側のパス] [コンテナ側のパス]です。 ※ローカル側のパスは、Dockerfileから見てではなくbuildコマンドを実行するディレクトリから見た相対パスです。今回はDocker composeを使ってイメージビルドを行うので、docker-compose.ymlから見た相対パスになっています。 COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer Laravelを使うためComposerをインストールします。 この書き方によりマルチステージビルドという方法でインストールされます。 ※マルチステージビルドという方法はイメージの軽量化に役立つようです。 公式ドキュメント:Use multi-stage builds WORKDIR /app コンテナを起動している時に $ docker-compose exec コンテナ名 bash というコマンドを実行すると、コンテナの中でbashを実行することができるのですが、 WORKDIRはその時のカレントディレクトリを指定しています。 設定ファイル(php.ini) COPY ./docker/app/php.ini /usr/local/etc/php/php.ini で出てきたPHPの設定ファイル php.iniを作成します。 作成したファイルの中身がこちらです。 /docker//app/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 = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanese こちらの記事の開発用php.iniをまるっとお借りしました。 開発用 / 本番用の設定例と、項目ごとの説明も載っていて面白かったので、ぜひご覧ください。 参考記事:PHP7.4 ぼくのかんがえたさいきょうのphp.ini - Qiita PHPコンテナを起動する ここまででPHPのコンテナの準備が出来たので、実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build docker compose up は docker-compose.yml に定義したサービスを起動します。 -d 「デタッチド」モードでコンテナを起動します。 (デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示する。「デタッチド」モードではバックグラウンドで動作する。) --build コンテナの開始前にイメージをビルドします。 (特に変更がない場合はキャッシュが使用される。) ※ $ docker-compose build → $ docker-compose up -d を順に行うのと同じです。 起動したコンテナを確認する 以下のコマンドで起動中のコンテナを一覧で確認することができます。 $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp STATUSがrunningになっていれば正常に起動しています。 今はPHPのコンテナしか作っていないので1つだけ表示されていますが、この後nginx, MySQLのコンテナを作り、最終的に3つのコンテナが表示されるようにします。 ここで今作業しているディレクトリの構成を確認してみます。 $ tree . ├── src // 作成された! ├── docker │ ├── app │ │ ├── Dockerfile │ │ └── php.ini └── docker-compose.yml するとdocker-compose.ymlに書いたバインドマウントの以下の部分を受けて、コンテナを起動したときに自動でsrcディレクトリが作成されたことが確認できます。(同様にコンテナ内にもappディレクトリが作成されています。) volumes: - ./src/:/app コンテナの中に入ってみる コンテナを起動中に以下のコマンドを実行すると、コンテナの中に入ってbashを実行することができます。 $ docker-compose exec app bash // appの部分はサービス名を指定する これで今コンテナの中に入れたので、Dockerfileの記述通りにコンテナが作られているか、またPHP・Composer・インストールした拡張機能が使えるか確認していきます。 // Dockerfileの「WORKDIR /app」で指定したカレントディレクトリ通りか確認 [app]:/app$ pwd /app // PHPのバージョン確認 [app]:/app$ php -v PHP 8.0.15 (cli) (built: Jan 26 2022 17:38:36) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.15, Copyright (c) Zend Technologies // Composerのバージョン確認 [app]:/app$ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.0.14 2021-05-21 17:03:37 // gitのバージョン確認 [app]:/app$ git --version git version 2.30.2 // インストール済の拡張機能の一覧 [app]:/app$ php -m [PHP Modules] bcmath intl pdo_mysql zip // たくさん出てくるので他は省略 // php.iniがコピー出来ているか確認 [app]:/app$ cat /usr/local/etc/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 = /var/log/php/php-error.log default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japaneseroot@0e3ba825df88 確認できたのでコンテナを抜けます。 $ exit // もしくは ctrl + d コンテナの外からコマンドを実行する 上記のコンテナの中で実行したコマンドは、 $ docker-compose exec サービス名 実行したいコマンド でコンテナの外から実行することもできます。(コンテナが起動中に限る) バインドマウントの挙動を確認する コンテナの外からコマンドが実行できるという確認も兼ねて、バインドマウントの動きを見る為、試しに以下のコマンドを実行してみます。 // コンテナ内の/app配下にファイルを作ってみる $ docker-compose exec app touch sample.php $ docker-compose exec app pwd /app $ docker-compose exec app ls sample.php コンテナ内の/app配下にファイルを作成しました。 この/appという場所は、docker-compose.ymlに書いた以下の記述の通りにバインドマウントされています。 volumes: - ./src/:/app これにより、プロジェクトディレクトリ配下の/srcにもsample.phpが作成されています。 $ ls src sample.php 以上より、コンテナ内の /app に対して行ったことが、プロジェクトの /src に反映していることが分かりました。 では次に /src への変更がコンテナ内の /app に対しても反映するか試してみます。 /src/sample.php をエディタで開いてファイルに hello と書きこみ、以下のコマンドを実行します。 $ docker-compose exec app cat sample.php hello // この時permission errorが出たら権限を変更する $ sudo chmod -R 777 ./src するとコンテナ内の ./app/sample.php も編集されていることがわかります。 このようにバインドマウントにより、ホスト側のディレクトリがコンテナ内へマウント出来ていることも確認できました。 試しに作成したファイルは不要なので消しておきます。 $ rm src/sample.php nginxのコンテナ(webサーバー) webサーバーとなるnginxのコンテナを作成します。 PHPコンテナのときと同じく、 docker-compose.ymlへの記述 nginxコンテナ用のDockerfile nginxコンテナ用の設定ファイル(default.conf) の流れで説明します。 docker-compose.yml(nginx) 既に作成しているdocker-compose.ymlに、nginxのコンテナについての部分を追記します。 docker-compose.yml services: api: // 中略 web: // サービス名 build: context: . dockerfile: ./docker/web/Dockerfile ports: - 8081:80 depends_on: - app volumes: - ./src/:/app 解説していきます。 build: ・volumes: ここはPHPコンテナと同様なので説明は省きます。 ports: ホスト側とコンテナ間のポート番号の対応付けを設定します。 書き方は ホスト側のポート番号 : コンテナのポート番号です。 ※今回ホスト側(自分のPC)は既に他の開発で使っているポートとの兼ね合いで 8081 を使いました。コンテナ側はnginxのデフォルトのポート番号である 80 にしています。 depends_on: サービスの起動順序を制御します。 web の depends_on に app と書いているので、 app → web の順に起動するように指定しています。 ※但しこの記述なしで $ docker-compose up -d --build をしてみても私の環境では全く問題なく動きました。が、サービス同士の依存関係を明示的に記すという意味でも書いておくに越したことはないという判断で書いています。 ※ nginxとphp間でTCPによるfpm接続についてや、depends_on オプションについては以下の記事がすごく勉強になったので是非読んでみてください。 参考記事:【docker-compose】depends_onとサービス名解決にまつわるエトセトラ - Qiita Dockerfile(nginx) 作成するDockerfileの全文はこちらです。 /docker/web/Dockerfile FROM nginx:1.20-alpine ENV TZ Asia/Tokyo COPY ./docker/web/default.conf /etc/nginx/conf.d/default.conf FROM , ENV , COPY の意味はPHPコンテナのDockerfileと同様なので詳しい説明は省略し、このファイルで設定していることを簡単にまとめます。 nginx公式のイメージ(Alpineベース)をベースイメージに使用 Nginx - Official Image | Docker Hub 参考記事:Dockerでよく利用されているAlpineは他のLinuxディストリビューションと比べて、どれだけ軽量なのか - プログラミングは芸術だ! 環境変数のタイムゾーンを設定する nginxの設定ファイル(default.conf)をコンテナ内にバインドマウント ※ちなみにnginxは1.18, 1.20などの偶数バージョンが安定バージョンであり、安定バージョンの使用を推奨されています。 公式ドキュメント:Installing NGINX Open Source 設定ファイル(default.conf) ./docker/web/default.confを作成します。 Laravel公式に用意されているnginxの設定例をべースに使います。 Laravel 8.x デプロイ root とfastcgi_passの設定のみ、このプロジェクトに合わせて書き換えています。 /docker/web/default.conf server { listen 80; server_name example.com; root /app/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 リクエストのルートディレクトリです。 root /app/public; と書いたので、 localhost:8081(docker-compose.ymlで設定したポート番号)にアクセスすると /app/publicを見に行きます。 fastcgi_pass FastCGIサーバーのアドレスです。 ※FastCGI:Webサーバ上で動くプログラムを一度起動したらしばらく待機させることによって、プログラムの開始と終了にかかる手間を減らし、動きを速くしたりWebサーバの負荷を軽減することができる仕組み。 app:9000; と書いたので、appコンテナの9000番ポートを指定しています。 nginxコンテナを起動する nginxのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)の2つが起動できました。 またdocker_sample-web-1の PORTS が 0.0.0.0:8081->80/tcp となっており、ホスト上の8081番ポートをコンテナの80番ポートへ割り当てられていることも確認できます。 コンテナの動作確認 nginxのバージョンを確認します。 $ docker-compose exec web nginx -v nginx version: nginx/1.20.2 コンテナの中に入ってみます。 $ docker-compose exec web bash OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH: unknown するとエラーになりました。 Alpineをベースとすると bash は使えず、 ash や sh は使えるようです。 $ docker-compose exec web ash [web]:/ $ pwd / 無事コンテナの中に入れました。 webサーバーとしての動作確認 リクエストに対してファイルを返しブラウザで表示できる nginxのコンテナからPHPのコンテナへphpを実行させることができる 上記2点を確認します。 $ mkdir src/public $ touch src/public/test.php test.phpを以下のように編集します。 /src/public/test.php <?php echo 'test.phpです'; phpinfo(); http://localhost:8081/test.php にアクセスすると以下のように表示され、webサーバーが正しく動作していることが確認できます。 試しに作成したファイルは不要なので消しておきます。 $ rm -rf src/* MySQLのコンテナ(データベース) データベースのMySQLコンテナを作成します。 以下の流れで説明します。 docker-compose.ymlへの記述 MySQLコンテナ用のDockerfile MySQLコンテナ用の設定ファイル(my.conf) docker-compose.yml(MySQL) 既に作成しているdocker-compose.ymlに、MySQLのコンテナについての部分を追記します。 docker-compose.yml services: // 中略 db: // サービス名 build: context: . dockerfile: ./docker/db/Dockerfile ports: - 3306:3306 environment: MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password TZ: 'Asia/Tokyo' volumes: - mysql-volume:/var/lib/mysql volumes: mysql-volume: build: ・ ports: 既出の通りです。 enviroment: 環境変数の設定です。 ※名前やDB名・ユーザー名・パスワードは好きなものを設定します。 ※実際のプロジェクト管理では、環境変数は .env に書いて .gitignore にするなどして、重要な情報が公開されないようにします。 MYSQL_DATABASE:DB名 MYSQL_USER:ユーザー名 MYSQL_PASSWORD:パスワード MYSQL_ROOT_PASSWORD:ルート権限のパスワード TZ:時間設定(Time Zone) volumes: 考え方はPHPやnginxのコンテナ同様ですが、先ほどまではホスト側のディレクトリを書いていたところに mysql-volume と書いています。 これにより mysql-colume という名前で作成した名前付きボリュームとコンテナ内を紐づけています。 Docker Volume Volumeとは、コンテナを破棄してもデータを永続的に保存できるように、コンテナ外に提供されているデータの保存領域です。 Dockerの管理下でホスト上にストレージ領域を確保しており、Linux なら /var/lib/docker/volumes/以下にあります。 参考記事:Docker、ボリューム(Volume)について真面目に調べた - Qiita なぜVolumeが必要なのか? コンテナが起動している間はDBのレコードは保存された状態が続きますが、例えば以下のようなコマンドでコンテナを新しく作り直したとするとデータベースの情報はゼロに戻ってしまいます。 // コンテナ削除 $ docker-compose down // コンテナ起動 $ docker-compose up -d これでは困るのでコンテナを破棄してもデータを残したい、、、というときにVolumesを使ってデータの永続化を行います。 ※ボリュームには名前付きボリュームと匿名ボリュームがありますが、通常は管理しやすい名前付きボリュームを使うと良いかと思います。 名前付きボリュームを作成する場合は、データの永続化対象のコンテナに対して volumes: オプションでバインドマウントを行うだけでなく、以下のようにdocker-compose.ymlのトップレベルでボリューム名を定義します。 docker-compose.yml volumes: - mysql-volume: Dockerfile(MySQL) 作成するDockerfileの全文はこちらです。 /docker/db/Dockerfile FROM mysql:8.0 COPY ./docker/db/my.cnf /etc/my.cnf 内容を簡単にまとめると、 公式のMySQLイメージをベースイメージに使用 Mysql - Official Image | Docker Hub MySQLの設定ファイル(my.cnf)をコンテナ内にバインドマウント ※ここで使用しているイメージではM1 Macでは動作しないという情報が見られました。どうやらOracleのMySQLチームがメンテしている mysql/mysql-server のイメージだと動作するようです。 mysql-server | Docker Hub 設定ファイル(my.conf) ./docker/db/my.cnfを作成します。 色んな記事で作成されているmy.cnfを参考にさせていただきながら書きました。 参考記事:【Docker】docker-composeでmysqlのコンテナを立てる 文字コード タイムゾーン ログ の設定を行っています。 /docker/db/my.conf [mysqld] # character character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # 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 MySQLコンテナを起動する MySQLのコンテナの準備が出来たので、また実際に起動してみます。 docker-sompose.ymlのあるディレクトリで以下のコマンドを実行します。 $ docker-compose up -d --build $ docker-compose ps NAME COMMAND SERVICE STATUS PORTS docker_sample-app-1 "docker-php-entrypoi…" app running 9000/tcp docker_sample-db-1 "docker-entrypoint.s…" db running 0.0.0.0:3306->3306/tcp docker_sample-web-1 "/docker-entrypoint.…" web running 0.0.0.0:8081->80/tcp api(PHPのコンテナ)、web(nginxのコンテナ)、db(MySQLのコンテナ)の3つが起動できました。 コンテナの動作確認 MySQLのバージョンを確認します。 $ docker compose exec db mysql -V mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL) ※この後Laravelのプロジェクトを作成したら、このDBとLaravelを接続します。 そしてマイグレーションを行ってDBにテーブルを作成してから、MySQLにログインしてDBを使う動作確認をしたいと思います。 Laravelのインストール Laravelプロジェクトの作成 LEMP環境が構築できたので、Laravelのアプリケーションを作っていきます。 appコンテナに入り、Laravelをインストールします。 $ docker compose exec app bash [app]:/app$ composer create-project --prefer-dist "laravel/laravel=8.*" . [app]:/app$ php artisan -v Laravel Framework 8.81.0 コンテナ内の/app配下にLaravelのプロジェクトが新規作成され、ホスト側の/src配下にも同じくLaravelのプロジェクトが出来ました。 localhost:8081にアクセスして、ブラウザでもLaravelのウェルカムページが表示できることを確認します。 DB接続 appコンテナ(Laravel)からdbコンテナ(MySQL)へ接続する設定を行います。 Laravelではデータベースへの接続設定を .env ファイルに定義しているので、 /src/.env のDBの部分を以下のように修正します。 /src/.env DB_CONNECTION=mysql DB_HOST=db // MySQLコンテナのサービス名 DB_PORT=3306 DB_DATABASE=database DB_USERNAME=root DB_PASSWORD=passwor DB_HOST にはMySQLコンテナのサービス名を指定します。 その他の項目もMySQLコンテナで設定した値(今回はdocker-compose.ymlのenviromentで定義)と同じ値を指定します。 DBに接続出来ているか確認する為、以下のコマンドを実行してマイグレーションを行います。 $ docker compose exec app bash [app]:/app$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (55.42ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (51.11ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (45.12ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (75.74ms) MySQLを使ってみる MySQLのコンテナに入ってDBを確認してみます。 $ docker-compose exec db bash [db]:/$ mysql -u root -p // パスワードを求められるので入力 [db] mysql> use database; [db] mysql> show tables; +------------------------+ | Tables_in_database | +------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | users | +------------------------+ 5 rows in set (0.00 sec) 先ほどマイグレーションを実行したので、Laravelのデフォルトで用意されているマイグレーションファイル通りにテーブルが作成されていることが確認できました。 最後に これでDocker(Docker Compose)を使ったLEMP環境の構築が完了です。 今回作成した環境は非常にシンプルなものなので、業務で使うとなるともっと設定を細やかに行ったり、開発環境・ステージング環境・本番環境それぞれの設定ファイルを用意して設定を切り替えたりといった作業が必要になるかと思いますが、仕組みが分かればあとは全てこの延長にあるのかなと思います。 またいずれ環境ごとの設定ファイル切り替えだったり、あとはNodeのコンテナを使ってNext.jsの環境構築もやってみたいです。 参考記事
- 投稿日:2022-01-31T00:58:20+09:00
PHP の error_reportingディレクティブ
error_reporting ディレクティブとは phpinfo() を実行すると、 error_reporting ディレクティブという設定値が整数値として表示されます。 この error_reporting ディレクティブは、エラー出力レベルを設定するもので、以下のエラーの種類それぞれについて、どのエラーを出力するかを 16ビットのビットマスクで構成し、10進数で表したものとなっています。 設定値 定義済み変数 説明 1 E_ERROR 重大な実行時エラー。 2 E_WARNING 実行時の警告 (致命的なエラーではない)。 4 E_PARSE コンパイル時のパースエラー。 8 E_NOTICE 実行時の注意メッセージ。 16 E_CORE_ERROR PHPの初期始動時点での致命的なエラー。 32 E_CORE_WARNING (致命的ではない)警告。 64 E_COMPILE_ERROR コンパイル時の致命的なエラー。 128 E_COMPILE_WARNING コンパイル時の警告(致命的ではない) 256 E_USER_ERROR ユーザーによって発行されるエラーメッセージ。 512 E_USER_WARNING ユーザーによって発行される警告メッセージ。 1024 E_USER_NOTICE ユーザーによって発行される注意メッセージ。 2048 E_STRICT コードの互換性を維持するためのコードの変更についての提案メッセージ。 4096 E_RECOVERABLE_ERROR キャッチできる致命的なエラー。 8192 E_DEPRECATED 実行時の注意。将来のバージョンで動作しなくなるコードについての警告。 16384 E_USER_DEPRECATED ユーザー定義の E_DEPRECATED同等の警告メッセージ。 error_reporting の設定値をわかりやすく しかしながら、これらのビットがどのエラーを表しているかを覚えるのもたいへんで、ビットマスクの計算も面倒ですよね。 そこで、 error_reporting の整数値をエラーの定義済み定数に分解する小さなプログラムを作成しました。 error_reporting.php <?php $level = $argv[1]; $types = array( 'E_ERROR', 'E_WARNING', 'E_PARSE', 'E_NOTICE', 'E_CORE_ERROR', 'E_CORE_WARNING', 'E_COMPILE_ERROR', 'E_COMPILE_WARNING', 'E_USER_ERROR', 'E_USER_WARNING', 'E_USER_NOTICE', 'E_STRICT', 'E_RECOVERABLE_ERROR', 'E_DEPRECATED', 'E_USER_DEPRECATED' ); foreach ($types as $type) { eval ("\$error['$type'] = $type ;"); $on = ($level & $error[$type]) == 0 ? '-' : '+'; echo implode(':', array($on,$type,$error[$type])).PHP_EOL; } 使い方 php error_reporging.php [error_reportingの設定値] error_reporting の設定値を引数にして、実行すると以下のように出力されます。各定義済み変数の左が「+」となっていれば出力されるエラー、「-」の場合は表示されないエラーです。 $ php error_reporting.php 22519 +:E_ERROR:1 +:E_WARNING:2 +:E_PARSE:4 -:E_NOTICE:8 +:E_CORE_ERROR:16 +:E_CORE_WARNING:32 +:E_COMPILE_ERROR:64 +:E_COMPILE_WARNING:128 +:E_USER_ERROR:256 +:E_USER_WARNING:512 +:E_USER_NOTICE:1024 -:E_STRICT:2048 +:E_RECOVERABLE_ERROR:4096 -:E_DEPRECATED:8192 +:E_USER_DEPRECATED:16384 よろしければお納めください。
- 投稿日:2022-01-31T00:58:20+09:00
error_reportingディレクティブ
error_reporting ディレクティブとは phpinfo() を実行すると、 error_reporting ディレクティブという設定値が整数値として表示されます。 この error_reporting ディレクティブは、エラー出力レベルを設定するもので、以下のエラーの種類それぞれについて、どのエラーを出力するかを 16ビットのビットマスクで構成し、10進数で表したものとなっています。 設定値 定義済み変数 説明 1 E_ERROR 重大な実行時エラー。 2 E_WARNING 実行時の警告 (致命的なエラーではない)。 4 E_PARSE コンパイル時のパースエラー。 8 E_NOTICE 実行時の注意メッセージ。 16 E_CORE_ERROR PHPの初期始動時点での致命的なエラー。 32 E_CORE_WARNING (致命的ではない)警告。 64 E_COMPILE_ERROR コンパイル時の致命的なエラー。 128 E_COMPILE_WARNING コンパイル時の警告(致命的ではない) 256 E_USER_ERROR ユーザーによって発行されるエラーメッセージ。 512 E_USER_WARNING ユーザーによって発行される警告メッセージ。 1024 E_USER_NOTICE ユーザーによって発行される注意メッセージ。 2048 E_STRICT コードの互換性を維持するためのコードの変更についての提案メッセージ。 4096 E_RECOVERABLE_ERROR キャッチできる致命的なエラー。 8192 E_DEPRECATED 実行時の注意。将来のバージョンで動作しなくなるコードについての警告。 16384 E_USER_DEPRECATED ユーザー定義の E_DEPRECATED同等の警告メッセージ。 error_reporting の設定値をわかりやすく しかしながら、これらのビットがどのエラーを表しているかを覚えるのもたいへんで、ビットマスクの計算も面倒ですよね。 そこで、 error_reporting の整数値をエラーの定義済み定数に分解する小さなプログラムを作成しました。 error_reporting.php <?php $level = $argv[1]; $types = array( 'E_ERROR', 'E_WARNING', 'E_PARSE', 'E_NOTICE', 'E_CORE_ERROR', 'E_CORE_WARNING', 'E_COMPILE_ERROR', 'E_COMPILE_WARNING', 'E_USER_ERROR', 'E_USER_WARNING', 'E_USER_NOTICE', 'E_STRICT', 'E_RECOVERABLE_ERROR', 'E_DEPRECATED', 'E_USER_DEPRECATED' ); foreach ($types as $type) { eval ("\$error['$type'] = $type ;"); $on = ($level & $error[$type]) == 0 ? '-' : '+'; echo implode(':', array($on,$type,$error[$type])).PHP_EOL; } 使い方 php error_reporting.php [error_reportingの設定値] error_reporting の設定値を引数にして、実行すると以下のように出力されます。各定義済み変数の左が「+」となっていれば出力されるエラー、「-」の場合は表示されないエラーです。 $ php error_reporting.php 22519 +:E_ERROR:1 +:E_WARNING:2 +:E_PARSE:4 -:E_NOTICE:8 +:E_CORE_ERROR:16 +:E_CORE_WARNING:32 +:E_COMPILE_ERROR:64 +:E_COMPILE_WARNING:128 +:E_USER_ERROR:256 +:E_USER_WARNING:512 +:E_USER_NOTICE:1024 -:E_STRICT:2048 +:E_RECOVERABLE_ERROR:4096 -:E_DEPRECATED:8192 +:E_USER_DEPRECATED:16384 よろしければお納めください。