DockerとRailsと私 - Rail on Docker 開発TIPS

# tl;dr

- タイトルの「私」は関係ありません。語感だけです。
- 主にDockerを用いた開発環境の話。
- Docker使って開発するとメリットも大きいけど、ハマりどころもあるし、デメリットもある。
- 目的を見極めて適切に使いましょう。

---

# who am i

- [github/bananaumai](https://github.com/bananaumai)
- [twitter/banana_umai](https://twitter.com/banana_umai)
- Working at [Livesense, inc](http://www.livesense.co.jp/) as SRE

---

# 背景 - 運用状況

- [転職会議](https://jobtalk.jp)で部分的に本番導入中（順次移行中)。
- Amazon ECSを利用中。
- サービス開発に関わる、チームの規模が大きくなってきて、それとあわせて、サービスもマイクロに分割し始めていた。

---

# 背景 - 開発現場の課題感とDocker化

以下、自分の所属する開発チームでの課題感とそれに対してのDocker化によって（想定している）メリット感です。

- 課題感
    - サービスはAWS EC2上で構築しているが、サーバーの管理をサービス開発に携わるアプリケーション開発者でセルフサービス化していくのにはハードルが高かった。
    - 複数のプロジェクトを触るので、普段開発していないがたまに触るサービスなどがあった時にローカルの開発環境のセットアップのための手間が大きい。
- Docker化によるメリット
    - EC2ベースに比べてアプリケーションサーバーの構築/管理がDockerのほうが容易で、セルフサービスしやすい。
    - 開発環境の差異を相対的に受けづらく、開発環境の構築が相対的に容易。

---

# 開発環境関連のTIPS

- 以下はdocker-composeを使ってローカル開発を行っていくという前提としています。
- Railsアプリケーションはdocker-compose上でrailsというサービスで設定。
- また、Docker Container上は`/rails`ディレクトリ上にアプリケーション構築。

```yaml:docker-compose.yml
version: '2'
services:
  web:
    build: .
    command: bin/rails s
    environment:
    volumes:
      - .:/rails
    links:
      - db
    ports:
      - "3000:3000"
    stdin_open: true
    tty: true
  db:
    snip...
```

---

## gemの管理(1)

[Docker公式のQuickstart](https://docs.docker.com/compose/rails/)ではGemfileの追加時に `bundle install` が使われているが、 `bundle lock` の方が良い。


```
$ edit Gemfile
$ docker-compose run rails bundle lock
$ docker-compose build # この中でbundle installを走らせる
```

---

## gemの管理(2)

- gemは$GEM_HOMEにおいたほうが良い (`{Rails.root}/vendor/bundle`は避ける)
- Rails.rootをDockerコンテナとマウントする際に、Gemfileの更新がうまくいかない。
- 以下のようにignore的にAnonymous Volumeを使っていたことがあるが、トラブルの原因になりがち。
 
```yaml:docker-compose.yml
version: '2'
services:
  rails:
    # snip
    volumes:
      - .:/rails
      - /rails/vendor # <- トラブルの元
```

※ IDEを使っている際に、vendorファイルがないとコード補完等で不便だったりしますが、RubyMineではRemote Ruby Interpereter Integrationを使えば解決します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/5811/3e41fb40-9bad-919a-4d1c-9e580b06532a.png)

---

## Debugger(1) - byebug / pry-byebugを使う

明示的に、docker実行時に仮想端末モードを有効にし、標準入力をオープンしておく必要がある。

```yaml:docker-compose.yml
version: '2'
services:
  rails:
    # snip
    stdin_open: true
    tty: true
```

---

## Debugger(2) - ruby-debug-ideを使ってIDEでデバッグ

以下RubyMineでの設定例ですが、他のIDEも同じような感じだと思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/5811/8f562e62-bc91-5e33-3508-ea24d52d7192.png)

その上で以下の形でコマンドを実行（bundlerでインストールした場合）。

```bash
$ docker-compose run -p 1234:1234 -p 3000:3000 rails bundle exec rdebug-ide --host 0.0.0.0 --port 1234 --dispatcher-port 26162 -- $COMMAND$
```

---

## Debugger(3) - byebugとruby-debug-ide両方を使う場合(問題)

- 現場では開発環境が自由であり、かつ、多人数で開発している。
- 使っているdebuggerが異なる。
- Mac等のホストOS上ではうまく動作しますが、Dockerコンテナ上でデバッグしようとした場合に、お互いのGemが競合してしまい、どちらも正常に動作しません。

```Gemfile.lock抜粋
...
    byebug (9.0.6)
...
    pry-byebug (3.4.3)
...
    ruby-debug-ide (0.6.0)
```

---

## Debugger(4) - byebugとruby-debug-ide両方を使う場合(回避方法)

Gemfileでデフォルトでrequireされないようにした上で、initializerの中で環境変数を見てDebuggerを振り分ける。

```ruby:Gemfile抜粋
group :development, :test do
  gem 'pry-byebug', require: false
  gem 'pry-rails', require: false
  gem 'ruby-debug-ide', require: false
  gem 'debase', require: false
end
```

```ruby:config/initializers/debugger.rb
if Rails.env == 'development'
  if ENV.has_key?('ENABLE_REMOTE_DEBUG')
    require 'ruby-debug-ide'
    require 'debase'

    Rails.logger.info "loaded: ruby-debug-ide"
  else
    require 'pry-rails'
    require 'pry-byebug'

    Rails.logger.info "loaded: pry-byebug"
  end
end
```

上記の場合、ENABLE_REMOTE_DEBUG環境変数が有効な場合にのみ、IDE Remote Debugを有効にしているので、以下のように実行。

```bash
$ docker-compose run -p 1234:1234 -p 3000:3000 -e ENABLE_REMOTE_DEBUG=true rails bundle exec rdebug-ide --host 0.0.0.0 --port 1234 --dispatcher-port 26162 -- $COMMAND$
```

---

## 秘密情報の扱い

- RoR5.1以上の場合
  - EncryptedSecretを使う。
  - けど、Docker上にVim等のEditorがいないと動かないのは残念。
- RoR5.0以下の場合
  - 環境変数で管理し、[転職会議](https://jobtalk.jp)では[pnzr](https://github.com/jobtalk/pnzr) というツールで暗号化してソースコード管理。

---

## アプリケーション実行 / テスト実行

```
$ docker-compose (up|run|exec)
```

特段なにってことはないのですが、やはりローカルマシンで実行するのは遅い。特にテスト等では顕著に差が出る気がする。

---

## CI

- CircleCI上でDocker on Dockerでbuild、テストを行っているが、やはりオーバーヘッドが大きくて遅いのが課題になっている。
- CircleCI2.0で任意のDocker Repositoryを指定できるので、そうしていきたい (<- まだやってない)

---

# 所感

- ローカルで開発する上では様々なオーバーヘッドがある。
- 例えば、少人数でスタートアップする際にHerokuとかでシンプルなアプリケーションを組めるならば、Dockerとか使わなくても良いような気がしてたりします。
- なので、実際に解決したい問題が明確にマッチするかという意識は持ったほうが良さそう。
- 自分の所属するチームでは冒頭にあげたような課題感があったが、改善を感じる。