はじめに
ActiveRecordの簡単な検証をしようと思っていたのですが、色々と環境を作るのが面倒臭いなと思いDockerでやってみました。いくつかハマりポイントがあったので、備忘録的に記しておきます。
参考
以下のQiitaの記事を参考にしました。この記事ではMySQLで記されていますが、私の趣味としてPostgreSQLにて実装しています。
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 bash
でRuby環境に入って逐次実行する形です。
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.sh
はrake db:create
やrake 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でした。
ソース
この記事を執筆している時点でのソースコードはこちら。