RubyスクリプトをDockerイメージ化する

はじめに

ActiveRecordの簡単な検証をしようと思っていたのですが、色々と環境を作るのが面倒臭いなと思いDockerでやってみました。いくつかハマりポイントがあったので、備忘録的に記しておきます。

参考

以下のQiitaの記事を参考にしました。この記事ではMySQLで記されていますが、私の趣味としてPostgreSQLにて実装しています。

qiita.com

Dockerで環境を作る

Ruby環境とPostgreSQL環境をDockerで用意するため、以下のdocker-compose.ymlファイルを用意します。

version: "3.8"
services:
  db:
    image: postgres:13.4-alpine
    ports:
      - 5432:5432
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: pgpass
      POSTGRES_USER: pguser

  app:
    image: ruby:3.0.2
    stdin_open: true
    tty: true
    depends_on:
      - db
    volumes:
      - .:/app
      - bundle:/usr/local/bundle
    environment:
      DB_USER: pguser
      DB_PASS: pgpass
      DB_HOST: db
    working_dir: /app

volumes:
  db-data:
  bundle:

後は上記のQiitaの記事にあるようにRakefileや各種コマンドを作ります。作っているときの実行はdocker-compose run --rm app bashRuby環境に入って逐次実行する形です。

Dockerfileを作る

ある程度プログラムができたらDockerfileを作ってDockerイメージを作成できるようにします。こんな感じ。

FROM ruby:3.0.2-slim

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

WORKDIR /app
COPY Gemfile .
# 使っているGemのバージョン固定のためにlockファイルもbundle installの前にコピー
COPY Gemfile.lock .
RUN bundle install
# RDBMSに対してデータベースを作成したりテーブルを作成したりする
COPY scripts/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
COPY . .

ENTRYPOINT [ "entrypoint.sh" ]

CMD ["ruby", "scripts/aggregate_todo_count_by_date.rb"]

上記Dockerfile上で作っているentrypoint.shrake db:createrake db:migrateを実行するためのもの。中身は以下のような単純なシェルスクリプトです。

#!/bin/bash

set -e

rake db:create
rake db:migrate

exec "$@"

イメージの作成と実行

Dockerfileを作ったら、

$ docker build -t イメージ名 .

でイメージを作ります。

動作確認には、動作確認用のdocker-compose.ymlを別ディレクトリに作ります。以下のようなものを作りました。

version: "3.8"
services:
  db:
    image: postgres:13.4-alpine
    ports:
      - 5432:5432
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: pgpass
      POSTGRES_USER: pguser

  app:
    image: イメージ名
    depends_on:
      - db
    environment:
      DB_USER: pguser
      DB_PASS: pgpass
      DB_HOST: db
    working_dir: /app

volumes:
  db-data:

後はdocker-compose upで動かしてあげればOK。

なお、このとき私が書いたRubyプログラムにはputsで進捗を表示するようにしていたのですが...

1_000.times do |i|
  puts "Create TodoItem #{i}"
  # 省略
end

ログを見ても進捗が表示されませんでした。

これは、$stdoutで出力される内容がバッファリングされているためで、同期的に出力する(バッファリングをやめる)にはプログラムの先頭の方で$stdout.sync = trueと書いてあげればOKでした。

docs.ruby-lang.org

ソース

この記事を執筆している時点でのソースコードはこちら。

github.com