Yucchiy's Note

Laravel5をDockerで動かす

January 16, 2015

    Laravel5で構築したアプリケーションの開発環境をDocker化するための方法を紹介する.

    サンプルアプリケーションはこちらで確認できる.

    準備

    以下の環境でLaravel5が動作するように, 環境を構築する.

    • OS

      • Ubuntu 14.04
    • ミドルウェア

      • Nginx
      • php-fpm 5.5
      • MySQL 5.5

    Laravel5のインストール

    まず, laravel5をインストールする. インストールにはcomposerを用いる. create-projectの時に, dev-developを指定するとインストールできる.

    mkdir dockerized-laravel5
    cd dockerized-laravel5
    composer create-project laravel/laravel application dev-develop

    PHPコンテナ

    nginx+php-fpmな環境で, laravelが動作するようにDockerfileを記述する.

    Dockerでは, 1コンテナ1プロセスにするのが好ましいが, 今回はnginxとphp-fpmを1コンテナで動かすために, supervisordをもちいる.

    Dockerfileは以下のとおり.

    FROM ubuntu:14.04
    
    ENV DEBIAN_FRONTEND noninteractive
    
    RUN apt-get update
    
    RUN apt-get install -yq --force-yes build-essential wget curl git ssh nginx nodejs-legacy npm mysql-client supervisor
    
    RUN apt-get install -yq --force-yes  php5-cli php5 php5-fpm php5-mysql php5-curl php5-mcrypt php5-memcached && \
        apt-get clean && \
        rm -rf /var/lib/apt/lists/*
    
    RUN php5enmod mcrypt
    
    RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    
    RUN mkdir -p /app
    WORKDIR /app
    
    ADD ./application/database /app/database
    ADD ./application/tests /app/tests
    ADD ./application/composer.json /app/composer.json
    ADD ./application/composer.lock /app/composer.lock
    RUN composer install --no-scripts
    
    ADD ./application /app
    RUN php artisan clear-compiled
    RUN php artisan optimize
    
    RUN usermod -u 1000 www-data
    RUN groupmod -g 1000 www-data
    
    RUN chown -R www-data:www-data /app
    
    RUN echo "daemon off;" >> /etc/nginx/nginx.conf
    ADD docker/nginx-site.conf /etc/nginx/sites-available/default
    
    ADD docker/supervisord.conf /etc/supervisord.conf
    
    EXPOSE 80
    
    CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]

    supervisord.conf

    [unix_http_server]
    file=/tmp/supervisor.sock
    
    [supervisord]
    logfile=/tmp/supervisord.log
    pidfile=/tmp/supervisord.pid
    nodaemon=false
    
    [rpcinterface:supervisor]
    supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
    
    [supervisorctl]
    serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
    
    [program:php5-fpm]
    command=/usr/sbin/php5-fpm -c /etc/php5/fpm --nodaemonize
    
    [program:nginx]
    command=/usr/sbin/nginx

    このDockerfileのポイントについて説明する.

    composer.jsonを先にADDする

    ADD ./application /appを行ったあとにRUN composer installとすると, composer.jsonの変更の有無にかかわらず, ./application内のどれかのファイルが変更されるごとに composer installが走る. そこで, 先にADD ./application/composer.json /app/composer.jsonRUN composer installを行って, その後に/applicationADDすることで, composer.jsonに変更がない場合は, その行がキャッシュされる. これは, RubyのBundlerで紹介されているテクニックをそのまま用いている.

    ただし, composerの場合はそのままではうまくいかない. 問題は2つある.

    • composer.jsonautoloadに指定されたclassmapに対応するクラスは, composer install時に存在する必要がある.
    • composer install後に, composer.json内のscriptsに記述された処理が実行される.

    この対処方法について述べる.

    composerのautoloadに対応する

    まず, composer.jsonのautoloadに対応する. といっても, composer install時にautoloadが必要とするクラスが存在するだけでOKなので, application内の, databasetestsディレクトリをADDすれば良い.

    ADD ./application/database /app/database
    ADD ./application/tests /app/tests

    composerのscriptsに対応する

    comopserは, 実行後に特定の処理を実行するためのフックが存在する. 具体的にはcomposer.json内のscriptsの項目の処理が実行される.

    laravelでは, composer install後に, 以下の処理が実行される.

    php artisan clear-compiled
    php artisan optimize

    artisanは, laravelのコマンドラインツールだが, これはlaravelのアプリケーションディレクトリが存在しないと実行できない. 上記のテクニックでは, composer install時には./application内のすべてのファイルが存在しないため, artisanが正常に実行できずにエラーとなる.

    この問題は, composer install実行後に, scriptsに記述された処理を行わず, laravelのアプリケーションディレクトリを追加後に, scriptsの処理を行うことで対処できる.

    具体的には, composer install--no-scripts付きで実行し, ./applicationをADDした後に, scriptsの項目の処理を実行する.

    RUN composer install --no-scripts
    
    ADD ./application /app
    RUN php artisan clear-compiled
    RUN php artisan optimize

    .dockerignore

    Dockerfileとは直接は関係ないが, Dockerコンテナに含める必要のないファイルやディレクトリは, .dockerignoreに記述することで, ADDやCOPYの対象から外れる.

    .gitなどは省いておくのと, laravelアプリケーションディレクトリ内の, vendorcomposer.lockも対象から省いておかないと, Dockerコンテナ側でビルドした内容と, Dockerホスト側のディレクトリには差異が生じて, アプリケーションが動かない可能性がある.

    MySQLコンテナ

    この記事と同様に, Docker Hub公式のhttps://registry.hub.docker.com/_/mysql/を用いる.

    dockerized-laravel5直下にfig.xmlを用意し, 以下のように記述する.

    環境変数をenvironment:に設定することで, MySQLの設定を変更することができる.

    また, my.cnfなどの設定を変更したい場合は, Dockerコンテナ内の/etc/mysqlに, ローカルのディレクトリをマウントすることで設定を置き換える方法もある(/path/to/mysql:/etc/mysqlをvolumesに追記する).

    fig.yml

    以上のDockerfileからPHPコンテナを立ち上げるためのFigの設定を示す. dockerized-laravel5ディレクトリ直下に, 以下の様な内容のfig.ymlを設置する.

    db:
      image: mysql:5.5
      environment:
        - MYSQL_ROOT_PASSWORD=himitsunopassword
      ports:
        - "3306"
      volumes:
        - /var/lib/mysql
    web:
      build: .
      ports:
        - "8080:80"
      volumes:
        - ./application:/app
      links:
        - db

    開発用途で利用するために, volumes項目に, ./application:/appと記述する. こうすることで, ローカルでファイルを編集した内容が即座にDockerコンテナ内に反映される.

    PHPコンテナとMySQLコンテナのリンクと設定

    上記の設定では, Dockerのlink機能を用いて, PHPコンテナからMySQLコンテナ内のデータベースを利用できるようになっている.

    アプリケーションからMySQLコンテナのDBに接続するための設定は, 環境変数を経由して取得できる.

    環境変数の一覧は, 以下のように確認できる.

    $ docker exec -it dockerizedlaravel5_web_1 env
    
    DB_1_PORT_3306_TCP_ADDR=172.17.0.13
    DB_1_PORT_3306_TCP_PORT=3306
    DB_1_NAME=/dockerizedlaravel5_web_1/db_1
    DB_1_ENV_MYSQL_ROOT_PASSWORD=himitsunopassword

    laravelのデータベースの設定は, env関数を利用して行う.

        'mysql' => [
            'driver'    => 'mysql',
            'host'      => env('DB_1_PORT_3306_TCP_ADDR', 'localhost'),
            'database'  => env('DB_DATABASE', 'forge'),
            'username'  => env('DB_USERNAME', 'root'),
            'password'  => env('DB_1_ENV_MYSQL_ROOT_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],

    開発環境として利用する

    figで管理しているコンテナの状態は, fig psで確認できる.

    $ fig ps
              Name                        Command               State             Ports
    --------------------------------------------------------------------------------------------
    dockerizedlaravel5_db_1    /entrypoint.sh mysqld            Exit 0   0.0.0.0:49153->3306/tcp
    dockerizedlaravel5_web_1   /usr/bin/supervisord -n -c ...   Exit 0

    起動は, fig upで行う.

    $ fig up
    Recreating dockerizedlaravel5_db_1...
    Recreating dockerizedlaravel5_web_1...
    Attaching to dockerizedlaravel5_db_1, dockerizedlaravel5_web_1

    起動中のコンテナでコマンドを実行するにはdocker execを利用する.

    $ docker exec dockerizedlaravel5_web_1 ./artisan route:list
    +--------+--------------------------------+-------------------------------------------------------+------+------------------------------------------------------------+------------+
    | Domain | Method                         | URI                                                   | Name | Action                                                     | Middleware |
    +--------+--------------------------------+-------------------------------------------------------+------+------------------------------------------------------------+------------+
    |        | GET|HEAD                       | /                                                     |      | App\Http\Controllers\WelcomeCo[email protected]               |            |
    |        | GET|HEAD                       | home                                                  |      | App\Http\Controllers\[email protected]                  |            |
    |        | GET|HEAD                       | auth/register/{one?}/{two?}/{three?}/{four?}/{five?}  |      | App\Http\Controllers\Auth\[email protected]       |            |
    |        | POST                           | auth/register/{one?}/{two?}/{three?}/{four?}/{five?}  |      | App\Http\Controllers\Auth\[email protected]      |            |
    |        | GET|HEAD                       | auth/login/{one?}/{two?}/{three?}/{four?}/{five?}     |      | App\Http\Controllers\Auth\[email protected]          |            |
    |        | POST                           | auth/login/{one?}/{two?}/{three?}/{four?}/{five?}     |      | App\Http\Controllers\Auth\[email protected]         |            |
    |        | GET|HEAD                       | auth/logout/{one?}/{two?}/{three?}/{four?}/{five?}    |      | App\Http\Controllers\Auth\[email protected]         |            |
    |        | GET|HEAD|POST|PUT|PATCH|DELETE | auth/{_missing}                                       |      | App\Http\Controllers\Auth\[email protected]     |            |
    |        | GET|HEAD                       | password/email/{one?}/{two?}/{three?}/{four?}/{five?} |      | App\Http\Controllers\Auth\[email protected]      |            |
    |        | POST                           | password/email/{one?}/{two?}/{three?}/{four?}/{five?} |      | App\Http\Controllers\Auth\[email protected]     |            |
    |        | GET|HEAD                       | password/reset/{one?}/{two?}/{three?}/{four?}/{five?} |      | App\Http\Controllers\Auth\[email protected]      |            |
    |        | POST                           | password/reset/{one?}/{two?}/{three?}/{four?}/{five?} |      | App\Http\Controllers\Auth\[email protected]     |            |
    |        | GET|HEAD|POST|PUT|PATCH|DELETE | password/{_missing}                                   |      | App\Http\Controllers\Auth\[email protected] |            |
    +--------+--------------------------------+-------------------------------------------------------+------+------------------------------------------------------------+------------+

    また, コンテナ内にログインしたい場合は, docker execで, /bin/bashなどを実行する.

    $ docker exec -it dockerizedlaravel5_web_1 /bin/bash
    [email protected]:/app# ls
    app  artisan  bootstrap  composer.json  composer.lock  config  database  gulpfile.js  package.json  phpspec.yml  phpunit.xml  public  readme.md  resources  storage  tests  vendor

    まとめ

    LaravelをDockerで動かすための方法を紹介した.

    また このテクニックをcomposerに応用するためのテクニックをいくつか示した.

    次に, 用意したDockerfileを用いて, テスト・CI・デプロイについて紹介していきたい.


    Yuichiro MUKAI
    Yuichiro MUKAIGame & Web Programmer

    シブヤで働くゲームプログラマー. C#(For Unity)をメインに, 趣味でPHPなどを書きます.

    Twitter / Facebook