docker-composeを使ってDocker上にLaravelを構築する方法(Nginx & php-fpm & MySQL)で使った、docker-compose.ymlとDockerfileの中身について。
##docker-compoer.yml
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: app_php-fpm
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
links:
- webserver
- db
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3307:3307"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local
・version: '3'
docker-composeはバージョンにより記述方法が異なる。基本的にはバージョン3を使用する。
##1. php-fpmのイメージとコンテナを作成 php-fpmのイメージとコンテナをサービス名appとして定義している。
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: app_php-fpm
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
links:
- webserver
- db
###1-1. イメージのビルド
PHPサービスのイメージを作成するための記述はbuild
とimage
。
build
でイメージを作成し、作成したイメージ名をimage
で指定している。
build:
context: .
dockerfile: Dockerfile
image: app_php-fpm
イメージの作成にはDockerfileを指定している。
context
で指定したパスから見て、dokerfile
で指定した場所の中のDockerfileを探す。
####・contextディレクティブ
Dockerfileを探すために基準となるパスを指定する。必須項目。
####・dockerfileディレクティブ
dockerfileはファイル名の指定がなければ、デフォルトで指定したディレクトリのDockerfileを探す。
contextで指定した場所にDockerfileがある場合は記述しなくてもいい。
なので、上記設定は以下でも同じ動きになる。
build:
context: .
image: app_php-fpm
###1-2. Dockerfile
FROM php:7.3-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl \
libonig-dev \
libzip-dev
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
####・FROM イメージ名:タグ名
ベースとなるイメージを指定。
ここではphp:7.3-fpmを指定している。タグを指定していない場合はlatestとなる。
buildを実行すると、まずはローカルで指定したイメージを探し、なければdocker hubを探しにいく。
####・バンドル関連ファイルのコピー
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
・COPY <ファイル1> <ファイル2>,,, <ディレクトリパス>
指定したローカルのファイルをdockerのディレクトリにコピーする。
####・WORKDIR パス コンテナ起動時の
# Set working directory
WORKDIR /var/www
以降のコマンドCOPY、RUN、CMD、ENTRYPOINTを実行する大元となるDockerコンテナ内のディレクトリを指定する。
コンテナ内に指定したディレクトリが存在しない場合は作成してくれる(エラーにならない)
**▼複数記述することも可能** 複数記述した場合は、そのディレクトリが追加され、ディレクトも移動してく。(cdコマンドみたいな感じ)
WORKDIR app
WORKDIR b
WORKDIR c
RUN pwd
//現在地
app/b/c
###Linux(Debian系)依存パッケージのインストール
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl \
libonig-dev \
libzip-dev
####・apt
Linux(DebianやUbuntuなど)のパッケージ管理コマンド。Advanced Package Toolの略。
####・apt-get
パッケージの操作・管理を行うコマンド。
####・apt-get update
インストール可能なパッケージの一覧を更新する。(一覧の更新のみでインストールはしない)
一覧は/etc/apt/sources.listにある。
####・apt-get install
sources.listに沿ってパッケージをインストール or アップデートする。
-y
オプション:すべてyesを選択する。
パッケージ | 内容 |
---|---|
build-essential | Debian系のOSを使うために必須 |
libpng-dev | png画像の読み込み・書き込み・編集 |
libjpeg62-turbo-dev | jpeg画像の読み込み・書き込み・編集 |
libfreetype6-dev | フォント関連の様々な操作をサポート(FreeType) |
locales | 言語設定 |
zip | zip操作の圧縮。zipコマンドが使えるようになる |
jpegoptim | jpg画像の圧縮 |
optipng | png画像の圧縮(画質を下げることなく圧縮。圧縮率低め) |
pngquant | png画像の圧縮(optipngよりも圧縮。画質は低下) |
gifsicle | gif画像とアニメーションの読み込み・書き込み・編集 |
vim | テキストエディタ |
unzip | zip操作の解凍。unzipコマンドが使えるようになる |
git | ソースコードなどの変更履歴を記録・追跡 |
curl | URLシンタックスを用いてファイルを送信または受信するコマンドラインツール |
libonig-dev | 正規表現ライブラリ。鬼車(onigruma) |
libzip-dev | zipアーカイブの読み取りと書き込み |
Linux(Debian系)の構築や必要なコマンドライブラリを一気にインストール。
・-devの意味
パッケージ名の後ろの-dev
は、developmentの略。プログラム開発で必要なヘッダーやツール、ライブラリなどが入っている。
##aptのキャッシュ削除 apt-get installでパッケージをインストールすると、キャッシュが追加され容量が大きくなっていく。
キャッシュは不要なので削除する。
ダウンロードしたパッケージは.deb形式で、var/cache/apt/ archives/ディレクトリに保存されます。これらのインストーラパッケージは、アンインストール後もローカルストレージに保持されます。
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
・apt-get clean
キャッシュを一掃する。
・rm -rf /var/lib/apt/lists/*
パッケージリストが削除する。apt-getで再度パッケージがインストールされたり、キャッシュファイルが増加するのを防ぐ。
▼補足
実行すると、パッケージに関する情報がないため、apt-get install
はE: Unable to locate package でエラーを返す。
apt-get update
を実行すればリストが復活する。
##PHP拡張機能(エクステンション)のインストール
# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd
####・docker-php-ext-install
DockerにPHPの拡張機能をインストールするためのコマンド。
####・インストールする拡張機能
PHP拡張機能 | 内容 |
---|---|
pdo_mysql | PHPからMySQLへのアクセスを可能にする |
zip | zipのアーカイブとその内部のファイルに対する透過的な読み書きを可能にする |
exif | 画像のメタデータ扱えるようになる。画像のヘッダ情報を読み込むなど |
pcntl | プロセス制御機能。プロセスの並行処理を行う |
gd | 画像をPHPスクリプトで生成する。画像のリサイズやグラフ作成などの描画機能を持たせることができる。 |
####docker-php-ext-configure
docker-php-ext-configure <拡張機能> <オプション>
指定した拡張機能の環境設定をする。環境設定は各拡張機能に割り当てられたオプションを使う。
####GDの主要オプション
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
GDの環境設定をしている。jepgやpngを使うGDをビルドするためにはlibpegやfreetype, libpngが必要なためインストールの設定をする。
オプション | 意味 |
---|---|
--with-gd[=DIR] | GDライブラリの保存場所を指定 |
--with-jpeg-dir[=DIR] | libjpegの保存場所 |
--with-freetype-dir[=DIR] | FreeType 2の保存場所 |
--with-png-dir[=DIR] | libpngの保存場所 |
次の行でGDをインストール。
RUN docker-php-ext-install gd
#####GDインストール時の注意点
PHP7.4以降からGDインストール時のオプションが変更となっている。(上記設定だとエラーが出る)
configure: error: unrecognized options: --with-gd, --with-freetype-dir, --with-jpeg-dir, --with-png-dir
PHP7.4以降は引数での環境設定が不要になる。必要なパッケージはapt-get install
でインストールしておく。
なので、docker-php-ext-config gd ~
は不要。
# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-install gd
###composerのインストール
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
####・curl -sS
-s
オプション
--silent。通常表示される進捗状況を表示しない。エラーメッセージも表示しない。
-S
オプション
--show-error。エラーメッセージを表示する。-sとセットで使うことでエラーを表示する。
curl --helpでオプション一覧が確認可能。
####・curl -sS https://getcomposer.org/installer
composerのinstallerをダウンドロードする。(composer-setup.phpを取得)
####・|
コマンドA | コマンドB
パイプライン。コマンドAの実行結果をコマンドBに渡す。
####・php -- --install-dir=/usr/local/bin --filename=composer
php [options] -- [args...]
なので、引数で、インストール先のディレクトリとファイル名を指定。
- --install-dir=/usr/local/bin
- -filename=composer
###ユーザーとグループの追加
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
####・groupadd
groupaddコマンドで新しいグループ
groupadd [オプション] [グループID] <グループ名>
-g
: --gidと同じ。作成するグループのID(GID)を指定。
指定がない場合は、未使用の番号を自動で割り振る。
**▼作成したグループの確認** グループの情報は /etc/group に保存されている。コンテナの中で確認できる。
$ docker exec -it app bash
www@a1984dd1d4a4:/var/www$ cat /etc/group
www:x:1000:
・①グループ名:②PW:③GID[④:所属メンバー名]
- グループ: www
- PW: x (セキュリティのため表示されない。設定なしの場合は*)
- GID: 1000
- 所属メンバー:なし
####・useradd useraddコマンドは新規ユーザーを作成し、ユーザーごとの設定ができる。
コマンドは長いが、最初のuseraddと最後のwww以外はすべてオプション。
useradd [オプション] ユーザー名
-u
: ユーザーID(UID)の指定
指定がない場合は、未使用の番号を自動で割り振る。
-m
: ユーザーのhomeディレクトリが存在しない場合に作成。
指定しない場合は、「/」配下にユーザー情報が保存される。
-s [dir]
: ユーザーのログインシェルをフルパスで指定(指定しなかった場合はデフォルト設定)
-g [グループ名/gid]
: 指定したユーザーをグループに追加する。
**▼作成したユーザー情報の確認** ユーザー情報は /etc/passwd に保存されている。コンテナの中で確認できる。
$ docker exec -it app bash
www@a1984dd1d4a4:/var/www$ cat /etc/passwd
www:x:1000:1000::/home/www:/bin/bash
・①ユーザー名:②PW:③UID:④GID::⑤/home/<ユーザー名>:⑥シェルのパス
- ユーザー名: www
- PW: x (セキュリティのため表示されない。設定なしの場合は*)
- UID: 1000
- GID: 1000
- ユーザーのホームディレクトリ: home/www
- ユーザーが使用するシェル: /bin/bash
###ローカルのPJをコンテナ内にコピー & ファイルオーナーの変更
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
・COPY [--chown=<user>:<group>] <ローカルdir> <コンテナdir>
ローカルの「.」をコンテナ内の「/var/www」にまるごとコピー。ユーザー名をwww、グループ名をwwwに変更。
###ユーザー名を指定
# Change current user to www
USER www
・USER <user名 or uid>[:<group名 or gid>]
###ポート開放とphp-fpmの起動
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
####・EXPOSE ポート番号
指定したポートでリッスン(外部からのアクセスに備えて待機)する。
ここでは、php-fpmの9000番ポートを開けておく。
####・CMD [
コマンド,
引数1,
引数2,,,]
CMDはコンテナ実行時のデフォルト処理を定める。Dockerfileに必須。記述は1つのみ。
php-fpmコマンドを実行して、php-fpmを起動。
以上がDockerfileの中身。 php:7.4-fpmイメージをベースに、必要なパッケージをインストール、ユーザー名や権限変更をしたのち、9000番ポートを開けてphp-fpm起動するイメージが出来上がり。
###イメージ名の設定 最後に作成したイメージに名前をつけて完了。(ここではapp_php-fpm)
image: app_php-fpm
##1-3. コンテナの設定 1-2までがイメージのビルド。続いてコンテナ起動時に実行される処理の確認。
#php-fpmサービスの続き
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
links:
- webserver
- db
###container_name: コンテナ名
コンテナを作成した時の名前。起動・停止・削除に使うのでわかりやすく短めの名前がおすすめ。
###restart: コンテナ停止時の処理フラグ
コンテナが停止した時の処理をフラグで指定する。
フラグ | 意味 |
---|---|
no | 再起動しない。デフォルト |
on-failure | エラー発生時にコンテナを再起動(終了コードがゼロ以外) |
always | 常に再起動。手動で停止時はDockerデーモンが再起動した場合にコンテナを起動する |
unless-stopped | 手動停止時を除いて再起動。Dockerデーモンを起動しても再起動しない。 |
▼再起動ポリシー
- コンテナ起動後が10秒以上立たない場合は自動再起動しない。(再起動ループを避ける)
- 手動でコンテナを停止すると、Dockerデーモンを再起動するか、コンテナを手動で再起動するまで再起動ポリシーを無視する。(再起動ループを避ける)
- 再起動ポリシーを適用できるのはコンテナのみです。(swarm サービスは対象外)
####・tty: <boolean>
疑似ターミナル (pseudo-TTY) の割り当て。
tty: true
で、ポート開放していないコンテナも起動状態をキープできる。(ないとコンテナがすぐに終了してしまう)
docker exec
コマンドでコンテナに入って操作する際に必要。
###environment 環境変数の設定。 書き方は2種類。`Key: Value`または、`- Key=Value`。
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
以下と同じ。
environment:
- SERVICE_NAME=app
- SERVICE_TAGS=dev
- 値を指定しない場合は、シェルの値を適用する。
- DockerfileのARGやENVで指定した環境変数と被る場合は、docker-composeの環境変数が優先される。
-
env_file
で読み込んだ環境変数と被る場合は、environmentが優先される。
**▼環境変数の優先度**
- environment
- env_file
- DockerfileのARG, ENV
env_fileは環境変数を定義したファイルを指定する。たくさんの環境変数を一括で読み込む場合に便利。
env_file:
- ./Docker/api/api.env
###working_dir コンテナ内のデフォルトの作業用ディレクトリを指定する。
working_dir: /var/www
コンテナに入ると/var/wwwに入る。ここはプロジェクトがコピーされるディレクトリ(Dockerfileにて設定)。
###Volumes ローカルのディレクトリをコンテナ内のディレクトリにマウントする。
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
- ホストのパス:コンテナ内の絶対パス
コンテナ側は絶対パス。ホスト側は相対でも絶対でもいい。ボリューム名を定義指定いる場合は、ボリューム名でもいい。
- ./:/var/www
ローカルの現在のディレクトリをコンテナの「var/www」と同期させる。
これで、ローカルのプロジェクトディレクトリで行った変更はコンテナ内に自動コピーされる。
自動同期したくないディレクトリやファイルは、.dockerignoreに記載する。
**▼ボリューム名について** ボリューム名はserviceの外でvolumesを使って定義する。参照する際のボリューム名は**「プロジェクト名_ボリューム名」**となる。
ボリューム名を指定しない(ホストのパスを指定した)場合はハッシュ値となる。
#PJ名:laravel
version: '3'
services:
#MySQL Service
db:
#省略
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
volumes:
dbdata:
driver: local
↓
$ docker volume ls
DRIVER VOLUME NAME
local laravel_dbdata
local f0f9aa131ed1d74c0d2e9ff87b065170a1b142bd6a90a76ce9dcddbee3c69984
###networks
docker-composeはnetworkを指定しない場合、プロジェクト名のネットワークが自動生成され、すべてのサービスがそのネットワーク内に入る。
networkを自分で定義すると、指定したサービスだけをそのnetworkで繋ぐことができる。
version: '3'
services:
app:
#省略
networks:
- app-network
#省略
networks:
app-network:
driver: bridge
▼ネットワークの確認
ネットワーク名は指定の有無により名前が変わる
- 指定なし:
プロジェクト名_default
- 指定あり:
プロジェクト名_ネットワーク名
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
2cee68b9dc5a django_default bridge local
761212ed4b49 laravel_app-network bridge
▼bridgeとは?
bridgeとはネットワークのブリッジモードのこと。複数のネットワークをつなげて、ひとまとめのネットワークにしている状態。
###links
サービスを関連づける。サービスを起動すると、linksで記述されているサービスも一緒に起動する。
version: '3'
services:
app:
#省略
links:
- webserver
- db
上記の場合、appにdbとwebserverがlinkしているため、appのみを起動すれば自動的にdbとwebserverも起動する。
起動の順序はlinksに記述したコンテナから。
$ docker-compose up -d app
Starting webserver ... done
Starting db ... done
Starting app ... done
以上で一つ目のphpサービスのdocker-composeの記述内容が完了。
処理をまとめると、
(1)イメージのビルド
Dockerfileを元に、php:7.4-fpmをベースイメージとして、必要なパッケージなどを保持した新たなイメージを作成。
(2)コンテナの起動
環境変数、ボリューム、ネットワーク、関連サービスを指定(リンク)してコンテナを起動。最後はDockerfileに記載されたCMDが実行され、イメージ通りのphp-fpmが起動する。
##2. Nginxのコンテナを作成 php-fpmのコンテナが作成できるようになったので、続いてWEBサーバーとなるNginxを作成する。
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
##2-1. イメージのビルド
イメージの作成は1行のみ。
image: nginx:alpine
docker-hubのnginx:alpine
をイメージとして使用。(alpine上に構築したnginx)
**▼alpineとは?** alpineは超軽量のLinuxディストリビューション。
▼Linuxディストリビューションとは?
LinuxのコアとなるプログラムをLinuxカーネルという。この状態では普通のOSとして使うことが難しい。
使いやすいようにパッケージをくっつけたのがLinuxディストリビューション。distributionは配布という意味なので、配布用Linuxということ。
Debian, Ubuntu, CentOSと同じ並びにalpineがくる。
##2-2. コンテナの定義
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
php-fpmサービスで使った内容とほぼ同じ。
作成したコンテナ名をwebserverとし、コンテナ手動停止(&エラー)以外は自動再起動する。
コンテナのbashを起動できるように擬似ターミナル(tty)を開けておく。
###ports
ホスト側:コンテナ側
でポート番号を指定する。
- "80:80"
であれば、localhost:80にアクセスすると、Nginxの80番ポートに繋がるという意味。
443ポートはhttpsプロトコル用のポート。
https://xxx
で接続すると、xxxのIPアドレスの443ポートに接続する。
###volumes
・- ./:/var/www
ローカルの現在のディレクトリを、Dockerコンテナ内のNginxの/var/wwwと同期。
・- ./nginx/conf.d/:/etc/nginx/conf.d/
ローカルの./nginx/conf.d/をnginxの/etc/nginx/conf.d/と同期。
###networks
「project名_app-network」というネットワークに入ったサービス同士とつなげる。
##2-3. DBの定義
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3307:3307"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
networks:
- app-network
設定はNignxサービスとほぼ同じ。
ベースイメージに、mysql:5.7.22を使用。
ポート番号はホスト側の3307をローカル側の3307につなげるよう設定。
###environment
環境変数でDB名やPWなどを渡している。
##2-4 networkとvolumeの定義 全体で使用するnetworkと、volume名をつけるものを定義している。。
#Docker Networks
networks:
app-network:
driver: bridge
app-networkを定義。ドライバーはbridge(複数のネットワークを一つとみなす)
#Volumes
volumes:
dbdata:
driver: local
名前ボリュームを設定する。名前ありボリュームを使うとデータを永続化できる。
ここでは、DBのデータはコンテナを終了しても残しておきたいので、dbdataボリュームを作成し、/var/lib/mysql/と連動させている。
コンテナ終了時にDocker内のデータは消えるが、ローカルにデータが残る。
Dockerではボリューム名dbdataでローカルのパスを保存してあるため、次のコンテナ起動時にそのローカルに接続し、/var/lib/mysql/配下にデータを保存する。(ローカルと同じ状態にする)
##まとめ php-fpmは自分のLaravelプロジェクトを反映するためイメージの再構築をしているのでビルドに時間がかかる。
NginxとDB(MySQL)は公式のイメージを取得するのみなのでビルドもあっさり。(ただdocker-hubからプルするだけ)
コンテナ起動時にポート番号、環境変数、ボリューム、ネットワークを指定している。