Laravelのインフラ構築をDockerでやってみました。
この記事ではLaravelのアプリケーションをコンテナ化するのではなく、
インフラだけをコンテナにして、アプリケーションはDockerの外でデプロイするようにしています。
ここに至る経緯として、最初はECSでの運用なども視野に入れていたのですが、
最終的に現在のチームのスキルなどを考慮して、コンテナの知識がそれほど高くないエンジニアでも気軽にデプロイできることを目指しました。
今回やりたいことはAnsibleなどを使えばいいんでしょうが、あんまり知識がないのでとりあえず多少知識のあるDockerを採用しました。
構築対象はまっさらのEC2インスタンスを想定しており、phpなどが入ってない想定です。
なので、Dockerでそのあたりも構築していきます。
というわけで、まずは構成を紹介して、後半で環境構築手順を説明していきます。
コンテナ構成
- web
- nginxを実行するコンテナ
- fpm
- php-fpmを実行するコンテナ
- php
- phpコマンドを実行するためのコンテナ
- worker
- キューワーカとして動作させるコンテナ
- scheduler
- スケジューラとして動作させるコンテナ
- db
- develop環境でのdbコンテナ
- phpmyadmin
- db管理用のPHPMyAdminコンテナ
- redis
- develop環境でのredisコンテナ
- composer
- composerを実行するためのコンテナ
- npm
- npmを実行するためのコンテナ
ファイル構成
laravel-project以下に必要なファイルを追加します。
+ laravel-project
+ docker
+ composer
- Dockerfile
+ fpm
- Dockerfile
+ npm
- Dockerfile
+ web
- Dockerfile
- default.conf
- .env.development
- docker-compose.yml
webコンテナ
webコンテナはnginxのイメージからdefault.confを書き換えて、phpの実行をfpmコンテナに渡すようにします。
ここではdefault.confはそれ以外何もしないシンプルなものとします。
server {
listen 80;
server_name localhost;
index index.php
charset utf-8;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
root /var/www/html/public;
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
include fastcgi_params;
}
}
FROM nginx:alpine
COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
fpmコンテナ
fpmコンテナはphp-fpmイメージをベースに諸々インストールします。
FROM php:7.2.17-fpm-alpine
RUN apk add --update --no-cache git openssh
RUN apk add --no-cache \
freetype-dev \
libjpeg-turbo-dev \
libpng-dev \
openssl-dev \
&& docker-php-ext-configure gd \
--with-freetype-dir=/usr/include/ \
--with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install \
gd \
mbstring \
pdo_mysql
WORKDIR /var/www/html
phpコンテナ、workerコンテナ、schedulerコンテナ
これらのコンテナはfpmのイメージを使います。
dbコンテナ、phpmyadminコンテナ、redisコンテナ
これらのコンテナはOfficialやPublicのイメージのまま使います。
composerコンテナ
Laravelではcomposerコマンドが必要になるのでcomposerのためのイメージを作成します。
FROM php:7.3.3-alpine
RUN apk add --update --no-cache git unzip && \
curl -sL https://getcomposer.org/installer | php && \
mkdir /usr/local/src && \
mv composer.phar /usr/local/src && \
ln -s /usr/local/src/composer.phar /usr/local/bin/composer
RUN mkdir -p /laravel-project
VOLUME ["/laravel-project"]
WORKDIR /laravel-project
ENTRYPOINT ["composer"]
CMD ["--help"]
npmコンテナ
npmコマンドを使うためのイメージを作成します。
FROM node:12.7-alpine
RUN apk add --update --no-cache pkgconfig autoconf automake libtool nasm build-base zlib-dev libpng-dev
RUN mkdir -p /laravel-project
VOLUME ["/laravel-project"]
WORKDIR /laravel-project
ENTRYPOINT ["npm"]
CMD ["help"]
docker-compose.yml
docker-compose.ymlを定義します。
ここでは1ノードで完結できる環境として構築します。
各コンテナに必要なディレクトリをマウントできるようにVolumeを設定します。
また、Laravelで必要な環境変数は.env.developmentファイルに書いておいて、コンテナに渡します。
fpmのイメージにlaravel_project_phpと名前を付けてphp、worker、schedulerで使いまわします。
php、worker、schedulerコンテナはentrypointを指定してartisanコマンドを実行することで、目的の動作をさせます。
version: "3"
services:
web:
build: ./docker/web
restart: always
ports:
- "80:80"
links:
- "fpm:fpm"
volumes:
- ./public:/usr/share/nginx/html:ro
- ./storage/app/public:/usr/share/nginx/html/storage:ro
fpm:
build: ./docker/fpm
image: laravel_project_php
restart: always
expose:
- "9000"
links:
- "db:db"
- "redis:redis"
volumes:
- .:/var/www/html
env_file: ./.env.development
php:
image: laravel_project_php
volumes:
- .:/var/www/html
env_file: ./.env.development
working_dir: /var/www/html
entrypoint:
- php
worker:
image: laravel_project_php
restart: always
links:
- "db:db"
- "redis:redis"
volumes:
- .:/var/www/html
env_file: ./.env.develop
working_dir: /var/www/html
entrypoint:
- php
- artisan
- queue:work
- redis
- --tries=3
scheduler:
image: laravel_project_php
links:
- "db:db"
volumes:
- .:/var/www/html
env_file: ./.env.develop
working_dir: /var/www/html
entrypoint:
- php
- artisan
- schedule:run
db:
image: mysql:5.7
restart: always
ports:
- "3306:3306"
volumes:
- ./docker/mysql/data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_USER=db_user
- MYSQL_PASSWORD=db_password
- MYSQL_DATABASE=db_database
command: --innodb-use-native-aio=0
redis:
image: redis:alpine
restart: always
ports:
- "6379:6379"
command: redis-server --appendonly yes
phpmyadmin:
image: phpmyadmin/phpmyadmin
restart: always
ports:
- "8080:80"
links:
- "db:db"
environment:
- PMA_ARBITRARY=1
- PMA_HOST=db
- PMA_USER=root
- PMA_PASSWORD=root
composer:
build: ./docker/composer
volumes:
- .:/laravel-project
npm:
build: ./docker/npm
volumes:
- .:/laravel-project
.env.developmentでLaravelの設定をしていきます。
今回セッションやキューはredisを使います。
APP_ENV=staging
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost/
LOG_CHANNEL=stack
LOG_LEVEL=debug
SMS_DISABLE=false
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=db_database
DB_USERNAME=db_user
DB_PASSWORD=db_password
BROADCAST_DRIVER=log
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_DRIVER=redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
環境構築
まずはこのあたりを参考にDockerとDocker Composeをインストールします。
AWS EC2 Amazon LinuxでDocker, Docker Composeをインストールする
https://qiita.com/shinespark/items/a8019b7ca99e4a30d286
インストールが済んだら、本記事の前半でご紹介したファイル構成を含んだlaravel-projectを配置します。
git clone laravel-project
cd laravel-project
Dockerイメージをビルドします。
docker-compose bulid
phpコマンドやcomposerコマンドはコンテナ上で実行するので、
コマンドのエイリアスを作っておくと便利です。
alias php='docker-compose run --rm php'
alias composer='docker-compose run --rm composer'
alias npm='docker-compose run --rm npm'
ここから先は上のエイリアスを定義している前提でお読みください。
Laravel環境構築
Laravelの環境構築をしていきます。
Composerを実行します。
composer install
ディレクトリのパーミッションを設定します。
chmod -R 777 storage bootstrap/cache
アプリケーションキーを生成します。
php artisan key:generate
.envファイルにキーが生成されるため、.env.developmentにコピーします。
public/storageのシンボリックリンクも作っておきます。
php artisan storage:link
Dockerコンテナを起動します。
docker-compose up -d
DBのマイグレーションをします。
php artisan migrate
npmビルドします。
npm install
npm run dev
スケジューラをcronに登録します。
sudo crontab -e
* * * * * /usr/local/bin/docker-compose -f /path-to-laravel-project/docker-compose.yml run --rm scheduler >> /dev/null 2>&1
一応これで起動はするはずですが、問題が少しあるので対策します。
問題:ログのパーミッション
この構成だと、ログがWebから書き込まれた場合と、スケジューラやワーカから書き込まれた場合でパーミッションが違ってしまいます。
本来ならパーミッションをそろえるなどすればいいのですが、妥協してcronで毎分ログファイルのパーミッションを変更してます。
sudo crontab -e
* * * * * chmod -R 777 /path-to-laravel-project/storage/logs >> /dev/null 2>&1
アプリケーションのデプロイ
laravel-projectディレクトリのファイルを更新するだけです。コンテナの再起動などは必要ありません。
今回は以上となります。