LoginSignup
3
6

More than 3 years have passed since last update.

RailsでMySQLとOracleの両方のDBを使う環境を構築

Posted at

訳あって2種類のDBを使う必要が出てきたので、その環境構築のメモ。
※まだ本番稼働まで進んでいないので、加筆修正の可能性ありです。

以下の環境で対応しました。

  • 既存のDBはOracle
  • 新規に用意するDBはMySQL
  • ローカル開発環境はDocker

環境用意

Railsで組みたかったのでrubyのDockerイメージを使いました。

Oracleは11系なので
https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html
から11.2.0.4の以下の3ファイルをダウンロード
※要Oracleアカウント
oracle-instantclient11.2-basic-11.2.0.4.0-1.x86_64.rpm
oracle-instantclient11.2-devel-11.2.0.4.0-1.x86_64.rpm
oracle-instantclient11.2-sqlplus-11.2.0.4.0-1.x86_64.rpm

rpmファイルではなくzipファイルにして、Dockerイメージもalpineにしたかったのですが、
alpineでoracle_clientを使う方法が大変だったため、一旦断念。
CentOSのDockerイメージを使ってrbenvrubyを入れるというのも考えましたが、
コンテナイメージの作成しやすさを考慮して以下のような形にしました。

先程ダウンロードしたファイルも含めて、ファイル構成はこのような感じです。

ファイル構成
.
├── Dockerfile
├── docker-compose.yml
└── docker/
     └── oralce/
        ├── Dockerfile
        ├── init.sql
        ├── oracle-instantclient11.2-basic-11.2.0.4.0-1.x86_64.rpm
        ├── oracle-instantclient11.2-devel-11.2.0.4.0-1.x86_64.rpm
        └── oracle-instantclient11.2-sqlplus-11.2.0.4.0-1.x86_64.rpm

それぞれのファイルは以下です。

Dockerfile
FROM ruby:2.6.3-slim-buster

ENV LANG C.UTF-8

RUN mkdir -p /app

ADD . /app
WORKDIR /app

RUN apt-get -y update && apt-get -y upgrade

# MySQL操作する用
RUN apt-get -y install mariadb-server libmariadb-dev

# Debian系でrpmファイルを使えるようにするライブラリ
RUN apt-get -y install alien

# Oracle設定
RUN alien -i docker/oracle/oracle-instantclient11.2-basic-11.2.0.4.0-1.x86_64.rpm && \
    alien -i docker/oracle/oracle-instantclient11.2-devel-11.2.0.4.0-1.x86_64.rpm && \
    alien -i docker/oracle/oracle-instantclient11.2-sqlplus-11.2.0.4.0-1.x86_64.rpm
ENV LD_LIBRARY_PATH /usr/lib/oracle/11.2/client64/lib
ENV NLS_LANG Japanese_Japan.AL32UTF8
ENV PATH $PATH:/usr/lib/oracle/11.2/client64/bin

OracleMySQLのコンテナも合わせて作成したかったのでdocker-composeを使用

docker-compose.yml
version: "2"

services:
  mysql:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "3306:3306"
    environment:
      - MYSQL_DATABASE=dbname_development
      - MYSQL_ROOT_PASSWORD=password

  oracle:
    build: docker/oracle # 作成時にDB作成など実行したかったのでoracle用のDockerfileを用意
    ports:
      - "1521:1521"
    volumes:
      - .:/tmp/data/

  app:
    build: .
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    links:
      - mysql
      - oracle
    environment: # MySQLとOracleの両方の接続先の情報を環境変数で追加
      - DATABASE_USERNAME=root
      - DATABASE_PASSWORD=password
      - DATABASE_NAME=dbname_development
      - DATABASE_HOST=mysql
      - DATABASE_ORACLE_USERNAME=oralceuser
      - DATABASE_ORACLE_PASSWORD=oraclepassword
      - DATABASE_ORACLE_NAME=oracle/xe
    tty: true
docker/oracle/Dockerfile
FROM wnameless/oracle-xe-11g

# docker-entrypoint-initdb.dとしてファイルを作成するとコンテナ作成時にSQLを実行してくれる
ADD init.sql /docker-entrypoint-initdb.d/
docker/oralce/init.sql
-- 表領域作成
CREATE TABLESPACE indx_oracledb DATAFILE '/tmp/indx_oracledb.dbf' SIZE 1G SEGMENT SPACE MANAGEMENT AUTO;

-- 作成した表領域を参照できるユーザー作成
CREATE USER oracleuser IDENTIFIED BY oraclepassword DEFAULT TABLESPACE indx_oracledb;

-- 作成したユーザーに権限を付与
GRANT connect,dba TO oracleuser;

コンテナ起動

docker-compose up -d

でコンテナを起動して

docker-compose exec app

でコンテナに移動します。

とりあえずRailsがほしいので

gem install rails
bundle exec rails new . -d mysql

Railsアプリを作成します。

両方のDBに接続するためのgemを追加

Oracleに接続する用のgemは
https://github.com/rsim/oracle-enhanced
https://github.com/kubo/ruby-oci8
を使います。

複数DB接続するために
https://github.com/eagletmt/switch_point
を使います。

Gemfile
# 下記gemを追加
gem 'activerecord-oracle_enhanced-adapter'
gem 'ruby-oci8', require: 'oci8'
gem 'switch_point'

gemを読み込みます。

bundle install

DB情報を記述します。

config/database.yml
development:
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DATABASE_USERNAME'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  database: <%= ENV['DATABASE_NAME'] %>
  host: <%= ENV['DATABASE_HOST'] %>

development_oracle:
  adapter: oracle_enhanced
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DATABASE_ORACLE_USERNAME'] %>
  password: <%= ENV['DATABASE_ORACLE_PASSWORD'] %>
  database: <%= ENV['DATABASE_ORACLE_NAME'] %>

switch_pointの設定を追加します。

config/initializers/switch_point.rb
SwitchPoint.configure do |config|
  config.define_switch_point :oracle, writable: :"#{Rails.env}_oracle"
end

それぞれのmodelを作成します。

app/models/mysql_table.rb
class MysqlTable < ApplicationRecord
end
app/models/oracle_table.rb
class OracleTable < ApplicationRecord
  # Oracleを参照するのでswitch_pointの設定を記述
  use_switch_point :oracle
end

それぞれのテーブル作成

MySQLのほうはRailsのmigrationで作成。
※作成までのステップは割愛

Oracleのほうは今回既存DBなので、sqlファイルにエクスポートしたものを使用。
任意の場所にファイルを置いて、sqlplusでエクスポートします。

# コンソールからOracleに接続
sqlplus sys/oracle@oracle/xe as sysdba

# 接続したらエクスポートしたファイルを読み込み
SQL> @dump.sql

それぞれのデータベースにRailsから接続

rails consoleで接続します。

irb(main):001:0> MysqlTable.first
   (11.7ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  MysqlTable Load (0.4ms)  SELECT  `mysql_tables`.* FROM `mysql_tables` ORDER BY `mysql_tables`.`id` ASC LIMIT 1
=> #<MysqlTable id: 1, name: "mysql table">

irb(main):002:0> OracleTable.first
  tables (130.2ms)  SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
  views (14.6ms)  SELECT LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
  synonyms (1006.2ms)  SELECT synonym_name, table_owner, table_name, db_link FROM all_synonyms where owner = SYS_CONTEXT('userenv', 'session_user')
  Sequence (4.5ms)  select us.sequence_name from all_sequences us where us.sequence_owner = 'ORACLEUSER' and us.sequence_name = upper('ORACLE_TABLES_seq')
  Primary Key (200.8ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'ORACLEUSER' AND c.table_name = 'ORACLE_TABLES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
  OracleTable Load (12.2ms)  SELECT * FROM (SELECT  "ORACLE_TABLES".* FROM "ORACLE_TABLES" ORDER BY "ORACLE_TABLES"."ID" ASC ) WHERE ROWNUM <= :a1  [["LIMIT", 1]]
  Column definitions (62.7ms)  SELECT cols.column_name AS name, cols.data_type AS sql_type, cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column, cols.data_type_owner AS sql_type_owner, DECODE(cols.data_type, 'NUMBER', data_precision, 'FLOAT', data_precision, 'VARCHAR2', DECODE(char_used, 'C', char_length, data_length), 'RAW', DECODE(char_used, 'C', char_length, data_length), 'CHAR', DECODE(char_used, 'C', char_length, data_length), NULL) AS limit, DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale, comments.comments as column_comment FROM all_tab_cols cols, all_col_comments comments WHERE cols.owner = 'ORACLEUSER' AND cols.table_name = 'ORACLE_TABLES' AND cols.hidden_column = 'NO' AND cols.owner = comments.owner AND cols.table_name = comments.table_name AND cols.column_name = comments.column_name ORDER BY cols.column_id
=> #<OracleTable id: 1, name: "oracle table">

それぞれのデータベースに接続できました。

今後の課題

Oracleのほうは基本的に定義変更しないデータベースなのでmigrationはほとんどしない予定ですが、
必要になったときにどうするか?
switch_pointを使っている場合はmigrationするのによくridgepoleの話が出てくるので、それを使うべきかなと思っています。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6