DockerでRails+MySQLの環境を構築
概要
基本的にはDockerの公式ドキュメントに則って行いました. Rails用のコンテナとDBサーバー用のコンテナをそれぞれDockerfileから用意し, それらを連携するためにdocker-composeを用いています. データベースとしてはMySQL, WebサーバーとしてはApache等ではなくRails serverを使います.
なぜそう記述するのか, なぜそのコマンドなのか, 等の理由もできるだけ述べていきます. ではやっていきましょう.
作業環境
マシンOSとDockerのバージョンです.
$ sw_vers ProductName: Mac OS X ProductVersion: 10.13.5 BuildVersion: 17F77 $ docker version Client: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:13:02 2018 OS/Arch: darwin/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:22:38 2018 OS/Arch: linux/amd64 Experimental: true
作業ディレクトリは以下のような構成です.
myapp/ ├── docker-compose.yml ├── db/ │ ├── Dockerfile │ ├── conf/ │ └── data/ └── web/ ├── Dockerfile ├── Gemfile └── Gemfile.lock
myapp/web/
にはrailsのファイルが入り,
myapp/web/conf/
にはMySQLの設定ファイル,
myapp/db/data/
にはMySQLのデータが入ります.
ひとつの疑問
ドットインストール等を見てみると, ローカル開発環境の構築にはCentOSが多く使われているように感じます. しかしDockerHubにある公式イメージを見てみると, Debianがベースとなっているものが多い印象です. なぜDockerではDebianが多く使われているのでしょうか. 理由は公式ドキュメント(日本語版)にありました.
私たちは Debian イメージ を推奨します。これは、非常にしっかりと管理されており、ディストリビューションの中でも小さくなるよう(現在は 150 MB 以下に)維持されているからです。
日本語訳されたドキュメントは少し情報が古いので, 英語の公式ドキュメントも見てみましょう.
We recommend the Alpine image as it is tightly controlled and small in size (currently under 5 MB), while still being a full Linux distribution.
こっちではAlpineを推奨していますね. しかしこのブログ記事を書くまで英語のほうは見ていませんでしたので, 本記事ではDebianをベースとして構築しています.
各種ファイル
5つのファイルを自分で用意します.
MySQLコンテナ用のDockerfile
# myapp/db/Dockerfile # ベースとなるイメージを指定 FROM mysql:5.7 # MySQLのルートユーザー用のパスワードを設定 ENV MYSQL_ROOT_PASSWORD=password
データベースのほうはこれだけです. 同じ内容をComposeファイルに書くこともできるので, 必ずしもこのDockerfileは必要ではありません. しかし今回は練習だと思って作りました. もしも好み通りにカスタマイズしたい場合はDockerfileに書くことになるでしょう.
ベースイメージとしてはmysql:5.7
を指定しました. mysql:8
ではうまくいきませんでした.
上でも触れたようにこのイメージはDebianがベースになっています. したがって当然ながらFROM debian
のようにOSのイメージを指定することもできます.
しかしそうすると, DockerfileにMySQLをインストールする命令等も書かなくてはならないので非常に面倒です. 実際mysql:5.7
のもとになっているDockerfileはかなりの行数が書かれていますしね. 素直に出来上がったものを採用するのが賢明でしょう.
ENV
命令ではコンテナ内の環境変数を設定することができます.
セキュリティ上はこんなところにパスワードを書くのはよろしくないんでしょうけど, Railsの練習用なのでよしとしましょう.
Webサーバーコンテナ用のDockerfile
# myapp/web/Dockerfile # Debianがベースのrubyイメージを指定 FROM ruby:2.5.1 # 必要なものをインストール RUN apt-get update -qq && apt-get -y install \ build-essential \ libpq-dev \ nodejs \ mysql-client # rails用のディレクトリを作成 RUN mkdir /myapp # ローカルマシン(Mac)からコンテナの中にファイルをコピー COPY Gemfile /myapp COPY Gemfile.lock /myapp # 作業ディレクトリを指定 WORKDIR /myapp # 上でコピーしたGemfileに従ってGemをインストール RUN gem install bundler && bundle install
こちらは公式ドキュメントを真似して書いています. しかし以下の5点異なるところがあります.
- ひとつめの
RUN
命令では, WebサーバーのコンテナからMySQLにアクセスするために, クライアントコマンドをインストールしています. COPY
命令の第二引数ではディレクトリだけを指定しています. ファイル名を変えるわけではないのでこれでよいでしょう.WORKDIR
命令の位置が異なります. とくにCOPY
命令で使っているわけではないのでこれでOKです.- bundlerをインストールしています. はじめからbundlerが導入されているのか不明だったのでこうしておきました.
- 最後の
COPY
命令がありません. 結局Composeファイル内でボリュームのマウントを指定するので必要ないと思われます.
2つのコンテナをまとめて扱うComposeファイル
# myapp/docker-compose.yml version: '3' services: web: build: ./web ports: - "80:3000" depends_on: - db volumes: - ./web:/myapp command: bundle exec rails s -p 3000 -b '0.0.0.0' db: build: ./db volumes: - ./db/data:/var/lib/mysql - ./db/conf:/etc/mysql/conf.d expose: - "3306"
初めてYAMLを書きました. タブ文字を使ってはいけないというところは注意が必要ですね. コメントで説明すると見づらくなりそうなので, 以下で詳細を述べていきます.
version:
ではComposeファイルのフォーマットのバージョンを指定しています. 2018年6月現在では3.6
が最新ですが, 小数まで指定する必要はなさそうです.services:
によってwebとdbの2つのサービスを定義しています.build:
によって, 各サービスに対応したDockerfileの場所を指定しています.ports:
はポートフォワーディングの指定です. 時刻と認識されないようにダブルクォートが必要です.localhost:3000
と指定するのは面倒なのでwell-knownポートの80を指定しています.depends_on:
により2つのサービスの依存関係を定義しています. これにより, dbが立ち上がった後にwebが立ち上がるようにしてくれます.volumes:
により, ローカルマシンのボリュームをマウントすることができます. これがあるので, Dockerfile内にCOPY . /myapp
命令は必要ないでしょう. ボリュームのマウントについては詳しくこちらにまとめておきました.command:
はコンテナ起動時にコンテナ内で実行するコマンドです. DockerfileにWORKDIR /myapp
と書いたおかげで,/myapp
でこのコマンドを実行してくれます.rails s
というコマンドはRails serverを起動するものです. デフォルトの3000ポートを指定していますね.expose:
により, 他のサービス(この場合はweb)へポートを解放します.
GemfileとGemfile.lock
Gemの一種であるRailsをインストールするので, Gemfileを作成し, コンテナ内にコピーします. Gemfile.lockは内容が空っぽのファイルを作成するだけでOKです.
source 'https://rubygems.org' gem 'rails', '5.2.0'
Railsアプリの骨組みを構築
Composeファイルがあるディレクトリであることを確認して, 以下のコマンドを実行します.
$ pwd /myapp $ docker-compose run web rails new . --force --database=mysql
このコマンドはdocker run <image>
と同じような働きをします. しかしdocker-composeの場合はサービス名 (この例ではweb)で指定できるのが便利ですね.
rails new
がコンテナ内で実行されます. アプリ名を引数に指定してこのコマンドを実行することにより, 指定した名前でアプリの骨組みが作成されます.
この場合はカレントディレクトリを表す.
が引数となっているので, カレントディレクトリ名がアプリ名に指定されたことになります.
DockerfileにおいてWORKDIR /myapp
と記述しているので, rails new
が実行されるディレクトリは/myapp
です. したがってmyapp
という名前のアプリが作られることになるわけですね. またRailsによって自動生成された各種ファイルはこのディレクトリに配置されることになります.
--force
はファイルの上書きを許可するオプションです. rails new
は新たにGemfileを作成しますが, このオプションにより先ほど用意したGemfileが上書きされます.
--database=mysql
は見たまんまですね. データベースの種別を指定しています.
Composeファイルにてcommand:
を指定していましたよね. これはコンテナが起動するときに発動すると言いました. しかし今回のdocker-compose run
実行時には発動しません. それはrails new
で上書きされたからです. command:
で指定したコマンドは上書きが可能なのです. はじめだけrails new
を上書きで実行しておいて, 一旦環境が構築されてしまえば, もとのコマンドによりRails serverが起動してくれるということです. うまいことできてますね.
イメージを再構築
rails new
したことにより, Gemfileが上書きされました. この状態を保持するためにイメージを再構築します. そのコマンドがdocker-compose build
です.
公式ドキュメントには以下のように書かれています.
Now that you’ve got a new Gemfile, you need to build the image again. (This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.)
つまりGemfileやDockerfileに変更があった場合はこのコマンドでrebuild(再構築)しろってことですね.
データベース設定をRailsに教える
データベースの設定ファイルを編集します.
ボリュームをマウントしているので, コンテナの中に入らずとも編集ができますね. ファイルパスはmyapp/web/config/database.yml
です.
# MySQL. Versions 5.1.10 and up are supported. # # Install the MySQL driver # gem install mysql2 # # Ensure the MySQL gem is defined in your Gemfile # gem 'mysql2' # # And be sure to use new-style password hashing: # https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html # default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # 変更箇所!! host: db # 変更箇所!! development: <<: *default database: myapp_development # 変更可能!! # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default database: myapp_test # 変更可能!! # As with config/secrets.yml, you never want to store sensitive information, # like your database password, in your source code. If your source code is # ever seen by anyone, they now have access to your database. # # Instead, provide the password as a unix environment variable when you boot # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database # for a full rundown on how to provide these environment variables in a # production deployment. # # On Heroku and other platform providers, you may have a full connection URL # available as an environment variable. For example: # # DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" # # You can use this database configuration with: # # production: # url: <%= ENV['DATABASE_URL'] %>
はじめに設定したrootユーザーのパスワードを書きましょう. またホストにdb
を指定します.
Dockerのおかげでサービス名で指定することができます.
開発環境とテスト環境のデータベース名があらじめ指定されていますが, 自分の使いやすい名前に変更することもできます.
サービスの立ち上げ
データベース設定ができたので, いよいよサービスの立ち上げです.
$ docker-compose up -d
-d
オプションによりバックグラウンドでサービスを起動することができます.
ではさっそくブラウザで localhost
にアクセスしてみましょう. するときっとエラーが出ると思います. これはデータベースが作成されていないことが原因です. ではデータベースを作成しましょう. 以下のコマンドを実行します.
$ docker-compose exec web rails db:create
公式ドキュメントではrake db:create
を実行していますが, これは古いタイプのコマンドです. Rails5を使う場合はrails db:create
を使いましょう.
さてこれでデータベースも作成されました. 再びlocalhost
へアクセスしてみましょう. どうでしょうか. Yay! You're on Rails!が表示されれば成功です.