こんにちは。Enjoy IT Life管理人の仁科(@nishina555)です。
前回、『Dockerのデータを永続化!Data Volumeの理解から始める環境構築入門』の記事でデータベースを例にとり、Dockerのデータの永続化について紹介しました。
データの永続化というと、データベース情報を指す場合が多いですが、アプリケーション開発を効率的に行うためにはデータベース情報以外にも永続化したほうがいい情報があります。
例えば、Ruby on Railsではインストールしたgemの一覧を永続化することで開発効率がグッとあがります。
しかし、Dockerの公式ドキュメント『Quickstart: Compose and Rails』もそうなのですが、RailsアプリをDocker化するチュートリアルでgemの永続化については省略されているケースも多くみられます。
そこで今回はデータボリュームを活用してgemの永続化をする手順、およびそのメリットについて紹介したいと思います。
今回は具体例としてRailsアプリのgemを利用しますが、『パッケージ管理ツールによってインストールされたライブラリ一覧を永続化する』というTIPSはRuby以外の言語でも活かせる知識だと思います。
ですので、Ruby以外の言語でアプリ開発をする場合もぜひ参考にしていただければと思います。
- ライブラリの永続化のメリットを理解したい
- gemの永続化の手順について知りたい
- サクサク開発できるDocekr環境を構築したい
目次
データの永続化に対応したDocker環境構築手順
ここからは具体的なサンプルアプリを例にとり、データを永続化する方法について紹介をしたいと思います。
今回は『Ruby on RailsとMySQL』で構成されたDocker環境をサンプルとして利用します。永続化する対象のデータはインストールされたgemです。
なお、『Railsアプリの準備』の手順は『Dockerのデータを永続化!Data Volumeの理解から始める環境構築入門』で紹介した手順と同じですので、詳細についてはそちらの記事を参考にしてください。
上記の記事を既に読まれている方は『インストールしたgemを永続化をする』から読み始めてください。
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', '~> 5.0.2'
次にDockerファイルを用意します。
Dockerfile
FROM ruby:2.6
WORKDIR /rails_app
COPY Gemfile* /rails_app/
RUN bundle install
上記のDockerファイルをビルドして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側のデータベース接続設定を行います。
config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
- password:
+ password: pass
- host: localhost
+ host: db
次にdocker-compose.ymlを作成します。
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
depends_on:
- db
db:
image: mysql:5.7
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: 'pass'
volumes:
mysql_data:
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"]
Dockerファイルを修正したので、イメージをビルドしなおします。
$ docker-compose build
Railsアプリで利用するデータベースを作成します。
$ docker-compose run web rake db:create
以上でサンプルとなるRailsアプリの雛形の作成が完了しました。
インストールしたgemを永続化をする
今回はgem_dataという名前付きボリュームを作成し、インストールしたgemの永続化をしたいと思います。
まずは永続化の対象となるgemがインストールされるディレクトリについて確認をします。
gemのインストール先はgem environment
で表示される『INSTALLATION DIRECTORY』の項目で確認できます。
$ docker-compose run web gem environment
RubyGems Environment:
(中略)
- INSTALLATION DIRECTORY: /usr/local/bundle
(中略)
gemのインストール先は/usr/local/bundle
であることがわかったので、このディレクトリのデータの永続化を行います。
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
+ - gem_data:/usr/local/bundle
depends_on:
- db
db:
image: mysql:5.7
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: 'pass'
volumes:
mysql_data:
+ gem_data:
永続化の確認
ここまででgemの永続化がされたDocker環境を構築することができました。
ここからは構築したDocker環境を実際に利用し、永続化のメリットなどについて紹介をしたいと思います。
まずはDockerコンテナを起動します。
$ docker-compose up
コンテナ起動後、新たにgemを追加してみます。今回は例としてransack
を追加してみます。
Gemfile
+ gem 'ransack'
gemを追加後、bundle install
を実行します。
$ docker-compose exec web bundle install
(中略)
Fetching ransack 2.3.0
Installing ransack 2.3.0
(中略)
Bundle complete! 16 Gemfile dependencies, 64 gems now installed.
Bundled gems are installed into `/usr/local/bundle`
ここで、一旦コンテナを削除します。
$ docker-compose down
もし、gemを永続化していない場合、上記のように『コンテナ上でgemをインストール → コンテナを削除』という手順をふむと、以下のようにコンテナ起動時にエラーが発生してしまいます。
エラーをみてみると、Bundler::GemNotFound
が発生しており、先ほどインストールしたはずのransack
が見つからない状態であることがわかります。
$ docker-compose up
Starting rails_app_db_1 ... done
Starting rails_app_web_1 ... done
Attaching to rails_app_db_1, rails_app_web_1
(中略)
web_1 | /usr/local/lib/ruby/2.6.0/bundler/resolver.rb:287:in `block in verify_gemfile_dependencies_are_found!': Could not find gem 'ransack' in any of the gem sources listed in your Gemfile. (Bundler::GemNotFound)
gemの永続化をしていない場合、コンテナ上でbundle installを実行してもDockerイメージにはgemの保存がされないためエラーが発生します。エラーを防ぐにはDocekrイメージに新しく追加したgemを保存するためにビルドを実行する必要があります。
つまり、gemの永続化をしないとgemをインストールするたびに毎回『ビルド実行 → コンテナ起動』という操作をしなければいけません。
しかも、イメージをビルドしなおす場合、ゼロからgemをインストールすることになるので、全てのgemがインストールが完了するまでに時間がかかります。
一方、gemを永続化した場合、『コンテナ上でgemをインストール → コンテナを削除』という手順をふんでも、問題なく再度コンテナを起動することができます。
$ docker-comose up
実際にインストールされたgemの一覧を確認してみると、コンテナ削除前にインストールしたransack
が含まれていることがわかります。
$ docker-compose exec web bundle list |grep 'ransack'
* ransack (2.3.0)
gemの永続化をすることで、永続化をしていなかった時は毎回おこなっていたイメージのビルドをする必要がなくなるため、開発効率があがります。
つまり、『毎回ゼロからgemのインストールをしなくてすむ』という点がgemを永続化する理由です。
では、確認のために作成されたデータボリュームを調べてみます。
データボリュームはdocker volume ls
で確認することができます。
実行結果をみると、gem_data(今回の場合はアプリケーション名のrails_appが頭についてrails_app_gem_data)のデータボリュームが作成されていることがわかります。
$ docker volume ls
DRIVER VOLUME NAME
local rails_app_gem_data
local rails_app_mysql_data
docker inspect
でデータボリュームの詳細を確認してみます。
『Labels』の項目を確認してわかる通り、rails_app(今回のサンプルアプリの名前)のデータボリュームであることがわかります。
$ docker inspect rails_app_gem_data
[
{
"CreatedAt": "2019-08-23T12:51:36Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "rails_app",
"com.docker.compose.version": "1.24.1",
"com.docker.compose.volume": "gem_data"
},
"Mountpoint": "/var/lib/docker/volumes/rails_app_gem_data/_data",
"Name": "rails_app_gem_data",
"Options": null,
"Scope": "local"
}
]
webコンテナの永続化された情報について確認してみます。
コンテナのマウント情報はdocker inspect
の『Mounts』の項目で確認できます。
結果をみてわかる通り、/usr/local/bundle
のディレクトリがrails_app_gem_data
という名前で永続化(データボリュームに保存)されていることがわかります。
$ docker inspect $(docker ps -f name=rails_app_db_1 -q) | grep -A 10 'Mounts'
"Mounts": [
{
"Type": "volume",
"Name": "rails_app_gem_data",
"Source": "/var/lib/docker/volumes/rails_app_gem_data/_data",
"Destination": "/usr/local/bundle",
"Driver": "local",
"Mode": "rw",
"RW": true,
"Propagation": ""
},
まとめ: Dockerビルドを行う頻度を減らして開発効率をあげよう
以上でインストールされたgemの永続化をおこなったRuby on RailsのDocker環境の構築方法について紹介を終わります。
Dockerイメージのビルドは時間のかかる作業ですので、永続化などを活用してビルド回数を減らせると開発効率をあげる事ができます。
- gem environmentコマンドでgemのインストール先が確認できる
- gemのインストール先ディレクトリに対して名前付きボリュームを作成することでgemの永続化ができる
- Dockerイメージのビルドをする機会を減らすことで開発効率をあげることができる
この記事がいいなと思いましたらツイッター(@nishina555)のフォローもよろしくお願いします!