これは ドリコム Advent Calendar 2017 の15日目です。
14日目は、宇佐美亮さんによる「これからお仕事でコミュニケーション機会が増える方へ」です。
ゲームアーキテクトグループの岩本です。
最近は、社内で開発しているゲームのパフォーマンスを見たり最適化について調査・対応をやってます。
まえふり
ここ最近、プライベートで数名とゲームを作っていまして、普段仕事では、UnityやCocos2d-xなどのクライアント開発がメインということもあり、サーバーサイド全般でAPI開発を担当することにして、私がメインで、サブで大学のパイセンと一緒に楽しく作っています。
ということで、ほぼほぼ私がガリガリ開発しているので、どうせならサーバー側で新しいことと前職でいろんな理由でできなかった内容を取り入れつつ、スキルアップを兼ねて黙々と作っていたりしていました。
クライアントは、UnityでWebGL向けに開発しているらしいので、そこの知見ももらったりしています。
そこで今回は、始めた触ったDocker関連で溜まった知見をまとめて記事にしてみました。
この記事では、DockerやVagrantなど各用語が出てきますが、それが何なのか最低限知っている前提で話しています。
簡単に解説しますが、基本、各用語の説明はしませんのでご注意ください。
目標
Dockerを初めて扱うに当たって、今回自分の中でいくつか決めた目標があります。
- Dockerを扱えるようになること。
- Dockerは、本番環境まで扱える製品なのか確かめる。
- Dockerを扱うことが何が便利になるのか。
- Dockerコンテナの構成を設計できるようになる。
- Dockerfileを作って、GitHubまたはDockerHubで公開してみる。
Dockerを本番環境で扱っている話をあまり聞いたことがなかったので、やっぱし**本番運用でDockerを扱うか問題**があるみたいなので、本番環境まで扱えるのか自分で確かめたいってな感じです。
構成
簡単に開発しているサーバー側の構成についてご紹介します。
- Local
- Vagrant
- Docker
- CentOS 6.9
- MySQL 5.6.38
- PHP 7.0.26
- Apache 2.2.15
- Development
- Amazon EC2
- Docker(LocalのDockerと同一)
- Amazon EC2
この構成にした理由ですが、前提として使い慣れているものを扱うこととし、dockerコンテナ内は一般的なLAMPにしました。前職でVagrant上にLAMP環境を構築し、Web開発していた内容をベースに、開発しながら調整していって、Dockerfileを書き換えて自分が一番扱いやすい環境を作ることを当初から目標にしています。
今回初めてDockerを使って、自分用のDockerfileを作り、GitHubとDockerHubにアップしました。
今後も定期的にメンテナンスと他にも環境作ってDockerfileにまとめたらアップしていこうと考えています。
環境構築の作業フェーズ
とりあえずローカルと開発環境までを準備できて問題なく開発できればいいや、と割り切って取り掛かっていきました。
実際に手を動かしていくと上記の図のような作業の流れになり、環境が安定するまでは、ずっとDockerコンテナ(Webサーバー構築)内で構築して→確認して→Dockerfileを書き換えて→再構築の繰り返しでした。
ちなみに「ダメだったら」というのは、
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<none> <none> fca4c61d0fa7 3 weeks ago 195MB
という状態でdocker build
に失敗していたり、docker exec
でDockerコンテナに入って確認していくと、想定通りの構築ができていなかったりなど色々ありました。
ですが、**開発環境を構築するときは、環境が出来上がった後なので、コマンド叩いて確認だけで問題なく終わりました。ここは、Dockerの強みである"アプリケーションのデプロイに最適化"**にあたると思います。
ループしている期間は、約2週間弱ぐらいだったと思います。
今でも開発しながら調整しており、作っては壊しての繰り返しをたまに(特にトラブルが起きたときなど)やってます。Dockerは0→1ベースで環境構築が行われるから、Dockerfileを書き換えてdocker build
してdocker run
するだけですぐに環境が作れます。失敗しているときはアレなものですが、、上手くいったときはとてもスッキリします笑
ですがこのとき、必要に応じてローカル・開発環境で動いていたときのログや状態をバックアップしてから復旧・反映作業する必要があって、ここが意外と手間になっているので、何か対策が必要だなぁと思っているものの、一旦放置しています笑
VagrantとDockerの組み合わせ
さて、VagrantもDockerも知っている人に関しては、『なんでVagrant使っているのか』と思ったのかもしれません。
個人的な理由として、次のように2つあります。
- ドットインストールのDockerのチュートリアルがVagrantとDockerの構成を使っていたから。
- Dockerは、カーネルに親密に関わっているから手元のPCよりもVM側で一元管理化したかったから。
特に、ドットインストールで解説されている内容はとてもわかりやすく、かつ他エンジニアに共有するときにやりとりしやすいからです。
『Dockerわからないなら、とりあえずドットインストール確認してね。』で終わりますよ笑
構成自体もUbuntuでLinuxベースだから問題なく、特に意識することなく開発できています。
今回のDockerコンテナの構成
Dockerを用いてアプリケーションを構築する際には、(おそらく)コンテナの構成は大きく分けて次の2パターンがあります。
すっごくわかりやすくしたので違和感があるかもしれませんが、だいたいこんな感じかなと思います。
右のパターンは、おそらくDockerHubの公式で提供されているDockerfileを一つ一つdocker build
して、docker run
するときにディレクトリの共有をすれば問題なく動くはずです。
今回は、左のパターンでDockerfileを作りました。
VagrantやEC2などで環境を作るときとだいたい同じ構成で作業できるので、いつも通りという感じです。というのも、右のパターンにしたときの連携方法がよくわからなかったので、まずは使い慣れた構成で作ってみて、Dockerをある程度知ってから右の方法を試してみようと考えたからです。
BoxとDockerfileについて
前職でVagrantを使っていたとき、VagrantBoxの共有が、共有する側も受け取って構築する側もとにかく大変でした。
何が大変だったかというと、VagrantBoxをビルドしたPCとこれから環境構築するPCのVagrantとVirtualBoxのバージョンの違いによって問題が起きるからです。
VagrantのBoxは、VagrantのバイナリデータでDockerfileはテキストデータだから、このような問題は、Dockerのメジャーアップデート(仕様変更の大規模アップデート)ぐらいでしか問題は起きないので、環境構築で工数取られるのは最初だけだと思います。Docker強いw
Dockerfileの作り方
ここからは、GitHub・DockerHubにアップしたDockerfileをどうやって作ったのかを紹介します。
作った後で気づいたんですが、上記コンテナの構成の右図の方が一番早く作れて、かつ安全だと思います笑
今回作ったDockerfileを見てもらって、環境構築の作業フェーズで解説した内容からイメージできると思いますが、、すごく地道でストレスが溜まる作業内容でした笑汗
オレオレDockerfile
さて、作ったオレオレDockerfileですが、次のリンク先を参考に作らせてもらいました。
いろんなDockerfileを参考に、オレオレDockerfileを書いてはdocker build
して、失敗して、書き直しての繰り返しで、成功したと思いきや正常に動いていなくてdocker rmi
して、オレオレDockerfileを書き直して...という作業でした _(:3 」∠ )_
- CentOS 6.9
- MySQL 5.6.38
- PHP 7.0.26(7.1)
- Apache 2.2.15
※最初、PHP7.1を入れていたのですが、トラブル集のところで解説している内容により、途中PHP7.0にダウングレードしました。
1度も環境構築したことがないMac上でdocker build
をやってみた
$ git clone https://github.com/gremito/centos-php7-lamp.git
$ cd centos-php7-lamp/
$ docker build -t gremito/lamp .
...
Successfully built 478776a96b92
Successfully tagged gremito/lamp:latest
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gremito/lamp latest 478776a96b92 41 seconds ago 2.13GB
centos 6.9 fca4c61d0fa7 3 weeks ago 195MB
$ docker run -d -p 80:80 gremito/lamp
1b2a54e337c37b05fde3010098e2781210a8ee91935e1d07955abaf67bad8520
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b2a54e337c3 gremito/lamp "/usr/bin/supervis..." 42 seconds ago Up 42 seconds 0.0.0.0:80->80/tcp, 3306/tcp focused_lamport
$ docker rename focused_lamport gremito_lamp
$ docker exec -it gremito_lamp /bin/bash
[root@1b2a54e337c3 html]#
[root@1b2a54e337c3 html]# cat /etc/redhat-release
CentOS release 6.9 (Final)
[root@1b2a54e337c3 html]# uname -a
Linux 1b2a54e337c3 4.9.41-moby #1 SMP Wed Sep 6 00:05:16 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
[root@1b2a54e337c3 html]#
[root@1b2a54e337c3 html]# exit
exit
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b2a54e337c3 gremito/lamp "/usr/bin/supervis..." 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp, 3306/tcp gremito_lamp
こんなにすんなり環境構築できるとか、、やっぱしDockerサイコーですね!!!(๑ ⁼̴̀ݖ⁼̴́๑)bグッ
トラブル集
とは言ったものの、ここまでたどり着くのに結構ハマったことがありました。
Docker固有は特になかったのですが、Vagrant + Docker + コンテナ(Webサーバ)の連携で起きたトラブルをここでまとめておくことにします。
vagrant up
したときにマウント失敗エラー
詳細
これはVagrantのトラブルになります。uidとgidが1000というのは、Ubuntu(Vagrant)側ではなく、CentOS(Dockerコンテナ)側のパーミッション関連みたいでした。
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
default: /vagrant => /Users/XXXXXXXX/vagrant/ubuntu_1404_trusty_amd64
Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:
mount -t vboxsf -o uid=1000,gid=1000 vagrant /vagrant
The error output from the command was:
: No such device
docker build
後にDockerImageをdocker run
する際に、
$ sudo docker run -d -p 80:80 -v /vagrant/share:/var/www/html gremito/lamp
という感じで、Ubuntu(Vagrant)側のディレクトリとCentOS(Dockerコンテナ)側のディレクトリを共有する設定をしています。おそらく、このファイル共有が原因でvagrant run
ができなくなるんでしょう。
解決内容
記事を参考に、Dockerを動かしているVagrantのディレクトリ(Vagrantfileがあるところ)で、
$ vagrant plugin install vagrant-vbguest
をすると解決できました。
vagrant-vbguestに関しては、下記リンクで解説されています。
MySQLが途端に動かなくなった...
詳細
急にmysqlが起動しないことが起きました。
確か、Vagrantを再起動したときだったよーな。
エラー内容をググっても最初は、原因がハッキリとわかりませんでした。
[root@fa2c02bdc678 html]# service mysqld start
/usr/bin/mysqld_safe: line 183: 24242 強制終了 nohup /usr/sbin/mysqld
--basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin
--user=mysql --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid
--socket=/var/lib/mysql/mysql.sock < /dev/null > /dev/null 2>&1
MySQL Daemon failed to start.
mysqld を起動中: [失敗]
解決内容
ちょうどとある記事でDockerは使っていないものの、構成が似ていてエラー内容も同じで、2パターンの解説方法があると解説されている方がいました。
- 仮想OSのメモリサイズを増やす
- Vagrantfileの設定変更 ← 簡単
- スワップ領域の確保
- VM側のスワップ領域を操作 ← めんどくさい...
確かにSwapが0だったので、まずは簡単な方から試してみようと思いVagrantのメモリを増やしたところ...上手くいきました笑
Dockerには特に関わっていないので、参考した記事の内容の通りVagnratによる問題でした。
ですが、最初は何が何だかわからなかったので、docker rmi
しては、Dockerfilを成功していたときの最初の状態に戻してみてdocker build
して、確認したものの結果変わらずで、Vagrant+Docker+Webサーバー(Dockerコンテナ)と連携していると原因特定がなかなか困難でした。
バージョン問題
今回使っているPHPのフレームワークは、FuelPHPという日本でしかあまり使われていないフレームワークになります。
これも前職でガッツリ使っていたのですが、まだPHP7は使い慣れないし何か問題があると困るからという理由でPHP5系を使い、fuelパッケージの中身をカスタマイズしながら、早く納品することに専念していたのでちゃんと使いこなせていなかった過去がありまして。
そこで今回、FuelPHPでPHP7を扱うとPHP5との違いはどのぐらいの効果があるのか、使いこなせなかったoilコマンドを使うこと、FuelPHPならではの強みをちゃんと学んでおきたかった、という理由があっての採用でした。
詳細
先にデータベースを作っていたので、modelとmigrationを作成してくれるoilコマンド"php oil r fromdb:model (テーブル名)
"を実行したところ、PhpErrorExceptionのエラーが発生してmodelとmigrationが作れませんでした。
解決方法
調べたところ、FuelPHP Forumsで似たような問題を問い合わせしている人のやりとりを見つけました。
『anyway I also tried php 7.0. And it made the job.』という解決内容があって、その後に『Your original issue should have been resolved now in 1.9/develop.』と答えている方がいたので、FuelPHPを1.9にアップデートすることもできたんですが、現在(2017/11)正式版の最新は1.8なのでPHPをダウングレードする方で解決しました。
これは、ちゃんと入れる前に調べておけば回避できたことなのですが、Dockerを使い慣れることに専念していたので失念していました。。。
これも特にDockerには関わっていない内容になります。
Docker絡みで話すと、最初、oilコマンドを叩く場所をどこでやったらいいのか迷ったことがあり、ちゃんと構成を振り返れば、Dockerコンテナに入ってoilコマンドを叩くとわかりました。
ただ、logディレクトリを作ったりするときは、状況によってMac側かUbuntu(Vagrant)側かのどっちかなんで、VagrantもDockerも使い慣れていない方に関しては、ここの原因調査するときはハマりどころかもしれません。
まとめ
目標の内容で結果、以下のようになりました。
- Dockerの使い方がわかって、最初わからなかった上記コンテナの構成の右図の作り方がわかるようになった。
-
ローカル・開発のサーバー環境構築で採用することが一番良いと感じた。
- 本番環境で採用するには難しい、まだまだ経験と情報が必要だと感じた。
- もっとオレオレDockerfile作って、オレオレ環境を充実させよう!
- ドリコム Advent Calendar 2017の12/12(火)に@hiracy さんが『Go言語とDockerでインフラエンジニア向けのシンプルなCLIツール開発環境を作る』っていうDocker関連の記事をアップされているので参考にしてみることに。
やってみて気づいたこと思ったこと
最後にざっと気づいたことを並べて終わりにしたいと思います。
- LinuxベースのOSだったら、特に問題なくDockerを扱ってすぐに環境が用意できる強みがある。
- ECS(Amazon Elastic Container Service)があるから、この構成だと柔軟に対応ができてAWSとの相性は良い。
- バージョンの選定は前もって調査した上で作る必要があること。特にDocker使っているなら尚更!!
- 作り直す前にバックアップが必要か確認すること。またDockerコンテナに入って調整した内容をちゃんとドキュメント化しておくこと。
- Macだったら、Vagrant使わずにDocker単体で使った方が上記**トラブル集のマウントやメモリ周りの問題は無くなる。**もしかしてVagrantいらない...?笑
- 普段、VagrantでWebアプリが開発できる環境をすでに持っていて作業している人には、なるべく今の環境を影響与えずに新しいソフトを入れたいってときにDockerは便利かも。
- 一度、Dockerを用いて開発・運用した経験がある人と飲みニケーションしたいなぁ笑
- この記事を書いている最中に、上記コンテナの構成の左と右の図の環境でどのぐらいパフォーマンスが違うのか試してみたいと思いまして、結果が面白かったら記事にしたいなぁと。
ここまで読んでいただき、ありがとうございました。
明日は、Unity Advent Calendar 2017で記事をアップします笑