1つのDockerでRailsとMySQLを立ち上げようとした
Updated Date: 2024/01/01 00:26
珍しく技術的な記事。
Dockerコンテナを複数立ち上げるのがめんどくさかったんで、 Railsアプリと一緒にDBも立ち上げてしまおうという邪道なことをやってみた。 それが意外と大変でめっちゃ時間かかったんで二度と同じ過ちを犯さないように書く。
まずは肝のDockerfileについてざっくり記述する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
FROM ruby:2.3.3
##########################################################################
# Ruby の設定
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
# 依存ライブラリインストール
RUN apt-get update && apt-get install -y expect lsb-release
RUN apt-get install -y \
autoconf \
coreutils \
(略)
# bundle install
RUN bundle install --deployment --path=vendor/bundle --without test
##########################################################################
# MyQSLインストール
RUN wget -O mysql-apt-config.deb http://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb
RUN expect -c "set timeout -1; spawn dpkg -i mysql-apt-config.deb; expect \"Which MySQL product do you wish to configure?\"; send \"1\n\"; expect \"Which server version do you wish to receive?\"; send \"1\n\"; expect \"Which MySQL product do you wish to configure?\"; send \"4\n\"; expect \"$\""
RUN apt-get update
RUN expect -c "set timeout -1; spawn apt-get install -y mysql-community-server --force-yes; expect \"Enter root password:\"; send \"\n\"; expect \"$\""
# 権限
ADD . /app
RUN chmod -R 777 /app/log/ \
&& chmod -R 777 /app/tmp/
# システム実行
CMD ["/bin/bash", "-c", ". ./start.sh"]
Ruby:2.3.3 はDebian。asset:compile
とか、db:migrate
やってねーじゃん?と思うだろうが、
それらはすべてDocker起動時に実行するのでここではやってない。
MySQLはパッケージからインストールしている。
インタラクティブな受け答えがあるのでexpect
使ってるけど、echo
使ったパターンのほうがリーダブルかもしれない。
1
2
3
RUN echo 'mysql-server mysql-server/root_password password pass' | sudo debconf-set-selections \
&& echo 'mysql-server mysql-server/root_password_again password pass' | sudo debconf-set-selections \
&& sudo apt-get install -y mysql-server
参考: teratail - DockerでMySQLサーバーを作成したいが、RUNコマンド内でMySQLが起動しない
最後の start.sh
がDocker起動時に実行されるコマンド。つまりこいつがPID 1 で起動することになる。
start.shはこんな感じ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
# -- 環境変数読み込み
. /app/environments.sh
# MySQL設定
find /var/lib/mysql -type f -exec touch {} \; && service mysql start
sleep 5
chown -R mysql:mysql /var/lib/mysql /var/run/mysqld
mysql -u root -e "create user myuser IDENTIFIED BY 'password'"
mysql -u root -e "create database myappdb"
mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'password'"
# DB初期設定
rake db:migrate:reset
rake db:seed
# precompile
RAILS_ENV=production bundle exec rake assets:precompile
# Rails起動
bundle exec rails s
皆さんの想像通り、シェルスクリプト内でいろんなことをやってる。
なので、このコンテナは docker run
した状態から40秒位Railsアプリの起動に時間を要する。
苦労した点
まとめると非常に楽にみえるのだが、冒頭述べたとおりめちゃくちゃ苦労した。 というのも、この環境はDocker for macにて検証していて、こいつのStorageの設定がoverlay2だと上手くいかないということがあるらしい。 OSの仕組みに疎いので僕が言及するよりいろんな参考サイトを見てほしい……。
参考サイト:
- Github - mysqlが起動しないため、dockerのコンテナが上がってこない
- Github - MySQL does not start with overlay2 and overlay but starts with aufs
僕は後者の参考サイトでいいね!のたくさんついてたやつを解決策に選んだ。また、起動するたびに権限が外れるらしいので、
そこもchown
を使って権限付与するようにしている。
発生したエラー
[ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
[ERROR] Fatal error: Can't open and lock privilege tables: Got error 140 from storage engine
まとめ
Dockerは1コンテナ1サービスが原則。 今回は横着したばっかりに痛い目にあった。 また、今回の例ではシェルスクリプトを単に起動しているだけだが、本来であれば正しくシグナル処理とプロセス管理が必要である。 そのため、SIGKILLあたりをインターセプトしたり、graceful指定したり考えたほうが良いとGoogleさんも言っている。