Skip to main content

ローカル開発環境向けRuby on Rails Docker環境

著者

Mikhail Tereschenko

blog feature ruby security

2022年11月2日

0 分で読めます

Ruby開発者の皆さん、こんにちは!ローカル開発環境にRuby on RailsのDocker環境を効率よく構築する方法をお探しなら、この記事が役立ちます。この記事は、ローカル開発用にmacOSへRubyをインストールする方法を紹介した前回の記事の続編です。

Ruby on Railsプロジェクトを構築する際、Ruby開発者はデータベースのほか、開発環境に必要な前提条件にも対応する必要があります。しかし、これらをすべてホストOSにインストールし、必要なバージョンを正確にそろえるのは、難しい場合があります。

新しいプロジェクトを作成するたびに、ボタンひとつで新しくクリーンな環境を作れたらと思っていました。そこで、Dockerのdocker-compose.ymlを使って簡単に起動・停止できる、Dockerベースの環境を作ることにしました。

Docker Composeとは?

Docker Composeは、プロキシ、データベース、ボリュームマウントなどのアプリケーションやサービスを設定して連携させ、再利用可能な方法でプロビジョニングできるInfrastructure as Code(IaC)ツールです。Dockerソフトウェア一式の一部としてインストールされ、YAMLを使って定義します。

このDocker Composeによるローカル開発環境は、さまざまな種類のプロジェクトに利用でき、Ruby on Railsだけに限定されません。個人開発のプロジェクトやフロントエンドのクライアントサイドプロジェクトにも使えます。自分のホストOSを変更するのではなく、Docker環境を使う主な理由は次のとおりです。

  • 設定が簡単

  • ロジックと環境を簡単に分離できる

  • 複数のプロジェクトで同じ環境を簡単に使える

  • 機密コードを含めずに、サポートが必要な人と環境を簡単に共有できる

  • ホストマシンをクリーンな状態に保てる

Ruby on RailsのDocker環境

ローカル開発用のRuby on Rails Docker環境をセットアップするには、次のものが必要です。

  • Unix系OS(私はmacOSを使っていますが、UbuntuなどのLinuxディストリビューションも利用できます。Windowsでも動作するはずです)

  • Git

  • make(3.81以降)

  • Docker(19.03.12以降)

  • docker-compose(1.26.0以降)

  • お好みのテキストエディター(私はVisual Studio Codeを使っています)

必要なものがそろったら、さっそく始めましょう。記事を読む時間がない方は、必要な手順と、これから確認するすべてのスニペットのソースコードを含むオープンソースリポジトリはこちらです。すでにこの記事を読んでいる方は、手順に沿って進めてください。

まずは、説明を簡単にするため、foo_bar_projectという名前のRuby on Railsプロジェクトを作成します。

1) Docker Compose環境の設定を取得し、Gitリポジトリを削除します。Gitのソースコードリポジトリで変更履歴をすべて管理したい場合は、後から独自のものを作成できます。

git clone git@github.com:mtereschenko/simple_ror_environment.git && cd simple_ror_environment && rm -rf .git

以降の手順は./environmentディレクトリ内で実行します。コマンドをそのディレクトリ内で実行していることを確認しましょう。

cd ./environment

2) 次に、この環境設定にプロジェクト名を割り当てます。デフォルトはtest_projectです。ここでの手順では、先ほど説明したfoo_bar_projectに変更します。そのためには、./environment/.env.exampleファイルを編集し、PROJECT_NAME変数の値を変更してください。変数の値には小文字のスネークケースを使うことが重要です。

PROJECT_NAME=foo_bar_project

3) 次のコマンドを実行して、プロジェクトを作成します。

make init

ご覧のとおり、applicationという新しいフォルダーが作成され、その中にRuby on Railsのコードが入っています。

4) 次に、Docker Composeでローカル開発環境をビルドします。アプリケーションのDockerfileまたはGemfileを変更した場合は、必ずこの手順を実行してください。

make build

5) これで、次のようにRubyのローカル開発環境を起動できます。

make start

ここでは、まだRuby on Railsアプリケーションは起動していません。起動するには、Docker環境のシェルを開き、railsプロセスを開始するよう指示する必要があります。次のように実行します。

make shell
rails s -p 3000 –binding=0.0.0.0

ローカル開発環境向けRuby on Rails Docker最後に、ブラウザーを開いてhttp://foo_bar_project.localhost/にアクセスすると、先ほど作成したRuby on Railsプロジェクトが表示されます。

では、これで何ができるようになったでしょうか?

  • プロジェクトには、実行に必要なものがすべて含まれた環境ディレクトリと、アプリケーションコードが含まれたapplicationディレクトリがあります。このディレクトリはDockerコンテナにマッピングされるため、Ruby Dockerイメージから完全にアクセスできます。

  • Rubyシェルにすばやくアクセスできるため、Rails、Rake、BundlerなどのRubyツールチェーンをローカルにインストールする必要はありません。

  • make shellコマンドを実行した後に、各種コマンドを実行できます。

  • このDocker Composeプロジェクトの設定には、あらかじめ2つのステージが用意されています。Rubyアプリケーションをどこかにデプロイする必要が生じた場合は、独自のステージを作成できます。とても簡単です。

上記のRuby on Rails用Docker環境の仕組みに興味がある方のために、Ruby Dockerイメージ、Makefile、そして全体を連携させるDocker Composeの定義に分けて解説します。

Ruby Dockerイメージ

まず、Ruby Dockerイメージは欠かせない要素です。Dockerイメージレイヤーのキャッシュを効率よく再利用できるように正しくビルドすることも、ローカル開発環境に関するその他の考慮事項と同様に重要です。

以下はRuby DockerイメージのDockerfileです。

FROM ruby:2.7.6-alpine3.16 as base_image
RUN apk add --no-cache git \
    build-base \
    libpq-dev \
    tzdata

FROM base_image as development
COPY ./artifacts/rails/Gemfile /tmp/Gemfile
COPY ./artifacts/rails/Gemfile.lock /tmp/Gemfile.lock
COPY ./containers/ruby/runners/runner.development.sh /rdebug_ide/runner.sh
RUN cd /tmp && \
  gem install ruby-debug-ide && \
  gem install debase && \
  bundle install && \
  chmod +x /rdebug_ide/runner.sh && \
  apk add --no-cache git \
  nodejs \
  yarn
WORKDIR /app

ENTRYPOINT ["tail", "-f", "/dev/null"]

FROM base_image as init
COPY ./containers/ruby/initializers/runner.init.sh /tmp/runner.sh
COPY ./containers/ruby/initializers/database.yml /tmp/database.yml
COPY ./containers/ruby/initializers/.gitignore /tmp/.gitignore
COPY ./containers/ruby/initializers/development.rb /tmp/development.rb
RUN chmod +x /tmp/runner.sh

WORKDIR /app

ENTRYPOINT ["/tmp/runner.sh"]

ご覧のとおり、正常に機能するRuby on Railsアプリケーション環境に必要な、次のような周辺設定を前提としています。

  • Ruby on Railsのデータベース接続情報を設定するdatabase.ymlファイル

  • 開発環境でのRuby on Railsの実行時設定を行うdevelopment.rb

これは、./environment/containers/ruby/initializersディレクトリにあるrunners.shスクリプトを使って実現します。

#!/bin/ash

cd /app

gem install rails

rails new . -d=postgresql --skip-git

yes | cp -rf /tmp/database.yml /app/config/database.yml
yes | cp -rf /tmp/development.rb /app/config/environments/development.rb
cp /tmp/.gitignore /app/.gitignore

このDockerfileではマルチステージDockerも利用しているため、同じ設定をテストやステージングなど異なるワークフローで使う場合に、特定の部分を各環境で効率よく再利用できます。

Rails用Docker Compose

`./environment/docker-compose.development.yml`にあるDocker Composeファイルは、Docker上でRuby on Railsアプリケーションを動かすために、次のサービスを連携させます。

  • nginx HTTPサーバー

  • Rubyアプリケーション

  • PostgreSQLデータベースサーバー

この設定で使用するRails用Docker Composeファイルは次のとおりです。

version: '3.7'
services:
  nginx:
    image: "${PROJECT_NAME}/nginx:development"
    container_name: ${PROJECT_NAME}-nginx
    build: 
      context: ./
      dockerfile: ./containers/nginx/Dockerfile
    depends_on:
      - ruby
    tty: true
    ports:
      - 80:80
    volumes:
      - ./`artifacts`/nginx/:/`var`/log/nginx:cached
   ruby:
    image: "${PROJECT_NAME}/ruby:development"
    container_name: ${PROJECT_NAME}-ruby
    depends_on:
      - postgres
    build:
      context: ./
      dockerfile: ./containers/ruby/Dockerfile
      target: development
    ports:
      - 13030:13030
    volumes:
      - ${APP_PATH}:/app:cached
    environment:
      DB_NAME: ${DB_NAME}
      PROJECT_NAME: ${PROJECT_NAME}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_PORT: ${DB_PORT}
      PUMA_WORKERS: 0
      RAILS_MAX_THREADS: 1
     postgres:
    image: "${PROJECT_NAME}/postgres:development"
    container_name: ${PROJECT_NAME}-postgres
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    build:
      context: ./
      dockerfile: ./containers/postgres/Dockerfile
    ports:
      - ${DB_PORT}:5432
    volumes:
      - postgres_volume:/var/lib/postgresql/data

volumes:
  postgres_volume:

Docker用Makefile

最後に、開発者がこのRubyのローカル開発環境を簡単に操作できるインターフェースを用意するため、Makefileを使用します。

.PHONY: help
# Make stuff

-include .env

export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1

.DEFAULT_GOAL := help

ARTIFACTS_DIRECTORY := "./artifacts"

CURRENT_PATH :=${abspath .}

SHELL_CONTAINER_NAME := $(if $(c),$(c),ruby)
BUILD_TARGET := $(if $(t),$(t),development)

help: ## Help.
@grep -E '^[a-zA-Z-]+:.*?## .*$$' Makefile | awk 'BEGIN {FS = ":.*?## "}; {printf "[32m%-27s[0m %s\n", $$1, $$2}'

init: ## Project installation.
@rm -f ./.env
@cp .env.example .env
@make init_app_directory
@make create_postgress_volume
@docker-compose -f docker-compose.init.yml build
@docker-compose -f docker-compose.init.yml up

build: ## Build images.
@make create_project_artifacts
@cp ${APP_PATH}/Gemfile "${ARTIFACTS_DIRECTORY}/rails/Gemfile"
@cp ${APP_PATH}/Gemfile.lock "${ARTIFACTS_DIRECTORY}/rails/Gemfile.lock"
@docker-compose -f docker-compose.$(BUILD_TARGET).yml build

shell: ## Internal image bash command line.
@if [[ -z `docker ps | grep ${SHELL_CONTAINER_NAME}` ]]; then \
echo "${SHELL_CONTAINER_NAME} is NOT running (make start)."; \
else \
docker-compose -f docker-compose.$(BUILD_TARGET).yml exec $(SHELL_CONTAINER_NAME) /bin/ash; \
fi

start: ## Start previously builded application images.
@make create_project_artifacts
@make start_postgres
@make start_ruby
@make start_nginx

run: ## Run ruby debugger session.
@docker-compose -f docker-compose.$(BUILD_TARGET).yml exec ruby /bin/ash /rdebug_ide/runner.sh

start_ruby: ## Start ruby image.
@if [[ -z `docker ps | grep ruby` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d ruby; \
else \
echo "Ruby is running."; \
fi

start_postgres: ## Start postgres image.
@if [[ -z `docker ps | grep postgres` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d postgres; \
else \
echo "Postgres is running."; \
fi

start_nginx: ## Start nginx image.
@if [[ -z `docker ps | grep nginx` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d nginx; \
else \
echo "Nginx is running."; \
fi

stop: ## Stop all images.
@docker-compose -f docker-compose.$(BUILD_TARGET).yml stop

create_project_artifacts:
mkdir -p ./artifacts/rails
mkdir -p ./artifacts/db

init_app_directory:
@mkdir -p ${APP_PATH}

create_postgress_volume:
@sed -i '' -r  "s/postgres_volume:/${PROJECT_NAME}_db_volume:/g" docker-compose.development.yml

Rubyアプリケーションを効率よく実行する

この記事を通じて、Dockerコンテナの設定を使い、Ruby on RailsなどのRubyアプリケーションをローカル環境で実行するスキルを身につけていただけたら幸いです。ローカルでRubyアプリケーションを動かす方法としては、Ruby仮想環境を使う方法もあります。詳しくは、以前の記事macOSにRubyをインストールする方法をご覧ください。

Rubyの開発環境が整ったら、Rubyアプリケーションのセキュリティ対策についても学んでみませんか。Rubyのセキュリティについては、次の記事をご覧ください。

開発者ファーストのコンテナセキュリティ

Snykは、コンテナイメージとKubernetesワークロードの脆弱性を検出し、自動的に修正します。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

blog feature pypi spoof
Article

オープンソースのメンテナーに、Snyk AI Security Platformをすべて無料で提供

オープンソースのメンテナーは、実際の脆弱性レポートに追われ、優先順位付けや修正、修正版のリリースを迅速に進めるための支援を必要としています。SnykのSecure Developer Programでは、条件を満たすプロジェクトにSnyk AI Security Platformを無料で提供します。

feature insights announcement
Blog

Node-gypサプライチェーン侵害:binding.gypに潜む自己増殖型npmワーム

新たなnpmワームがbinding.gypを悪用し、インストール時にnode-gypを起動。ライフサイクルスクリプトを使わずに悪意あるパッケージからコードを実行します。認証情報を窃取し、GitHub上に潜伏し、メンテナー間で自己増殖します。