はじめに
AWS Elastic Beanstalk(PHPランタイム)を利用したポータルサイトを運営しています。APサーバはWordPress on EC2、DBサーバはRDS(MySQL)という典型的な構成です。その他、ALBやEFSを活用したスケーラブルな構成、CodePipelineを活用したCI/CDの実践など、ベストプラクティスのお世話になっています。先人たち知恵に感謝です。
そんなポータルサイトですが、ローカル開発環境の整備がおざなりになっていました、、、。この度、Docker for Desktop (on Windows 10) を利用して、ローカル開発環境を構築したので、手順やハマったことを共有します。特に、APサーバのOSにAmazon Linux 2を採用したことが、情報の混乱による遠回りを招きました、、、。この記事を読んだ方が、少しでも近道できれば幸いです。
(2019/08/12 追記)
ボリュームマウントするuploadsフォルダの所有者をhttpdプロセスの起動ユーザ(apacheなど)に変更するようにDockerfileを修正しました。これをやっておかないと、メディアのアップロードや物理削除でエラーになります。
開発環境を整理する
まず、開発環境について整理します。AWS上の開発環境を便宜上、リモート開発環境と呼びます。厳密には、リモート/ローカル環境間で各種バージョンを一致させるべきですが、今回はそこまで厳密性にこだわりませんでした。APサーバは、Amazon Linuxのサポート期限が2020年6月30日と迫っていることもあり、Amazon Linux 2を選択しました。これがハマった要因だったのです、、、。
コピー元のリモート開発環境(AWS)
- APサーバ:64bit Amazon Linux 2018.03 v2.8.12 running PHP 7.2 (AWS Elastic Beanstalk)
- DBサーバ:Amazon RDS for MySQL 5.6
コピー先のローカル開発環境(Docker for Desktop on Windows 10 Enterprise)
- APサーバ:Amazon Linux 2(Docker公式イメージ)
- DBサーバ:MySQL 5.7(Docker公式イメージ)
ローカル開発環境でやりたいこと
ローカル開発環境でやりたいことは、とてもシンプルです。
- リモート開発環境と同じgitリポジトリを利用したい(ローカルはfeatureブランチで)
- リモート開発環境と同じデータ(データベースおよびメディアファイル)を利用したい
つまり、(1) APサーバ的なAWS Elastic Beanstalkがやっていることの再現、(2) DBサーバ的なデータ移行です。ただ単に、WordPress環境をローカルPC上に構築するのであれば、WordPressおよびMySQL両方ともDocker公式イメージから構築すれば良いので瞬殺です(クイックスタート・ガイド:Docker Compose と Wordpress)。しかし、AWS Elastic Beankstalk環境と同じようにgitリポジトリを利用するために、Amazon Linux 2から環境構築することにしました。
Dockerコマンドで手順を確認してみる
Dockerfileやdocker-compose.ymlをゴリゴリ書き始める前に、Dockerコマンドで地道に手順を確認することにしました。
まず、最新のAmazon LinuxイメージすなわちAmazon Linux 2イメージを取得して、/sbin/init
でOS起動してみましたが、いきなり以下のエラーが発生してしまいました。/sbin/init
がない、、、これは先が思いやられます。
$ docker pull amazonlinux:latest
$ docker run -it -d --name hoge-web amazonlinux:latest /sbin/init
4cb36ea9...(省略)
docker: Error response from daemon: OCI runtime create failed: container_linux.g
o:344: starting container process caused "exec: \"/sbin/init\": stat /sbin/init:
no such file or directory": unknown.
Amazon Linux 2の公式イメージは、かなりスリムアップされており、systemdすら初期インストールされていないようです。Qiitaの記事を検索してみると、「まず/bin/bash
で起動してsystemd
をインストールしてから/sbin/init
で起動せよ」とのことでしたので、やってみました。まず、/bin/bash
で起動します。
$ docker rm hoge-web
$ docker run -it -d --name hoge-web amazonlinux:latest /bin/bash
b567ea12...(省略)
初期状態では、httpd
が未インストールどころかps
コマンドやping
コマンドすら打てない、ということが分かりましたので、色々追加していきます。細やかなピックアップが面倒だったので、LAMPをインストールしてしまいます。
$ docker exec -it hoge-web /bin/bash
bash-4.2# amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2
bash-4.2# yum install -y systemd xinetd httpd mod_ssl php php-mbstring wget curl tar
bash-4.2# exit
続いて、必要な設定ファイルをコピーしておきます。aws_env.conf
はAWS Elastic Beanstalkが生成するファイルのひとつで、Apacheプロセスへ環境変数を渡すためのものです。その他、systemctl
でhttpd
を自動起動するための設定ファイルやSSL関連ファイルなどがあります。
$ docker cp web/etc/httpd/conf/httpd.conf hoge-web:/etc/httpd/conf
$ docker cp web/etc/httpd/conf.d/aws_env.conf hoge-web:/etc/httpd/conf.d
$ docker cp web/etc/sysconfig/httpd hoge-web:/etc/sysconfig
$ docker cp web/lib/systemd/system/httpd.service hoge-web:/lib/systemd/system
$ docker cp web/ssl/hoge.crt hoge-web:/etc/pki/tls/certs/localhost.crt
$ docker cp web/ssl/hoge.key hoge-web:/etc/pki/tls/private/localhost.key
続いて、systemd
インストール済みのコンテナを/sbin/init
で起動します。httpd
の自動起動を設定後、再びコンテナイメージをコミットします。これがマスタイメージになります。
$ docker commit hoge-web hoge-web:latest
sha256:2f1d9b04...(省略)
$ docker stop hoge-web && docker rm hoge-web
$ docker run -it -d --name hoge-web hoge-web:latest /sbin/init
$ docker exec hoge-web systemctl enable httpd
$ docker commit hoge-web hoge-web:latest
Dockerコマンドから設定ファイルへ
Dockerコマンドで手順が確認できましたので、その内容を設定ファイルで再現していきます。webサービス(WordPress)とdbサービス(MySQL)を利用するので、docker-compose.yml
を使います。
Dockerコマンドで確認した手順の流れ
簡単にまとめると、以下の4ステップです。
- Amazon Linux公式イメージを
/bin/bash
で起動 -
systemd
やhttpd
などをインストールし、マスタイメージをコミット - マスタイメージを
/sbin/init
で起動 -
httpd
の自動起動を設定し、マスタイメージをコミット
手順をDockerfileに落とし込む
手順1と手順2は、Dockerfile
で対応します。先ほどの確認手順に加えて、PHPデバッグ向けにxdebug
をインストールしています。また、AWS Elastic BeanstalkのPHPランタイム環境を再現するために、ディレクトリ作成やシンボリックリンク作成も行っています。
FROM amazonlinux:latest
CMD /bin/bash
# install software packages
RUN amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2 \
&& yum update -y && yum clean all \
&& yum install -y gcc xinetd httpd mod_ssl php php-mbstring php-devel php-pear wget tar \
&& pecl install xdebug \
&& mkdir -p /var/app/current \
&& cd /var/www \
&& rmdir html \
&& ln -s /var/app/current html \
&& mkdir -p /var/www/hoge/local/wp-content/uploads \
&& chown -R apache:apache /var/www/hoge
# copy configration files
COPY web/etc/httpd/conf/httpd.conf /etc/httpd/conf
COPY web/etc/httpd/conf.d/hoge-alias.conf /etc/httpd/conf.d
COPY web/etc/httpd/conf.d/aws_env.conf /etc/httpd/conf.d
COPY web/etc/php.d/xdebug.ini /etc/php.d
COPY web/etc/sysconfig/httpd /etc/sysconfig
COPY web/lib/systemd/system/httpd.service /lib/systemd/system
# copy certificate & private key
COPY web/ssl/hoge.crt /etc/pki/tls/certs/localhost.crt
COPY web/ssl/hoge.key /etc/pki/tls/private/localhost.key
続いて、手順3と手順4をDockerfile
に落とし込みます。
FROM hoge-web:latest
CMD /sbin/init
# enable httpd auto-start
RUN systemctl enable httpd
手順をdocker-compose.ymlに落とし込む
さあ、いよいよdbサービスとwebサービスをまとめるときがやってきました。最終的なdocker-compose.yml
は以下の通りです。何点か工夫を加えました。
-
db/initdb.d
に初期化用のSQLスクリプトを配置- CREATE DATABASEステートメントでデータベース作成
- EXPORTしたダンプファイルでDROP/CREATE/INSERT(=データ移行)
- ローカルPC側のクライアントツールからのアクセスを考慮し、3306ポートを公開
- buildを二段階にするため、web-initとwebに分割
- OSに渡す環境変数を
env_file
として独立 - 自動起動する
systemctl start httpd
がエラーにならないよう特権(privileged: true
)を指定
version: '3'
services:
db:
image: mysql:5.7
container_name: hoge-db
volumes:
- "./db/data:/var/lib/mysql"
- "./db/initdb.d:/docker-entrypoint-initdb.d"
restart: always
environment:
MYSQL_ROOT_PASSWORD: hoge_wordpress
MYSQL_USER: hoge_wordpress
MYSQL_PASSWORD: hoge_wordpress
ports:
- "3306:3306"
web-init:
image: hoge-web:latest
build:
context: ./
dockerfile: ./docker/web-init/Dockerfile
command: /bin/bash
container_name: hoge-web-init
web:
depends_on:
- db
image: hoge-web:latest
build:
context: ./
dockerfile: ./docker/Dockerfile
# command: /usr/sbin/httpd -DFOREGROUND
command: /sbin/init
container_name: hoge-web
volumes:
- "./web/HOGE_WP:/var/app/current"
- "./web/uploads:/var/www/hoge/local/wp-content/uploads"
links:
- db
ports:
- "80:80"
- "443:443"
privileged: true
restart: always
env_file: ./web/etc/sysconfig/httpd
Docker Composeで環境構築する
ついに、AWS Elastic Beanstalk(PHPランタイム)を利用したポータルサイトのローカル開発環境を構築するときがやってきました。面倒な手順はすべて設定ファイルにまとめましたので、コマンドはとてもシンプルです。
$ docker-compose build web-init web
$ docker-compose up -d db web
なお、docker-compose up
するときにサービス名を明示しないと、web-init
サービスもcreate
されてしまい、ごみコンテナが残ってしまうので、注意が必要です。
さいごに
docker-compose.yml
のコメントにあるように、試行錯誤を重ねる中で/sbin/init
ではなく、/usr/sbin/httpd
で起動してしまう方法も考えました。この場合、systemctl
を利用せずにhttpd
を起動/停止するため、/sbin/init
でのコンテナ起動が不要となります。そうすると、かなりシンプルになるのですが、Amazon Linux 2上でのsystemd
を利用したサービス制御ができなくなってしまうため、/sbin/init
での起動にこだわりました。
また、手順を確認したときのように、Dockerコマンドで対応すれば何とでもなるのですが、Docker Composeの利用にこだわりました。少しトリッキーな気もしますが、環境構築のための手順はかなりシンプル化できたと思います。
参考
https://qiita.com/harukisan/items/6910684bbf2043a29812
https://qiita.com/Targityen/items/6e80b855b79d521412f0
https://qiita.com/issei_0403/items/0dd1ce7407e720338ea8
https://qiita.com/idani/items/79de4a525eaeaa16e497