訳あって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イメージを使ってrbenv
でruby
を入れるというのも考えましたが、
コンテナイメージの作成しやすさを考慮して以下のような形にしました。
先程ダウンロードしたファイルも含めて、ファイル構成はこのような感じです。
.
├── 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
それぞれのファイルは以下です。
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
Oracle
とMySQL
のコンテナも合わせて作成したかったのでdocker-composeを使用
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
FROM wnameless/oracle-xe-11g
# docker-entrypoint-initdb.dとしてファイルを作成するとコンテナ作成時にSQLを実行してくれる
ADD init.sql /docker-entrypoint-initdb.d/
-- 表領域作成
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
を使います。
# 下記gemを追加
gem 'activerecord-oracle_enhanced-adapter'
gem 'ruby-oci8', require: 'oci8'
gem 'switch_point'
gemを読み込みます。
bundle install
DB情報を記述します。
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の設定を追加します。
SwitchPoint.configure do |config|
config.define_switch_point :oracle, writable: :"#{Rails.env}_oracle"
end
それぞれのmodelを作成します。
class MysqlTable < ApplicationRecord
end
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
の話が出てくるので、それを使うべきかなと思っています。