プロローグ
えらい人:今うちらさ、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 ファイルの実行もある。- Ansible の 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
を用意したから、
1 |
> 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で手作業でビルドする運用だと環境握り込むようなもんだしそもそも面倒だからやめよう。
<<ドタドタ・バタリ>>
よし、こんな具合でどうだ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
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
を使ってコンテナを立てる。
1 2 3 4 5 |
FROM ghcr.io/GMORBLOGDUMMY/DEV_ENV_COMMON_IMAGE # 以下、このアプリ向けの設定ファイルの COPY や、環境設定の RUNが実行される。 # : # : |
docker-compose.yml
も用意して、ボリュームマウントでホストOSとコンテナのファイルシステムを同期したり、 環境変数設定やポート設定など必要な設定を入れる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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
から生成した。docker-compose exec
でホスト側OSから直接実行できる。- シェルスクリプト化で長いオプションのコマンドも短くできる。