Dockerでローカル開発環境を構築してみよう
XAMPPを使われている方は、ApacheやMySQLを立ち上げる際にStartボタンをぽちっとしていると思います。
Dockerは、そのApacheやMySQLをコンテナという単位で管理して、立ち上げたり色々カスタマイズできたりします。
コンテナはイメージを元に作成され、イメージはDockerfileを元にします。
なぜDockerを使うのか
色々あると思いますが、まずは環境をいろいろな場所に持ち運びやすくする、ことだと思います。
CircleCIでは、ビルド・テストなどを行う環境としてイメージを指定できます。AWS ECRにイメージを追加しECSでコンテナを起動すれば、それを本番環境にすることもできます。Dockerfileを渡せば他の人に共有することも簡単です。
ローカル開発環境を作ることは、その第一歩だと思います。
Dockerfileはベースがある
以下は例として使用するDockerfileです。PHPとApacheの環境を作ります。
FROM php:7.3.10-apache
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
curl sendmail zip unzip libz-dev libpq-dev libzip-dev \
libfreetype6-dev libjpeg62-turbo-dev libpng-dev libssl-dev libmcrypt-dev \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install -j$(nproc) pdo_mysql \
&& docker-php-ext-install -j$(nproc) pdo_pgsql \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-configure zip --with-libzip \
&& docker-php-ext-install -j$(nproc) zip \
&& pecl install redis \
&& docker-php-ext-enable redis
ENV APACHE_DOCUMENT_ROOT /var/www/public
COPY ./ssl /etc/apache2/ssl
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
&& sed -ri -e 's!/etc/ssl/certs/ssl-cert-snakeoil.pem!/etc/apache2/ssl/localhost+3.pem!g' /etc/apache2/sites-available/default-ssl.conf \
&& sed -ri -e 's!/etc/ssl/private/ssl-cert-snakeoil.key!/etc/apache2/ssl/localhost+3-key.pem!g' /etc/apache2/sites-available/default-ssl.conf \
&& a2enmod rewrite \
&& a2enmod headers \
&& a2enmod ssl \
&& a2ensite default-ssl
RUN echo '#!/bin/sh\nphp artisan "$@"' >> /bin/pa \
&& chmod u+x /bin/pa
WORKDIR /var/www
このDockerfileにはベースとしているイメージ(のDockerfile)があります。
https://github.com/docker-library/php/blob/master/7.3/stretch/apache/Dockerfile
Dockerfileはベースとなるものに追記したり上書きしたりしてカスタマイズしていきます。
Dockerfile詳細
FROM php:7.3.10-apache
FROM <image>
でベースイメージを指定しています。:7.3.10-apacheはタグで、バージョン指定に使います。
タグを指定しないとlatestというタグになりますが、ビルドする時期によってバージョンが変わってしまうためよくありません。
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
curl sendmail zip unzip libz-dev libpq-dev libzip-dev \
libfreetype6-dev libjpeg62-turbo-dev libpng-dev libssl-dev libmcrypt-dev \
&& rm -rf /var/lib/apt/lists/*
RUN <command>
でコマンドを実行します。ここではPHP extensionのインストールに必要なライブラリをインストールしています。
インストールが終わったら、不要なファイルをrm -rf /var/lib/apt/lists/*
で削除しています。
RUN docker-php-ext-install -j$(nproc) pdo_mysql \
&& docker-php-ext-install -j$(nproc) pdo_pgsql \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-configure zip --with-libzip \
&& docker-php-ext-install -j$(nproc) zip \
&& pecl install redis \
&& docker-php-ext-enable redis
PHP Extensionをインストールしています。ベースイメージが用意しているdocker-php-ext-install
コマンドなどを使用しています(詳細は後述)。
ENV APACHE_DOCUMENT_ROOT /var/www/public
ENV <name> <value>
でコンテナ内で使える環境変数を設定します。もし同じ環境変数を指定すると、上書きします。
ここでは環境変数APACHE_DOCUMENT_ROOTに/var/www/publicを代入しています。
COPY ./ssl /etc/apache2/ssl
COPY <source> <target>
で、ホストにあるファイルをコンテナ内にコピーします。
ここでは./sslをコンテナ内の/etc/apache2/sslとしてコピーしています。localhostでもhttpsでアクセスできるようにするため、sslディレクトリには鍵と証明書が入っています。
参考:https://qiita.com/walkers/items/b90a97a99bbb27f6550f
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
&& sed -ri -e 's!/etc/ssl/certs/ssl-cert-snakeoil.pem!/etc/apache2/ssl/localhost+3.pem!g' /etc/apache2/sites-available/default-ssl.conf \
&& sed -ri -e 's!/etc/ssl/private/ssl-cert-snakeoil.key!/etc/apache2/ssl/localhost+3-key.pem!g' /etc/apache2/sites-available/default-ssl.conf \
&& a2enmod rewrite \
&& a2enmod headers \
&& a2enmod ssl \
&& a2ensite default-ssl
sedコマンドを使ってApache設定ファイルの書き換えをします。ここではドキュメントルートを/var/www/publicへ変更と、先ほどCOPYしたssl鍵や証明書の指定を行なっています。
例えば's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g'
は「/var/www/html」を環境変数APACHE_DOCUMENT_ROOTの値に置換しており、/etc/apache2/sites-available/*.conf
は置換するファイルを指定しています。
a2enmodコマンドはモジュールの有効化、a2ensiteコマンドはdefault-sslという設定ファイルを有効化しています。
RUN echo '#!/bin/sh\nphp artisan "$@"' >> /bin/pa \
&& chmod u+x /bin/pa
php artisanコマンドをpaと短くうてるようにしています。
aliasコマンドを使用していないのは、ホストからdocker exec pa
と利用できるようにするためです(aliasはコンテナ内のみで有効のため)。
WORKDIR /var/www
WORKDIR <path>
で作業ディレクトリを指定します。以後、RUN, COPY, CMDなどがここで実行されるようになります。
ここではコンテナのbashを利用する際に、カレントディレクトリが/var/wwwになるように、最後ですがWORKDIRを指定しています。
このDockerfileにはありませんが、CMD
という命令があります。ベースにしたDockerfileには以下があります。
CMD ["apache2-foreground"]
コンテナを立ち上げると、CMDに記述したコマンドが実行されます。
CMDは複数書かれると、一番最後に書かれたものが優先されます。自分のDockerfileでベースイメージで指定されたCMDを上書きできるということです。
ベースイメージが用意しているコマンド
ベースにしているphpイメージのDockerHubページを見てみます。
We provide the helper scripts docker-php-ext-configure, docker-php-ext-install, and docker-php-ext-enable to more easily install PHP extensions.
とあります。PHP extensionをインストールするときに便利なコマンドを用意している、とのことです。
説明しているDockerfileでも使用しています。
RUN docker-php-ext-install -j$(nproc) pdo_mysql \
&& docker-php-ext-install -j$(nproc) pdo_pgsql \
...
このように便利なコマンドを準備していたり、カスタマイズする際に使う環境変数を説明してくれていたりするので、DockerHubのページを見るようにしましょう。
信頼できるイメージ
ベースにするイメージは、公式イメージのみにした方が無難です。
公式イメージはDocker社が品質をチェックしてくれています。公式イメージ以外は悪意のあるソフトや脆弱性がある場合があります。
実際にコンテナを立ち上げよう
上記のDockerfileで、実際にコンテナを立ち上げてみます。
まずimage build <path>
コマンドで、Dockerfileからイメージをビルドします。カレントディレクトリにDockerfileをおき、以下のコマンドをうちます。
$ docker image build -t my-php-apache:7.3.10 .
-t my-php-apache:7.3.10
はオプションで、my-php-apache:7.3.10というイメージをビルドします。.
でDockerfileのパスを指定しています。
ビルドが終わったら、container run <image>
コマンドで立ち上げてみます。-p 80:80
で、ホストの80ポートでコンテナの80ポートにアクセスできるようにしています。
ホストにApacheを立ち上げるXAMPPとは違いコンテナは仮想環境ですから、ホストとコンテナのポートをバインディングして(繋げて)あげます。
$ docker container run -p 80:80 my-php-apache:7.3.10
ブラウザでlocalhostにアクセスしてみると、403エラーです。
上記のDockerfileではApacheのドキュメントルートを/var/www/publicに変更していました。コンソールを見ると、
Warning: DocumentRoot [/var/www/public] does not exist
とありますから、403エラーではあるもののApacheが動いていることが確認できます。
コンテナ内にログインして、publicディレクトリを作ってみましょう。まず起動したコンテナの情報をみます。container ls
コマンドはコンテナの情報をみることができます。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
346587eac502 my-php-apache:7.3.10 "docker-php-entrypoi…" 10 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp bold_johnson
コンテナのIDがわかったので、container exec <containerId> <command>
コマンドでコンテナ内のコマンドを実行します。-it
はホストとコンテナの入出力を繋げています。
以下は、containerIdを自分のコンテナIDにして実行ください。
$ docker container exec -it 346587eac502 bash
root@346587eac502:/var/www#
コンテナのbashにログインできました。publicディレクトリを作り、中にhtmlファイルを置いてみます。
root@346587eac502:/var/www# mkdir /var/www/public
root@346587eac502:/var/www# echo '<p>test</p>' > /var/www/public/index.html
もう一度ブラウザでlocalhostをみてみると・・・
ちゃんと動いてます!
コンテナライフサイクル
コンテナは色々な状態があり、詳しくは
Docker入門 コンテナのライフサイクル
によくまとまっていますが、簡易化すると以下の図のようになります。
現在my-php-apacheコンテナは起動中です。なので停止〜削除してみます。
$ docker container stop 346587eac502
ブラウザでlocalhostをみても、Apacheからレスポンスが返ってきません。docker container ls -a
で状態を確認すると、STATUSがExitedになっています。-a
は停止しているコンテナも表示するオプションです。
さらに削除してみましょう。
$ docker container rm 346587eac502
docker container ls -a
をうってもコンテナの情報が出てきません。削除が完了しました。
これで、起動〜削除まで一通りこなせました。
常時実行するコンテナ、しないコンテナ
話は変わりますが、ApacheやMySQLコンテナは、リクエストを待ち受けてもらわなければいけません。よってコンテナはずっと立ち上がっている状態にします。
しかし用途によっては実行結果を受け取ったら、もうコンテナを削除してもよい場合もあります。例えばcomposerをホストにインストールせずに使ってみましょう。composerコマンドを使う時だけcomposerコンテナはあればいいのですから、以下のコマンドで立ち上げます。
$ docker container run --rm -it -v $PWD:/app composer install
--rm
は実行後にコンテナをすぐ削除します。
-v
はボリュームを指定します。詳しくは次回で説明しますが、ホストとコンテナでデータを共有する仕組みです。
composer.jsonがあるディレクトリで、上記のコマンドを実行してみましょう。
$ docker container run --rm -it -v $PWD:/app composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 84 installs, 0 updates, 0 removals
- Installing symfony/polyfill-ctype (v1.12.0): Downloading (100%)
- Installing phpoption/phpoption (1.5.0): Downloading (100%)
...
Package manifest generated successfully.
composer.lockやvendorディレクトリがホスト側に現れ、インストールされていることがわかります。以下のような流れでcomposer installを実行できました。
- composerがインストールされたコンテナが立ち上がる
- ボリュームで$PWDのファイルがコンテナの/appに共有される
- コンテナ内で
composer install
が実行される - インストールされた結果が$PWDに共有される
- composer installコマンド終了後、コンテナが削除される。
イメージの指定はどこ?
docker container run --rm -it -v $PWD:/app composer install
はどこでイメージを指定しているの?と思われるカモですが、composer
がイメージ名です。
ややこしいので割り切って説明しますが、composerイメージでdocker container run composer composer install
と**打たなくて済むように**してくれています。
コマンドが違う?
他のページではdocker ps
コマンドを使っている人もいます。docker ps
と打つと、docker container ls
と同じく起動中のコンテナ一覧が表示されます。docker exec
とdocker container exec
も同じです。
これは操作する対象が分かりやすいように、ということで後から長いコマンドが追加されました。
参考
docker container / image コマンド新旧比較
個人的には新しい(長い)コマンドの方が統一感があって好きなのですが、タイプ数が少ない古いコマンドも便利なので、主要なものは両方覚えちゃいましょう。
終わりに
今回は簡単なDockerfileとdockerコマンドの紹介でした。
次回は複数のコンテナを管理するDocker-composeと、Dockerボリューム・ネットワークを紹介予定です。
->Docker入門 〜Docker-compose, ネットワーク, ボリューム編〜
FORK Advent Calendar 2019
16日目 【WordPress】今更だけど、WysiWygエディタ(ビジュアルエディタ)で書いた記事にCSSを適用する方法をまとめてみた話 @Kodak_tmo
18日目 今年の正月はNuxtとFirebaseで作ったカードゲームで甥っ子に尊敬されたい @yoh_zzzz