手軽な環境構築!DockerでのLaravel環境構築スタートガイド

Docker

こんにちは!

今回は、Dockerを使ってLaravelの開発環境の構築を手軽に行う方法を解説していきます。

Dockerの中身のコードに関してもわかりやすく解説していきます。

以下のような方が対象です。

  • Dockerでの環境構築方法がわからない
  • いつもコピペしてて応用が効かない
  • Dockerfileの中身がわからない

この記事を読むことで、以下のことがわかります。

  • Dockerの基礎知識
  • Dockerを使ったLaravel開発環境の構築
  • Docker Composeを使ったコンテナ管理
  • 具体的なDockerコードの意味
  • Docker環境のカスタマイズ

以下、筆者の経験です。

  • プログラミング歴3年以上
  • 現役ITエンジニア
  • 自社サービスを0から開発した経験複数あり

それでは早速見ていきましょう!

本記事の前提条件

注意点として、今回はMacOSを想定した記事となっております。

その点ご注意ください。

また、Docker Desktopはすでにインストールが完了しているものとして進めます。

まだDocker Desktopをインストールしていないという方は、以下の記事からインストールすることをおすすめします。

【入門】Docker Desktopとは何ができるの?インストールと使い方

Dockerの基礎知識

Dockerとは

Dockerとは、Docker社が開発しているコンテナ型の仮想環境を作成、配布、実行するためのプラットフォームです。

Dockerはホストマシンのカーネルを利用し、ホストOSの上にコンテナと呼ばれる仮想環境を作成することができます。

このコンテナ型仮想環境の中で、アプリケーションを開発したり動かしたりすることができるわけです。

Dockerのメリット

では、Dockerを使うとなぜ良いのでしょうか。

それは以下のような利点があるからです。

  1. インフラをコード化することができる
  2. ファイルを共有することで誰でも全く同じ環境を手早く作ることができる
  3. 本番環境へのデプロイが容易

例えば開発チームがあったとして、開発メンバーはある人はWindowsのPC、ある人はMacのPCとバラバラで存在すると、ある人の環境ではアプリケーションが動くが、他方の環境では動かないといった現象が発生することがあります。

ところが、Dockerを使っていれば、開発メンバーは環境が同じなので一人一人アプリケーションの挙動が違うなんてことは起こりません。

こういった利点から、現在ではDockerが広く使用されるようになりました。

DockerでのLaravel環境構築手順

さて、ここから本記事の主題であるDockerを使ったLaravelの環境構築手順を解説していきます。

今回は以下の構成の環境を作成していきます。

  • Webサーバー→Apache
  • プログラミング言語→PHP
  • フレームワーク→Laravel
  • データベース→MySQL
  • データベース管理→phpMyAdmin

次に、ディレクトリ構成は以下のようになります。

app
docker
  |_app
  |  |_Dockerfile
  |  |_000-default.conf
  |  |_php.ini
  |_db
     |_Dockerfile
     |_my.cnf
docker-compose.yml

今回のデモ環境のディレクトリを容易しておきましょう

$ mkdir demo
$ cd demo

Laravelプロジェクト作成

まずはじめに、Laravelプロジェクトを作成していきましょう。

以下のコマンドを叩きます。

$ composer create-project laravel/laravel=9.x --prefer-dist app

すると、/appのLaravelプロジェクトのディレクトリが作成されます。

Dockerfileの作成と設定

このセクションからは本記事の主題であるDockerfileの書き方などを解説していきます。

PHPとApacheの作成、設定

次に、PHPとApacheのDockerfileの作成に移っていきます。

docker/appディレクトリに移動し、Dockerfileを作成します。

$ mkdir docker && cd docker // dockerディレクトリ作成して移動
$ mkdir app && cd app // appディレクトリ作成して移動
$ touch Dockerfile // Dockerfile作成

少し補足しておくと、Dockerfileを作成する際のファイル名は必ずDockerfileにしておいてください。 これは決まりになっており、別の名前にすると動作しません。 ファイル名を変更することもできますが、その場合は別途設定を追加する必要が出てきます。

次に、テキストエディタでDockerfileを編集します。

FROM php:8.1-apache

COPY /docker/app/php.ini /usr/local/etc/php/
COPY /docker/app/000-default.conf /etc/apache2/sites-available/000-default.conf

# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

RUN apt-get update \
    && apt-get install -y \
    git \
    zip \
    unzip \
    vim \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql

# Laravelで必要になるmodRewriteを有効化する
RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite

順番に解説していきます。

まず、一番上の行。

FROM php:8.1-apache

Dockerfileでは、インストラクションと呼ばれるものに引数をつけて実行するのが基本です。

インストラクションには以下の種類があります。

  • FROM・・・新たに作成するDockerイメージのベースとなるイメージを指定する
  • RUN・・・Dockerイメージのビルド時に実行するシェルスクリプト
  • CMD・・・Dockerコンテナが立ち上がった時にデフォルトで実行されるコマンド
  • COPY・・・ホストのファイルやディレクトリをDockerイメージにコピーする
  • ENV・・・Dockerfile内の環境変数を設定します。設定された環境変数はDockerfile内で使うことができ、Dockerイメージから生成される全てのコンテナで使用可能です
  • WORKDIR・・・コマンドを実行する作業ディレクトリを指定します

そのため、一番上の行はこれから作成するDockerイメージのベースとなるイメージを指定しています。 今回の場合はPHPとApacheが入ったイメージをベースイメージとして指定しています。

次に、以下コマンドでPHPの設定ファイルとApacheの設定ファイルをコンテナの中にコピーしています。

COPY /docker/app/php.ini /usr/local/etc/php/
COPY /docker/app/000-default.conf /etc/apache2/sites-available/000-default.conf

次に、以下コマンドで今後必要となるcomposerをインストールしています。

RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

次に、以下コマンドでgitやzipやvimなどの必要となるコマンドをインストールします。

RUN apt-get update \
    && apt-get install -y \
    git \
    zip \
    unzip \
    vim \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql

ポイントは、最初にapt-get updateでパッケージマネージャを最新の状態に更新してから実行することと、pdo_mysqlをインストールすることです。

最後に、Apacheで必要となるmod_rewriteを有効化しておきます。

RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite

mod_rewriteを簡単に説明しておくと、URLの操作やリダイレクト処理などを行なっているApacheの機能で、有効/無効に設定することができます。

基本的には有効にしておきましょう。

次に、PHPの設定ファイルであるphp.iniを作成します。

touch php.ini

テキストエディタを開いて編集します。

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.language = "Japanese"

memory_limit = 1024M
post_max_size = 256M
upload_max_filesize = 50M
max_execution_time = 600
extension=pdo_mysql

ここではタイムゾーンや日本語設定、メモリサイズなどの最低限の設定を行なっています。

次にApacheの設定ファイルを作成していきます。

$ cd .. && touch 000-default.conf // docker/appディレクトリに移動してファイル作成

テキストエディタを開いて編集します。

<VirtualHost *:80>
    DocumentRoot /var/www/html/public

    <Directory /var/www/html/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

MySQLの作成、設定

同様に、dbディレクトリを作成してMySQLのDockerfileも作成していきます。

mkdir db && cd db
touch Dockerfile

テキストエディタで編集します。

FROM mysql:8.0.29-debian

# 設定ファイルの上書き
COPY /docker/db/my.cnf /etc/mysql/conf.d
RUN chmod 644 /etc/mysql/conf.d

RUN mv /etc/apt/sources.list.d/mysql.list /etc/apt/sources.list.d/mysql.list.disabled
RUN apt-get update && apt-get install -y curl && apt-get clean
RUN curl -sSfL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --import
RUN gpg --batch --export "B7B3B788A8D3785C" > /etc/apt/keyrings/mysql.gpg
RUN mv /etc/apt/sources.list.d/mysql.list.disabled /etc/apt/sources.list.d/mysql.list

RUN apt-get update && apt-get install -y locales \
    && echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen \
    && locale-gen ja_JP.UTF-8 \
    && update-locale LANG=ja_JP.UTF-8

ENV LANG ja_JP.UTF-8

CMD ["mysqld"]

こちらも流れはPHP,Apacheの場合とほぼ同様です。

以下でベースイメージを指定します。

FROM mysql:8.0.29-debian

次に、MySQLの設定ファイルをコンテナ内にコピーします。

COPY /docker/db/my.cnf /etc/mysql/conf.d
RUN chmod 644 /etc/mysql/conf.d

以下は、通常は設定しなくても動くはずですが、2024/02/21時点の筆者の環境ではGPGキーエラーが発生しておりましたので追記しました。

RUN mv /etc/apt/sources.list.d/mysql.list /etc/apt/sources.list.d/mysql.list.disabled
RUN apt-get update && apt-get install -y curl && apt-get clean
RUN curl -sSfL <https://repo.mysql.com/RPM-GPG-KEY-mysql-2023> | gpg --import
RUN gpg --batch --export "B7B3B788A8D3785C" > /etc/apt/keyrings/mysql.gpg
RUN mv /etc/apt/sources.list.d/mysql.list.disabled /etc/apt/sources.list.d/mysql.list

次に、ロケールや文字コードなどの設定を行います。

RUN apt-get update && apt-get install -y locales \
    && echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen \
    && locale-gen ja_JP.UTF-8 \
    && update-locale LANG=ja_JP.UTF-8

最後に、環境変数を設定してコンテナ起動時にmysqldコマンドを叩きます。

ENV LANG ja_JP.UTF-8
CMD ["mysqld"]

こちらでDockerfileは完成になります。

次に、MySQLの設定ファイルを作成します。

$ touch my.cnf

テキストエディタで編集します。

[client]
default-character-set=utf8mb4
[mysqld]
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

ここでは、デフォルトの文字コードその他の設定を行なっています。

Docker Composeを使ったコンテナの管理

ここまでで、PHP,Apache,MySQLのDockerfileを作成を完了しました。

Dockerfileは全て作成しましたので、最後にdocker-compose.ymlを作成してDocker Composeを利用したDockerコンテナの管理ができるようにしましょう。

まずはファイルを作成します。

$ touch docker-compose.yml // demoディレクトリ直下に作成

テキストエディタで編集していきます。

version: '3'

services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    container_name: prdev_app
    volumes:
      - ./app:/var/www/html
    ports:
      - 8000:80
    depends_on:
      - database
  database:
    build:
      context: .
      dockerfile: docker/db/Dockerfile
    container_name: prdev_db
    platform: linux/amd64
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 管理ユーザのパスワード
      MYSQL_DATABASE: データベース名
      MYSQL_USER: ユーザ名
      MYSQL_PASSWORD: ユーザのパスワード
    ports:
      - 3306:3306
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: prdev_phpmyadmin
    environment:
      PMA_HOST: database
      PMA_PORT: 3306
    ports:
      - 8080:80
    depends_on:
      - database
volumes:
  db_data: {}

docker-compose.ymlファイルでは、各コンテナをサービスとして記述していきます。

ファイル内のservices直下に記述していきます。

補足として、docker-compose.ymlはインデントで各ブロックを識別しており、インデントがズレているとうまく動作しませんので、ご注意ください。

以下はPHP,Apacheのコンテナです。

app:
  build:
    context: .
    dockerfile: docker/app/Dockerfile
  container_name: prdev_app
  volumes:
    - ./app:/var/www/html
  ports:
    - 8000:80
  depends_on:
    - database

それぞれの意味を解説します。

build・・・Dockerfileを指定しています。contextとありますが、これはどこをルートディレクトリとするかの指定を行なっており、今回の場合はdocker-compose.ymlファイルが置かれているディレクトリをルートディレクトリとしています。

container_name・・・コンテナが起動する際のコンテナ名です。

volumes・・・ホスト(ローカル)とコンテナ内のファイルやディレクトリを同期させています。この同期することを、一般的に「マウント」と呼びます。記述方法は、「ホスト側の相対パス:コンテナ側の絶対パス」となります。

ports・・・「ホスト側のポート:コンテナ側のポート」と記述します。ホスト側のポートをコンテナ側のポート繋げています。

depends_on・・・依存関係を示しています。今回の場合は、appサービスをdatabaseサービスに依存させています。この場合、databaseサービスのコンテナ起動を待ってからPHP,Apacheのコンテナを起動します。逆にコンテナを停止するときは、appサービスのコンテナを停止してからdatabaseサービスのコンテナを停止します。

他のサービスも似たような書き方のため、以降は差分のみ説明していきます。

database:
  build:
    context: .
    dockerfile: docker/db/Dockerfile
  container_name: prdev_db
  platform: linux/amd64
  command: --default-authentication-plugin=mysql_native_password
  volumes:
    - db_data:/var/lib/mysql
  environment:
    MYSQL_ROOT_PASSWORD: 管理ユーザのパスワード
    MYSQL_DATABASE: データベース名
    MYSQL_USER: ユーザ名
    MYSQL_PASSWORD: ユーザのパスワード
  ports:
    - 3306:3306

platform・・・これはM1Mac用の記述で、apple slicon製のPCの場合は必要な記述です。

environment・・・環境変数の設定。MySQLのデータベース名やユーザ名、パスワードなどを設定します。

以下、phpmyadminも同様に記述していきます。

これで、docker-compose.ymlも作成できたので、最後にコンテナを立ち上げてみましょう。

$ docker-compose build // イメージをビルド
$ docker-compose up -d // コンテナ起動

正常に立ち上がって、以下にアクセスできれば成功です。

http://localhost:8000

Laravel側のデータベース接続設定

コンテナがうまく立ち上がっても、現状のままだとLaravel側からMySQLに接続することができません。

最後にLaravel側の設定を追加しておきましょう。

$ cd app

envファイルをテキストエディタで編集します。

DB_CONNECTION=mysql
DB_HOST=database #データベースのサービス名
DB_PORT=3306
DB_DATABASE=データベース名
DB_USERNAME=ユーザ名
DB_PASSWORD=パスワード

注意点として、DB_HOSTはデータベースのサービス名を記述してください。

はい!これでLaravelからMySQLコンテナへ接続することができました。

応用編:カスタマイズや最適化

このセクションは応用編ということで、より効率的で保守性の高いDockerfileの書き方や設計を説明していきます。

いくつかあるとは思いますが、以下は筆者が重要だと思うものを取り上げます。

非root権限として実行する

コンテナ内でプログラムをrootユーザで実行するとセキュリティ的なリスクがあります。

root権限が与えられると、全てのファイルが見えてしまい、操作可能になってしまいます。

非root権限として実行するようにするには、以下のような設定を行なっておく必要があります。

FROM php:8.1-apache

...

# 以下2行を追加
RUN adduser myuser && chown -R myuser /var/www/html
USER myuser

不要なパッケージをインストールしない

たぶん必要だろうということで、あれもこれもパッケージをインストールしたり、どこかの記事を何も考えずにコピペしたりすると、ファイル容量や複雑さが増してしまいます。

できる限り不要なパッケージはインストールしないようにしましょう。

--no-install-recommendsオプションをつけると、不要なパッケージのインストールを避けることができるようです。

RUN apt-get update \
    && apt-get install -y --no-install-recommends \ # オプション追加
    git \
    zip \
    unzip \
    vim \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql

ビルドコンテキストの利用

ビルドコンテキストを利用すると、指定したディレクトリ以下のファイルを全てDocker deamonに送信します。

build:
  context: .

ビルドコンテキストで指定したディレクトリ以下のファイル容量があまりに多いと、buildに時間がかかってしまったり、メモリ消費が激しくなります。

そのため、Dockerfile用のディレクトリを作成し、余分なファイルやディレクトリを配置しないようにしましょう。

今回のデモでは、ディレクトリ構成が以下のようになっているため、余分なファイル等を配置しないように設計していました。

docker
  |_app
  |  |_Dockerfile
  |  |_000-default.conf
  |  |_php.ini
  |_db
     |_Dockerfile
     |_my.cnf

.dockerignoreを使った除外

ビルドコンテキストの利用のセクションと関係しますが、ビルドコンテキストで指定したディレクトリ配下に余分なファイル等が存在する場合でも、.dockerignoreで除外ファイルを指定すれば指定ファイルを無視できます。

記述方法などは以下から確認できます。

.dockerignore ファイル

マルチステージビルドを使う

マルチステージビルドとは、段階を分けてイメージのビルドを行うことができる機能です。

DockerfileにFROM行を複数書くことで、各段階で異なるベースイメージを指定できます。

最終的なイメージとして採用されるのは最終ステージなので、前ステージの成果物は最終ステージにコピーすることでマルチステージビルドを実現できます。

今回のデモではホスト側でLaravelをインストールしてコンテナにマウントしていました。

しかし、Dockerビルド時にLaravelをインストールしたいような場合もあるかと思います。

このような場合は、マルチステージビルドを活用することができ、Laravelを作成するパートとコンテナを作成するパートに分けることで、コンテナに余分なリソースが入らない工夫ができます。

具体的な例を見てみましょう。

# Laravelをインストールするパート
FROM amazonlinux:2 as vendor

# PHPインストール
RUN amazon-linux-extras install -y php8.1
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerのインストール
RUN cd /usr/bin && curl -s <http://getcomposer.org/installer> | php && ln -s /usr/bin/composer.phar /usr/bin/composer

# Laravelインストール
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

# コンテナを作成するパート
FROM php:8.1-apache

COPY --from=vendor /var/www/ /var/www/

COPY /docker/app/php.ini /usr/local/etc/php/
COPY /docker/app/000-default.conf /etc/apache2/sites-available/000-default.conf

RUN apt-get update \
    && apt-get install -y \
    git \
    zip \
    unzip \
    vim \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql
   
# Laravelで必要になるmodRewriteを有効化する
RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite

上記の例では、最初にAmazon LinuxのベースイメージでComposerのインストールやLaravelのインストールなどを行なっています。

後続のコンテナを作成するパートでは、前段階であるLaravelインストールの成果物として、「/var/www/laravel」だけをコンテナ内にコピーしています。

COPY --from=vendor /var/www/ /var/www/

このようにすることで、例えばComposerのリソースなどをコンテナ内に作らずにビルドでき、イメージサイズを軽減することができます。

最後に

いかがでしたでしょうか。

今回はDockerを使ったLaravelの環境構築ということで、環境構築方法をできる限り丁寧にわかりやすく解説していきました。

ただDockerを使って環境構築できるようになるだけではなく、コードの意味やパフォーマンスや保守性を意識した構築ができるようになってほしいという筆者の思いから今回記事を作成しました。

少しでもDockerを使った環境構築を行う開発者の助けになれば幸いです。

ではでは😊

参考リソース

参考リンク

Dockerfile を書くベストプラクティス

社内のDockerfileのベストプラクティスを公開します

マルチステージ ビルドを使う

作成したLaravelコンテナを軽量化する

参考書籍


仕組みと使い方がわかる Docker&Kubernetesのきほんのきほん [ 小笠原種高 ]

コメント

タイトルとURLをコピーしました