開発コンテナー(Dev Containers)でRails開発環境を準備する

Visual Studio CodeではDev Containers拡張機能を導入することで開発コンテナーが利用できます。

ソフトウェア開発をコンテナーの中で行うことができるので、開発用のツールの種類やバージョンをプロジェクト毎に管理することが容易になり、ホストOSに影響を与えず使用できます。また、複数人で開発する際に環境を揃えるのが容易になります。

コンテナーの名前の通り、利用にはDockerやPodmanなどのコンテナー実行環境を必要とします。WindowsではWSLにコンテナー実行環境を用意すると、WSL拡張機能を併用してコンテナー実行環境を使用できるようになります。

開発コンテナーの使用にあたってコマンドパレット内の開発コンテナー: コンテナー機能の構成にテンプレートがあるので、これを使うと手っ取り早いです。

基本的には、テンプレートから生成された定義ファイルをカスタマイズして自分好みの構成にして使うことになると思います。

テンプレートの内容から変更する箇所が多いようなら、最初から自前で定義を準備してしまう方が早いかもしれません。

作成する開発コンテナー定義

以下の内容で開発コンテナー定義を作成します。

  • ベースイメージはmcr.microsoft.com/devcontainers/ruby:1-3.3-bullseyeを使用する
  • Ruby-LSPSolarGraphのgemと拡張機能を追加する
    • その他Ruby Test ExplorerGit Historyの拡張機能も追加する
  • bundlerのパッケージ導入先パスをプロジェクト内のvendor/bundleディレクトリに設定

開発コンテナー定義ファイルの作成

Visual Studio Codeのワークスペースに.devcontainerディレクトリを作成し、以下2つのファイルを作成します。

  • devcintainer.json
  • Dockerfile

.devcontainer/devcontainer.json

{
  "name": "Ruby",
  "build": {
    "dockerfile": "./Dockerfile"
  },
  "containerEnv": {
    "TZ": "Asia/Tokyo"
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "Shopify.ruby-lsp",
        "hbenl.vscode-test-explorer",
        "connorshea.vscode-ruby-test-adapter",
        "donjayamanne.githistory",
        "castwide.solargraph"
      ]
    }
  }
}
  • Dockerfileを指定して、随時ビルドして開発コンテナーとして使用する
  • コンテナー内の環境変数を設定し、タイムゾーンを東京にしておく
  • 初期導入する拡張機能を指定する

.devcontainer/Dockerfile

FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye
RUN su vscode -c "gem install ruby-lsp solargraph" && \
    su vscode -c "bundle config set path 'vendor/bundle'"
  • Microsoftが開発コンテナー用に提供しているRuby 3.3のコンテナーイメージをベースイメージに指定
  • Ruby-LSPとSolarfraphのgemをインストール
  • bundlerのgemインストール先のディレクトリを設定

Railsプロジェクトの作成

コンテナーで再度開くを選択して、コンテナーを起動します。

情報
初回や定義の変更時はコンテナーイメージのpullやdocker buildが実行されるため、操作可能になるまで時間がかかります。

操作可能になったところで、ターミナルを開きます。

bundlerの初期化

bundle initを実行してbundlerを初期化します。

vscode ➜ /workspaces/project1 $ bundle init
Writing new Gemfile to /workspaces/project1/Gemfile

Gemfileの修正

生成されたGemfileを開き、gem “rails”のコメントを解除します。

# frozen_string_literal: true

source "https://rubygems.org"

- # gem "rails"
+ gem "rails"
情報
Railsのバージョンを指定する場合は、この時点でgem "rails", "~> 7.2.1”といった形でバージョンを指定しておきます。

Gemのインストール

Gemfileを保存し、bundle installを実行します。

vscode ➜ /workspaces/project1 $ bundle install
実行ログ全量
vscode ➜ /workspaces/project1 $ bundle install
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Fetching rake 13.2.1
Installing rake 13.2.1
Fetching base64 0.2.0
Fetching benchmark 0.4.0
Fetching bigdecimal 3.1.8
Fetching concurrent-ruby 1.3.4
Fetching connection_pool 2.4.1
Fetching drb 2.2.1
Fetching logger 1.6.1
Fetching minitest 5.25.1
Fetching securerandom 0.3.2
Fetching uri 1.0.2
Fetching builder 3.3.0
Fetching erubi 1.13.0
Installing base64 0.2.0
Fetching racc 1.8.1
Installing benchmark 0.4.0
Fetching crass 1.0.6
Installing bigdecimal 3.1.8 with native extensions
Installing concurrent-ruby 1.3.4
Fetching rack 3.1.8
Installing connection_pool 2.4.1
Fetching useragent 0.16.10
Installing drb 2.2.1
Fetching nio4r 2.7.4
Installing logger 1.6.1
Fetching websocket-extensions 0.1.5
Installing minitest 5.25.1
Fetching zeitwerk 2.7.1
Installing securerandom 0.3.2
Fetching timeout 0.4.2
Installing uri 1.0.2
Fetching marcel 1.0.4
Installing builder 3.3.0
Fetching mini_mime 1.1.5
Installing erubi 1.13.0
Fetching date 3.4.0
Installing racc 1.8.1 with native extensions
Installing crass 1.0.6
Fetching io-console 0.7.2
Installing rack 3.1.8
Fetching stringio 3.1.2
Installing useragent 0.16.10
Fetching thor 1.3.2
Installing nio4r 2.7.4 with native extensions
Installing websocket-extensions 0.1.5
Fetching i18n 1.14.6
Fetching tzinfo 2.0.6
Installing zeitwerk 2.7.1
Fetching rack-session 2.0.0
Installing timeout 0.4.2
Fetching rack-test 2.1.0
Installing marcel 1.0.4
Fetching rackup 2.2.1
Installing mini_mime 1.1.5
Fetching nokogiri 1.16.7 (x86_64-linux)
Installing date 3.4.0 with native extensions
Installing io-console 0.7.2 with native extensions
Installing stringio 3.1.2 with native extensions
Installing thor 1.3.2
Fetching websocket-driver 0.7.6
Installing i18n 1.14.6
Fetching net-protocol 0.2.2
Installing tzinfo 2.0.6
Installing rack-session 2.0.0
Installing rack-test 2.1.0
Installing rackup 2.2.1
Fetching psych 5.2.0
Installing websocket-driver 0.7.6 with native extensions
Installing net-protocol 0.2.2
Fetching net-pop 0.1.2
Fetching net-smtp 0.5.0
Fetching reline 0.5.11
Installing psych 5.2.0 with native extensions
Installing net-pop 0.1.2
Installing net-smtp 0.5.0
Installing reline 0.5.11
Installing nokogiri 1.16.7 (x86_64-linux)
Fetching loofah 2.23.1
Installing loofah 2.23.1
Fetching rails-html-sanitizer 1.6.0
Installing rails-html-sanitizer 1.6.0
Fetching rdoc 6.7.0
Installing rdoc 6.7.0
Fetching irb 1.14.1
Installing irb 1.14.1
Fetching activesupport 8.0.0
Fetching net-imap 0.5.1
Installing activesupport 8.0.0
Fetching rails-dom-testing 2.2.0
Fetching globalid 1.2.1
Fetching activemodel 8.0.0
Installing net-imap 0.5.1
Fetching mail 2.8.1
Installing rails-dom-testing 2.2.0
Fetching actionview 8.0.0
Installing globalid 1.2.1
Fetching activejob 8.0.0
Installing activemodel 8.0.0
Fetching activerecord 8.0.0
Installing mail 2.8.1
Installing actionview 8.0.0
Fetching actionpack 8.0.0
Installing activejob 8.0.0
Installing activerecord 8.0.0
Installing actionpack 8.0.0
Fetching actioncable 8.0.0
Fetching activestorage 8.0.0
Fetching actionmailer 8.0.0
Fetching railties 8.0.0
Installing actioncable 8.0.0
Installing activestorage 8.0.0
Fetching actionmailbox 8.0.0
Fetching actiontext 8.0.0
Installing actionmailer 8.0.0
Installing railties 8.0.0
Installing actionmailbox 8.0.0
Installing actiontext 8.0.0
Fetching rails 8.0.0
Installing rails 8.0.0
Bundle complete! 1 Gemfile dependency, 61 gems now installed.
Bundled gems are installed into `./vendor/bundle`
Post-install message from i18n:
PSA: I18n will be dropping support for Ruby < 3.2 in the next major release (April 2025), due to Ruby's end of life for 3.1 and below (https://endoflife.date/ruby). Please upgrade to Ruby 3.2 or newer by April 2025 to continue using future versions of this gem.

Railsプロジェクトの作成

bundle exec rails new .を実行してRailsプロジェクトを作成します。

既に存在するGemfile.gitignoreに対して書き換えて良いか聞かれるので、Yを応答(デフォルトなのでそのままEnterキー押下)します。

vscode ➜ /workspaces/project1 $ bundle exec rails new .
       exist  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
    conflict  Gemfile
Overwrite /workspaces/project1/Gemfile? (enter "h" for help) [Ynaqdhm] 
実行ログ全量
vscode ➜ /workspaces/project1 $ bundle exec rails new .
       exist  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
    conflict  Gemfile
Overwrite /workspaces/project1/Gemfile? (enter "h" for help) [Ynaqdhm] 
       force  Gemfile
         run  git init -b main from "."
Initialized empty Git repository in /workspaces/project1/.git/
      create  app
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/views/pwa/manifest.json.erb
      create  app/views/pwa/service-worker.js
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/brakeman
      create  bin/dev
      create  bin/rails
      create  bin/rake
      create  bin/rubocop
      create  bin/setup
      create  bin/thrust
      create  Dockerfile
      create  .dockerignore
      create  bin/docker-entrypoint
      create  .rubocop.yml
      create  .github/workflows
      create  .github/workflows/ci.yml
      create  .github/dependabot.yml
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/storage.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_8_0.rb
      create  config/locales
      create  config/locales/en.yml
/usr/local/lib/ruby/3.3.0/json/common.rb:3: warning: ostruct was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.5.0.
You can add ostruct to your Gemfile or gemspec to silence this warning.
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  log
      create  log/.keep
      create  public
      create  public/400.html
      create  public/404.html
      create  public/406-unsupported-browser.html
      create  public/422.html
      create  public/500.html
      create  public/icon.png
      create  public/icon.svg
      create  public/robots.txt
      create  script
      create  script/.keep
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
       exist  vendor
      create  vendor/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  test/system
      create  test/system/.keep
      create  test/application_system_test_case.rb
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_8_0.rb
         run  bundle install --quiet
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /workspaces/project1/Gemfile.lock
         run  bundle binstubs bundler
       rails  importmap:install
       apply  /workspaces/project1/vendor/bundle/ruby/3.3.0/gems/importmap-rails-2.0.3/lib/install/install.rb
  Add Importmap include tags in application layout
      insert    app/views/layouts/application.html.erb
  Create application.js module as entrypoint
      create    app/javascript/application.js
  Use vendor/javascript for downloaded pins
      create    vendor/javascript
      create    vendor/javascript/.keep
  Configure importmap paths in config/importmap.rb
      create    config/importmap.rb
  Copying binstub
      create    bin/importmap
         run  bundle install --quiet
       rails  turbo:install stimulus:install
       apply  /workspaces/project1/vendor/bundle/ruby/3.3.0/gems/turbo-rails-2.0.11/lib/install/turbo_with_importmap.rb
  Import Turbo
      append    app/javascript/application.js
  Pin Turbo
      append    config/importmap.rb
         run  bundle install --quiet
       apply  /workspaces/project1/vendor/bundle/ruby/3.3.0/gems/stimulus-rails-1.3.4/lib/install/stimulus_with_importmap.rb
  Create controllers directory
      create    app/javascript/controllers
      create    app/javascript/controllers/index.js
      create    app/javascript/controllers/application.js
      create    app/javascript/controllers/hello_controller.js
  Import Stimulus controllers
      append    app/javascript/application.js
  Pin Stimulus
  Appending: pin "@hotwired/stimulus", to: "stimulus.min.js"
      append    config/importmap.rb
  Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
      append    config/importmap.rb
  Pin all controllers
  Appending: pin_all_from "app/javascript/controllers", under: "controllers"
      append    config/importmap.rb
         run  bundle install --quiet
         run  bundle binstubs kamal
         run  bundle exec kamal init
Created configuration file in config/deploy.yml
Created .kamal/secrets file
Created sample hooks in .kamal/hooks
       force  .kamal/secrets
       force  config/deploy.yml
       rails  solid_cache:install solid_queue:install solid_cable:install
      create  config/cache.yml
      create  db/cache_schema.rb
        gsub  config/environments/production.rb
      create  config/queue.yml
      create  config/recurring.yml
      create  db/queue_schema.rb
      create  bin/jobs
        gsub  config/environments/production.rb
      create  db/cable_schema.rb
       force  config/cable.yml

.gitignoreの修正

.gitignore/vendorの定義を追加します。

+ # Ignore bundler files.
+ /vendor
情報
.gitignoreの整備が終わったところで、gitにinitial commitしておくと良いでしょう。