🐳

VSCodeでDocker上のRailsをデバッグする(vscode-rdbgのTCP/IP接続でリモートデバッグする)

2023/12/06に公開

はじめに

この記事ではRuby 3.1、Rails 7で標準添付されたdebug gemVSCode rdbg Ruby Debugger拡張機能(以下、rdbg)を利用してDockerコンテナのRailsアプリをリモートデバッグできるようにします。

rdbgではUNIXドメインソケットとTCP/IPでの接続をサポートしています。今回はDockerコンテナを利用するのでTCP/IPを利用します。

デバッグ用の設定の説明

まずは結論から。compose.ymlとlaunch.jsonを下記の通り設定します。

compose.ymlではrdbgのオプションで-nでスクリプトが止まらないようにし、--openでホストとポートを指定、-cでRailsアプリが起動するようにします。

compose.yml
command: bash -c "rdbg -n --open --host 0.0.0.0 --port 12345 -c -- bin/rails s -b 0.0.0.0"

VSCodeの「実行とデバッグ」のlaunch.jsonではdebugPortlocalfsMapを指定します。
localfsMapのディレクトリの指定はDockerのボリュームの指定順とは異なり/remote_dir:/local_dirとなります(詳細はGitHubのvscode-rdbgを参照してください)。

.vscode/launch.json
    {
      "type": "rdbg",
      "name": "Attach with rdbg",
      "request": "attach",
      "debugPort": "localhost:12345",
      "localfsMap": "/rails:${workspaceFolder}"
    }

ステップ バイ ステップでの構築

前提

以下がインストールされている前提です。

  • Docker Desktop
  • Visual Studio Code
  • VSCode rdbg Ruby Debugger

1. 素のRailsアプリを作成

Railsはgem install railsしたrailsgemを使ってrails newすることを前提としています。事前にGemfileとDockerfileを作成してもいいですが、今回は初手からDockerで構築する方法です。コンテナが終了しても作成したRailsアプリが失われないように-vオプションをつけてボリュームをローカルPCに永続化します。

  1. 作業用のフォルダを作成し、
mkdir blog && cd $_
  1. Rubyのコンテナを起動します。
docker run -it --rm -v $(pwd):/blog ruby:3.2.2-slim-bullseye bash
  1. コンテナ内でパッケージのアップデートと必要なパッケージをインストールします。今回はデータベースはSQLite3を利用します。
apt-get update -qq && apt-get install --no-install-recommends -y build-essential git libsqlite3-0
  1. railsgemをインストールし、
gem install rails
  1. Railsアプリを作成します。
rails new blog
  1. 最後にコンテナから出ます。
exit

これで-vに指定したローカルPCのディレクトリに素のRailsアプリが作成されました。

2. 開発用のDockerfileとcompose.yml、entrypoint.shを作成

Rails 7.1からproduction環境用のDockerfileが追加されています。区別するために別名で作成します。

  1. Dockerfile.devを作成します。
vi Dockerfile.dev
Dockerfile.dev
# syntax = docker/dockerfile:1

ARG RUBY_VERSION=3.2.2
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base

ENV LANG C.UTF-8
ENV TZ=Asia/Tokyo

ENV APP_ROOT /rails
RUN mkdir -p ${APP_ROOT}
WORKDIR ${APP_ROOT}

FROM base as build

RUN apt-get update -qq \
  && apt-get install --no-install-recommends -y build-essential \
  libsqlite3-0 \
  pkg-config

COPY Gemfile ${APP_ROOT}/
COPY Gemfile.lock ${APP_ROOT}/

RUN bundle config jobs 4 \
  && bundle config build.nokogiri --use-system-libraries \
  && bundle install

COPY . ${APP_ROOT}

RUN mkdir -p ${APP_ROOT}/tmp/pids ${APP_ROOT}/tmp/sockets

FROM base

RUN apt-get update -qq \
  && apt-get install --no-install-recommends -y \
  curl \
  git \
  sqlite3 \
  libvips \
  && rm -rf /var/lib/apt/lists /var/cache/apt/archives

COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build ${APP_ROOT} ${APP_ROOT}

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT [ "entrypoint.sh" ]
  1. entrypoint.shを作成します。
vi entrypoint.sh
entrypoint.sh
#!/bin/bash
set -e

rm -f /rails/tmp/pids/server.pid

exec "$@"
  1. compose.ymlを作成します。
vi compose.yml
compose.yml
version: "3"
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: bash -c "rdbg -n --open --host 0.0.0.0 --port 12345 -c -- bin/rails s -p 3000 -b 0.0.0.0"
    environment:
      WEB_CONCURRENCY: 0
      RAILS_ENV: development
      RUBYOPT: -W:deprecated
    ports:
      - 3000:3000
      - 12345:12345
    volumes:
      - .:/rails
      - bundle_vol:/usr/local/bundle
    tty: true
    stdin_open: true
volumes:
  bundle_vol:

コンテナをビルドして起動する

  1. コンテナをビルドし、
docker compose build
  1. 起動します。
docker compose up

ログに「Debugger can attach via TCP/IP (0.0.0.0:12345)」と表示されるようになります。

Debuggerが有効になる

デバッグする

  1. VSCodeを開き、アクティビティバーの「実行とデバッグ」を選択し、「実行とデバッグ」ボタンをクリックし、

実行とデバッグ

  1. Ruby(rdbg)を選択します。

Ruby(rdbg)

  1. launch.jsonにある「Attach with rdbg」の項目に追記します。
.vscode/launch.json
    {
      "type": "rdbg",
      "name": "Attach with rdbg",
      "request": "attach",
      "debugPort": "localhost:12345",
      "localfsMap": "/rails:${workspaceFolder}"
    }
  1. 「Attach with rdbg」を選択して「デバッグの開始」を行うと

デバッグの開始

  1. ログに「DEBUGGER: Connected.」と表示されデバッグができるようになります。

DEBUGGER: Connected.

あとは必要に応じてブレークポイントを設置すると停止するようになります。Attach方式のため普段は通常のRailsアプリとして動きます。必要に応じてAttachさせてください。

おまけ:UNIXドメインソケットでのデバッグ

Dev Containersを利用すると開発環境をチームで統一できます。Dev ContainersのコンテナでRailsアプリを開いた場合はUNIXドメインソケットでもデバッグできます(もちろん上記で説明してきたTCP/IP接続も利用可能です)。

RUBY_DEBUG_OPEN=trueをつけて起動するとSocket経由でアタッチできるようになります。

変更点はcompose.ymllaunch.jsonの下記の箇所です。

compose.yml
command: bash -c "RUBY_DEBUG_OPEN=true rails server -b 0.0.0.0"
.vscode/launch.json
  "configurations": [
    {
      "type": "rdbg",
      "name": "Attach with rdbg",
      "request": "attach"
    }
  ]

おまけのおまけ:コンテナを終了しても履歴が残るようにする

Dev Containers拡張機能を利用してコンテナで開発していくとコンテナをビルドして再起動したりするとhistory系が失われることがあります。ということでローカルPCに

  1. 適当にディレクトリを追加し
mkdir docker
  1. .bashrcを追加し、
vi docker/.bashrc
docker/.bashrc
function share_history {
  history -a
  history -c
  history -r
}
PROMPT_COMMAND='share_history'
shopt -u histappend
  1. .bash_historyと、
touch docker/.bash_history
  1. .irb_historyを追加し、
touch docker/.irb_history
  1. compose.ymlのvolumesに追加します。
compose.yml
    volumes:
      - .:/rails
      - ./docker/.bashrc:/root/.bashrc
      - ./docker/.bash_history:/root/.bash_history
      - ./docker/.irb_history:/root/.irb_history
      - bundle_vol:/usr/local/bundle

.bash_history.irb_history.dockerignore.gitignoreに追加するのを忘れずに。

成果物

上記の内容とGetting Started with Railsのブログアプリを実装したコードはこちらです。

https://github.com/trysmr/zenn-using-vscode-rdgb

Discussion