こんにちは。Enjoy IT Life管理人の仁科(@nishina555)です。
前回、『環境構築からデータ作成まで!Rails x MySQLのDocker Compose環境構築手順』の記事でRuby on RailsとMySQLを組み合わせたDocker環境をDocker Composeで構築する手順について紹介しました。
上記の記事ではrootユーザーでデータベースに接続をしました。
Ruby on Railsの開発をするにあたり、データベースは開発環境(development)に加え、テスト環境(test)にも用意するのが一般的です。
開発環境で作成されたデータは開発環境のデータベース、テストコードで作成されたデータはテスト環境のデータベースに保存されます。
テスト環境のデータベースに接続できないとテストコードを実行することができないので、不完全な開発環境といっても言っても過言ではありません。
rootユーザーであれば開発環境およびテスト環境のデータベースへのアクセス権限はデフォルトで用意されています。
しかし、一般ユーザーでDocker環境に構築された複数のデータベースに接続をする場合は少し工夫が必要です。
Ruby on Railsでの開発に限らず、一般ユーザーでDocker環境に存在する複数のデータベースに接続したいというケースは多いと思います。
そこで今回は、Ruby on Railsの開発環境およびテスト環境のデータベースに接続する手順を具体例にとり、Docker上の複数のデータベースに一般ユーザーで接続するための環境構築手順について紹介をしたいと思います。
以下のような疑問を持たれている方の参考になればと思います。
- Rails x MySQLのDocker環境を構築したんだけど、テスト環境のデータベースにうまくアクセスできない
- Doceker環境のデータベースに接続しようとすると『Access denied for user…』って言われるんだけど、原因かイマイチわからない
目次
Railsアプリの全体像
今回サンプルとして紹介するアプリ名はrails_app、利用するデータベースはMySQLMySQLのDocker起動時に、MYSQL_DATABASEで定義されたデータベースの作成と、MYSQL_USERにMYSQL_DATABASEへのアクセス権限付与が行われます。とします。
今回登場するデータベースは以下の通りです。
登場するデータベース
環境 | データベース名 |
開発環境 | rails_app_development |
テスト環境 | rails_app_test |
データベース接続設定は以下の通りです。
データベース接続設定
変数 | 値 |
user | webapp |
password | webapp_password |
port | 3306 |
root_password | root |
利用するDockerのベースイメージは以下の通りです。
Dockerのベースイメージ
コンテナ名 | イメージ |
webコンテナ | ruby:2.6 |
dbコンテナ | MySQL:5.7 |
なお、記事中の『Railsアプリ』という言葉は『webコンテナとdbコンテナをあわせたWebシステム全体のこと』と定義します。
Railsアプリの準備
一般ユーザーでデータベースに接続するための設定方法を紹介する前に、サンプルとなるRailsアプリおよびDockerの設定ファイルを作成をします。
イメージでいうと以下のようなDocker環境を構築します。
『Ruby on Railsの雛形の作成と必要ファイルの準備』の項目で紹介している手順は『環境構築からデータ作成まで!Rails x MySQLのDocker Compose環境構築手順』の記事と同じです。
すでに上記の記事を読まれている方は『一般ユーザーでのDB接続設定を追加する』から読みはじめていただければと思います。
Ruby on Railsの雛形の作成と必要ファイルの準備
まずは作業ディレクトリの作成・移動をします。
$ mkdir rails_app && cd rails_app
Docker環境のrubyを利用してローカル環境にGemfileを用意します。
$ docker run --rm -v `pwd`:/rails_app -w /rails_app ruby:2.6 bundle init
作成されたGemfileの『gem “rails”』の部分のコメントアウトを外します。
Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
- # gem "rails"
+ gem "rails"
次にDockerfileを用意します。
Dockerfile
# ruby:2.6のDockerイメージをベースとして利用
FROM ruby:2.6
# 作業ディレクトリを/rails_appに指定
WORKDIR /rails_app
# ローカルのGemfileをDokcerにコピー
COPY Gemfile /rails_app/Gemfile
# /rails_appディレクトリ上でbundle install
RUN bundle install
上記のDockerfileをビルドしてDockerイメージを作成します。
$ docker build -t rails_app .
これでrailsが実行できるDockerイメージが作成できたので、rails new
でRailsアプリの雛形を作成します。
$ docker run --rm -v `pwd`:/rails_app rails_app rails new . -B --database=mysql
ここまででRuby on Railsの雛形を作成することができました。
Dockerファイルを以下のように編集し、Ruby on Railsが起動するDockerイメージの準備をしておきましょう。
Dockerfile
FROM ruby:2.6
+ RUN apt-get update -qq && apt-get install -y nodejs
WORKDIR /rails_app
COPY Gemfile /rails_app/Gemfile
RUN bundle install
+ CMD ["rails", "server", "-b", "0.0.0.0"]
一般ユーザーでのDB接続設定を追加する
Ruby on Rails側のデータベース接続設定を行います。
config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
- username: root
+ username: webapp
- password:
+ password: webapp_pass
- host: localhost
+ host: db
次にdocker-compose.ymlを作成します。
一般ユーザーでデータベースに接続をする場合、MySQLのDockerイメージに対して以下の変数を定義する必要があります。
変数 | 内容 |
MYSQL_USER | ユーザー名 |
MYSQL_PASSWORD | ユーザーパスワード |
MYSQL_DATABASE | データベース名 |
MYSQL_ROOT_PASSWORD | rootパスワード |
MySQLのDocker起動時に、MYSQL_DATABASEで定義されたデータベースの作成と、MYSQL_USERにMYSQL_DATABASEへのアクセス権限付与が行われます。
一般ユーザーでデータベースに接続する場合、rootパスワードは利用しませんが、MYSQL_ROOT_PASSWORDの設定は必須ですので忘れずに定義しておきましょう。
MySQLのDockerイメージに関する環境変数の詳細についてはDockerHub『MySQL: Environment Variables』を確認してください。
MySQLイメージの環境変数およびwebコンテナの起動オプションをまとめるとdocker-compose.ymlは以下のようになります。
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
depends_on:
- db
db: # Railsのhostと一致させる
image: mysql:5.7
environment:
MYSQL_USER: 'webapp'
MYSQL_PASSWORD: 'webapp_pass'
MYSQL_DATABASE: 'rails_app_development'
MYSQL_ROOT_PASSWORD: 'pass'
Railsアプリの挙動確認
ここまででデータベース続設定ができたのでDockerコンテナを起動させてみましょう。
$ docker-compose up
MYSQL_DATABASEで定義されたデータベースが自動で作成されるため、webコンテナ立ち上げ後、localhost:3000
にアクセスすると以下のような画面が表示されるはずです。
データベース一覧を見ると確かにrails_app_developmentが存在していることがわかります。
$ docker-compose exec db mysql -uroot -ppass
mysql> show databases;
+-----------------------+
| Database |
+-----------------------+
| information_schema |
| mysql |
| performance_schema |
| rails_app_development |
| sys |
+-----------------------+
5 rows in set (0.01 sec)
また、一般ユーザーで接続することもできます。
$ docker-compose exec db mysql -uwebapp -pwebapp_pass -D rails_app_development
しかし、テスト環境用のデータベースを作成しようとすると以下のように『Access denied for user ‘webapp’@’%’ to database ‘rails_app_test’』といったエラーが表示されます。
$ docker-compose exec web rake db:create RAILS_ENV=test
Mysql2::Error::ConnectionError: Access denied for user 'webapp'@'%' to database 'rails_app_test': CREATE DATABASE `rails_app_test` DEFAULT CHARACTER SET `utf8`
Couldn't create 'rails_app_test' database. Please check your configuration.
上記のエラーは、ユーザー(webapp)にテスト環境のデータベース(rails_app_test)のアクセス権限がないことが原因です。
開発環境と同様にテスト環境のデータベースもMYSQL_DATABASEで定義することでアクセス権限が付与されるようにしたいのですが、1コンテナにつきMYSQL_DATABASEは1つしか定義できません。
一般ユーザーで接続させる設定
ではここから今回の本題である、一般ユーザーで複数台のデータベースに接続できるDocker環境の構築方法について紹介します。
今回はRailsアプリの開発環境のデータベースであるrails_app_developmentとテスト環境のデータベースであるrails_app_testの両データベースに一般ユーザーのwebappが接続できることをゴールとします。
今回は2つの方法について紹介します。
- MySQLイメージ起動時にgrant文を実行する
- データベースごとにdbコンテナを用意する
以下ではそれぞれの方法について詳しく紹介します。
MySQLイメージ起動時にgrant文を実行する
MySQLのDockerイメージでは/docker-entrypoint-initdb.d
にスクリプト(.sql
、.sh
、.sql.gz
)を配置しておくとDocker起動時に実行してくれるという機能があります。1
この機能を活用し、grant文を実行するSQLを用意することでDockerが起動したらアクセス権限が付与されるようにします。
図で表現すると以下のようになります。
$ mkdir docker-entrypoint-initdb.d && cd $_
$ vim 00_grant.sql
rails_app_testに対するアクセス権限をwebappに付与するSQLは以下のようになります。
docker-entrypoint-initdb.d/00_grant.sql
GRANT ALL ON `rails_app_test`.* TO webapp@'%';
ローカル環境に作成したSQLをDocker環境に配置させるため、ローカル環境とDocker環境のディレクトリのマウントをします。
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
depends_on:
- db
db:
image: mysql:5.7
environment:
MYSQL_USER: 'webapp'
MYSQL_PASSWORD: 'webapp_pass'
MYSQL_DATABASE: 'rails_app_development'
MYSQL_ROOT_PASSWORD: 'pass'
+ volumes:
+ - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
/docker-entrypoint-initdb.d
配下のスクリプトはMySQLイメージからDockerコンテナを作成するときに実行されるため、コンテナを一旦破棄(down)してから起動(up)します。
$ docker-compose down db
$ docker-compose up db
改めて、先ほど失敗したテスト環境のデータベース作成を実行してみましょう。
SQLでテスト環境のデータベース(rails_app_test)に対してアクセス権限を付与したので、今回は問題なく作成できました。
$ docker-compose exec web rake db:create RAILS_ENV=test
Created database 'rails_app_test'
mysqlコマンドも問題なく実行できていますし、データベースも問題なく作成されています。
$ docker-compose exec db mysql -uwebapp -pwebapp_pass -D rails_app_test
mysql> show databases;
+-----------------------+
| Database |
+-----------------------+
| information_schema |
| rails_app_development |
| rails_app_test |
+-----------------------+
3 rows in set (0.00 sec)
rails console
も開発環境(development)およびテスト環境(test)に問題なく接続できています。
docker-compose exec web rails c
irb(main):001:0> ENV['RAILS_ENV']
=> "development"
$ docker-compose exec web rails c -e test
irb(main):003:0> ENV['RAILS_ENV']
=> "test"
データベースごとにコンテナを用意する
MYSQL_DATABASEは1コンテナにつき1つしか定義できません。
そこでMYSQL_DATABASEで定義する必要のあるデータベースの数だけコンテナを作成するというアプローチを紹介します。
今回はテスト環境のデータベースを配置するコンテナ名を『test-db』とします。
図で表現すると以下のようになります。
docker-compose.ymlを以下のように変更します。
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
depends_on:
- db
+ - test-db
db:
image: mysql:5.7
environment:
MYSQL_USER: 'webapp'
MYSQL_PASSWORD: 'webapp_pass'
MYSQL_DATABASE: 'rails_app_development'
MYSQL_ROOT_PASSWORD: 'pass'
+ test-db:
+ image: mysql:5.7
+ environment:
+ MYSQL_USER: 'webapp'
+ MYSQL_PASSWORD: 'webapp_pass'
+ MYSQL_DATABASE: 'rails_app_test'
+ MYSQL_ROOT_PASSWORD: 'pass'
またRuby on Railsのテスト環境(test)のデータベース接続設定を以下のように変更します。
config/database.ymlを変更します。
config/database.yml
test:
<<: *default
database: rails_app_test
+ host: test-db
設定が完了したらDockerを起動して確認をしてみます。
$ docker-compose up
dbコンテナにはrails_app_development、test-dbコンテナではrails_app_testに接続できることがわかります。
$ docker-compose exec test-db mysql -uwebapp -pwebapp_pass -D rails_app_test
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| rails_app_test |
+--------------------+
$ docker-compose exec db mysql -uwebapp -pwebapp_pass -D rails_app_development
mysql> show databases;
+-----------------------+
| Database |
+-----------------------+
| information_schema |
| rails_app_development |
+-----------------------+
2 rows in set (0.00 sec)
rails console
も開発環境(development)およびテスト環境(test)それぞれに問題なく接続できています。
docker-compose exec web rails c
irb(main):001:0> ENV['RAILS_ENV']
=> "development"
$ docker-compose exec web rails c -e test
irb(main):003:0> ENV['RAILS_ENV']
=> "test"
まとめ
以上でDocker環境で複数のデータベースに対して一般ユーザーで接続するための方法の紹介を終わります。
今回は『GRANT文をスクリプトで実行させる』『コンテナを複数用意する』という2つの方法を紹介したので、ぜひ参考にしていただければと思います。
この記事がいいなと思いましたらツイッター(@nishina555)のフォローもよろしくお願いします!