僕はまだDockerの辛さを知らなかった

## はじめに
Leverages Advent Calendar 2日目担当のいっちーです。

弊社の開発環境の移行を行いましたので、その振り返りです。

## TL;DR
- 開発環境はもともとオンプレのサーバで稼働していた
    - 簡単にスペックアップできず、辛かった
    - スノーフレーク化している
- 開発環境をDocker化した
    - ローカル開発環境を持てた
    - 結局スペック問題は発生した
    - レビュータイミングの調整は変わらず辛いらしい
    - docker-sync何もしてないから壊れた
        - なにかしたら治った！
    - docker-sync何かしたら壊れた

## 本題
そもそも弊社の移行前の開発環境(一部の媒体)については次の通りです。

- リモートに開発環境が存在
- オンプレミス
- リモート環境では各個人のディレクトリ配下にアプリのファイルを配置
- Virtual HostによりRootPathを切り分け

という状態でした。
オンプレミス！という言葉を聞いて、むむっ！となる人もいると思います。(僕自身がその一人)
実際にむむっ！となることがありましたのでそられも含めて書いていきます。

#### インターネットにつながっていないと(全く)開発できない

当然のことながら、リモート開発環境なので、インターネットにつながっていないと、開発ができません。
コードを書くことはローカルのエディタで可能ですが、実際に書いたコードが反映されることは無いのです。もちろんデザインレビューを他の人から確認してもらうことも叶いません。

#### 簡単にスペックアップできない
これが一番困りましたね(僕は困ってないです)。
何が起こったかというと、

- プロジェクトのデータ保存用のストレージが枯渇
- yarnを実行している間コンピュータリソース的に他の人の作業に影響がでる
- インストールされているものが何かわからないw

という現象です。

スペックアップに関しては、サーバーの管理先に依頼して、対応して頂く必要があります。
(社外の人に依頼することになるため、コミュニケーションコストが発生します)

また、インストールされているミドルウェアやソフトウェアの確認は自分たちでできますが、時間かかりそうなのでやりたくはないですねw

#### 開発環境なのに開発に必要なソフトウェアなどを簡単にインストールできない
開発環境といえば、色々試行錯誤すると思います。
しかし、色んな人が色んなものを入れてしまうと、他の開発者にも影響が出てしまいます。
そのため、 `sudo`の権限がなかったりして、「実験的に使いたいのに… `sudo yum`が実行できない…」というような状態に陥ってしまうこともあります。

### 辛いのでDocker化してみた
そこで、Dockerを使って開発環境構築してしまおう、と考えました。

複数プロジェクトの開発を行っている人もいるので、そういった要望にも答えられるように次のような構成を考えました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72350/c23013bb-9aaa-bf3d-9964-a1dd475cdefc.png)


コンテナは次の4つです

- nginx
- project-A
- project-B
- project-C

コンテナの構成はとても簡単なものにしており、コンテナ間ネットワーク周りは`docker-compose`に任せております。
そのため、各アプリケーションへのアクセスは、 `nginx`の `server_name`と同じホスト名を、Mac内にある`/etc/hosts`ファイルに設定を加えるのみです。

こうすることで、 `project-A`の開発を行っている人の場合は、`project-A`のコンテナを立ち上げて、ブラウザから `http://pj-a.localhost`へアクセスするのみとなります。

同じように、 `project-B`と `project-C`を開発している人の場合は、
プロジェクトに応じて、コンテナを立ち上げ、 `http://pj-b.localhost`や `http://pj-c.localhost`にアクセスするかたちになります。

### 発生した問題
とはいえ、数々の問題がありました…。

#### Docker for MacのVolume Sync遅いマン
Dockerには `volumes`というデータ永続化用の機能を持っています。
永続化する理由としては、言わずもがなですが、コンテナ上でデータの更新などを行ったとしても、コンテナを破壊すると内部のデータごと消えてしまうからです。
使い方としては、次のように `-v`をつけてPATHを指定します。

```shell
$ docker run -d --rm -v $(pwd):/tmp imageName:tag
```

`docker-compose`の場合は、

```yaml:docker-compose.yml
# ...
services:
  pja:
    # ...
    volumes:
      - ./project-data/project-A:/www/project-A:cached
```
というように `volumes`を指定します。

ここで問題になってくるのが、Volume同期の遅さです。
[公式ドキュメント](https://docs.docker.com/docker-for-mac/osxfs-caching/)を見ると `cached`をつけると早くなる、などの記載があり試してみました。

##### Runの場合

```diff
- $ docker run -d --rm -v $(pwd):/tmp imageName:tag
+ $ docker run -d --rm -v $(pwd):/tmp:cached imageName:tag
```

##### docker-composeの場合
```diff:docker-compose.yml
# ...
services:
  pja:
    # ...
    volumes:
-      - ./project-data/project-A:/www/project-A
+      - ./project-data/project-A:/www/project-A:cached
```

しかし、 `cached` を試してみても… 1s程の改善にしかならなかったです。
(このときのレスポンスタイムは、 13s)

ここらへんで、「Docker辛い…」と思い始め、
公式ドキュメントを参考にした速度改善は力尽きました。。。

#### 救いの手であるdocker-syncがぶっ壊れた
`docker for mac`を使うメリットよりも、VirtualBoxなどに移行したほうがいいのでは？と思い始めたころに `docker-sync`と出会いました。

`docker-sync`については、 [docker-syncでホスト-コンテナ間を爆速で同期する](https://qiita.com/pocari/items/0340049742927f3a94b7)という記事を見ていただきたいです。
[こちら](https://docker-sync.readthedocs.io/en/latest/miscellaneous/performance.html)に `docker-sync`が出しているパフォーマンス表があります。

`docker for mac`が明らかに遅いですが、 `docker-sync`版を見たときにこれだ！と思いました。
早速、 `docker-sync`を利用して環境を作って、アクセスしてみると、レスポンスタイムが3sまで縮んでおり、初期に比べて爆速になりました。

ここで終わればよかったんですが、
その数ヶ月後くらいに、ヤツが来ました。

**Docker for macのバージョンアップ！！！！**

Docker for macのバージョンを上げてしまったところ、 `docker-sync`が動かなくなりました。
正確には動かなくなったのではなく、ファイルの更新をしてくれなくなりました。
`docker-compose up`を実行した際には、その時の最新版になっているのですが、実際に変更を加えてもそれがコンテナ内のファイルに変更が加わらなくなりました。
全く原因も分からず、`issue`も立っていなかったので、力尽きました。

一日寝かせて、ふっと、 同期方法がだめになったのでは？と思い、同期方法を `native_osx`にしたところ、これが見事に当たり、再びできるようになりました。
これが、2019年5月くらいの出来事ですね。

そして、2019年10月下旬…再び問題が発生しました。
[Docker-sync with unision strategy broken for newer MacOS / homebrew installation](https://github.com/EugenMayer/docker-sync/issues/687)

gemのアップデートも、docker for macのアップデートも行ったのですが、解決していないです。
`docker-sync`の`0.5.13`を利用していることも確認しましたが、だめでした…
(`native_osx`を使っても、ファイルの更新がされない…)

この問題は力尽きてしまい、まだ解決に至っていないです。(知見ほしい)

#### PCのスペック問題
デザイナーさんはAbstractというツールを利用しているのですが、
AbstractとDockerを併用するとメモリが枯渇し、PCが動かなくなってしまうことが多々ありました。
デザイナーさんはコーディングを行ったりしながら、Abstractを確認することもあるので、同時に起動できないとなると、きっと作業効率も落ちてしまったのでは？と思っています。

最終的には、マンモスPCだとしても、メモリが足りなくなるんだなぁと思いました。

### 良かったこと
下げて最後に上げるので、ここからは良かったことを述べます。

---

#### 構成がコードで管理されているためスノーフレーク状態にならない
Dockerなので、`Infrastructure as code`、 `Immutable Infra`を体現しています。
そのため、コンテナを起動する際に、コンテナ内に何がインストールされているのかを事前に知ることができますし、コンテナを破壊して再構築すれば試行錯誤前の状態になります。

開発する上で、コンテナ内に入って試行錯誤するので環境が壊れてもいいというのは非常にありがたいですね。
更に、開発者は、インストールしたいものをインストールして、Dockerfileに記述し、PRを作成するというフローができます。

これが、リモートの開発環境だったら、試行錯誤を簡単にできない、かつスノーフレーク状態になるので辛いところです。
(例えば `sudo`の実行権限がない…など)

#### 社内ネットワーク(サブネット)間で確認ができるようになった
開発途中のものを一度ネット上に上げる必要がなくなり、
確認してほしい人とのタイミングが合いさえすれば、すぐに確認してもらうことができるようになりました。

デザイン周りとかは特にPR上げる前にレビューしていただきたいところなので、良くなりました。

#### Dockerに詳しくなった
DockerのVolumeや、ネットワーク周りはちょっと詳しくなった気がします。
人並みには使っていましたが、ちゃんとドキュメントを読んで改善するみたいなことは全然していなかったのでいい機会になりました。
`--volumes-from`と `-v`って同じでしょ？と思っていた頃の自分が懐かしいです。
(ちゃんと読まなくても違うの分かるはず)

ネットワーク周りでいうと、`depends_on`や`links`周りですね。(`links`は `depends_on`があれば必要ない)
`depends_on`はコンテナの起動順番の制御するために使いますが、それ以外にも名前解決の設定もしてくれるという驚きの機能がありました。
起動順番に関しては、あくまで起動タイミングであり、起動完了を待つわけではない、ということは知らなかったので、知見を得ました。

## おわりに
### そして今…
開発環境で利用するファイルのリポジトリができました。

サブネット間通信では、レビューする時間を開発者と合わせなければならず、コミュニケーションコストになるという問題が発生しました。
開発では使ってもらっていますが、上記の問題により、リモートの環境はどちらにせよ必要になったので、ECSを利用してリモートの確認環境を作っています。
Docker環境で開発を行っているので、ECSを利用するという選択肢は良かったです。

### 今後の取組
まだまだ改善すべきことが、山盛りなので、実際に使っている人にヒアリングして改善していきます。

とは言いつつも、個人的な感想ですが、
MacでDockerを使うのはまだ早かったなぁwと思いました。
Linuxなら早いので、そろそろMac OSXからLinuxに移行する日も近そう！