なるべく最新Verで構築するRails6開発環境(Docker + Rails + Nginx + Puma + MySQL)
はじめに
こんにちは、Web系エンジニア転職にむけて学習中の Npakk と申します。
Railsを学習するにあたって開発環境を構築したので、その手順を少し解説を交えながらご紹介します。
Dockerでの構築経験はあまりなく、経験も乏しいのであくまで参考程度にご覧ください。
もし間違いやご指摘などあれば、ぜひぜひお願いいたします!
Dockerを使用して、ローカル環境でRailsのWelcomeページを確認できるまでが、この記事のゴールです。
参考記事
- ほとんどこちらの記事を参考にさせていただいてます。
- こちらも参考にさせていただきました。
対象読者
前提
- Macユーザーを対象としています
- Docker for Macがインストールされているものとします
- Nginx と Puma を連携させています
- 最新バージョンは、記事執筆時点で最新という意味です
- 全てのソフトウェアが最新バージョンなわけではありません
- 最新版だから動作が安定していたり、速度が速いというわけではありません
バージョン
- ホストOS(macOS Catalina 10.15.5)
- Docker(19.03.8)
- docker-compose(1.25.5)
- Ruby(2.7.1)
- Ruby on Rails(6.0.3.2)
- Nginx(1.19.0)
- MySQL(8.0)
1. ディレクトリの作成
ディレクトリ構成としては以下のようになります。
あくまで、手動で作成する項目のみ記載しています。
/webapp ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── containers │ └── nginx │ ├── Dockerfile │ └── nginx.conf ├── docker-compose.yml ├── entrypoint.sh └── environments └── db.env
1-1. アプリケーションルート
どこでもいいですが、わかりやすいところがおすすめです。
$ mkdir webapp
1-2. Nginxコンテナ用ディレクトリ
Nginxは、Rails・DBとは別のディレクトリを作ります。
設定ファイルや DockerFile を別途配置します。
$ mkdir -p webapp/containers/nginx
1-3. 環境変数用ディレクトリ
DBで使うユーザーのパスワードなどを記載したファイルを配置します。
$ mkdir webapp/environments
2. コンテナ生成用のファイルを作成
以降はアプリケーションルート内での操作となります。
$ cd webapp
2-1. Rails用Dockerfile
Ruby と Node.js、yarnのバージョン指定は後述する docker-compose.yml から引数として指定します。
Rails6からは標準で Webpacker というgemを使用しており、yarn というパッケージ管理ソフトに依存しています。
yarn がないとうまく動かないため、これをコンテナにインストールする必要があります。
また entrypoint.sh についてですが、Dockerをコマンドで停止せずに強制終了してしまうと、Railsサーバーが開かれたままになるため、次からコンテナを起動するときにエラーが発生します。
その問題を回避するために server.pid を削除しています。
$ vim Dockerfile
ARG RUBY_VERSION FROM ruby:$RUBY_VERSION ARG NODE_MAJOR ARG YARN_VERSION # ログインシェルとしてbashを使用する SHELL ["/bin/bash", "-c"] # nodejs取得 RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash - # yarn取得 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - &&\ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list # リポジトリを更新し依存モジュールをインストール RUN apt-get update -qq &&\ apt-get install -y\ build-essential\ nodejs\ yarn=$YARN_VERSION-1 # ルート直下にwebappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ) RUN mkdir /webapp WORKDIR /webapp # ホストのGemfileとGemfile.lockをコンテナにコピー ADD Gemfile /webapp/Gemfile ADD Gemfile.lock /webapp/Gemfile.lock # bundle installの実行 RUN bundle install # ホストのアプリケーションディレクトリ内をすべてコンテナにコピー ADD . /webapp # puma.sockを配置するディレクトリを作成 RUN mkdir -p tmp/sockets # コンテナ開始時に必ず実行されるシェルスクリプトをコンテナにコピー ADD entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"]
2-2. Gemfile
Railsの最新バージョンをこのファイルで指定します。
$ vim Gemfile
source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem 'rails', '6.0.3.2'
2-3. Gemfile.lock
このファイルは作るだけで、中身は空で大丈夫です。
$ touch Gemfile.lock
2-4. Nginx用Dockerfile
$ vim containers/nginx/Dockerfile
FROM nginx:1.19.0 # インクルード用のディレクトリ内を削除 RUN rm -f /etc/nginx/conf.d/* # Nginxの設定ファイルをコンテナにコピー ADD nginx.conf /etc/nginx/conf.d/webapp.conf # ビルド完了後にNginxを起動 CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
2-5. Nginx用設定ファイル
$ vim containers/nginx/nginx.conf
# プロキシ先の指定 # Nginxが受け取ったリクエストをバックエンドのpumaに送信 upstream webapp { # ソケット通信したいのでpuma.sockを指定 server unix:///webapp/tmp/sockets/puma.sock; } server { listen 80; # ドメインもしくはIPを指定 server_name example.com [or 192.168.xx.xx [or localhost]]; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # ドキュメントルートの指定 root /webapp/public; client_max_body_size 100m; error_page 404 /404.html; error_page 505 502 503 504 /500.html; try_files $uri/index.html $uri @webapp; keepalive_timeout 5; # リバースプロキシ関連の設定 location @webapp { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://webapp; } }
2-6. DB接続用設定ファイル
ユーザー名・パスワードなどは適宜変更してください。
$ vim environments/db.env
MYSQL_ROOT_PASSWORD=db_root_password MYSQL_USER=user_name MYSQL_PASSWORD=user_password
2-7. entrypoint.sh
#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"
2-8. docker-compose.yml
argsにRubyなどのバージョンを指定します。
ここで指定することによりDockerfileに値が渡されます。
$ vim docker-compose.yml
version: '3.7' services: app: build: context: . args: RUBY_VERSION: '2.7.1' NODE_MAJOR: '14' YARN_VERSION: '1.22.4' env_file: - ./environments/db.env command: bundle exec puma -C config/puma.rb volumes: - .:/webapp - public-data:/webapp/public - tmp-data:/webapp/tmp - log-data:/webapp/log depends_on: - db db: image: mysql:8.0 env_file: - ./environments/db.env volumes: - db-data:/var/lib/mysql web: build: context: containers/nginx volumes: - public-data:/webapp/public - tmp-data:/webapp/tmp ports: - 80:80 depends_on: - app volumes: public-data: tmp-data: log-data: db-data:
3. Railsアプリケーションの生成と編集
3-1. Railsアプリケーションの生成
ここまでファイルとディレクトリを準備できたら、Railsアプリケーションを作成します。
以下のコマンドを実行すると、コンテナ内でRailsアプリケーションが生成されます。
DBにはMySQLを指定し、gemをこの時点でインストールしないようにbundle installの実行を抑制しています。
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle
コンテナ内で生成されたアプリケーションをホスト側から編集するには、いちいちコンテナを実行しないといけないため不便です。
そこで、コンテナ内に生成されたディレクトリと、ホスト側のアプリケーションルートを繋ぎます。
こうすることによって、ホスト側のアプリケーションルートにファイルが生成されます。
これらのファイルを編集すれば、コンテナ内のディレクトリにも反映されるようになります。
(既にこの対応は以下の箇所で行っているので、安心してください。)
#省略 volumes: - .:/webapp #省略
実行時に発生するエラー・警告について
Railsアプリケーション生成コマンド実行時、いくつかエラーと警告が発生します。
「失敗した!」と思われる前に、以下に記載するものについては無視してください。
(記載した以外のエラーや警告がもし発生した場合、一度最後まで手順を実行することをおすすめします。)
apt-key
deb.nodesource.comから落としてきたシェルスクリプトに記載されたapt-keyコマンドで発生した警告です。
詳細を調べてもいまいちよくわからなかったのですが、これを無視してもRailsの環境は構築できます。
(誰か詳しい方がいたら教えてください…)
Step 6/18 : RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash - ・ ・ ・ + curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - Warning: apt-key output should not be parsed (stdout is not a terminal)
debconf
これもいまいちよくわかりません。(書いといてなんだって話なんですが。)
apt-utilsをインストールしても消えなかったです。
無視しても大丈夫です。
Step 8/18 : RUN apt-get update -qq && apt-get install -y build-essential nodejs yarn=$YARN_VERSION-1 ・ ・ ・ debconf: delaying package configuration, since apt-utils is not installed
mysql2
Railsアプリケーション生成コマンドを実行して、最後に出力されたのがこのエラーです。
Gemfileにリストされたmysql2のgemがないよってことで、bundle installを迫ってきています。
このコマンドを実行した時点では、GemfileにはRailsしか記載されておらず、推測ですが、下記の流れで処理されているのではないかと思います。
- Gemfileに記載されたRalilsがインストールされる
- Rails newにより、Gemfileの記載が更新されmysql2などが追加される
- 最後に、Gemfileに書かれたRails以外のgemがインストールされていないためエラーが発生
このエラーを無視してもWelcomeページは確認できるので、一度最後まで手順を実施してみてください。
Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.
3-2. 権限変更
生成したRailsアプリケーションのファイル群の所有権が root となっているので、現在のログインユーザーに変更します。
$ sudo chown -R $USER:$USER .
3-3. puma.rbの編集
$ vim config/puma.rb
元の記載は削除して、以下の内容を貼り付けてください。
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i threads threads_count, threads_count port ENV.fetch("PORT") { 3000 } environment ENV.fetch("RAILS_ENV") { "development" } plugin :tmp_restart app_root = File.expand_path("../..", __FILE__) bind "unix://#{app_root}/tmp/sockets/puma.sock" stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
3-4. database.ymlの編集
$ vim config/database.yml
元の記載は削除して、以下の内容を貼り付けてください。
default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV.fetch('MYSQL_USER') { 'root' } %> password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %> host: db development: <<: *default database: webapp_development test: <<: *default database: webapp_test
上記のMYSQL_USER と MYSQL_PASSWORD は DB接続用の情報ファイル で定義した環境変数名を設定します。
4. イメージのビルドとコンテナの起動
いよいよコンテナを起動します!
4-1. イメージのビルド
Dockerfile 及び、DockerHub より引っ張ってきたイメージを全てビルドします。
$ docker-compose build
ビルドが完了したら以下のコマンドで確認します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE webapp_web latest 0ae7b3fc51fd 38 seconds ago 132MB webapp_app latest d661a9898271 47 seconds ago 1.27GB <none> <none> 83d4ec18ac0c 6 minutes ago 1.06GB ruby 2.7.1 9b840f43471e 9 days ago 842MB nginx 1.19.0 2622e6cca7eb 3 weeks ago 132MB mysql 8.0 be0dbf01a0f3 3 weeks ago 541MB
4-2. コンテナの起動
ビルドしたら、下記のコマンドでコンテナを立ち上げます。
$ docker-compose up -d
コンテナが起動しているか確認します。
$ docker-compose ps
全てのコンテナの State が Up となっていることを確認してください。
Name Command State Ports --------------------------------------------------------------------------- webapp_app_1 entrypoint.sh bundle exec ... Up webapp_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp webapp_web_1 /docker-entrypoint.sh /bin ... Up 0.0.0.0:80->80/tcp
ビルドに失敗した場合
イメージのビルドに失敗したり、コンテナの起動に失敗するとローカルにゴミファイルがたまってしまいます。
一度全てきれいにしたい場合は、コンテナとイメージを全て削除するコマンドを使います。
まずは、コンテナの起動を止めてから実行してください。
コンテナを停止する
$ docker-compose stop
すべてのコンテナを削除する
$ docker rm $(docker ps -q -a)
すべてのイメージを削除する
$ docker rmi $(docker images -q)
5. DB設定
5-1. 権限の付与
DBの操作を一般ユーザーで行うため、実行権限を付与します。
GRANT文を記述したSQLファイルを作成します。
user_name は DB接続用の情報ファイル に設定した MYSQL_USER の値に置き換えてください。
$ vim db/grant_user.sql
GRANT ALL PRIVILEGES ON *.* TO 'user_name'@'%'; FLUSH PRIVILEGES;
dbコンテナに向けてクエリを送ります。
パスワードを求められるので、rootのパスワードを入力してください。
$ docker-compose exec db mysql -u root -p -e"$(cat db/grant_user.sql)"
権限が付与されたか確認します。
パスワードを求められるので、一般ユーザーのパスワードを入力してください。
$ docker-compose exec db mysql -u user_name -p -e"show grants;"
実行結果が横に長くてみづらいかもしれません。
MySQL5系では全ての権限が付与されている場合、「ALL PRIVILEGES」と表示されていたみたいですが、8系ではちゃんと全ての権限名が表示されるため、このような横に長い結果になっているようです。
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Grants for user_name@% | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `user_name`@`%` | | GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,INNODB_REDO_LOG_ARCHIVE,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,XA_RECOVER_ADMIN ON *.* TO `user_name`@`%` | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
5-2. DBの作成
railsコマンドでDBを作成します。
$ docker-compose exec app rails db:create
6. 確認
お疲れさまでした!
下記のlocalhostをクリックして、Welcomeページが表示されるでしょうか。
Rails と Ruby のバージョンが指定したものであるか、確認してください。
あとがき
うまくWelcomeページが表示されましたでしょうか?
参考記事を見ながら構築していったのですが、Rails6用に書かれていなかったため、かなり苦労しました…。(Rails学ぶことが目的だったので、Rails5でよかったですね…)
Rails6に対応したあとはバージョンの記載を一元管理したり、色々なサイトで書かれていることを網羅して今回の構築ファイルたちができあがりました。
とりあえずはこの環境でRails勉強していきます!
もし間違い・ご指摘などあればぜひお願いします。
今度はRails関連の記事でお会いしましょう!(多分)