ローカル開発環境を作り易いDockerfileのフォルダ構成


 - 本番環境にDockerを導入し、Dockerのメリットである不変性を利用してWebアプリのローカル開発環境を整えたときのノウハウメモ。
 - Beanstalkを使っていますがBeanstalkのことはここには書いてません。
 - 実際に作ったWebアプリは↓こんな感じ

| 項目 | 内容 |
|:--|:--|
| 概要 | Webアプリケーション。シングルページアプリ。 |
| 環境 | AWS, ElasticBeanstalk |
| サーバーサイド言語 | Java, gradle |
| フロントエンド言語 | ReactJs, Gulp |
| キャッシュ | Redis |
| DB | mariadb10系 |

## 作るコンテナ

 - サンプルとして2つのコンテナを作成することにします。作成するコンテナは以下の2つ。
 - frontendコンテナがアクセスを受け付けて、apiへのアクセスだけをbackendコンテナへプロキシします。　

| コンテナ名 | 用途 | OS | ポート |
|:--|:--|:--|:--|
| backend | サーバーサイド側（Java）を実行するためのコンテナ | alpineLinux | 8080 |
| frontend | フロントエンド側（React,HTML）を実行するためのコンテナ | alpineLinux | 443 |

## フォルダ構成

 - backendコンテナを例にあげます。基本的に `base` と `put` に別れます。
 - それぞれのフォルダにDockerfileが存在します。

```
-/
 |- backend
 	|- base
 	|	|- Dockerfile
 	|	|- ***.conf
 	|- put
 		|- Dockerfile
```

### baseで行っていること

 - プロジェクトに必要なミドルウェア類のインストールや、設定ファイル（conf等）のCOPYを行います。
 - baseフォルダには独自に以下のルールを設けています。
	 - `RUN`は1回のみ。 `&&` で繋げる。
	 - 変更があるものを入れない。（confで都度変更しなければならない場合はbaseには入れない）

こんな感じ

```Dockerfile
FROM alpine:3.3

RUN apk --update add openjdk8-jre \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /var/log/project /project \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
```

### putで行っていること

 - `base`でビルドしたものに対してプロジェクトの注入を行います。
 - putフォルダには独自に以下のルールを設けています。
	 - RUNは極力書かない。（自分のチームでは`put`フォルダにRUNは書かれていません）
	 - 極力、`FROM`, `COPY`, `EXPOSE`, `CMD` の4行だけにする。
	 - このフォルダにあるものは絶対に人の手ではビルドしない。
		 - 自分のチームではputのビルドをJenkinsでビルドしています。

```Dockerfile
FROM ↑で作ったコンテナ
COPY project.jar /opt/project.jar
EXPOSE 8080
CMD ["java","-jar","/opt/project.jar"]
```

### 全体的にはこんな感じ

 - リポジトリに格納されているフォルダ構造としてはこんな感じになります。

```
-/docker-codes
 |- backend
 |	|- base
 |	|	|- Dockerfile
 |	|- put
 |	|	|- Dockerfile
 |- frontend
 	|- base
 	|	|- Dockerfile
 	|	|- ssl.key
 	|	|- ssl.pem
 	|	|- nginx.conf
 	|	|- service.conf
 	|- put
 		|- Dockerfile
```

## ローカル開発環境として動かすには

 - ローカルでは`docker-toolbox`を使用して動かします。
 - 上記の`put`の役割を docker-compose で代用して動かします。

### putフォルダを適用しない状態でどうやってローカルで動かすのか

 - こんな感じで `docker-compose` で代用できます。

| 項目 | 内容 |
|--:|:--|
| プロジェクトファイルの `COPY` | docker-composeでディレクトリをマウントさせます |
| `EXPOSE` 設定 | `docker-compose`のportsでローカルの `docker-machine` にポートを紐付けます |
| `CDM` 設定 | `docker-compose`の`command`か`entrypoint`で対応 |

 - たとえば以下の`dockerfile`の場合

```Dockerfile
FROM registry.example.com/backend-base-image:1.0
COPY project.jar /opt/project.jar
EXPOSE 8080
CMD["java","-jar","/opt/project.jar"]
```

 - こんな感じの`docker-compose`になります。

```docker-compose.yml
backend:
	hostname: backend
	image: registry.example.com/backend-image:1.0
	volumes:
		- /opt/project-backend:/opt
	ports:
		- "8080:8080"
	links:
		- mariadb:mariadb #データベースが必要な場合。
		- redis:redis     #redisキャッシュサーバーが必要な場合
	entrypoint:
		java -jar /opt/project.jar
#---------------
#実際には複数のコンテナで動かすと思うのでfrontendコンテナがある場合は以下のように記述されると思います。
#---------------
frontend:
	hostname: frontend
	image: registry.example.com/frontend-image:1.0
	volumes:
		- /opt/project-frontend/public:/opt
	ports:
		- "443:443"
	links:
		- backend:backend
	entrypoint:
		/usr/sbin/nginx -g 'daemon off;'
mariadb:
	.........
redis:
	.........
```

 - このように`docker-compose.yml`を組み立てて、baseでビルドしたコンテナをローカル開発環境として使用します。

## まとめ

 - ビルドを`base`と`put`に分けてそれぞれでビルドする。
 - `base`でビルドしたものをローカルの開発環境として使う。
 - ローカルでは`put`でビルドする代わりにdocker-composeで工夫する。

## 最後に
 - この構成のほうがイケてるなどありましたら教えて下さいm(__)m