docker環境のrails5をstep実行でdebugする

# docker環境のrails5をstep実行でdebugする

## 概要

  先日 [dockerでrails5環境構築](http://qiita.com/pocari/items/456052a291381895f8b3)で開発環境を作りました。
  で、やはり開発する上ではguiでstep実行できると楽だなと思い、
  
  [Visual Studio Codeを使ってRailsをデバッグ実行してみよう](http://qiita.com/chimame/items/56e48ab3145312ff1786)　
  
  の記事を見つけたのでdocker-machine上で実行されているrailsに対して試してみようとしたところ、先日に引き続きハマり倒したのでその作業メモです。
  
## 目標のデバッグ環境

  構築した [dockerでrails5環境構築](http://qiita.com/pocari/items/456052a291381895f8b3)の環境は下記のような構成で動いています。

  ![fig-01.png](https://qiita-image-store.s3.amazonaws.com/0/34692/a6174a98-4e71-3f57-f740-3c97146606db.png)
この環境でホストOS(osx)で実行しているVisual Studio Codeのデバッガでステップ実行したいわけです。
  
  以下先日の「[dockerでrails5環境構築](http://qiita.com/pocari/items/456052a291381895f8b3)」の状態からの変更点を記載していきます。
  
#### railsコンテナの設定

  基本的には「[Visual Studio Codeを使ってRailsをデバッグ実行してみよう](http://qiita.com/chimame/items/56e48ab3145312ff1786)」の通りに進めていきます。
  
  - Gemの追加
 
    `ruby-debug-ide`と`debase`を`development`環境に追加します。
  
    ```rb:app/Gemfile
    group :development do
      # 〜略〜
    
      gem 'ruby-debug-ide'
      gem 'debase'
    end
    ```

  - rdebug-ideでrailsを起動するために起動スクリプト修正
  
    コンテナ起動時に環境変数`DEBUG_MODE`を渡すことでデバッグモードでrailsが起動できるように変更します。
  
    ```sh:app/bin/run-docker-compose.sh
    #!/bin/sh
    
    SCRIPT_DIR=$(cd $(dirname $0) && pwd)
    
    export DB_DOCKERFILE_DIR=${SCRIPT_DIR}/../db
    export WEB_DOCKERFILE_DIR=${SCRIPT_DIR}/../web
    export WEB_APP_ROOT_DIR=${SCRIPT_DIR}/../../app
    
    export MYSQL_DATABASE=mydb
    export MYSQL_USER=dbuser
    export MYSQL_PASSWORD=dbuser_pass
    export MYSQL_ROOT_PASSWORD=root
    
    export START_RAILS_COMMAND="bundle install --path=vendor/bundle && rm tmp/pids/server.pid;"
    
    # debugモードに切り替える設定
    if [ "$DEBUG_MODE" = "1" ] ; then
      # debug mode
      START_RAILS_COMMAND="${START_RAILS_COMMAND} bundle exec rdebug-ide --host 0.0.0.0 --port 1234 --dispatcher-port 26162 -- bin/rails s -b 0.0.0.0"
    else
      # normal mode
      START_RAILS_COMMAND="${START_RAILS_COMMAND} bin/rails s -b 0.0.0.0"
    fi
    YML_FILE=$SCRIPT_DIR/docker-compose.yml
    
    docker-compose -f $YML_FILE $*    
    ```

    起動コマンド`command`を直書きしていた部分を↑で設定した`START_RAILS_COMMAND`で置き換えます。
    また`rdebug-ide`が使うポートを`ports`に追加します。
    
    ```yml:app/bin/docker-compose.yml
    db:
      container_name: db
      build: ${DB_DOCKERFILE_DIR}
      ports:
        - "3306:3306"
      environment:
        MYSQL_DATABASE: ${MYSQL_DATABASE}
        MYSQL_USER: ${MYSQL_USER}
        MYSQL_PASSWORD: ${MYSQL_PASSWORD}
        MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    
    web:
      container_name: web
      build: ${WEB_DOCKERFILE_DIR}
      ports:
        - "3000:3000"
        - "1234:1234"
        - "26162:26162"
      links:
        - db:db
      volumes:
        - ${WEB_APP_ROOT_DIR}:/var/myapp
      environment:
        DATABASE_NAME: ${MYSQL_DATABASE}
        DATABASE_USER: ${MYSQL_USER}
        DATABASE_PASS: ${MYSQL_PASSWORD}
        DATABASE_HOST: db
      command: sh -c "${START_RAILS_COMMAND}"
    ```
   
    - railsコンテナの`Dockerfile`の`EXPOSE`にも`1234`, `26162`を追加します。
    
    ```docker/web/Dockerfile
    〜略〜
    EXPOSE 3000 1234 26162
    〜略〜    
    ```
       
   
    これでデバッグモードで起動出来るようになったので、`build`しなおしてから起動確認します。
    下記のように`Fast Debugger`の表示になってwait状態になれば起動成功です。
    (この状態ではまだ`rails`は起動されていないのでブラウザでアクセスしても何も表示されません。)

    
    ```sh
    # Dockerfileを直したので再ビルド
    docker/bin/run-docker-compose.sh build
    
    # DEBUG_MODEを指定して起動スクリプトを実行
    DEBUG_MODE=1 docker/bin/run-docker-compose.sh up
    〜略〜
    web    | Fast Debugger (ruby-debug-ide 0.6.0, debase 0.2.1, file filtering is supported) listens on 0.0.0.0:1234
    ```  

#### Visual Studio Codeの設定

   `Ruby`用の拡張機能のインストールについては
   
   > [Visual Studio Codeを使ってRailsをデバッグ実行してみよう](http://qiita.com/chimame/items/56e48ab3145312ff1786)
   
   を参考にしてください。
   
   この記事にならってデバッグ用の設定ファイルを作成します。

   ただし今回は`rails`は`docker`で動いているので`remoteHost`の設定を`docker-machine`のipに変更し、`remoteWorkspaceRoot`をコンテナ内でデプロイしているパス（ここでは`/var/myapp`)に変更しておきます。
   
   ```json:app/.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for rdebug-ide",
      "type": "Ruby",
      "request": "attach",
      "cwd": "${workspaceRoot}",
      "remoteHost": "192.168.99.100",
      "remotePort": "1234",
      "remoteWorkspaceRoot": "/var/myapp",
      "useBundler": true
    }
  ]
}
   ```
   
   これで`Visual Studio　Code`側の設定も終わったので、先日作った`hello_controller.rb`にブレイクポイントを設定してみます。
   
#### デバッグ実行

  これで`rails`側も`Visual Studio Code`側も設定ができたのでデバッグ実行！ということで試してみました。
  
  ・・・が、案の定、全くうまく行かず。いくつもハマりどころが合ったので項目毎に解決方法を記載していきます。
  
  - デバッグサーバが立ち上がらない問題
    
    先ほどの設定確認ではすんなり`rdebug`サーバが立ち上がっていましたが、まずここでつまずきました。
    元々遅いと思っていたのですが、このデバッグサーバに関しては耐え切れないほど起動が遅くてとても使ってられませんでした。
    
    原因はホスト側でソースを編集できるように`VOLUME`で`app`フォルダ以下をマウントしていたのが原因のようでした。
    `Virtual Box`の共有フォルダはすこぶる遅いらしく普通の方法ではどうしようもないようです。
  
    そこでいろいろ調べたところ、
    
    > [docker-machine-nfsでOSXのディレクトリをマウントする](http://qiita.com/hnakamur/items/b899f06c942629c211e9)
    
    の記事を見つけました。`Virtul Box`の共有フォルダを`nfs`でマウント出来るようにしてくれるツールだそうです。
    開発元のサイトを見てみると`Homebrew`でインストールもできて、引数無しで実行した場合は`docker-machine`で通常行われる`/User`以下を(macの場合)設定してくれるそうなので、簡単に導入できました。
    
    > https://github.com/adlogix/docker-machine-nfs
    
    ```
    brew install docker-machine-nfs
    ```
    
    でインストールし、`docker-machine`が起動されている状態で、その`docker-machine`名（ここでは`default`)を引数に実行するだけのようです。
    
    ```
    docker-machine-nfs default
    〜略〜                                                                                                                │~                                            |+  16 # debugモー»
    --------------------------------------------                                                                                                                │~                                            |+  17 if [ "$DEBU»
                                                                                                                                                                │~                                            |+  18   # debug m»
     The docker-machine 'default'                                                                                                                               │~                                            |+  19   START_RAI»
     is now mounted with NFS!                                                                                                                                   │~                                            |+  20 else↲
                                                                                                                                                                │~                                            |+  21   # normal »
     ENJOY high speed mounts :D                                                                                                                                 │~                                            |+  22   START_RAI»
                                                                                                                                                                │~                                            |+  23 fi↲
    --------------------------------------------    
    ```
    
    途中`sudo`を使っているようでパスワードを求められたのでパスワード入力し、上記のような表示になればOKです。
    なんとも頼もしいメッセージです。
        で、結果はというと、とんでもなくデバッグサーバの起動が早くなりました。というかすべてが早くなりました。感謝。


  - 途中でステップ実行がふんずまる問題
  
    これでデバッグサーバがまともに動くようになったのでデバッグしてみます。
    
    ```sh
    # debugサーバ実行
    DEBUG_MODE=1 docker/bin/run-docker-compose.sh up
    〜略〜
    web    | Fast Debugger (ruby-debug-ide 0.6.0, debase 0.2.1, file filtering is supported) listens on 0.0.0.0:1234
    ```  

デバッグ準備OKなので、`Visual Studio Code`側からアタッチします。    
先ほど作ったデバッグ設定を選択して起動します。
    
![スクリーンショット 2016-04-21 23.16.20.png](https://qiita-image-store.s3.amazonaws.com/0/34692/6e982aad-9d10-1c13-4a54-28c1bbe368a9.png)
  

すると、railsのコンソール側でpumaが起動します。

```sh
    web    | => Booting Puma
    web    | => Rails 5.0.0.beta3 application starting in development on http://0.0.0.0:3000
    web    | => Run `rails server -h` for more startup options
    web    | => Ctrl-C to shutdown server
    web    | Puma starting in single mode...
    web    | * Version 3.4.0 (ruby 2.3.0-p0), codename: Owl Bowl Brawl
    web    | * Min threads: 5, max threads: 5
    web    | * Environment: development
    web    | * Listening on tcp://0.0.0.0:3000
    web    | Use Ctrl-C to stop
```
    
    
この状態で、`http://192.168.99.100:3000/hello` にアクセスします。

するとバッチリ`hello_controller.rb`に仕掛けたブレイクポイントで止まります・・・・・止まりますが、そのままステップ実行していくと、どうしても途中でステップ実行が止まります。しかもページもずっとロード中のままぐるぐる回り続けます。。

全く止まらないとかエラーになるとかならまだ分かるんですが、途中までステップ実行出来るだけにこれにはハマりまくりました。

で、いろいろ考えたところ、だいたいローカルホストでうまくいくのに、`docker-machine`でうまくいかない時の定番はネットワーク問題なので、それなら`localhost`と通信している体にしてやろう！ということで、ポートフォワーディングで、ローカルの`1234`, `26162`と`docker-machine`の同ポートを繋いでやることにしました。(コンテナと`docker-machine`の同ポートは本来の`docker`の機能でフォワードされています。)

要は下記のような感じです。(以下の`VSC`は`Visual Studio Code`のこと)

もともと
　　　　`VSC` ----> docker-machineのポート`1234` ----> railsコンテナのポート`1234`
という経路でデバッグサーバにアクセスしていたところを
　　　　`VSC` ----> localhostのポート`1234` ----> docker-machineのポート`1234` ----> railsコンテナのポート`1234`

と、ローカルの`1234`ポート経由でアクセスしようということです。

ということで、`VisualStudioCode`のデバッグ設定を変更します。
下記のように`remoteHost`を`localhost`にします。

```json:app/.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for rdebug-ide",
      "type": "Ruby",
      "request": "attach",
      "cwd": "${workspaceRoot}",
      "remoteHost": "localhost",
      "remotePort": "1234",
      "remoteWorkspaceRoot": "/var/myapp",
      "useBundler": true
    }
  ]
}
```

次に、ローカルのポートを`docker-machine`のポートに繋ぎます。
`docker-machine`の`ssh`コマンドにはその後の引数に`ssh`コマンドのポートフォーワードの書式をそのまま書けるのでそれを利用します。

フォワードするだけなので、適当にターミナルを起動して下記コマンドで`docker-machine`にログインしたらそのままほったらかします。

```sh
docker-machine ssh default -L 1234:localhost:1234 -L 26162:localhost:26162
```

この状態で、再度デバッグサーバを立ち上げて、デバッグしてみるとバッチリステップ実行できました。

  - デバッグコンソールで変数を参照すると時々止まる問題
  
　　これは未だ原因不明なのですが、ステップ実行中に`Visual Studio Code`のデバッグコンソールで変数をinspectするとコンソールに内容が表示できて便利なのですが、これを行うとタイミングによって（？）デバッグセッションが止まってしまいます。。とりあえず左側のペインで変数の内容とコールスタックが確認できるので致命的に困るようなことでもないため放置です。どなたか解決方法ご存知でしたら教えて下さい。

  - 絶対間違ってない箇所で謎の引数違うよエラーが出る問題
    
    これは今回の記事用に作ったリポジトリで起きなかったので微妙なのですが、`byebug`gemと一緒に`ruby-debug-ide`gemを使うと変なエラーが出ることがあるそうです。実際別のプロジェクトで試していた時に`pry-byebug`を同時に使っていたのですが、ブレイクポイントの箇所のコードで明らかに問題ない処理がエラーになって落ちてしまっていました。

> [RubyMineでdebugできない](http://qiita.com/3yatsu/items/df193f09dd4d03d2cd7c)

や

> http://stackoverflow.com/questions/31046586/cant-debug-rails-app-in-rubymine

を参照しました。


ここまでの内容を反映したのを 
https://github.com/pocari/rails5-docker-sample/tree/ver02-enable-remote-debug 
にあげています。