2018年11月4日日曜日

DockerでRails5.2.1とMySQL8を動かすgitプロジェクトを作る方法


背景

Dockerとは、Linux環境をコンテナという単位で管理できるプログラムです。
これを利用すると、複数人数で開発する時に各PCでの環境構築する際の手間を減らせます。

RailsとはRuby on Railsの略称であり、Rubyというプログラミング言語でwebアプリを作るためのフレームワークです。
これを利用すると webアプリ(データを登録、編集したりするwebサイト)を比較的早く作れます。

MySQLとは、データベース(以下DB)を扱うプログラムの1つです。
Railsアプリで扱うデータを保存するのに使います。

gitとはバージョン管理プログラムの1つです。
これを利用すると、githubやbitbucketなどを通して、他の人とプロジェクトを共有できます。

現行最新のRails 5.2.1とMySQL8のDockerでの連携に手間取ったので、備忘録を兼ねて方法を共有します。

使ったもの

docker-compose

dockerコマンドだけで連携を実現するのは難しいので、docker-composeというdockerの便利プログラムを使います。
docker.ceとdocker-composeをインストールしてください。

docker.ce
Ubuntu向け: Get Docker CE for Ubuntu
Windows向け: Install Docker for Windows
Mac向け: Install Docker for Mac

docker-compose
Install Docker Compose

この記事の動作確認を行った環境はこちらです。
$ lsb_release -a
No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 18.04.1 LTS
Release:    18.04
Codename:    bionic
$ docker --version
Docker version 18.06.1-ce, build e68fc7a
$ docker-compose --version
docker-compose version 1.22.0, build f46880fe

git

バージョン管理プログラムです。
インストールしてください。

Git - Downloads

プロジェクト新規作成時の操作

Railsプロジェクトの作成にDockerを利用することで、PCにRubyが入っていなくてもRailsプロジェクトを新規作成できます。

今回は例として、rails-docker-practiceというフォルダでプロジェクトを構築します。

まず、構築する元となるフォルダを作成し、その中に入ります。
個人的な好みで、この記事ではgitprojectsというフォルダの中にプロジェクト作成します。
mkdir ~/gitprojects/rails-docker-practice
cd ~/gitprojects/rails-docker-practice

空のGemfile.lockを含むwebフォルダを作成します。このwebフォルダの中にRailsアプリを構築します。
mkdir web
touch web/Gemfile.lock

webフォルダの中に、RailsをインストールするためのGemfileを作成します。
~/gitprojects/rails-docker-practice/web/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 5.2.1'

webフォルダのGemfileとGemfile.lockを利用してrubyコンテナを作成するためのGemfileを作成します。
~/gitprojects/rails-docker-practice/Dockerfile
FROM ruby:2.5.3
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /web
WORKDIR /web
ADD web/Gemfile /web/Gemfile
ADD web/Gemfile.lock /web/Gemfile.lock
RUN bundle install
ADD web /web

webフォルダをRailsのrubyコンテナとしてとして、dbフォルダをMySQLコンテナのデータ置き場として連携するためのdocker-compose.ymlを作ります。
~/gitprojects/rails-docker-practice/docker-compose.yml
version: '3'
services:
  db:
    image: mysql
    command: mysqld --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3316:3306'
    volumes:
      - ./db/mysql/volumes:/var/lib/mysql

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./web:/web
    ports:
      - "3000:3000"
    depends_on:
      - db

MySQL8からデフォルトの設定ではパスワード認証を使わないようになっているため、上記のdocker-compose.ymlで「command: mysqld --default-authentication-plugin=mysql_native_password」 をdbに記述してパスワード認証を有効にしています。
これがないと、下記のようなエラーが表示されて、RailsとMySQLを連携できません。
Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib/x86_64-linux-gnu/mariadb18/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory
Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf8", "pool"=>5, "username"=>"root", "password"=>"password", "host"=>"db", "database"=>"web_development"}
rake aborted!
Mysql2::Error: Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib/x86_64-linux-gnu/mariadb18/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory

Dockfile、Gemfile、docker-compose.ymlを作ったら、下記のコマンドを実行してMySQLを使うRailsのプロジェクトを作ります。
Gemfileの上書き時の警告が出なくても良いと思うので、「--force」オプションを付けています。
コンテナをビルドする時にbundle installしないとインストールした内容が破棄されてしまうので、「--skip-bundle」オプションを付けて、Railsプロジェクト作成時のbundle installを行わないようにしています。
docker-compose run web rails new . -d mysql --force --skip-bundle

上記のコマンドでwebフォルダにRailsのファイルが、dbフォルダにMySQLのデータファイルが作成されます。 Dockerのコンポーネントによって作られたファイルは所有権がrootになっていて扱いにくいので、下記のコマンドでwebとdbフォルダを所有権を自分に変更します。
sudo chown -R $USER:$USER web db

dbフォルダの所有権を変更していないと、docker-composeコマンドでコンテナを作り直すときに下記のようなエラーが出ます。
sudoを付けてdocker-compseコマンドを実行すれば良いのですが、sudoを付けたり付けなかったりするのが手間なので、上記のコマンドで権限を変えておくのが良いと思います。

Railsのプロジェクトができたら、Dockerを通してMySQLに接続するために、database.ymlのパスワードとホストを下記のように変更します。
~/gitprojects/rails-docker-practice/web/config/database.yml
#  password:
  password: password
#  host: localhost
  host: db

上記のdatabase.ymlの変更は、sedコマンドを利用して下記のようなコマンドでも行なえます。
sed -i -e 's/password:$/password: password/g' web/config/database.yml
sed -i -e 's/host: localhost$/host: db/g' web/config/database.yml

Railsプロジェクトのフォルダに「.git」ディレクトリが勝手に作られますが、不要なので消しておきます。
sudo rm -r web/.git

rails newで更新されたGemfileに書かれているライブラリをDockerコンテナで扱うために、コンテナを作り直します。
(作り直しはGemfileを更新する度に必要になります。)
docker-compose build

Railsプロジェクトができたのでdb:createでDBの作成をしようとすると、下記のようなエラーが出ることがあります。
Starting docker-test_db_1 ... done
Mysql2::Error: Failed to create schema directory 'web_development' (errno: 13 - Permission denied): CREATE DATABASE `web_development` DEFAULT CHARACTER SET `utf8`
Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf8", "pool"=>5, "username"=>"root", "password"=>"password", "host"=>"db", "database"=>"web_development"}
rake aborted!
ActiveRecord::StatementInvalid: Mysql2::Error: Failed to create schema directory 'web_development' (errno: 13 - Permission denied): CREATE DATABASE `web_development` DEFAULT CHARACTER SET `utf8`

コンテナを止めるとMySQLの初期化が済むからか上記のエラーが出なくなるので、下記のコマンドでコンテナを止めます。
docker-compose down

MySQLコンテナの初期化が済んでいれば、下記のコマンドでRails用のDBを作成できます。
docker-compose exec web rake db:create

以上の操作でRailsのプロジェクト作成とDBの作成ができました。
下記のコマンドでRailsとMySQLのコンテナを動かします。
docker-compose up

上記のコマンドを実行している状態でブラウザから http://localhost:3000 にアクセスすると、Railsアプリのトップページを確認できます。

dbフォルダが作成されますが、DBに関するデータは各PCで作成すればgitに含まなくても良いので、gitignoreに記述てgitの管理から除外します。
~/gitprojects/rails-docker-practice/.gitignore
db/

上記の.gitignoreを作成してから下記のようなコマンドでgitprojectを開始すれば、必要最低限のものだけがgitの管理対象になって良いと思います。
git init
git add .
git commit -m 'Initial commit'

Rails + MySQLのDockerプロジェクトを新規作成できました。

既に作成しているプロジェクトをDocker化する操作

既存のRailsプロジェクトがprojectAとして存在しているのを例に説明します。
先ほど紹介した新規プロジェクト作成と被る部分もありますが、「それは前の章を見てね」だと読みにくくなりそうなので、冗長になることを承知でエラーは省きながら同じ手順も記述します。

まず、projectAのフォルダ名をwebに変更し、新しく作ったprojectAというフォルダの中に移動します。
mv projectA web
mkdir projectA
mv web projectA

projectAに入り、今までのprojectAのgitフォルダを、新しいprojectAのルートフォルダに移動します。
cd projectA
mv web/.git ./

先ほどのプロジェクトを新規作成する説明でも紹介したのと同じDockerfile、docker-compose.yml、.gitignoreを作成します。
既にRailsプロジェクトができているので、Gemfileの作成は不要です。
projectA/Dockerfile
FROM ruby:2.5.3
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /web
WORKDIR /web
ADD web/Gemfile /web/Gemfile
ADD web/Gemfile.lock /web/Gemfile.lock
RUN bundle install
ADD web /web
projectA/docker-compose.yml
version: '3'
services:
  db:
    image: mysql
    command: mysqld --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3316:3306'
    volumes:
      - ./db/mysql/volumes:/var/lib/mysql

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./web:/web
    ports:
      - "3000:3000"
    depends_on:
      - db
projectA/.gitignore
db/

ここまでできたらこのようなフォルダ構成になります。
projectA
  |- web: Railsプロジェクト
  |- Dockerfile
  |- docker-compose.yml
  |- .gitignore
  |- .git

Dockerを通してMySQLに接続するために、Railsのdatabase.ymlのパスワードとホストを下記のように変更します。
projectA/web/config/database.yml
#  password:
  password: password
#  host: localhost
  host: db

上記のdatabase.ymlの変更は、sedコマンドを利用して下記のようなコマンドでも行なえます。
sed -i -e 's/password:$/password: password/g' web/config/database.yml
sed -i -e 's/host: localhost$/host: db/g' web/config/database.yml

MySQLコンテナ初期化のために、ビルドして実行した後にコンテナを終了します。
docker-compose build
docker-compose up
docker-compose down

MySQLにRailsのDBを作ります。
docker-compose run web rake db:setup

以上の操作でRailsのプロジェクト作成とDBの初期化ができました。
下記のコマンドでRailsとMySQLのコンテナを動かします。
docker-compose up

上記のコマンドを実行している状態でブラウザから http://localhost:3000 にアクセスすると、Railsアプリのトップページを確認できます。

上記の.gitignoreを作成してから下記のようなコマンドでgitprojectを開始すれば、必要最低限のものだけがgitの管理対象になって良いと思います。
git init
git add .
git commit -m 'Initial commit'

既存のRailsプロジェクトをMySQLのDockerプロジェクトに変更できました。

開発に参加する人が行う操作

MySQLコンテナ初期化のために、ビルドして終了します。
docker-compose build
docker-compose down

RailsのDBを作成します。
docker-compose run web rake db:setup

RailsとMySQLを動かします。
docker-compose up

上記のコマンドを実行すると、 http://localhost:3000 にアクセスしたら、Railsが動いていることを確認できると思います。

この状態になれば、webフォルダのRails関係のファイルを編集するなど、開発が行えると思います。

開発時に利用するであろうコマンド

コンテナが動いているときはexec、動いていなければrun

「docker-compose up」や「docker-compose start」などで、コンテナが既に動いている場合
docker-compose exec ..
例:
docker-compose exec rake db:migrate

コンテナがまだ動いていない場合
docker-compose run ..
例:
docker-compose run rake db:migrate

これより先の例はrunコマンドを使いますが、コンテナが動いているなら上記の例のように「exec」に置き換えられます。

「rails」コマンドを実行する

docker-compose run web rails ..
例:
docker-compose run web rails g model user name:string

「rake」コマンドを実行する

docker-compose run web rake ..
例:
docker-compose run web rake db:migrate

MySQLに入る

rakeのdbconsoleを利用します。
docker-compose run web rake dbconsole

コンテナをビルドしなおす

Gemfileを更新した後などに実行します。
自分の環境では「docker-compose build」だけでは、下記のエラーが発生することがありました。
db uses an image, skipping
Building web
Traceback (most recent call last):
  File "site-packages/docker/utils/build.py", line 96, in create_archive
PermissionError: [Errno 13] Permission denied: '/home/asuki/gitprojects/rails-docker-practice/db/mysql/volumes/ca-key.pem'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bin/docker-compose", line 6, in 
  File "compose/cli/main.py", line 71, in main
  File "compose/cli/main.py", line 127, in perform_command
  File "compose/cli/main.py", line 282, in build
  File "compose/project.py", line 378, in build
  File "compose/service.py", line 1067, in build
  File "site-packages/docker/api/build.py", line 154, in build
  File "site-packages/docker/utils/build.py", line 31, in tar
  File "site-packages/docker/utils/build.py", line 100, in create_archive
OSError: Can not read file in context: /home/asuki/gitprojects/rails-docker-practice/db/mysql/volumes/ca-key.pem
[32413] Failed to execute script docker-compose

上記の対策として、sudoを付けてビルドを実行する
sudo docker-compose build
もしくは、dbフォルダの所有権を変える
chmod $USER:$USER -R db
docker-compose build
のどちらかでビルドできました。

ビルド時に発生するエラーの回避方法について、もっと良いやり方がありましたら教えていただけると嬉しいです。

まとめ

MySQLに関連するエラーに戸惑いましたが、DockerでRailsプロジェクトを動かし、gitで管理できるようになりました。

何かの参考になれば嬉しいです。
なお、記事の内容に誤りや不備があったり、もっと良い方法がありましたら、情報をいただけるとありがたいです。

参考

[Rails] DockerでRails + MySQLの開発環境をつくる手順
今更だけどdocker composeのベストプラクティス

更新履歴

2019/02/18 MySQLコンテナ初期化の手順にdocker-compose upコマンドを追加しました。
2023/05/12 著作権侵害警告を受けたので、ロゴ画像の利用を止めました。

0 件のコメント :