Rail5.1 rc1+Dockerでいい感じの環境を構築する
目的
いい感じに使える、Dockerの開発環境を構築する。 最終的な目的はProductionでDockerを使うイメージを固めること。
環境
docker for mac はいい噂を聞かないのでVagrant上でDockerを使用する。 当然、docker for mac でも動く。
Railsアプリ作成
bundle init
gem 'rails', '~> 5.1.0.rc1'
bundle instlal --path vendor/bundle
sprockets, turbolinksを抜いています。 DBはmysqlを使用
bundle exec rails new --skip-bundle -d mysql --skip-sprockets --skip-turbolinks .
ローカルにbundle設定があるとダメなので消します。
rm -rf vendor/bundle rm -rf .bundle
Docker install
sudo apt-get update sudo apt-get install \ linux-image-extra-$(uname -r) \ linux-image-extra-virtual sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get install docker-ce
dockerグループが出来ているので、vagrantユーザーをdockerグループに追加する必要があります。 その後、vagrantからlogoutし再度sshで入ります。
sudo gpasswd -a vagrant docker
docker-compose install
sudo curl -L "https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
Dockerfile
Dockerfileを作成します。 alpine linuxをベースにRuby2.4.1の入っているイメージを元にします。 必要なパッケージをインストール後、不要なファイルを消したりします。 mysql2 gem をbuildするために必要なmariadb-devを入れるとmariadb本体も付いてきてしまうため、mariadb本体のファイルを削除しています。
cmd, entrypointはrun時に指定するので付けていません。
Dockerfile
FROM ruby:2.4.1-alpine ENV LANG ja_JP.UTF-8 ENV BUILD_PACKAGES="curl-dev build-base" \ DEV_PACKAGE="mariadb-libs mariadb-client mariadb-client-libs tzdata" RUN gem install bundler \ && apk --update --upgrade add $BUILD_PACKAGES \ && apk add mariadb-dev tzdata linux-headers postgresql-dev sqlite-dev git nodejs \ && rm /usr/lib/libmysqld* \ && echo 'gem: --no-document' > /etc/gemrc WORKDIR /app COPY Gemfile Gemfile COPY Gemfile.lock Gemfile.lock RUN bundle install EXPOSE 3000
docker-compose.ymlを作成します。
spring server起動用のコンテナをwebと同一Dockerfileから作っています。 springについては以下を参考にさせていただきました。
参考
https://github.com/jonleighton/spring-docker-example http://tech.degica.com/ja/2016/06/14/dockerized-rails-development/
user指定時に、ホストOSのユーザーIDとグループIDを指定している。 ユーザーを指定しない場合、rootで実行されるため、Railsコマンドで生成したファイルがrootの所有となって、ホストOSのエディタから編集が出来なくなる。
version: '2' services: db: image: mysql:5.7.17 volumes: - ./store:/var/lib/mysql environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' web: build: . command: bundle exec rails s user: "${uid}:${gid}" working_dir: /app environment: RACK_ENV: 'development' volumes: - .:/app ports: - "3000:3000" depends_on: - db spring: build: . command: docker/run.sh spring volumes: - .:/app
mysqlの設定
※追記 mysqlコンテナを用意するようにしたのでこちらは不要になりました。
DockerコンテナからホストOSのMySQLに接続するためにホストOS側のMySQLの設定を修正します。
sudo vi /etc/mysql/my.cnf
bindするアドレスに0.0.0.0を追加します。
bind-address = 127.0.0.1 bind-address = 0.0.0.0
MySQLを再起動します。
sudo service mysql restart
bindアドレス同様に、 rootアカウントがlocalhostからしかログインできないためユーザーを追加します。
mysql -u root CREATE USER 'root'@'172.17.0.2'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.17.0.2'; FLUSH PRIVILEGES;
開発
docker-compose系のコマンドを叩くのが面倒な為、scriptを用意しています。
- 引数なしでヘルプ表示
- 第一引数がupなら、docker-compose up
- 第一引数がrspecならRACK_ENV=testでrspec起動
- 第一引数がsならフォアグラウンドでRails起動 port指定
- 第一引数がspringならspringサーバーをバックグラウンドで起動
- 引数ありの場合は引数をそのまま渡す。
docker/bundle.sh
#!/bin/bash ENV=$RACK_ENV export uid=$UID export gid=$GID if [ "$ENV" = 'production' ]; then docker-compose run --entrypoint=$entrypoint -e RACK_ENV=$ENV web s exit 0 fi entrypoint=docker/run.sh if [ "$1" = 'up' ]; then docker-compose up elif [ "$1" = 'rspec' ]; then docker-compose run --entrypoint=$entrypoint -e RACK_ENV=test web $@ elif [ "$1" = 'spring' ]; then docker-compose run -u "$UID:$GID" --entrypoint=$entrypoint -d spring spring elif [ "$1" = 's' ]; then docker-compose run -p 3000:3000 -u "" --entrypoint=$entrypoint -e RACK_ENV=development web s else docker-compose run -u "$UID:$GID" --entrypoint=$entrypoint -e RACK_ENV=development web $@ fi
docker/run.sh
alpine linuxにはbash等が入っていなく、わざわざ入れる必要もないコードなのでデフォルトのシェルであるashで書いています。
#!/bin/ash if [ "$RACK_ENV" = 'development' -o "$RACK_ENV" = 'test' -o "$RACK_ENV" = '' ]; then if [ $# -eq 0 ]; then bundle exec rails -h elif [ $1 = 'rspec' ]; then bundle exec $@ elif [ $1 = 'spring' ]; then bundle exec spring binstub --all #bundle exec spring binstub --remove --all bin/spring server else time bundle exec rails $@ fi exit 0 fi bundle exec unicorn -c config/unicorn.rb -E $RACK_ENV
deploy
deploy時に使用するDockerfileを作る
FROM ruby:2.4.1-alpine ENV LANG ja_JP.UTF-8 ENV BUILD_PACKAGES="curl-dev build-base" \ DEV_PACKAGE="mariadb-libs mariadb-client mariadb-client-libs tzdata" RUN gem install bundler \ && apk --update --upgrade add $BUILD_PACKAGES \ && apk add mariadb-dev tzdata linux-headers postgresql-dev sqlite-dev git nodejs \ && rm /usr/lib/libmysqld* \ && echo 'gem: --no-document' > /etc/gemrc WORKDIR /app COPY Gemfile Gemfile COPY Gemfile.lock Gemfile.lock RUN bundle install ENV APP_HOME /app RUN mkdir -p $APP_HOME WORKDIR $APP_HOME COPY . $APP_HOME EXPOSE 3000
circle_ciでbuildして、testを実行。 testが通ったら、imageをECRなり、DockerHubなり、GCRへpushする。
まとめ
当初、DockerfileでアプリのソースをCOPYしてイメージを作っていたが、 ソースを含めてイメージを作るのはdeploy時だけでいいのでやめた。 軽く開発してみたが上手く進められている。 deploy周り(CricleCI含め)を妄想しかしていないので、いざやってみたらまた変わるかもしれない。
問題点が1つあって、user指定で実行するとbundler関係のディレクトリがroot所有なのでwarningが出る。 解決策が思いつかない。どなたかいい解決策があれば教えてください。
`/` is not writable. Bundler will use `/tmp/bundler/home/unknown' as your home directory temporarily.
今回のソースは以下にあります。 https://github.com/astapi/development_rails_with_docker