背景
「LaravelってどうやってHTTPサーバと連携するんだ?」と思い、調査しましたが結構はまったので、自分用のメモも兼ねて記事を書きます。
やりたいこと
・Laravelを使ったWebアプリサーバを立てたい
・Dockerを使って、コマンド一発で楽にWebサーバとWebアプリサーバを立てたい
特にDockerを使ってWebサーバとWebアプリサーバを立てるのは、Kubernetesを使うのに必須だったりするので、鍛錬もかねて、実施しました。
システムの全体図
システムというほどのものではないですが、下記のようにDockerの環境を構築しました。
・Nginxのコンテナを立てて、80番と443番でHTTP/HTTPSのリクエストを受け付ける
・NginxのコンテナとWebアプリのコンテナは9000番で通信
・Webアプリのコンテナには、外部から直接アクセスはできない
負荷分散は今回は未実施です。
(勉強のためKubernetesやりたいので、取り組んだらまた投稿します。)
今回もdocker-compose(コンテナをまとめて管理できるツール)を使いました。
Composeファイル
ディレクトリ構造は下記のようにしました。
project/
├ nginx/
│ └ dockerfile
│ └ server.conf
├ php-fpm/
│ └ dockerfile
│ └ www.conf
├ web/
│ └ Laravelアプリ/
├ docker-compose.yml
docker-compose.ymlは下記のようにしました。
version: '3'
services:
web:
build:
context: ./nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./web/public:/etc/nginx/public
- ../ssl/certs/:/etc/pki/tls/certs/
- ../ssl/private/:/etc/pki/tls/private/
depends_on:
- app
container_name: blog_web
app:
build: ./php-fpm
volumes:
- ./web:/var/www/
container_name: blog_app
webがNginxコンテナで、appがphp-fpm+laravelのコンテナです。
Nginxコンテナは
・外部から、80番と443番を受け付けるようにし、appと通信できる
・publicディレクトリにおいてある、cssやjsを直接さわれるようにする(staticファイルはnginxから直接返す)
・sslの証明書と鍵は、アクセスの権限を調整して、マウントして見れる
php-fpm+Laravelコンテナは
・ソースをまるまるマウントする
という感じです。
Nginxのコンテナ
下記のように書くだけです。
FROM nginx:latest
# 設定ファイルを指定の場所に置く
COPY ./server.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events{
worker_connections 2048;
multi_accept on;
use epoll;
}
http {
charset UTF-8;
# versionを表示しない
server_tokens off;
include /etc/nginx/mime.types;
default_type text/plain;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
ssl_protocols TLSv1.1 TLSv1.2;
server {
listen 80;
server_name localhost;
# 80番でアクセスしてきた人は、443番のリダイレクトを返す
return 301 https://$host$request_uri;
}
server {
listen 443;
ssl on;
server_name localhost;
# 証明書
ssl_certificate /etc/pki/tls/certs/example.crt;
# 秘密鍵
ssl_certificate_key /etc/pki/tls/private/example.key;
# rootディレクトリ
root /var/www/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# indexファイルの指定
index index.php index.html;
charset utf-8;
# アクセスしてきたパスに対応するファイルを返す
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# staticファイルはnginxが直接返す
# cssファイル
location /css/ {
alias public/css/;
}
# jsファイル
location /js/ {
alias public/js/;
}
# imageファイル
location /images/ {
alias public/images/;
}
# fontsファイル
location /fonts/ {
alias public/fonts/;
}
# faviconリクエストはログを残さない
location = /favicon.ico { access_log off; log_not_found off; }
# php-fpmとの連携
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;
}
# 指定のパス以外へのアクセスを禁止する
location ~ /\.(?!well-known).* {
deny all;
}
# 証明書更新のパスへアクセスできるようにする
location /.well-known/ {
alias public/.well-known/;
}
# クローラがアクセスできるようにする
location /robots.txt {
alias public/robots.txt;
}
# サイトマップにアクセスできるようにする
location /sitemap.xml {
alias public/sitemap.xml;
}
}
}
まだわかっていない箇所もありますが、
Nginxのコンテナは、
fastcgi_pass app:9000;
とすることで、Webアプリのコンテナのpublicディレクトリのindex.phpをたたきにいけるようにしています。
私はここの記載の仕方でちょっとはまりました。。。
cssファイルやjsファイルなどのstaticファイルは、Nginxコンテナがアプリのpublicディレクトリをマウントしていて、Nginxから直接返せるようにしています。
クローラが見に来れるようにrobots.txtとsitemap.xmlにアクセスできるようにしています。
php-fpm+Laravelコンテナ
dockerfileに入る前に、そもそもphp-fpmとは何ぞ???というのが自分の中であったので、調べました。
公式ドキュメントとか見ましたが、一番わかりやすかったのは他者様の記事
php-fpmとは
php向けのFastCGIで、メモリでキャッシュを保持することで高速にWebサーバ上でPHPを動作させるアプリケーション
ということです。
この他者様の記事ではコンテナ間の通信のことも書いてありましたが、今回Nginxとphp-fpmはTCPで通信しています。
次やる時はUNIXドメインソケットで通信しようかと思います。
さて、Dockerfileですが、下記のようにしました。
FROM php:7.4.7-fpm
RUN apt update -y
RUN apt install -y libfcgi0ldbl curl git unzip wget vim
# nginxというユーザを作る
RUN useradd -m -s /bin/sh -u 1000 nginx
# 設定ファイルを指定の場所に置く
COPY ./www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
# Composeインストール
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# nodejs 12インストール
RUN apt install -y npm
RUN npm install n -g
RUN n 12
# ユーザ変更
USER nginx
# 作業ディレクトリ
WORKDIR /var/www
VOLUME ["/var/run/php-fpm"]
[www]
user = nginx
group = nginx
listen = 9000
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
request_terminate_timeout=30s
php-fpmの設定はwww.confの設定ファイルと公開されているDockerImageで設定は簡単にできました。
あとは、コンテナにComposerとnodejs12を入れたかったので、Dockerfile内でインストールするようにしました。
実際のnpmパッケージのインストールと、composerのビルドは、コンテナを立ち上げた後、コマンドで実行しています。
docker exec blog_app composer install --optimize-autoloader --no-dev
docker exec blog_app npm install
docker exec blog_app npm run production
このようにしたのは、マウント後にnpmパッケージのインストールやcomposerのビルドをしないと、ソースがない時にdockerビルドが走り、失敗してしまうからです。
dockerfile内で、ソースをとってくる処理を書いている場合は、dockerfile内でnpmパッケージのインストールやcomposerのビルドができます。
Composeのビルド
あとは、コンテナのビルド+コンテナ起動して、npmパッケージのインストールやcomposerのビルドして終了です。
docker-compose build
docker-compose up -d
ドメイン設定・SSL対応
ドメインはこちらで買いました。
DNSはAWSのものを使いたかったので、Route53を使用しました。
設定は下記です。
DNSの設定はこちらを参考しました。
最低限の設定は下記で、
・Aレコード:グローバルIPとホスト名の紐付け
・NSレコード:ドメインのDNSサーバ(ドメインを購入したサイトのネームサーバにこの値を登録します。)
・SOAレコード:上位のDNSサーバ
オプションで、
・CNAMEレコード:別ホスト名
・TXTレコード:今回はクローラの認証情報記載
を記載しています。
証明書の取得は、お金をかけずにやりたかったので、
他者様の記事を参考にbacmeでオレオレ証書を取得しました。
ホスト名とマシンの紐付けができていれば、やることは単純で、
bacmeをgit cloneしてきて、取得したホスト名で
./bacme -w /var/www/example/ example.com www.example.com
を実行します。wは、.well-knownディレクトリのパスです。
(80番でwell-knownのパスにアクセスできないと↑のコマンドは失敗します。アクセスできるようにNginxの設定を見直してみてください。)
成功すると、bacmeディレクトリ直下に証明書と秘密鍵ができますので、
Nginxのマウント先(/etc/pki/tls/certs/)に指定してください。
参考文献
nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話
次は、最低限のセキュリティ設定とKubernetesのことを調べます。