25
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FORKAdvent Calendar 2019

Day 17

Docker入門 ~Dockerfile編~

Last updated at Posted at 2019-12-16

Dockerでローカル開発環境を構築してみよう

XAMPPを使われている方は、ApacheやMySQLを立ち上げる際にStartボタンをぽちっとしていると思います。
Dockerは、そのApacheやMySQLをコンテナという単位で管理して、立ち上げたり色々カスタマイズできたりします。
コンテナはイメージを元に作成され、イメージはDockerfileを元にします。

なぜDockerを使うのか

色々あると思いますが、まずは環境をいろいろな場所に持ち運びやすくする、ことだと思います。
CircleCIでは、ビルド・テストなどを行う環境としてイメージを指定できます。AWS ECRにイメージを追加しECSでコンテナを起動すれば、それを本番環境にすることもできます。Dockerfileを渡せば他の人に共有することも簡単です。
ローカル開発環境を作ることは、その第一歩だと思います。

Dockerfileはベースがある

以下は例として使用するDockerfileです。PHPとApacheの環境を作ります。

Dockerfile
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詳細

Dockerfile
FROM php:7.3.10-apache

FROM <image>でベースイメージを指定しています。:7.3.10-apacheはタグで、バージョン指定に使います。
タグを指定しないとlatestというタグになりますが、ビルドする時期によってバージョンが変わってしまうためよくありません。

Dockerfile
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/*で削除しています。

Dockerfile
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コマンドなどを使用しています(詳細は後述)。

Dockerfile
ENV APACHE_DOCUMENT_ROOT /var/www/public

ENV <name> <value>でコンテナ内で使える環境変数を設定します。もし同じ環境変数を指定すると、上書きします。
ここでは環境変数APACHE_DOCUMENT_ROOTに/var/www/publicを代入しています。

Dockerfile
COPY ./ssl /etc/apache2/ssl

COPY <source> <target>で、ホストにあるファイルをコンテナ内にコピーします。
ここでは./sslをコンテナ内の/etc/apache2/sslとしてコピーしています。localhostでもhttpsでアクセスできるようにするため、sslディレクトリには鍵と証明書が入っています。
参考:https://qiita.com/walkers/items/b90a97a99bbb27f6550f

Dockerfile
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という設定ファイルを有効化しています。

Dockerfile
RUN echo '#!/bin/sh\nphp artisan "$@"' >> /bin/pa \
  && chmod u+x /bin/pa

php artisanコマンドをpaと短くうてるようにしています。
aliasコマンドを使用していないのは、ホストからdocker exec paと利用できるようにするためです(aliasはコンテナ内のみで有効のため)。

Dockerfile
WORKDIR /var/www

WORKDIR <path>で作業ディレクトリを指定します。以後、RUN, COPY, CMDなどがここで実行されるようになります。
ここではコンテナのbashを利用する際に、カレントディレクトリが/var/wwwになるように、最後ですがWORKDIRを指定しています。


このDockerfileにはありませんが、CMDという命令があります。ベースにしたDockerfileには以下があります。

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でも使用しています。

Dockerfile
RUN docker-php-ext-install -j$(nproc) pdo_mysql \
  && docker-php-ext-install -j$(nproc) pdo_pgsql \
  ...

このように便利なコマンドを準備していたり、カスタマイズする際に使う環境変数を説明してくれていたりするので、DockerHubのページを見るようにしましょう。

信頼できるイメージ

ベースにするイメージは、公式イメージのみにした方が無難です。
Web 1920 – 1.jpg

公式イメージは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エラーです。
1.png
上記の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をみてみると・・・

2.png

ちゃんと動いてます!

コンテナライフサイクル

コンテナは色々な状態があり、詳しくは
Docker入門 コンテナのライフサイクル
によくまとまっていますが、簡易化すると以下の図のようになります。

docker.png

現在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を実行できました。

  1. composerがインストールされたコンテナが立ち上がる
  2. ボリュームで$PWDのファイルがコンテナの/appに共有される
  3. コンテナ内でcomposer installが実行される
  4. インストールされた結果が$PWDに共有される
  5. 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 execdocker container execも同じです。
これは操作する対象が分かりやすいように、ということで後から長いコマンドが追加されました。

参考
docker container / image コマンド新旧比較

個人的には新しい(長い)コマンドの方が統一感があって好きなのですが、タイプ数が少ない古いコマンドも便利なので、主要なものは両方覚えちゃいましょう。

終わりに

今回は簡単なDockerfileとdockerコマンドの紹介でした。
次回は複数のコンテナを管理するDocker-composeと、Dockerボリューム・ネットワークを紹介予定です。
->Docker入門 〜Docker-compose, ネットワーク, ボリューム編〜

:christmas_tree: FORK Advent Calendar 2019
:arrow_left: 16日目 【WordPress】今更だけど、WysiWygエディタ(ビジュアルエディタ)で書いた記事にCSSを適用する方法をまとめてみた話 @Kodak_tmo
:arrow_right: 18日目 今年の正月はNuxtとFirebaseで作ったカードゲームで甥っ子に尊敬されたい @yoh_zzzz

25
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?