dockerで立ち上げたRailsのキャッシュストアをRedisに設定する


docker-composeを用いてRailsサーバをAPIとして立ち上げ,
Reactで書いたフロントからAPIを叩く構成でアプリケーションを開発しています.

API側の負荷を抑えるため, クエリ結果をキャッシュするためにRedisを導入した時のメモになります.
Docker, docker-composeを用いた環境構築やRedisの概要には触れていませんのでご了承ください.

開発環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.1
BuildVersion:   19B88
$ docker -v
Docker version 19.03.1, build 74b1e89
$ docker-compose -v
docker-compose version 1.24.1, build 4667896b

Redis導入前

Dockerfile
FROM ruby:2.6.5-slim

# db, js関係の環境をインストール
RUN apt-get update -qq && apt-get install -y mariadb-client libmariadb-dev-compat build-essential apt-transport-https curl imagemagick && \
    curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
    apt-get install -y nodejs && \
    curl -sS http://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

# Rails (bundle install)
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp


docker-compose.yml
version: "3"
services:
  web:
    build: .
    command: /bin/sh -c "rm -f /myapp/tmp/pids/server.pid && bundle exec rails s -p '3001' -b '0.0.0.0'"
    environment:
      - DATABASE=myapp_development
      - DATABASE_USER=root
      - DATABASE_PASSWORD=password
      - DATABASE_HOST=db
    volumes:
      - .:/myapp
    ports:
      - 3001:3001
    depends_on:
      - db

  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    environment:
      - MYSQL_DATABASE=myapp_development
      - MYSQL_ROOT_USER=root
      - MYSQL_ROOT_PASSWORD=password
    volumes:
      - mysql_vol:/var/lib/mysql
    ports:
      - 3306:3306

  volumes:
    mysql_vol:


config/environments/development.rb
Rails.application.configure do
  # 略
  # Enable/disable caching. By default caching is disabled.
  # Run rails dev:cache to toggle caching.
  if Rails.root.join('tmp', 'caching-dev.txt').exist?
    config.action_controller.perform_caching = true
    config.cache_store = :memory_store
    config.public_file_server.headers = {
      'Cache-Control' => "public, max-age=#{2.days.to_i}"
    }
  else
    config.action_controller.perform_caching = false
    config.cache_store = :null_store
  end
  # 略
end

Redisの導入

1. キャッシュの有効化

先ほど載せたDockerfileを除く2つのファイルを編集する
...前に, 先にやっておくことがあります!!

config/environments/development.rb のコメントにあるように,
Railsのdevelopment環境ではデフォルトでキャッシュが無効になっているのです.
これに気付かず, かなりハマってました...😅

Enable/disable caching. By default caching is disabled.
Run rails dev:cache to toggle caching.

ということで, 書いてあるコマンドをそのまま実行します.

$ docker-compose run web rails dev:cache
Starting myapp_db_1    ... done
Development mode is now being cached.


2. Redisコンテナの作成

docker-composeで起動させるコンテナにRedisを追加します.

docker-compose.yml
version: "3"
services:
  web:
  # 略
    depends_on:
      - db
+     - redis

  db:
  # 略
+ redis:
+   image: redis
+   ports:
+     - 6379:6379
+   volumes:
+     - "./app/redis:/data"

volumes:
  mysql_vol:


3. キャッシュストアの設定

Gemfileに必要なgemを追加後, ビルドしてbundle installを走らせます.

Gemfile
#+ gem 'redis'
+ gem 'redis-rails'
$ docker-compose build

Railsでのキャッシュ先を, 先ほど追加したRedisコンテナに指定します.
後ほどの動作確認のため, 保存期限は短めに設定しておきます.

config/environments/development.rb
Rails.application.configure do
  # 略
  if Rails.root.join('tmp', 'caching-dev.txt').exist?
    config.action_controller.perform_caching = true
-   config.cache_store = :memory_store
+   config.cache_store = :redis_store, 'redis://redis:6379/0', { expires_in: 1.minute }
    config.public_file_server.headers = {
      'Cache-Control' => "public, max-age=#{2.days.to_i}"
    }
  else
    config.action_controller.perform_caching = false
    config.cache_store = :null_store
  end
  # 略
end

設定は以上で完了です 🎉


4. 動作確認

Redisコンテナ

まずはコンテナを再起動させて, Redisコンテナが正常に動作していることを確認しましょう.
コンテナ内にアタッチしてredis-cliを起動後, 以下のようにRedisのコマンドが実行できればokです.

$ docker-compose up -d
Creating myapp_db_1    ... done
Creating myapp_redis_1 ... done
Creating myapp_web_1   ... done
$ docker exec -it myapp_redis_1 /bin/bash
root@05aaca0b40db:/data# redis-cli
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name 'hoge'
OK
127.0.0.1:6379> get name
"hoge"
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> keys *
(empty list or set)

Redisコマンド一覧はこちら

Rails.cacheを使ってみる

いよいよRailsでキャッシュを使ってみましょう.
ここでは以下のコマンドを使用します.

  • Rails.cache.write(key, value) : キャッシュデータを保存
  • Rails.cache.exist?(key) : key を持つキャッシュデータがあるか確認
config/routes.rb
Rails.application.routes.draw do
  get 'hoge/:id', to: 'redis_samples#hoge'
end
app/controllers/redis_samples_controller.rb
class RedisSamplesController < ApplicationController
  def hoge
    Rails.cache.write('name', 'hogehoge') if params[:id].to_i > 0
    render json: Rails.cache.exist?('name')
  end
end


コンソールからAPIを叩きます.
最初はパラメータに id = 1 を与えてキャッシュデータを保存します.
もちろんtrueが返ってきます.

$ curl localhost:3001/hoge/1
true


実際にRedisコンテナからキャッシュストアを見てみると,
先ほど保存したキャッシュデータが保存されていることが確認できます!
また, ttlコマンドでキャッシュデータの有効期限の残りを秒単位で取得できます.

$ docker exec -it myapp_redis_1 /bin/bash
root@05aaca0b40db:/data# redis-cli
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"\x04\bo: ActiveSupport::Cache::Entry\t:\x0b@valueI\..."
127.0.0.1:6379> ttl name
(integer) 49

指定した有効期限を経過した後に実行すると,
きちんとキャッシュデータが削除されていることも確認できます.

127.0.0.1:6379> keys *
(empty list or set)


最後に削除されたことをRails側からも確認しましょう.
パラメータを id = 0 としてAPIを叩くとfalseが返ってきます.

$ curl localhost:3001/katagamis/hoge/0
false

終わりに

さくっとできるかと思いきや結構手間取ってしまい, 忘れないうちにメモとして残しておきました.
まるっとチュートリアル的な記事ではないので, 開発進めてく途中の参考程度になれば幸いです.
ご意見・質問などあれば遠慮なく!!

参考記事