ローカル環境を汚したくない人のためのDocker🐳入門

この記事は、TIS Advent Calendar 2019 の 13 日の記事です！
かなり遅くなった割に、アドバンスな内容でもなくて申し訳ないです。。。 :bow: 
この記事が、少しでも誰かの刺激になれば幸いです

## ある日

Macbook を Catalina へアップデートしようとした時、"あれ、なんで入れたんだっけ？このアプリは？🤔"だったり
"あの時に、あのライブラリ入れたはずなんだけど...😥"なんて思ったんで、この際なのでクリーンインストールてしまおうと思いましてね。

ただ、また同じことになったら嫌だな〜嫌だな〜、さ〜てどうしたもんか？なんてぼやいていたら、
とある人から、"え？まだ、nodeとかgoをローカル環境に入れてるの？"なんて言うもんだから、”エッッッッッッ！”なんて思って

”じゃ〜あ、どうしてるんですか？”なんて聞いてみたら、Docker でコンテナ化すれば良いなんて言われてしまったんですよ。

## はじめに

なんてどこかで聞いたような語り口調もさておき、その時に聞いた話が"なるほどな！"と思ったので、皆さんに共有です。
割としっかりと開発をしていた人なら当然ってことかもしれないので、あまり期待はしないでください...😅

## なにをするか

- ローカル環境に実行環境を用意せず、Dockerで実行をするだけでなく開発もできる環境を作る
  - これの良いところは、ポータビリティは言わずもがな `一つの端末で異なるバージョンの開発環境を構築したい！けど、面倒な切り替えは嫌だ！`なんて時に便利

以下の環境で検証しています。

OS: macOS Catalina 10.15.1
Docker(CLI): 19.03.5

Docker を普段利用してる開発者でもないですし、検証もちょっと前に実施したので間違っていたりすることもあると思います。
その時には、コメントで指摘もらえると幸いです！

## やること

ここでは、例として create-react-app で作成したテンプレートアプリを実行する Docker 環境を作り、そこにアクセスして開発環境を構築します！
大まかに分けて、やることは以下です。

1. Docker 実行環境の構築
2. Docker で開発する環境を作成する
3. 作成した Docker Image を使って開発する

### 1. Docker 実行環境の構築

なにはともあれDockerの環境を構築しないといけません。
私はMacを使っていますので、[Homebrew](https://brew.sh/index_ja)を使って Docker をインストールしました。

```Shell
# 開発ツールのインストール
xcode-select --install

# Homebrew 🍺のインストール
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# 今回利用するツール群のインストール
brew install git docker
brew cask install google-chrome visual-studio-code
```

これで、 Docker の他に Google Chrome や VSCode といった GUI アプリケーションが使えるようになったはずです！
以下のコマンドで、 Docker の version を確認してみましょう！

```Shell
$ docker --version
Docker version 19.03.5, build 633a0ea
```

さて、それでは Node.js の開発環境を Docker で構築していきましょう

### 2. Docker で開発する環境を作成する

React 環境を1から構築するのは面倒です...
ここでは、 [React 公式](https://ja.reactjs.org/docs/create-a-new-react-app.html#create-react-app) で推奨されている `Create React App` を使いましょう！

"じゃあ、 npm で create-react-app をインストールするために、Node.jsをインストールして..." なんてやっていたら、本末転倒です
ではどうするのか？
Node.js を使える Docker Image をダウンロードして使いましょう！
ここでは、 node が DockerHub で公式に管理している Docker Image を利用します。

```Shell
$ docker pull node:12.13.1-alpine3.10
```

pull が完了すると、 node が利用できる Docker Image がローカル環境に構築されたと思います。
この docker image で一時的に、 create-react-app を実施します。

以下のコマンドを実行すると、先ほど pull してきた docker image を利用して作成したコンテナで作業ができます

```Shell
$ mkdir my-app && cd $_
$ docker run -it -v $PWD:/workspace/my-app node:12.13.1-alpine3.10 sh

# Terminal の表示が切り替わる
/ # 
```

さて、この Container を利用してホストのカレントディレクトリに creater-react-app で作成される app を構築しましょう！
ホストのカレントディレクトリが Docker Container の `/workspace/my-app` にマウントされていますので、そのディレクトリに create-react-app で生成される app を作成します。
(npm のコマンド詳細の説明は省きます！)

```Shell
# 以下は全てContainer内でのコマンド実行です

/ # cd /workspace
/ # npm init                               <-- /workspace で node project を作成する
/ # npm install create-react-app           <-- create-react-appをインストール
/ # npx craete-react-app my-app --use-npm  <-- create-react-app で、 my-app に react-app を作成する
/ # exit
```

これで、 `my-app` に `create-react-app` で作成された react app が配置され、起動できる状態になりました！ :ok_hand: 
さて、それではmy-appディレクトリで、実行用の Dockerfile を作成しましょう！
(今回は BuildKit も利用せず、シンプルな Dockerfile を作成します)

my-app/Dockerfile

```Dockerfile
FROM node:12.13.1-alpine3.10

# 作業ディレクトリを /workspace/my-app に変更
WORKDIR /workspace/my-app

# ホストの my-app ディレクトリを、作業ディレクトリへコピー 
COPY package.json package-lock.json .

## ビルド時に `npm install` を実施する 
RUN npm install

## srcなど、頻繁に変更が加わると思われるファイルをコピーする
COPT src/ public/ .

## run 時に `npm start`を実施する
CMD npm start
```

### 3. 作成した Docker Image を使って開発する

では、作成した Dockerfile をビルドしましょう！
以下のコマンドで、 `my-app-image` と言うイメージ名( `v1.0.0` と言うタグ名)で Docker Image をビルドして、その Image 上で create-react-app で作成した my-app を実行できます。
（Docker Containerの3000番ポートでアプリが起動するため、ホストの8090番をフォワーディングしています。）


```Shell
$ cd my-app/
$ docker build -t my-app-image:v1.0.0 .
$ docker run -d my-app-image:v1.0.0 
```

ここで、Google Chromeで　`localhost:8090` にアクセスしてみてください！🚀
下のような画面が表示されると思います！ :thumbsup: 

<img width="1080" alt="スクリーンショット 2019-12-13 22.04.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320615/bf349852-1cff-a779-b41d-85ba9708fccd.png">

今後、ファイルを更新したら
Docker Container を stop & ビルド & Container を起動することで、開発が継続できます。 

Happy Clean Hacking! 😎

## まとめ

面倒でしたのでこちらでは省略しましたが、実際はDockerfileもbuild用と実行環境を用意してMulti-stageビルドをしたり、docker-compose を使ってコンテナオーケストレーションなどを実行しています。
（以前は Hot Reload もできていたのですが、今回は確認が取れなかったので紹介はしませんでしたが、日開発では Hot Reload などは必須だと思います…）
ただ、上記にあげた方法で `ホスト端末にはnodeを全く入れることなく、nodeでの開発ができる環境構築`を行うことができました。

上記のサンプルであげたサンプルは非常にシンプルなサンプルでしたが、Multi-stage buildやdocker-composeを利用することで、今まで多くの人が苦労して管理していた `Wiki/{フロントエンド|バックエンド}環境構築` :imp: や `開発利用ライブラリバージョン一覧` :thermometer_face: といったドキュメントも README.md を作成するだけでよくなるかと思います。
また、近いうちにバックエンドとフロントエンドをビルドした例なども投稿できれば投稿します！

それでは、良いクリスマスをお過ごしください！ :christmas_tree: