MAC環境でVirtualBox+Vagrant+DockerでReact開発環境を作る╰(°ㅂ°)╯

\#stayhomeの時間を活用して、「仮想環境」とか「コンテナ開発」とかに慣れ親しもうという魂胆です。作業を進める上でよくわからなかった点など、周辺情報を個人的な備忘録として残します。

#### ゴール
作ったり壊せたり自由にできる開発環境を立ち上げられるようになること。React.jsの勉強を始める予定なので、React.jsの雛形アプリをブラウザで表示させるところまでをゴールとする。

### 作業の流れ

ステップが多く、私のような初学者は、わからない部分を調べている間に迷子になることもしばしば。一つ一つの動作確認をしながら作業を進めていきます。

【前半戦：ホストOS側での作業】

- 事前準備
 - vagrantプラグインの導入確認
- VMの設定
  - vagrant初期化
  - Vagrantfileの編集
- dockerとdocker-composeの導入
  - Dockerfileの作成
  - docker-compose.ymlの作成
- ホストOSとゲストOS間のファイル同期の設定
  - mutagen.yml作成

【後半戦：ゲストOS側での作業】

- VM起動
  - vagrantでVM起動
  - VMにssh接続
- VMの動作確認
  - ホストOSとのフォルダ共有を確認
  - dockerとdocker-compose動作
- React.jsの導入
  - Dockerコンテナの起動
  - 起動中のコンテナに入る
  - create-react-appのインストールとアプリのひな形作成
  - アプリの実行


#### 教材
基本となる教科書はこちらの二冊(記事)です。
- [Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説【遅いMac for Dockerを卒業】](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2)
- [DXを大幅に低下させるDocker for Macを捨ててMac最速のDocker環境を手に入れる](https://qiita.com/yuki_ycino/items/cb21cf91a39ddd61f484)

その他、以下のQiita記事を参考にさせていただきました。
- [Docker環境内でcreate-react-app](https://qiita.com/mii288/items/aac597bc02575831ea90)
- [Windows環境においてVirtualBox+Vagrant+DockerでReact開発環境を作る](https://qiita.com/y1row/items/a1bfc05bab451960245f#%E4%BB%AE%E6%83%B3%E7%92%B0%E5%A2%83%E8%B5%B7%E5%8B%95)＊Windows向けに書かれた記事ですが、仮想環境起動のパート以降の後半部分を中心に参考にしました。

#### 作業ディレクトリ
作業を行うディレクトリは、ホームフォルダ下に置いてある開発(develop)ファイルにVM（仮想マシン）フォルダを作成。その配下にsample-pjなる場所を作りました。
/Users/home/develop/vm/sample-pj

#### 実行環境・ソフトウェア・パッケージ・プラグイン
- macOS Mojave version 10.14.6
- VirtualBox Version 6.1.6
- Vagrant 2.2.9
- VirtualBox Image ubuntu/xenial64
- Vagrant Plugin
  - vagrant-disksize 
  - vagrant-hostsupdater 
  - vagrant-mutagen 
  - vagrant-docker-compose
- Docker version 19.03.8
- docker-compose version 1.24.0


#### 前提
[Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説【遅いMac for Dockerを卒業】](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2)に習って、以下のステップまで進めた状態からスタートします。

- VirtualBoxをインストール
- Vagrantをインストール
- Vagrant Boxをダウンロード
- プラグインの導入

``` terminal:~/develop/vm/sample-pj
$ vagrant box list
ubuntu/xenial64 (virtualbox, 20200505.0.0)

$ vagrant plugin list
vagrant-disksize (0.1.3, global)
vagrant-docker-compose (1.5.1, global)
vagrant-hostsupdater (1.1.1.160, global)
vagrant-mutagen (0.1.2, global)
```
この↑状態が確認できたところから開始です(｀ω´)


# 【前半戦：ホストOS側での作業】


## VMの設定

### vagrant初期化

``` terminal:~/develop/vm/sample-pj
$ vagrant init ubuntu/xenial64

# 上記コマンドを実行して、下のような結果が出てくれば完了。
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
```

##### 何が起きたかチェーーーーーック( ·∀·)ﾉ

``` terminal:~/develop/vm/sample-pj
$ ls

# sample-pj（プロジェクトディレクトリ）内の構造を見てみると、
# Vagrantfileができていることが確認できます。
Vagrantfile
```
### Vagrantfileの編集

Vagrantfileは、仮想機械を構築するための構成情報を記述するための設定ファイルです。

( ˘ω˘ )? 「Vagrantfileにどんなことが書かれているのか？」という疑問については、[Vagrantにおける仮想マシンの設定](https://weblabo.oscasierra.net/vagrant-config-vm/)に素敵な感じにまとまっているので、詳しくはそちらを参照ください。

``` terminal:~/develop/vm/sample-pj
# vimエディタでVagrantfileを開きます。
$ vim Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
（略）
```

Vimエディタの使い方については、[Vim初心者に捧ぐ実践的入門](https://qiita.com/okamos/items/c97970ab34ff55ff3167)などのQiita記事を参考にしてください。初見では、とっつきにくい見た目をしていますが、何度か触っているうちに慣れてきました。



早速、教科書＝（[Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説【遅いMac for Dockerを卒業】](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2)）の[Vagrantのセットアップ](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2#vagrant%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)を参照して、Vagrantfileを編集します。

``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
# /sample-pj/Vagrantfile
Vagrant.configure('2') do |config|

  # VMを立ち上げる際のboxの指定
  config.vm.box = 'ubuntu/xenial64'

  # VMに設定するホスト名
  # 後述のmutagen.yml内の記述と揃える必要があるので覚えて置くこと。
  config.vm.hostname = 'sample-app'

  config.vm.network :private_network, ip: '192.168.50.10'
  # ポートへフォワードの設定
  config.vm.network "forwarded_port", guest: 3000, host: 3000

  config.vm.provider :virtualbox do |vb|
    vb.gui = false
    vb.cpus = 4
    vb.memory = 4096
    vb.customize ['modifyvm', :id, '--natdnsproxy1', 'off']
    vb.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']
  end

  config.disksize.size = '30GB'
  config.mutagen.orchestrate = true

　# config.vm.synced_folderは、ホストOSとゲストOS間で共有するフォルダを指定します。
  config.vm.synced_folder './', '/home/vagrant/app', type: "rsync",
    rsync_auto: true,
    rsync__exclude: ['.git/', 'node_modules/', 'log/', 'tmp/']

  # config.vm.provisionは、初回（VM作成時）に実行されます。
  # ここではDockerとdocker-composeの導入について書かれています。
  config.vm.provision :docker, run: 'always'

  # docker-compose の設定
  config.vm.provision :docker_compose,
    yml: "/vagrant/docker-compose.yml",
    compose_version: "1.24.0",
    run: "always"
end
```

**ここで詰まった！！ERR_CONNECTION_REFUSEDエラー(´ε｀；)**

ポートへフォワードの設定とdocker-composeのインストールについては、私が追記編集した部分です。

ポートフォワードの設定については、[Vagrant + VirtualBoxで仮想環境側のポートをあける](https://qiita.com/kkam0907/items/4a345cd5e834969d3859)を参考にしました。この記述なし（教科書通り）で作業を進め、ブラウザでhttp://localhost:3000/ でアプリを開いてみようとすると、ERR_CONNECTION_REFUSEDという下記のエラーが出てきて困ってしまいましたが、ポートフォワードの設定を加えて解決できました。

>ブラウザ(http://localhost:3000/)
>>This site can’t be reached
localhost refused to connect.

>>Try:
>>Checking the connection
Checking the proxy and the firewall

>>ERR_CONNECTION_REFUSED


**ここで詰まった！！docker_composeがvagrant upでインストールされない(´ε｀；)**

docker-compose の設定ですが、教科書通りにconfig.vm.provision :docker_composeだけでは、vagrant up時にインストールされませんでした。バージョン情報などを追加し、無事インストールできるようになりました。原因は全くわかりません・・・。



## dockerとdocker-composeの導入

Vagrantfileでは、config.vm.provisionの部分で、初回のvagrant up（VM作成時）にdockerとdocker-composeがインストールされるように設定しました。

以下では、docker-compose upによるコンテナ実行時に呼び出される、docker-compose.ymlとDockerfileの２つを作成していきます。


### docker-compose.ymlの作成

```terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
# docker-compose.ymlを作成
vagrant@sample-app:~/app$ vi docker-compose.yml

# 下記のように書き換え
version: "3"
services:
  #コンテナ名。後ほど、この名前を指定してコマンドを叩くことになります。
  node:　
    build:
      # 参照するファイルのある場所を、docker-compose.ymlからの相対パスで指定。
　　　 # 今回は同じ階層・同じフォルダ内にあるので、下記のような表記になる。
      context: .
　　　 # 参照するファイル。今回はDockerfile_nodeという名前です。
      dockerfile: Dockerfile_node　
    # ストレージの指定　ホストOS:ゲストOS
    volumes:
      - ./:/usr/src/app
    ports:
      - "3000:3000"
    # docker-compose up後、即コンテナが終了することなく、待機状態とする。
    tty: true
```


### Dockerfileの作成


``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
# Dockerfileを作る。
# docker-compose.ymlで指定した通り、今回はDockerfile_nodeという名前にします。
$ vi Dockerfile_node

# Dockerfileを編集

# どのイメージを基にするか。今回はreactの開発環境なので、node.jsを使用します。
# 現時点で最新・コンパクトなバージョンを指定しました。
FROM node:14.2-alpine

# 作業ディレクトリ
WORKDIR /usr/src/app
```
作業ディレクトリ（WORKDIR）については、色々なサイトを見ましたが、Linux系では/usr/src/appがお作法になっているようです。「/usr/src/appとは？」という疑問に関しては、　[Linux豆知識 217「/usr/src」ディレクトリ](https://linuc.org/study/knowledge/547/)を参照。



### ホストOSとゲストOS間のファイル同期の設定

Vagrantfileの config.vm.synced_folderの記述で、ホストOSとゲストOS間で共有するフォルダを指定しましたが、config.vm.synced_folderによるファイル共有は、vagrant upによるVM作成時**のみ**実行されます。つまり、**常時シンクロしてくれるわけではない**そうです。

[DXを大幅に低下させるDocker for Macを捨ててMac最速のDocker環境を手に入れる - ファイル同期の手段](https://qiita.com/yuki_ycino/items/cb21cf91a39ddd61f484#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8C%E6%9C%9F%E3%81%AE%E6%89%8B%E6%AE%B5)に習って、ホストOSとゲストOS間のファイル同期は、後述のmutagenというプラグインを使います。


### mutagen.yml作成

[DXを大幅に低下させるDocker for Macを捨ててMac最速のDocker環境を手に入れる - ファイル同期のソリューション 「Mutagen」](https://qiita.com/yuki_ycino/items/cb21cf91a39ddd61f484#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8C%E6%9C%9F%E3%81%AE%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3-mutagen)参考に、mutagen.ymlを作成、編集していきましょう。


``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
# touchコマンドでmutagen.yml新規作成
$ touch mutagen.yml 

# sample-pj（プロジェクトディレクトリ）内の構造を見てみると、mutagen.ymlが追加されているのが確認できます。
$ ls
# 実行結果↓
Vagrantfile	mutagen.yml
```

``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
# multagen.ymlファイルを開く
$ vim mutagen.yml

# multagen.yml内を編集
sync:
  app:
    # 双方向同期モード 但し、alphaが優先、ベースになる
    mode: "two-way-resolved"
    # alphaとbetaでエンドポイントを指定
    alpha: "./"
    # sample-appというhostの中の、/home/vagrant/appディレクトリ
    # sample-appは、Vagrantfile内のconfig.vm.hostname = 'sample-app'で指定した名前と揃える必要があります
    beta: "sample-app:/home/vagrant/app"
    # ignore（＝無視）なので、以下に掲げる内容を無視するということかな？
    ignore:
　　　 # vcs....バージョン管理システム（Version Control System)のことかな？それがtrueとは...? よく、わかりませんでした・・・。
      vcs: true
      # 一連のパスで指示されているディレクトリは無視するということかな？この辺よくわかりませんでした・・・。
      paths:
        - "/node_modules"
        - "/log"
        - "/tmp"
```
( ˘ω˘ )? 「multagen.ymlが何をしてくれているのか？」という疑問については、以下を参考にしまして、掴め（たような気に）ました。

- [Mutagenでファイル同期が簡単に出来た話](https://www.te-nu.com/entry/2019/11/10/144153)
- [Mutagen公式ドキュメントFile synchronization]
(https://mutagen.io/documentation/synchronization)

以上で、ファイル共有の設定が完了しました。
#### 以下の４つのファイルが準備できた段階で、ホストOS側での作業は終了です。

``` terminal:~/develop/vm/sample-pj
$ ls
# 実行結果↓
Dockerfile_node		docker-compose.yml
Vagrantfile		mutagen.yml
```


# 【後半戦：ゲストOS側での作業】
ここからは、VMを起動させてゲストOS側でReactの開発環境を作っていきます。
### VagrantでVM起動

早速、VMの起動へ。

``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
$ vagrant up
```
##### ディレクトリ内をチェーーーーーック( ·∀·)ﾉ

``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
$ ls

# 実行結果↓
Dockerfile_node
Vagrantfile
docker-compose.yml
mutagen.yml
mutagen.yml.lock
ubuntu-xenial-16.04-cloudimg-console.log
```
作った覚えのない、ubuntu-xenial-16.04-cloudimg-console.logというファイルが新しくできていることがわかります。
( ˘ω˘ )? cloudimg-console.logって、何してくれるの？という疑問については、[cloudimg-console.logとは](https://qiita.com/yuna1001/items/7da629a9a5e4f6111c1a#ubuntu-bionic-1804-cloudimg-consolelog%E3%81%A8%E3%81%AF)を参考にしました。名前の前半のubuntu-xenial-16.04の部分は、仮想OSの種類に依るようです。


### VMにssh接続

``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
$ vagrant ssh

# 実行結果↓
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-178-generic x86_64)
（略）
# Vagrantfileで指定したホスト名でターミナルが開く。
vagrant@sample-app:~$ 
```
vagrant@sample-app:~$ というように、ターミナルが表示されればOK。vagrantユーザーでsample-appというホスト（＝ゲストOS）にログインされています。


##### VMの中身をチェーーーーーック( ·∀·)ﾉ

``` terminal:vagrant@sample-app
# /（ルートディレクトリ）配下にLinuxのディレクトリが並んでいます。
vagrant@sample-app:~$ cd /
vagrant@sample-app:/$ ls
# 実行結果↓
bin   home            lib64       opt   sbin  tmp      vmlinuz
boot  initrd.img      lost+found  proc  snap  usr      vmlinuz.old
dev   initrd.img.old  media       root  srv   vagrant
etc   lib             mnt         run   sys   var
```
仮想環境にゲストOSが導入されていることが確認できました。ちなみに、Linuxのディレクトリの構造についての参考URLはこちら[Linuxのディレクトリ構造（一覧）を理解する](https://www.linuxmaster.jp/linux_skill/2010/02/06linux.html)を参照ください。

##### ホストOSとのフォルダ共有状況ををチェーーーーーック( ·∀·)ﾉ

``` terminal:vagrant@sample-app
# ~（ホームディレクトリ）
vagrant@sample-app:~$ cd 
vagrant@sample-app:~$ ls
# 実行結果↓
app


# appへ移動し、カレントディレクトリのパスを出力する
vagrant@sample-app:~/app$ pwd
# 実行結果↓ 
/home/vagrant/app　#multagen.ymlファイルで指定したゲストOS側のエンドポイントと一致していることがわかります。


# appの中も見てみましょう。
vagrant@sample-app:~$ cd app
vagrant@sample-app:~/app$ ls
# 実行結果↓
Dockerfile_node     mutagen.yml
Vagrantfile         mutagen.yml.lock
docker-compose.yml  ubuntu-xenial-16.04-cloudimg-console.log
```

試しに、/app内でtestという名前でディレクトリを作成してみます。

``` terminal:vagrant@sample-app
# /app内でtestという名前でディレクトリを作成
vagrant@sample-app:~/app$ mkdir test
vagrant@sample-app:~/app$ ls

# 実行結果↓
Dockerfile_node
Vagrantfile
docker-compose.yml
mutagen.yml
mutagen.yml.lock
test  # 新しく作ったディレクトリ
ubuntu-xenial-16.04-cloudimg-console.log
```

一度、VMを離れて、ホストOS内の作業ディレクトリを見てみると、testという名前のファイルができているのが確認できました。

``` terminal:vagrant@sample-app
# exitでVMからログアウト
vagrant@sample-app:/vagrant$ exit

# 実行結果↓
logout
Connection to 127.0.0.1 closed.
```


``` terminal:~/develop/vm/sample-pj(ﾎｽﾄOS内ﾌﾟﾛｼﾞｪｸﾄﾃﾞｨﾚｸﾄﾘ)
$ ls

# 実行結果↓
Dockerfile_node
Vagrantfile
docker-compose.yml
mutagen.yml
mutagen.yml.lock
test # こちらにもtestディレクトリができています
ubuntu-xenial-16.04-cloudimg-console.log

# ファイルの共有が確認できたのでtestフォルダを削除します
$ rm -r test

# もう一度VMへssh接続
$ vagrant ssh

# 実行結果↓
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-178-generic x86_64)
(略)
```
VM側でのtestディレクトリが消えているか確認します。

``` terminal:vagrant@sample-app
vagrant@sample-app:~$ cd app
vagrant@sample-app:~/app$ ls

# 実行結果↓　こちらでもtestファイルが無くなっていました。
Dockerfile_node     mutagen.yml
Vagrantfile         mutagen.yml.lock
docker-compose.yml  ubuntu-xenial-16.04-cloudimg-console.log
```
以上で、ホストOSとのフォルダ共有が確認できました。


##### dockerとdocker-compose動作も併せて、チェーーーーーック( ·∀·)ﾉ

``` terminal:vagrant@sample-app
# dockerの動作チェック
vagrant@sample-app:~$ docker --version
# 実行結果↓
Docker version 19.03.8, build afacb8b7f0

# docker-composeの動作チェック
vagrant@sample-app:~$ docker-compose --version
# 実行結果↓
docker-compose version 1.24.0, build 0aa59064
```
バージョン情報が表示されて、dockerとdocker-composeが導入されていることが確認できました。


### Dockerコンテナの起動

```terminal:vagrant@sample-app
# /appディレクトリにてコマンド実行。バックグラウンドでの実行するため-dオプションをつけます。
vagrant@sample-app:~/app$ docker-compose up -d

# コンテナの実行状況を確認しましょう
vagrant@sample-app:~/app$ docker-compose ps
# 実行結果↓
   Name             Command          State           Ports        
------------------------------------------------------------------
app_node_1   docker-entrypoint.sh    Up      0.0.0.0:3000->3000/tc
```

### 実行中のコンテナに入る

```terminal:vagrant@sample-app
# 実行中のコンテナに入る。node部分は、docker-compose.ymlで指定したコンテナ名。
vagrant@sample-app:~/app$ $ docker-compose exec node sh
# 実行結果↓　以下が表示されれば無事にコンテナ内に入れています。
/usr/src/app # 
```

### コンテナ内でcreate-react-appのインストールとアプリのひな形作成

```terminal:vagrant@sample-app
# creat-react-appのインストール
/usr/src/app # npm install -g create-react-app

# react-sampleという名前でアプリのひな形を作成
/usr/src/app # create-react-app react-sample

/usr/src/app # ls
# 実行結果↓
Dockerfile
Vagrantfile
docker-compose.yml
mutagen.yml
mutagen.yml.lock
react-sample　# 新しいreactアプリが立ち上がっています
ubuntu-xenial-16.04-cloudimg-console.log
```

### アプリの実行

```terminal:vagrant@sample-app
# react-sampleディレクトリに移動
/usr/src/app # cd react-sample

# react-sampleを実行
/usr/src/app/react-sample # yarn start
# 実行結果↓
Compiled successfully!

You can now view react-sample in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://172.18.0.2:3000

Note that the development build is not optimized.
To create a production build, use yarn build.
```

### ゴォォォォォル(´∀｀*) 

ブラウザでhttp://localhost:3000 へアクセスしてみます。以下のような画面が出てくればOKです。

<img width="1188" alt="Screen Shot 2020-05-15 at 10.52.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406662/29779e72-14da-0c6a-e513-24173063d8ec.png">


### おまけ：再度コンテナに入る

一度コンテナを立ち上げた後、作業のためにコンテナに入るまでの手順はこちら。

```
# ホストOSの作業ディレクトリにて、VMを起動
ﾎｽﾄOS作業ﾃﾞｨﾚｸﾄﾘ $ vagrant ssh

# ゲストOSのreact-sampleアプリへ移動し、コンテナを実行
ｹﾞｽﾄOS:~$ cd app
ｹﾞｽﾄOS:~/app$ cd react-sample
ｹﾞｽﾄOS:~/app/react-sample$ docker-compose up -d

# コンテナに入る
ｹﾞｽﾄOS:~/app/react-sample$ docker-compose exec node sh

# コンテナ内
/usr/src/app #
```

ついでに、ソフトウェアの導入を確認。React.jsの開発ができる環境になっているようです。

```
/usr/src/app # node --version
v14.2.0
/usr/src/app # npm --version
6.14.4
/usr/src/app # yarn --version
1.22.4
```


以上！！

### 初・初・初学者です(´·ω·`)
暗中模索な感じで始めてしまい、うまくいかず途中で投げ出したくもなりましたが、なんとか動くところまで持ってこれました。多々、理解違いがあるかと思います。今後、間違いに気づいた点などは、適宜修正していこうと思います。