開発環境 - Docker を使用した 可搬性 と 可変性 のある Web Application の開発環境を考える

## 要約

- 効率的にWebアプリケーションを開発するための開発環境の構成を考えてみた。
- Vagrant と Docker の組み合わせでホストOSに依存しない開発環境を構築する。
- Webの三層構造のうち、アプリケーション部分の開発に注力できるようにする。

## はじめに

Webアプリケーションを開発するときにいつも気をつけていることや、前提としていることを整理しながら、効率的にWebアプリケーションを開発できる開発環境を考えてみました。

### もくじ

- 概要
- A. Web サイト を リバースエンジニアリング（？）する
- B. Web アプリケーション の開発を開始して動作するまで
- C. 効率的な Web アプリケーションの開発環境を求めて

### 想定読者

- チーム開発などを行う際、共通の開発環境の構築にお悩みの方。
- Webアプリケーションを開発してみたいけれど、どのように準備したら良いか分からない方。

## 概要

### 書くこと

- Web アプリケーションを開発する環境のソフトウェアスタック

### 書かないこと

- 開発環境の構築方法

### 想定環境

この記事で紹介するシステムの構成イメージは下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/bc87ef47-c102-a047-6142-3494fba71fc9.png)

この構成の特徴は次の通りです。

- **Host OS が Windows でも macOS でも良い**<br>→vagrant+VirtualBox を仮想環境の基盤とすることで、 Host OS に依存しない開発環境を実現します。
- **Docker の使用によって起動や再構築のスピードを上げる**<br>→Vagrant の場合、より深い層からの仮想化が行われるため、起動や再構築のスピードが気になります。
- **Host OS が認識するツールの差分を極力なくす**<br>→Docker の場合、 for mac と for Windows の挙動で気になることが以前ありました。<br>そこで、Vagrant で構築するレイヤを共通の基盤とすることで可能な限り同じ条件でコンテナが動作するよう意識しています。
- **Docker と VirtualBox の同居**<br>→Hyper-V 関連で、１つの HostOS にどちらかしか入らないので、汎用性が高い Vagrant+VirtualBox を残し、 Docker はその上という構成を取っています。
- **アプリケーションコンテナ以外の共通化**<br>→Web サービスの構成は、基本的には Web/App/DB の３つのレイヤーで役割分担されており、標準的に求められる機能も定まっています。プログラムで積極的に手を加えるところ以外は、極力共通化をすることで、新しい言語に挑戦するときにもスムースに導入できることを想定しています。
- **VS Code Remote Development**<br>→デバッグのために、コンテナに構築した構成の一部言語などをHostOS に構築する手間を省き、仮想環境上のコードを直接、実装、デバッグ出来ます。VS Code の UI と 拡張機能を違和感なく使用でき大変便利です。
- **Heroku**<br>→AWS で VPC 関連の構成をガッツリと構築するのは好きですが、スモールスタートやお試し実装の際にはそこまでやってられません。ローカルで動作検証したら、すぐさま外部アクセス可能な状況にするために、 Heroku を活用しましょう。

## A. Web サイト を リバースエンジニアリング（？）する

技術スタックは 概要 に記載した通りですが、なぜその構成になるのかをもう少し丁寧に記載してみます。

### A-1. Web ブラウザで見えるもの

Qiita を Web ブラウザで開くと次のようなページが開きます。
Web ブラウザは HTML、CSS、JavaScript のコードとメディア情報からこのような表示を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/3ae2dc72-a741-7089-589d-7f3a817d215e.png)


### A-2. Web ブラウザに情報が返るまで

Web ブラウザが HTML、CSS、JavaScript などを取得する際に、データが流れる物理機器はざっくりと次のような構成になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/45c348d2-a421-dc94-2449-c9ff8052e0f8.png)

### A-3. バックエンドの構成 と システム要素

AWSでWebアプリケーション が稼働しているとした場合、 Webブラウザからの要求を受け付けてデータを返却する際には次のようなシステム要素が必要となります。（ある程度の拡張性を考慮した場合の構成です。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/bd43fc6f-6098-dc97-8ee7-09c86095ad10.png)

- 実装したアプリケーションは「Instances」上で実行されます。
- アプリケーションは、処理の負荷状況に応じて複数の「Instances」上で実行されます。
- データの特性に応じて、RDBやCache、Storageにそれぞれのデータが保存されます。

### A-4. ユーザ数 と システムの構成

A-3.)では、ある程度の拡張性がある前提のシステム構成を図示しましたが、アプリケーションの利用ユーザによって構築するシステムの基本構成は変わってきます。
AWSでシステムを構築する際に、ユーザ数とシステムの構成について非常に参考になる記事が次のものです。

- [Amazon AWSでユーザ数1100万以上にスケーリングするためのビギナーズ・ガイド - POSTD](https://postd.cc/a-beginners-guide-to-scaling-to-11-million-users-on-amazons/)

この記事に記載のある内容をざっくりとまとめると下表のようになります。

|ユーザ数|台数|Route53|EIP|拡張方法|AZ|ELB|RDS|CDN|Cache|
|--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|1|単|○|○|垂直|単|-|-|-|-|-|
|10-|複|○|○|垂直|単|-|△|-|-|
|100-|複|○|○|垂直|単|-|○|-|-|
|1000-|複|○|○|垂直|複|○|○|-|-|
|1万-10万|複|○|○|水平|複|○|○|○|○|
|50万-|複|○|○|水平|複|○|○|○|○|
|100万-|複|○|○|水平|複|○|○|○|○|
|1000万-|複|○|○|水平|複|○|○|○|○|
|1100万-|複|○|○|水平|複|○|○|○|○|

このようにある通り、数千人まではサーバのスケールアップ（スペックアップ）にて対応が可能になりますが、それ以上の場合はスケールアウト（負荷状況に応じた複数台のアプリケーション構成）が必要となります。
また、ユーザ数が数百人を超えた段階で、アプリケーションサーバとDBサーバの分割することが目安として記載されています。

### A-5. バックエンドの構成を抽象化して考える

Webアプリケーションは「三層構造」や「三層アーキテクチャ」という呼び方で３つに区分することが出来ます。
この三層構造のうち、DBに関連する箇所を少しだけ分割して図にしたものを次に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/4bf3b23e-3a4c-0899-e31d-b8ffbdcb0f77.png)

#### App Server の注意事項

- 環境依存で可変となるパラメータは`.env` で定義する。
- データを内部に抱えない。


## B. Web アプリケーション の開発を開始して動作するまで

Webアプリケーションを開発する際は、ざっくりと次のような手順を踏むことになります。
（本当にざっくりです。いきなり本番環境を構築する訳でなく、開発環境を構築した後、そこで開発と動作検証をした上で本番が動き出すということが注意点です。）

1. 企画と仕様を固める
2. 開発環境を構築する
3. アプリケーションを開発してローカルで動作検証する
4. リモート環境を構築する
5. リモート環境で動作検証する
6. 本番環境で動作を確認する

### 環境の定義

ここで、「本番環境」「開発環境」「リモート環境」という言葉を使用しましたが、これらはプロジェクトや業界、企業によって呼び名や定義が異なっており、時として非常にややこしくなってしまいます。
この環境定義については、以下の記事で少しだけ概要を記載しているので興味があれば参照ください。

- [Deployment environment - Webアプリケーションの環境定義（本番/検証/開発/...） について考えてみる  #Qiita](https://qiita.com/anfangd/items/7fab147e45f54e691298)

環境定義は「状況によって呼び名や定義が異なる」ことが前提ですが、クラウド全盛期の2020年01月現在において、ある程度の規模のシステムでは次のような環境分割がされることが多いかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/be6d47c9-8c41-ea48-c553-bcc64987085a.png)

クラウド以前は、VPSや専有サーバなどをレンタルしたり、企業毎にサービス専用のデータセンターを構築したりしており、複数の環境をネットワークレベルで分割することの難易度が高かったように思います。
このため、環境分割も、１つのサーバの上に、Apache の VirtualHost で環境分割したりなど、環境構成が全く同じということはあまりなかったのではないでしょうか。

一方で現在は、AWSなどを使用することで、ネットワークレベルで同じ構成を構築することが容易になり、環境差分は各インスタンスのスペックや設定値のみといった形で、複数の環境を構築し、用途や目的に応じて使用することが当たり前になってきたように思います。

こういった背景の中で、Webアプリケーションは、用途に応じた環境にスムースにデプロイされ、設定値のみの切り替えでどの環境でも適切に動作することが求められます。

## C. 効率的な Web アプリケーションの開発環境を求めて

ここでは、複数の言語でも対応可能なアプリケーション開発環境の構成を記載します。

### プラットフォーム

まず基本となる構成は次の通りです。
Vagrant と Docker をベースとしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/8ca7b97d-6d92-6cce-2a39-7ad74deb7fc1.png)

上記の基本構成を構築する手順は次の通りです。

```bash:command(bash@HostOS)
git clone https://github.com/anfangd/vagrantfile-centos7-and-docker centos-7-docker && cd $(basename $_ .git)

vagrant plugin install vagrant-vbguest
vagrant plugin install vagrant-proxyconf

vagrant up
```

### PHP / Laravel

基本構成をベースとして【PHP / Laravel】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/894567da-c351-121e-dda3-6a218c6e9f68.png)

上記の構成を実現する docker-compose のスクリプトは以下を参照ください。

- [anfangd/docker-compose-php-laravel - GitHub](https://github.com/anfangd/docker-compose-php-laravel)

### Java ( Kotlin ) / SpringBoot

基本構成をベースとして【Java ( Kotlin ) / SpringBoot】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/85fc0d9e-c8cc-3ed9-52de-d51676a0807b.png)

### Node.js ( AWS Lambda )

基本構成をベースとして【Node.js ( AWS Lambda )】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/20209af2-7db3-1635-2495-6b73a7700ed6.png)

上記の構成を実現する方法については以下の記事を参照ください。

- [docker-lambda - AWS Lambda の開発効率を爆アゲする  #Qiita](https://qiita.com/anfangd/items/bb448e0dd30db3894d92)

### Ruby / Rails

基本構成をベースとして【Ruby / Rails】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/9b369884-fb20-1a32-47eb-bc38b12265b3.png)

上記の構成を実現する docker-compose のスクリプトは以下を参照ください。

- [anfangd/docker-compose-ruby-rails - GitHub](https://github.com/anfangd/docker-compose-ruby-rails)

### Python / Django

基本構成をベースとして【Python / Django】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/73767e49-feda-455e-17aa-ff33da347777.png)


### Go / Gin

基本構成をベースとして【Go / Gin】を開発する際の構成は下図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79222/83f0cc21-1100-79b1-362a-54134d2acc51.png)

## おわりに

記事のテーマが広すぎて話題が発散してしまいました。反省です。

新しい開発環境を整備するというのは非常に手間で時間を食うことが多いので、「C. 効率的な Web アプリケーションの開発環境を求めて」部分のプラットフォームと各言語の docker-compose のスクリプトを揃えることで、アプリケーションの実装にすぐに入れるような状態を作っておきたいものです。

## 参考

- [Visual Studio Code - Remote Development を使用して PHP Xdebug を使ったデバッグを行う #Qiita](https://qiita.com/anfangd/items/6daccf1d40c84d4e4ced)