オレオレDockerfileを作って、サーバーサイドの開発をやっている話。

![ドリコム Advent Calendar 2017](https://tech.drecom.co.jp/wp-content/uploads/2017/11/eyecatch_1712AdventCalendar_header.png)

これは [ドリコム Advent Calendar 2017](https://adventar.org/calendars/2575) の15日目です。
14日目は、宇佐美亮さんによる「[これからお仕事でコミュニケーション機会が増える方へ](https://tech.drecom.co.jp/working_communication/)」です。

ゲームアーキテクトグループの岩本です。
最近は、社内で開発しているゲームのパフォーマンスを見たり最適化について調査・対応をやってます。


## まえふり
ここ最近、プライベートで数名とゲームを作っていまして、普段仕事では、UnityやCocos2d-xなどのクライアント開発がメインということもあり、サーバーサイド全般でAPI開発を担当することにして、私がメインで、サブで大学のパイセンと一緒に楽しく作っています。

ということで、ほぼほぼ私がガリガリ開発しているので、どうせなら**サーバー側で新しいこと**と**前職でいろんな理由でできなかった内容**を取り入れつつ、スキルアップを兼ねて黙々と作っていたりしていました。
クライアントは、UnityでWebGL向けに開発しているらしいので、そこの知見ももらったりしています。

そこで今回は、始めた触ったDocker関連で溜まった知見をまとめて記事にしてみました。
この記事では、DockerやVagrantなど各用語が出てきますが、それが何なのか最低限知っている前提で話しています。
簡単に解説しますが、基本、**各用語の説明はしません**のでご注意ください。


### 目標
Dockerを初めて扱うに当たって、今回自分の中でいくつか決めた目標があります。

- Dockerを扱えるようになること。
- Dockerは、本番環境まで扱える製品なのか確かめる。
- Dockerを扱うことが何が便利になるのか。
- Dockerコンテナの構成を設計できるようになる。
- Dockerfileを作って、GitHubまたはDockerHubで公開してみる。

Dockerを本番環境で扱っている話をあまり聞いたことがなかったので、やっぱし**[本番運用でDockerを扱うか問題](http://www.atmarkit.co.jp/ait/articles/1703/27/news006.html)**があるみたいなので、本番環境まで扱えるのか自分で確かめたいってな感じです。

　
 ### 構成
簡単に開発しているサーバー側の構成についてご紹介します。

- Local
    - Vagrant
    - Docker
        - CentOS 6.9
        - MySQL 5.6.38
        - PHP 7.0.26
        - Apache 2.2.15
- Development
    - Amazon EC2
        - Docker（LocalのDockerと同一）

　
この構成にした理由ですが、前提として**使い慣れているものを扱う**こととし、dockerコンテナ内は一般的なLAMPにしました。前職でVagrant上にLAMP環境を構築し、Web開発していた内容をベースに、開発しながら調整していって、Dockerfileを書き換えて**自分が一番扱いやすい環境を作る**ことを当初から目標にしています。

今回初めてDockerを使って、自分用のDockerfileを作り、[GitHub](https://github.com/gremito/centos-php7-lamp)と[DockerHub](https://hub.docker.com/r/gremito/lamp/)にアップしました。
今後も定期的にメンテナンスと他にも環境作ってDockerfileにまとめたらアップしていこうと考えています。


### 環境構築の作業フェーズ
とりあえず**ローカルと開発環境までを準備できて問題なく開発できればいいや、と割り切って**取り掛かっていきました。　

　　　<img src="https://qiita-image-store.s3.amazonaws.com/0/82601/c2e89a83-6923-d77e-ad2a-016b9448de5b.png" width=65%>

　
実際に手を動かしていくと上記の図のような作業の流れになり、環境が安定するまでは、ずっと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の組み合わせ

　　<a href="http://alandargan.com/using-vagrant-docker/"><img src="https://qiita-image-store.s3.amazonaws.com/0/82601/7c261f86-032f-97a3-dd47-6ee4f1fe2f56.png" width=60%></a>
　参考: [Using Vagrant & Docker](http://alandargan.com/using-vagrant-docker/)


さて、VagrantもDockerも知っている人に関しては、『なんでVagrant使っているのか』と思ったのかもしれません。
個人的な理由として、次のように2つあります。

- [ドットインストールのDockerのチュートリアル](https://dotinstall.com/lessons/basic_docker)がVagrantとDockerの構成を使っていたから。
- Dockerは、カーネルに親密に関わっているから手元のPCよりもVM側で一元管理化したかったから。

　
特に、ドットインストールで解説されている内容はとてもわかりやすく、かつ他エンジニアに共有するときにやりとりしやすいからです。

『Dockerわからないなら、とりあえずドットインストール確認してね。』で終わりますよ笑
構成自体もUbuntuでLinuxベースだから問題なく、特に意識することなく開発できています。


### 今回のDockerコンテナの構成
Dockerを用いてアプリケーションを構築する際には、（おそらく）コンテナの構成は大きく分けて次の２パターンがあります。

　<img src="https://qiita-image-store.s3.amazonaws.com/0/82601/2e8fd286-7f7b-3cc8-8ef7-083f0c80689d.png" alt="dockerのイメージ図.png" title="dockerのイメージ図.png" width=45%>　　<img src="https://qiita-image-store.s3.amazonaws.com/0/82601/d9027ef7-bb43-d505-83c4-00ecca6cf964.png" alt="dockerのイメージ図2.png" title="dockerのイメージ図2.png" width=45%>


すっごくわかりやすくしたので違和感があるかもしれませんが、だいたいこんな感じかなと思います。
右のパターンは、おそらく[DockerHubの公式で提供されているDockerfile](https://hub.docker.com/explore/)を一つ一つ`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](https://github.com/gremito/centos-php7-lamp/blob/master/Dockerfile)をどうやって作ったのかを紹介します。
作った後で気づいたんですが、上記コンテナの構成の右図の方が一番早く作れて、かつ安全だと思います笑

今回作った[Dockerfile](https://github.com/gremito/centos-php7-lamp/blob/master/Dockerfile)を見てもらって、環境構築の作業フェーズで解説した内容からイメージできると思いますが、、すごく地道でストレスが溜まる作業内容でした笑汗


### オレオレDockerfile
さて、作ったオレオレDockerfileですが、次のリンク先を参考に作らせてもらいました。
いろんなDockerfileを参考に、オレオレDockerfileを書いては`docker build`して、失敗して、書き直しての繰り返しで、成功したと思いきや正常に動いていなくて`docker rmi`して、オレオレDockerfileを書き直して...という作業でした ＿(:3 」∠ )_

- **CentOS 6.9**
    - [OFFICIAL REPOSITORY CentOS](https://hub.docker.com/_/centos/)
- **MySQL 5.6.38**
    - [mohri1219/centos6.7-ruby2.2.2-mysql5.6](https://hub.docker.com/r/mohri1219/centos6.7-ruby2.2.2-mysql5.6/~/dockerfile/)
    - [CentOS6.8にMySQL5.6をインストール](https://qiita.com/zakki/items/f065c79bbe287481c292)
    - [MySQL5.6が起動しない時](https://qiita.com/nnmr/items/79400922df5810756bce)
- **PHP 7.0.26（7.1）**
    - [tplatform/aws-linux-httpd24-php70](https://hub.docker.com/r/tplatform/aws-linux-httpd24-php70/~/dockerfile/)
    - [CentOS6／CentOS7にPHP5.6／PHP7をyumでインストール](https://qiita.com/ozawan/items/caf6e7ddec7c6b31f01e)
    - [CentOSにPHP7をインストールする方法～PHPのバージョンにご注意！](http://affiwork.net/php-install/)
    - [yumでCentOS6.7にPHP7をインストールする方法とハマった点](https://www.lancork.net/2016/05/yum-centos67-php7-install/)
- **Apache 2.2.15**
    - [nickistre/centos-lamp](https://hub.docker.com/r/nickistre/centos-lamp/~/dockerfile/)

※最初、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`ができなくなるんでしょう。
　

#### 解決内容
[記事](https://qiita.com/okoi/items/05921db3612b7476aa60)を参考に、Dockerを動かしているVagrantのディレクトリ（Vagrantfileがあるところ）で、
> $ vagrant plugin install vagrant-vbguest

をすると解決できました。
vagrant-vbguestに関しては、下記リンクで解説されています。

* [Vagrant の Box の Guest Additions を最新化する方法](https://weblabo.oscasierra.net/vagrant-vbguest-plugin-1/)
* [Vagrantプラグインの使い方（vbguest, hostmanager, sahara, snapshot）](http://www.task-notes.com/entry/20150714/1436842800)
　

### 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 を起動中:                                           [失敗]
```

　
#### 解決内容
ちょうど[とある記事](http://nekopunch.hatenablog.com/entry/2014/03/22/020507)でDockerは使っていないものの、構成が似ていてエラー内容も同じで、２パターンの解説方法があると解説されている方がいました。

* 仮想OSのメモリサイズを増やす
    * Vagrantfileの設定変更　　　 ← 簡単
* スワップ領域の確保
    * VM側のスワップ領域を操作　← めんどくさい...

確かにSwapが0だったので、まずは簡単な方から試してみようと思いVagrantのメモリを増やしたところ...上手くいきました笑
Dockerには特に関わっていないので、参考した記事の内容の通りVagnratによる問題でした。

ですが、最初は何が何だかわからなかったので、`docker rmi`しては、Dockerfilを成功していたときの最初の状態に戻してみて`docker build`して、確認したものの結果変わらずで、**Vagrant+Docker+Webサーバー（Dockerコンテナ）と連携していると原因特定がなかなか困難**でした。


### バージョン問題
 <a href="https://fuelphp.com/"><img src="https://qiita-image-store.s3.amazonaws.com/0/82601/11533da9-7a9f-6a66-e8e5-c7753ecaf166.png" title=fuel_logo_light.png" width=40%></a>

　
今回使っている**PHPのフレームワークは、FuelPHPという日本でしかあまり使われていないフレームワーク**になります。

これも前職でガッツリ使っていたのですが、まだPHP7は使い慣れないし何か問題があると困るからという理由でPHP5系を使い、fuelパッケージの中身をカスタマイズしながら、早く納品することに専念していたのでちゃんと使いこなせていなかった過去がありまして。
そこで今回、FuelPHPでPHP7を扱うとPHP5との違いはどのぐらいの効果があるのか、使いこなせなかったoilコマンドを使うこと、FuelPHPならではの強みをちゃんと学んでおきたかった、という理由があっての採用でした。


#### 詳細
先にデータベースを作っていたので、modelとmigrationを作成してくれるoilコマンド"`php oil r fromdb:model (テーブル名)`"を実行したところ、PhpErrorExceptionのエラーが発生してmodelとmigrationが作れませんでした。


#### 解決方法
調べたところ、[FuelPHP Forums](https://fuelphp.com/forums/)で[似たような問題を問い合わせしている人のやりとり](https://fuelphp.com/forums/discussion/14013/oil-scaffold-admin-fatal-error)を見つけました。

『*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](https://adventar.org/calendars/2575)の12/12（火）に@hiracy さんが『[Go言語とDockerでインフラエンジニア向けのシンプルなCLIツール開発環境を作る](https://qiita.com/hiracy/private/84a5d126727fbc887e1c)』っていうDocker関連の記事をアップされているので参考にしてみることに。

 

### やってみて気づいたこと思ったこと
最後にざっと気づいたことを並べて終わりにしたいと思います。


- LinuxベースのOSだったら、特に問題なくDockerを扱ってすぐに環境が用意できる強みがある。
- [ECS（Amazon Elastic Container Service）](https://aws.amazon.com/jp/ecs/)があるから、この構成だと柔軟に対応ができてAWSとの相性は良い。
- バージョンの選定は前もって調査した上で作る必要があること。特にDocker使っているなら尚更！！
- 作り直す前に**バックアップが必要か確認**すること。またDockerコンテナに入って調整した内容を**ちゃんとドキュメント化**しておくこと。
- Macだったら、Vagrant使わずにDocker単体で使った方が上記**トラブル集のマウントやメモリ周りの問題は無くなる。**もしかしてVagrantいらない...？笑
- 普段、VagrantでWebアプリが開発できる環境をすでに持っていて作業している人には、なるべく**今の環境を影響与えずに新しいソフトを入れたいってときにDockerは便利**かも。
- 一度、**Dockerを用いて開発・運用した経験がある人と飲みニケーションしたい**なぁ笑
- この記事を書いている最中に、**上記コンテナの構成の左と右の図の環境でどのぐらいパフォーマンスが違うのか試してみたい**と思いまして、結果が面白かったら記事にしたいなぁと。

　
ここまで読んでいただき、ありがとうございました。
明日は、[Unity Advent Calendar 2017](https://qiita.com/advent-calendar/2017/unity)で記事をアップします笑