プロローグ
えらい人:今うちらさ、PC上にVirtualBox入れて、VagrantとAnsibleで環境開発してるでしょ。
私:あーそうですねえ。
えらい人:あれさ、中の環境が色々古くなっちゃってさ。環境作るのも大変だし、入社した人が毎回必ずなにか新しいエラーを踏んでて、どうしましょうかって毎回訊かれるんだよね。
私:そうですね。環境構築ってたまにしかやらないし、たまにやるといつの間にか落とし穴ができちゃってますよね。
えらい人:あれをさ。Dockerに切り替えよう。
私:はい?(あれ確か中ではげしく色んな事やってたような)
えらい人:仮想マシンはやっぱ重いし、構築作業もすごい時間かかるし、使い続けてもつらみしか無い。
私:・・・ええとそれは、ローカルの開発環境の話ですよね。
えらい人:うん。本番とかテスト環境とかはとりあえずそのままでいい。ローカルの開発環境だけでいいから。
私:りょ、了解しました。。。
現状の構造
まず、現状の環境を確認しよう。 ここで開発環境と言っているのはPHPでwebアプリ開発をする環境だ。現状はこうなっている。

- 開発者各人のPCにVirtualBoxによる仮想マシンがあって、アプリ開発用のディレクトリがホスト側のディレクトリに同期されている。
- アプリ開発用のディレクトリの下にApacheの仮想ホストのDocumentRoot が設定されている。そして複数あるアプリそれぞれに割り当てられている。画面デザインテンプレートや認証を管理する共通アプリケーションもある。
- 開発用DBも仮想マシン内で動いていて、各アプリからアクセスしている。
- 開発作業としては、ホスト側のPhpStormでソースを編集し仮想マシン側のOSで実動作&単体テスト実行などをしている。
- 動作確認としては、ホスト側のブラウザで各アプリケーションにアクセスして行う。
- 仮想マシン側OSに必要なファイル群が2つのgitリポジトリで管理されている。VirtualBox関係のファイルのリポジトリと、Ansible関係のファイルのリポジトリ。
- 各アプリケーションはそれぞれのgitのリポジトリで管理されている。
さてこれをコンテナ化するのだが、どうやって作ろうか?
現状の環境構築手順によると、概ね以下の手順になっている。
- 大元のCentOS のboxファイルがあるので、これで
vagrant upする。 - 起動した仮想マシンにsshして、OSへの設定を施すコマンドを実行する。
ansible-playbookの yml ファイルの実行もある。 - 開発したいアプリケーションをgithubから
git cloneし、仮想マシン内にて設定を施すコマンドを実行する。(これは各アプリ固有の環境設定である。)
じゃあ、これと同じことを Dockerfile でやればよいか。今までは手作業が結構あったけどなるべく Dockerfile に押し込むようにしよう。あと開発用DBはすでに別途コンテナ化されているからここには要らないな。 Docker のネットワーク経由で接続するようにしよう。
作ってみよう!
では作ってみよう。開発環境だけ作ってもテストにならないから、アプリケーションを一個選んでこれで動作検証しながら作ろう。アプリ開発ができるようになればOKだ。今後も継続的に改修が続きそうなアプリを選ぼう。
まずベースイメージどうしよう?php:apache というイメージが dockerhubにはあって、PHPとApacheが込みになっている。だがこれのLinuxのディストリビューションは Debian だ。ディストリビューションが違うとファイルシステムとかいろいろ差異が出てしまう。吸収のゴタゴタ大変かもな。
基本的にPHPのモジュール含めて既存環境と同じにする必要があるわけだから、素のCentOSのイメージから始めて Vagrantfile や Ansible や手作業でやってることをDockerfileに移植していこう。
共通部分のイメージ化
<<ドタドタ・バタリ>>
うん。とりあえず一通りできたぞ。Dockerfile と docker-compose.yml を用意したから、
> docker-compose up -d
などと実行すれば、サーバが立ち上がる。ファイルの同期の設定もできている。アプリの環境設定の手作業がいくつか必要なのだけれど…
動作としては、アプリは動作する。phpunit を叩いて単体テストの全部実行もカバレッジレポートの生成もできる。ホスト側からファイルを修正するとコンテナ内部に反映される。ううむとりあえずこれでいいかな。
さて。
1個のアプリ向けにはできた。これを複数あるアプリに展開したいわけだがどうしよう。 いわゆるOSのレベルの設定は皆同じで、共通アプリケーションというものもあるわけだ。今作ったファイルを各アプリに手作業でコピペで展開すると、共通部分を更新したときの再展開が面倒なことになるかもな。
それに、今の手順では、共通アプリケーションで必要な composer install を手動実行しないといけない。コンテナが立ち上がってファイルをマウントしてからじゃないと実行できないからしょうがないといえばそうなんだけど。
でも共通部分はそんなに変更されないから、こういうところだけ共通イメージ作ってそれに入れられないかな。Dockerfile で共通アプリケーションのファイルを COPY して、composer install しておけばよいな。開発者各人がやる作業は少ないほうがいい。
各アプリでの composer install は、、、各アプリの compose.json は、一応開発対象だから変更されるかもしれない。これは各開発者にやってもらおう。
図にするとこんな感じだ。黄色い箇所は共通化できる。共通イメージにしてしまおう。

共通イメージの配置場所と自動ビルド
共通イメージの置き場所は、、、認証の管理がしやすいから、Github Container registry に置いてしまおう。以前、Mavenアーティファクトを Github Packages に置いてたから、それと同じようにやれば良いかな。
ついでに build と push も Github Actions でやろう。誰かのPCで手作業でビルドする運用だと環境握り込むようなもんだしそもそも面倒だからやめよう。
<<ドタドタ・バタリ>>
よし、こんな具合でどうだ。
name: build common image and push
on:
push:
tags:
- '*.*.*' # リリースタグを付けたときに実行される。
jobs:
build:
env:
REPOSITORY_URI: ghcr.io/GMORBLOGDUMMY/DEV_ENV_COMMON_IMAGE
PAT_FOR_CI: ${{ secrets.PAT_FOR_CI }} # Github Container registry にログインするためのpatをシークレットから設定する。
steps:
- name: determine_tag # リリースタグを切り出す。
id: determine_tag # 値の受け渡しのためid設定が必要
run: |
set -e -x
semver=$(echo ${{ github.ref }} | cut -c 11- )
echo ::set-output name=tag_semver::${semver}
- name: login to ghcr.io
run : |
echo $PAT_FOR_CI | docker login ghcr.io -u ci-user --password-stdin
- name: checkout code myself. # 共通開発環境のファイル群をチェックアウト
uses: actions/checkout@v2
with:
path: common_dev_env
- name: checkout Ansible yaml file # Ansibleのファイル群をチェックアウト 共通開発環境とはパスを分ける。
uses: actions/checkout@v2
with:
repository: GMORBLOGDUMMY/SAMPLE_ANSIBLE_MODULE
path: ansible_module
- name: build and push images # イメージをビルド&プッシュ。タグ番号もつける。
run: |
set -e -x
docker build -t $REPOSITORY_URI:latest -f common_dev_env/Dockerfile
docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:${{ steps.determine_tag.outputs.tag_commit_hash }}
docker push $REPOSITORY_URI:latest
docker push $REPOSITORY_URI:${{ steps.determine_tag.outputs.tag_commit_hash }}
これで、リリースタグをつけた時にGithub Container registryにイメージがpushされるようになった。
で、各アプリ側では、こういう Dockerfile を使ってコンテナを立てる。
FROM ghcr.io/GMORBLOGDUMMY/DEV_ENV_COMMON_IMAGE # 以下、このアプリ向けの設定ファイルの COPY や、環境設定の RUNが実行される。 # : # :
docker-compose.yml も用意して、ボリュームマウントでホストOSとコンテナのファイルシステムを同期したり、 環境変数設定やポート設定など必要な設定を入れる。
services:
sample-application:
build: .
container_name: sample-application
ports:
- "80:80"
volumes:
- ../.:/var/www/html/sample-application
environment:
- TZ=Asia/Tokyo
tty: true
privileged: true # apacheを使うための設定
command: /sbin/init # apacheを使うための設定
networks: # DB接続するためのネットワーク設定
default:
external: true
name: local_dev_network
よしこれでできた。あとは実際に開発する人に使ってもらって、バグ指摘含めてフィードバックをもらいながら直していこう。開発ツールは本来開発者自分自身で工夫するのが良いしな。自分自身で創意工夫を発揮しないとつまらないよな。
まとめ
今回は今まで使っていた VirtualBoxベースの開発環境から Dockerベースの環境を作ることができた。 既存環境へのファイルの変更は最小限に抑えたから、既存環境がすでにつくってあるひとも変更なく開発を続けられる。(徐々にDockerに切り替えてもらおう。)
今回の記事には書けなかったが、以下のようなこともやっている。とりあえずお蔵入りとしよう。日の目を見るときがあるだろうか。
- ホストOSとコンテナ間のファイルの同期が遅いのでアプリ全体の動作が遅い。 mutagenを導入して高速化した。
- PhpStormでのデバッグ実行や単体テストが実行できるように設定をした。
- 定型的なコマンド操作(アプリごとの環境構築など)をシェルスクリプトにして
Dockerfileから生成した。
