VSCode + Docker + Docker-compose +Railsのデバッグの設定方法

# 概要
`Dev container`の環境を作らず、ステップ実行でデバッグが出来る環境を作成しました。今回は、その方法を記載します。

# 事前設定
以下がインストールされている事が前提です。
- **Docker Desktop**
- **Visual Studio Code**
- **VSCode rdbg Ruby Debugger**

# ディレクトリー構成
```sh
.
├── db/
│   ├── conf/
│   │   └── .env　　　
│   └── postgres_volume # ← コンテナー起動時に自動生成
├── web/
│   ├── vscode/
│   │   └── lanuch.json
│   ├── # rails関連のファイル
│   ├── Dockerfile
│   ├── Gemfile
│   └── Gemfile.lock
└── compose.yml
```

# 環境構築
- `Dockerfile`の構成は下記
```Dockerfile
FROM ruby:3.2.2
ENV LANG=ja_JP.UTF-8
ENV TZ=Asia/Tokyo

RUN apt-get -y update && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /web
COPY Gemfile /web/Gemfile
COPY Gemfile.lock /web/Gemfile.lock

RUN bundle install
```

- `compose.yml`の構成は下記
`compose.yml`には、デバッグを行う為の設定を記述
(`dbg -n --open --host 0.0.0.0 --port 12345`と`"12345:12345"`がデバッグ用の設定となる)
```compose.yml
version: "3.9"
services:
  db:
    container_name: pgsql
    image: postgres:latest
    env_file: ./db/conf/.env
    volumes:
      - ./db/postgres_volume:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    restart: always
  web:
    container_name: ruby_debug_environment
    build: ./web
    command: >
      bash -c "
        rm -f tmp/pids/server.pid &&
        bundle install &&
        rails db:migrate &&
        rdbg -n --open --host 0.0.0.0 --port 12345 -c -- bundle exec rails s -p 3000 -b '0.0.0.0'
      "
    volumes:
      - ./web:/web
    ports:
      - "3000:3000"
      - "12345:12345"
    depends_on:
      - db

```

- `DB`用の`env`ファイルを作成する。　必要に応じて追加も可能
```db/conf/.env
POSTGRES_USER=任意のユーザー名
POSTGRES_PASSWORD=任意のパスワード
POSTGRES_DB=任意のDB名
```

- `Gemfile`を作成
```ruby
source "https://rubygems.org"
# 最新バージョンにする事を推奨
gem "rails", "~> 7.1.3"
```


- `Rails`の環境を作成するコマンド
```sh
docker compose run web rails new . --force --database=postgresql --skip-docker --skip-test
```

- `Gemfile`に下記を記載する
(`Rspec`、`debug`を追加)
```ruby
group :development, :test do
  # デバッグ導入
  gem "debug"

  # Rspecの導入
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end
```

- 再度、ビルドを行う
```sh
docker compose build --no-cache 

# docker imageのnoneを消去
docker rmi $(docker images -f "dangling=true" -q)
```

- `database.yml`の`development`と`test`に`.env`で記載した`DB`情報を記載する
<font color="red">※ 下記は入力例</font>
```database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: # 任意のDB名
  username: # 任意のユーザー名
  password: # 任意のパスワード
  host: db # サービス名を記述する(基本はdb)
  port: 5432

test:
  <<: *default
  database: # 任意のDB名
  username: # 任意のユーザー名
  password: # 任意のパスワード
  host: db # サービス名を記述する
  port: 5432
```

- `DB`を作成する
```sh
docker compose run web rails db:create 
```

- コンテナーを立ち上げる
```sh
docker compose up -d 
```

- `Rspec`を導入する
```sh
docker exec ruby_debug_environment bundle exec rails g rspec:install
```


## Railsを構築する際に参考にした手順
https://docs.docker.jp/compose/rails.html


# デバッグの設定方法
## launch.jsonの設定
- 通常のデバッグと`Rspec`のデバッグの設定を`launch.json`に記載する
:::note warn
- Rspecのデバッグをする場合は、ローカル環境に`Ruby`と`Rails`が動作する環境を構築する必要があります
:::
```launch.json
{
    "version": "0.2.0",
    "configurations": [
        # 通常のデバッグ設定
        {
            "type": "rdbg",
            "name": "Debug Ruby",
            "request": "attach",
            "debugPort": "localhost:12345",
            "localfsMap": "/app:${workspaceFolder}",
        },
        # Rspecのデバッグ設定
        {
            "type": "rdbg",
            "name": "Debug Rspec with current file",
            "rdbgPath": "bundle exec rdbg",
            "request": "launch",
            "command": "rspec",
            "script": "${file}",
            "args": ["-p", "port番号"],
            "askParameters": true,
            "env": {
                "DATABASE_URL": "データベースの種類://DBのユーザー名:DBのパスワード@localhost:port番号/接続するデータベースの名"
            } 
        }
    ]
}
```

- `env`に、`Rspec`で使用するテスト`DB`の接続情報を記載する事で、`Rspec`のデバッグ起動時に、その`DB`を参照する
- `DATABASE_URL`には、`/config/database.yml`の`test`に記載した内容を記述する
```json
# 入力例
"env": {
    "DATABASE_URL": "postgresql://root:postgres@localhost:5432/ruby_debug_development_test"
}
```
## デバッグの実施方法
１. `VScode`のメニューバーにある[`ファイル`]、[`開く`]の順に押下する
２. [`web`]を選択して、[`開く`]を押下する

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/9b763786-af43-fe90-0fb9-c6a0b4de8d77.png)

３. 任意の場所にブレイクポイントを設置し、ステップ実行でデバッグを行う
![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/8191a8a3-caa7-d1c5-1bed-8b0c612f0560.png)

:::note warn
- エラーが表示されるが、ステップ実行でのデバッグに影響はない
- 下記のエラーは、ローカル環境に`gem 'debug'`がない為、表示される。もし、このエラーを解消させるには、ローカル環境に直接`Ruby`、`Rails`が動作する環境を作成し、`gem install debug`を行う事で解消される
```sh
[Start session]
{
    "d":{},
    "f":"*********-****-****-****-***********","g":"rdbg","h":"Debug Ruby",
    "i":{
            "uri":{"$mid":1,
            "fsPath":"/to/path/ruby_debug_environment/web",
            "external":"file:////to/path/ruby_debug_environment/web",
            "path":"/to/path/ruby_debug_environment/web",
            "scheme":"file"
        },
        "name":"web",
        "index":0
    },
    "j":{
        "type":"rdbg",
        "name":"Debug Ruby",
        "request":"attach",
        "debugPort":"localhost:12345",
        "localfsMap":"/web:/to/path/ruby_debug_environment/web",
        "__configurationTarget":6,
        "rdbgExtensions":["traceInspector"],
        "rdbgInitialScripts":[]
    }
}
zsh:1: command not found: rdbg

Error: /bin/zsh -lic 'rdbg --version': exit code is 127
Make sure to install rdbg command (`gem install debug`).
If you are using bundler, write `gem 'debug'` in your Gemfile.
```
:::

# おまけ
## コードの設定
- routes.rbの設定
```web/config/routes.rb
Rails.application.routes.draw do
  get "up", to: "rails/health#show", as: :rails_health_check
  get "cat_fact", to: "application#cat_fact"
end
```
- controllerの設定
```web/app/controllers/application_controller.rb
require 'faraday'
require 'json'

class ApplicationController < ActionController::Base
  def cat_fact
    url = 'https://catfact.ninja/fact'
    response = Faraday.get(url)

    if response.success?
      data = JSON.parse(response.body)
      render plain: data['fact']
    else
      render plain: "Error: #{response.status}"
    end
  rescue Faraday::ConnectionFailed => e
    render plain: "Error: #{e.message}"
  end
end
```

- エンドポイント
```sh
http://localhost:3000/cat_fact
```


# GitHub

https://github.com/kengo-sk8/ruby_debug_environment


# 感想
このデバッグ環境を作成するのは、凄く苦労しました。ただ、`Rspec`のデバッグに課題があるので、公式のドキュメントで何らかの動きがあれば、追記したいと考えています。
　このデバッグシステムを導入すると開発が凄く楽になるので、是非試して頂ければと思います。




# 参考記事
- [[公式]vscode-rdbg](https://github.com/ruby/vscode-rdbg)
- [Docker上のRailsアプリをvscode-rdbgのTCP/IP接続でリモートデバッグする](https://zenn.dev/trysmr/articles/3f63e7622ef5e4)
- [VSCodeでDockerを使ったRailsを開発しよう~Ruby3.1以降 Debug編~](https://blog.uzumaki-inc.jp/vscodedockerrailsruby31-debug)