laravel
docker-compose

Dockerでlaravelの本番環境構築をしてみた (php-pfm,nginx, mysql, redis)

More than 1 year has passed since last update.

こんにちは

タイトルの通りに今日はDockerでlaravelの本番環境構築経験を述べたいと思います。

私はまだ日本語を勉強中なので。わかりにくいことがあれば申し訳ありません。

よろしくお願いします。

前回は開発環境について話しました。下記です。
http://qiita.com/mytv1/items/f26cd77f2801357dee8f

今回の本番構築は前回に述べた開発環境構築と違うところがいくらかあります。主にはサービス管理仕方とサービスをビルドイメージについてです。

詳細は下記にお書きします。

本番環境

下記は自分の本番環境情報です:

  • ソフトウェア:
    OS : alphine 3.5.2
    Docker-Client : 17.06.0-ce
    Docker-Server: 17.06.0-ce
    docker-compose : 1.15.0

  • ハードウェア:
    awsのサービスを使います。t2.mircoの3つのインスタントです。

今度はdocker swarmを使うということで、上の3つインスタントを一つの管理ノードと2つのワーカーノードに割り当てる予定です。

予定の環境構築

  1. docker swarm モードで走ります。
  2. 下記のサービスを上げます:
    • app : laravelの処理を行うサービス
    • web : nginxを持つサービス
    • database : データベース管理サービス
    • redis : redisサービス

ブラウザーの確認でデフォルトの「Laravel」が表示できると ok だという認識ですすめました。

まずはプロジェクトのディレクトリの構造について話したいと思います。

1. ディレクトリ構造

projectname/
    www/
        /* laravel source code */
    conf/
        web/
            prod.vhost.conf
            dev.vhost.conf
    prod.web.dockerfile
    prod.app.dockerfile
    prod.docker-compose.yml
    dev.web.dockerfile
    dev.app.dockerfile
    dev.docker-compose.yml

インターネット上で参考が見つからないので、構造は自分で考えて上記でしてみました。

  • www : laravelのマインのソースコードを置きる場所。
  • conf: サービスのコンテナーに入れたいファイルを置きる場所。今はnginxの vhost.confだけあります。
  • prod.<service-name>.dockerfile : 本番で利用するイメージをビルドする指定のdockerfile
  • prod.docker-compose.yml : 本番環境のdocker-compose設定。
  • dev.<service-name>.dockerfile : 開発環境でサービスのイメージをビルドするdockerfile
  • dev.docker-compose.yml : 開発環境のdocker-compose設定。

理由:

  • 本番と開発環境の設定が違うので、dockerfiledocker-compose.ymlも別にします。
  • wwwディレクトリはLaravelに関係するファイルだけを設置したいです。dockerに関するのは上にします。

この構造の問題:

  • dockerfileの数はサービスの数と一緒に上がります。サービスが増えると、ファイルが多くて見難くなります。
  • 他のモージュルのxdebugphpadminなどについてはまだ考えません。追加すると構築が難しくなるかわかりません。

ひとまずこれで次に勧めます。

2.docker-compose.yml

各設定ファイル内容を説明するという形式ですすめたいと思います。まずはdocker-compose.ymlです。

2.1. appサービス

version: '3'
services:

  # The Application
  app:
    image: myrepo/laravel-app
    working_dir: /var/www
    volumes:
      - /var/www/storage
    environment:
      - "DB_HOST=database"
      - "REDIS_HOST=cache"
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
    tty: true
    ports:
      - "9000:9000"

  • version: '3': docker-composeのバージョン。このバージョンは2よりswarmモードをよくサポートすると言われますので、このバージョンをすすめます。
  • image: myrepo/laravel-app サービスが利用するイメージ。このイメージをビルドやり方は前述にお話します。
  • volume記載されるボーリュームはcontainerがストップされても残るように宣言。laravelとしては、このディレクトリはログとキャッシュviewを保存しますね。
  • enviroment 環境変数を宣言します。Laravel利用する変数は.envだけにいれてもいいと思います。
  • logging ログ形を宣言します。私は「json-file」を選びます。
  • tty: true この項目はあまりわかりませんが、宣言しないとサービスが始まる途端すぐに止まることがあります。デプロイする時にこのサービスは自分自身のターミナルがなくて、pseudo terminal指定しない問題になるかなと思います。
  • ports: ホストの9000ポートをサービスの9000ポートに紐付けます。

2.2. webサービス

# The Web Server
  web:
    image: myrepo/laravel-web
    ports:
      - 80:80
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
    depends_on:
      - app

depends_on: webappサービスに依存するという関係を宣言します。記載しなくて、webサービスがappより先に
起動すると、nginxでappが見つからないエラーになります。

2.3. databaseサービス

   # The Database
  database:
    image: mysql:latest
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      - "MYSQL_DATABASE=homestead"
      - "MYSQL_USER=homestead"
      - "MYSQL_PASSWORD=secret"
      - "MYSQL_ROOT_PASSWORD=secret"
    ports:
        - "33061:3306"
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: 10

2.4. redisサービス

  # redis
  cache:
    image: redis:3.0-alpine

databaseredisサービスは公開のmysqlとredisイメージを利用します。

設定はこれで終わります。本番環境にこのファイルを投げればokだと思います!
次はappwebサービスが利用するイメージをビルドする仕方を話したいと思います。

3.docker イメージをビルド

前提:

docker hub アカウント所持しています。

例として、こらからはそのアカウント名は「myrepo」とします。

3.1. myrepo/laravel-app イメージをビルド

3.1.1. dockerfileを用意

prod.app.dockerfile

FROM php:7.0.4-fpm

COPY ./www/.env.prod ./www/.env
COPY ./www /var/www

WORKDIR /var/www

RUN apt-get update -y && apt-get install -y zip unzip

RUN docker-php-ext-install pdo mbstring

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php composer-setup.php \
    && php -r "unlink('composer-setup.php');" \
    && php composer.phar install --no-dev --no-scripts \
    && rm composer.phar

RUN chmod -R 777 /var/www/storage \
        /var/www/bootstrap/cache

RUN php artisan key:generate
RUN php artisan config:clear
RUN php artisan config:cache
RUN php artisan optimize
  • FROM php:7.0.4-fpm: ベースイメージの宣言
COPY ./www/.env.prod ./www/.env
COPY ./www /var/www

ソースコードをコピーします。.env.prodを事前に用意しました。

  • RUN apt-get update -y && apt-get install -y zip unzip .. && rm composer.phar : zip, unzip, pdo, mbstring, composerをインストールします
  • 最後のところのコマンドはLaravelセットアップコマンドですね。

3.1.2. dockerfileでイメージをビルド

docker build -f prod.app.dockerfile -t myrepo/laravel-app .

成功であれば、Successfully tagged myrepo/laravel-app:latestが表示されますね。

3.1.3. docker hubにイメージをプッシュ

docker login
docker push myrepo/laravel-app

appのイメージが制作できました!次の webのイメージビルドも同じです。

3.2. myrepo/laravel-web イメージをビルド

3.2.1. dockerfileを用意

prod.web.dockerfile

FROM nginx:1.10-alpine

ADD conf/web/prod.vhost.conf /etc/nginx/conf.d/default.conf

COPY ./www/public /var/www/public

publicディレクトリのコピーで、staticファイルを参照しはやくなるようにと言われるので、してみました。

vhost.conf

server {
    listen 80;
    index index.php index.html;
    root /var/www/public;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        sendfile off;
    }
}

3.2.2. dockerfileでイメージをビルド

docker build -f prod.web.dockerfile -t myrepo/laravel-web .

3.2.3. docker hubにイメージをプッシュ

docker push myrepo/laravel-web

これで必要なイメージが出来上がりました!
本番で構築を実行する前にチェックのため開発の環境で一度実行しましょうか。ということで、次は開発環境で、本番環境を仮として実行します。

4. 開発環境で本番環境構築を実行してみます

docker swarmモードを利用するということで、swarmモードを初期化します。
docker swarm init #init swarm mode

stackにデプロイ:
docker stack deploy --compose-file prod.docker-compose.yml mysrv

mysrvは全体サビースの名前です。任意に好きな名前でしました。

docker stack deploy を実行した後、走っているサビース(docker service ls)やプロセス(docker ps)をチェックすることができます。

自分の場合はdocker ps結果は以下です。

my@mymy:~/Projects/mysrv$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                       NAMES
811e818e83b9        myrepo/laravel-web:latest       "nginx -g 'daemon ..."   3 minutes ago       Up 3 minutes        80/tcp, 443/tcp             mysrv_web.1.hogvz22nxo19b1w92ev3pu5xz
426b99609e97        myrepo/laravel-app:latest       "php-fpm"                3 minutes ago       Up 3 minutes        9000/tcp                    mysrv_app.1.n763hd569tu5hxd3u6m3ee02e
6a02ce819570        mysql:latest                    "docker-entrypoint..."   3 minutes ago       Up 3 minutes        3306/tcp                    mysrv_database.1.sc8j9xvqmkaotyjpotol9nucf
5238474eba93        redis:3.0-alpine                "docker-entrypoint..."   3 minutes ago       Up 3 minutes        6379/tcp                    mysrv_cache.1.qbtraxvu6kcchiwm6w5sjkgsj

deploy成功で問題がなければ、ブラウザーで結果を確認できます:http://0.0.0.0/
デフォルトのLaravel画面が表示すると想定します。

Note :
開発の時に私は使っていたコマンドはdocker stackではなく、docker-compose upでした。
docker-compose upを本番で実行することができます。でも swarm モードではないので、いろいろな制限があります。

違うところはdockerの公開ページとここなどに参考することができます。

開発で本番構築を実行しても問題なくということで、本番で実行しましょう!

5. 本番で構築を実行

本番で実行するのは前述で述べたのと全く同じです!初回時の基本なステップを話します。

  1. prod.docker-compose.ymlを本番にコピー
    コマンドのscpなどで、ファイルを本番に上げます。

  2. swarmを初期化
    docker swarm init

  3. docker stack deploy --compose-file prod.docker-compose.yml mysrv

  4. servicesとprocessが正常に動くかをチェック

~/myproject $ docker service ls
ID                  NAME                        MODE                REPLICAS            IMAGE                           PORTS
fvoyz57upgxr        mysrv_app                   replicated          1/1                 myrepo/laravel-app:latest   *:9000->9000/tcp
jq491ivl1cid        mysrv_database              replicated          1/1                 redis:3.0-alpine                *:33061->3306/tcp
ltr1yqrny80b        mysrv_web                   replicated          1/1                 myrepo/laravel-web:latest   *:80->80/tcp
s4laek150xv7        dockercloud-server-proxy    global              1/1                 dockercloud/server-proxy        *:2376->2376/tcp

docker service lsで、mysrvの3つの子供サービスが上がったと確認しました。

サーバーのipは x.x.x.xであれば、http://x.x.x.xLaravelのデフォルト画面が表示すると思います。
laravel-aws.png
上は自分のawsサーバーに上がった結果です。

ダウドしたい時はdocker stack rm mysrvを実行します。

これで終わります!

ソースコードは下記です。
https://github.com/mytv1/sample-docker-laravel-prod

終わりに

サーバー構築についてあまり経験がない私は簡単なLaravelプロジェクトを上げました。
nginx, mysqlなどのイメージはもうdocker hubで用意されていてよかったと思います。swarmのおかげで、将来はいろいろサーバー構築コストも削減できればよかったと思います。

参考

https://medium.com/@shakyShane/laravel-docker-part-2-preparing-for-production-9c6a024e9797