PG1X WIKI

My Knowledge Base

User Tools

Site Tools


tech:infra:docker:rails-development-with-docker:rails-development-with-docker

Rails Development with Docker

Bootstrapping

慣れてる人向け

If you familiar with Docker Application Development, simply clone following repository.

$ git clone https://github.com/wnoguchi/template-rails-with-docker.git awesome-web-app
Cloning into 'awesome-web-app'...
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 9 (delta 1), reused 8 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), done.

then

cd awesome-web-app/
git ls-files -z | xargs -0 sed -i '' -e 's/myapp/awesome-web-app/g'
rm -rf .git/
diff --git a/Dockerfile b/Dockerfile
index 04c0d27..fcb3798 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,13 +1,13 @@
 FROM ruby:2.4
 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
-RUN mkdir /myapp
-WORKDIR /myapp
-COPY Gemfile /myapp/Gemfile
-COPY Gemfile.lock /myapp/Gemfile.lock
+RUN mkdir /awesome-web-app
+WORKDIR /awesome-web-app
+COPY Gemfile /awesome-web-app/Gemfile
+COPY Gemfile.lock /awesome-web-app/Gemfile.lock
 RUN echo 'install: --no-document' >> ~/.gemrc && \
   echo 'update: --no-document' >> ~/.gemrc && \
   cp ~/.gemrc /etc/gemrc && \
   chmod uog+r /etc/gemrc
 RUN bundle install -j4
-COPY . /myapp
+COPY . /awesome-web-app
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 631c085..b3115a5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,11 +3,11 @@ services:
   db:
     image: postgres
   web:
-    image: myapp
+    image: awesome-web-app
     build: .
     command: bundle exec rails s -p 3000 -b '0.0.0.0'
     volumes:
-      - .:/myapp
+      - .:/awesome-web-app
     ports:
       - "3000:3000"
     depends_on:

Docker での開発に慣れてない人向け

上でやってることをイチからやっていきます。 まずは空ディレクトリを作って cd する。

myapp はアプリ名に置換。( awesome-web-app とか)

mkdir myapp
cd myapp

Dockerfile を直下に作る。

Dockerfile
FROM ruby:2.4
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN echo 'install: --no-document' >> ~/.gemrc && \
  echo 'update: --no-document' >> ~/.gemrc && \
  cp ~/.gemrc /etc/gemrc && \
  chmod uog+r /etc/gemrc
RUN bundle install -j4
COPY . /myapp

Bundlerで並列処理??bundle installを爆速で処理する方法。 - Qiita

-j4 大事。 4 並列で bundle install を実行するということ。

今の最新をとってきます。

Gemfile
source 'https://rubygems.org'
gem 'rails'

めんどくさい人向け

cat <<EOF >Gemfile
source 'https://rubygems.org'
gem 'rails'
EOF

空の Gemfile.lock を用意しておく。

touch Gemfile.lock

アプリを識別するタグ名は image のところ書き換えて。 すなわち myappawesome-web-app に全置換。

docker-compose.yml
version: '3'
services:
  db:
    image: postgres
  web:
    image: myapp
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
$ ls -l
total 24
-rw-r--r--  1 wnoguchi  staff  259 Feb  3 20:20 Dockerfile
-rw-r--r--  1 wnoguchi  staff   43 Feb  3 20:30 Gemfile
-rw-r--r--  1 wnoguchi  staff    0 Feb  3 20:30 Gemfile.lock
-rw-r--r--  1 wnoguchi  staff  243 Feb  3 20:41 docker-compose.yml

rails new

いよいよ Rails アプリケーションを作成する。

docker-compose run web rails new . --force --database=postgresql

–forceGemfile を上書きしています。

ひとしきり rails new が終わったら コンテナイメージの再ビルドが必要です。 なぜならコンテナイメージ内の Gemfile が古いままだからです。

docker-compose build

First Commit

このあたりで一発コミットをかます。

Git をまだ設定していないって?

Gitをインストールしたら真っ先にやっておくべき初期設定 - Qiita

git config --global user.name "First-name Family-name"
git config --global user.email "username@example.com"
git config --global core.editor 'vim -c "set fenc=utf-8"'
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global push.default simple
git config --global core.precomposeunicode true
git config --global core.quotepath false
$ git config -l
credential.helper=osxkeychain
user.name=Wataru NOGUCHI
user.email=wnoguchi@pg1x.com
core.editor=vim -c "set fenc=utf-8"
core.precomposeunicode=true
core.quotepath=false
color.diff=auto
color.status=auto
color.branch=auto
push.default=simple
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true

.gitignore もちょっと使いやすく変えておく。 以下を追加する。

ぼくのかんがえたさいきょうの .gitignore - Qiita

.gitignore
# Temporary Files
#---------------------------
# vim
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
# Emacs
.\#*
# Backup files
#---------------------------
*~
*.orig
*.bak
# yyyyMMdd
*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]
# yyyyMMddHHmm
*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]
# yyyyMMddHHmmss
*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]
# OS dependent files
#---------------------------
.DS_Store
Thumbs.db
# Bundler specific
#---------------------------
/vendor/bundle/
/.bundle/
# Office specific
~$*
*.tmp
# Vagrant specific
.vagrant/
$ git init
Reinitialized existing Git repository in /Users/wnoguchi/repos/myapp/.git/
$ vim .gitignore
$ git add -A
$ git commit -m "Initial commit."
[master (root-commit) 65fdcc1] Initial commit.
 78 files changed, 1313 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Dockerfile
 create mode 100644 Gemfile
 create mode 100644 Gemfile.lock
 create mode 100644 README.md
 create mode 100644 Rakefile
 create mode 100644 app/assets/config/manifest.js
 create mode 100644 app/assets/images/.keep
 create mode 100644 app/assets/javascripts/application.js
 create mode 100644 app/assets/javascripts/cable.js
 create mode 100644 app/assets/javascripts/channels/.keep
 create mode 100644 app/assets/stylesheets/application.css
 create mode 100644 app/channels/application_cable/channel.rb
 create mode 100644 app/channels/application_cable/connection.rb
 create mode 100644 app/controllers/application_controller.rb
 create mode 100644 app/controllers/concerns/.keep
 create mode 100644 app/helpers/application_helper.rb
 create mode 100644 app/jobs/application_job.rb
 create mode 100644 app/mailers/application_mailer.rb
 create mode 100644 app/models/application_record.rb
 create mode 100644 app/models/concerns/.keep
 create mode 100644 app/views/layouts/application.html.erb
 create mode 100644 app/views/layouts/mailer.html.erb
 create mode 100644 app/views/layouts/mailer.text.erb
 create mode 100755 bin/bundle
 create mode 100755 bin/rails
 create mode 100755 bin/rake
 create mode 100755 bin/setup
 create mode 100755 bin/spring
 create mode 100755 bin/update
 create mode 100755 bin/yarn
 create mode 100644 config.ru
 create mode 100644 config/application.rb
 create mode 100644 config/boot.rb
 create mode 100644 config/cable.yml
 create mode 100644 config/database.yml
 create mode 100644 config/environment.rb
 create mode 100644 config/environments/development.rb
 create mode 100644 config/environments/production.rb
 create mode 100644 config/environments/test.rb
 create mode 100644 config/initializers/application_controller_renderer.rb
 create mode 100644 config/initializers/assets.rb
 create mode 100644 config/initializers/backtrace_silencers.rb
 create mode 100644 config/initializers/cookies_serializer.rb
 create mode 100644 config/initializers/filter_parameter_logging.rb
 create mode 100644 config/initializers/inflections.rb
 create mode 100644 config/initializers/mime_types.rb
 create mode 100644 config/initializers/wrap_parameters.rb
 create mode 100644 config/locales/en.yml
 create mode 100644 config/puma.rb
 create mode 100644 config/routes.rb
 create mode 100644 config/secrets.yml
 create mode 100644 config/spring.rb
 create mode 100644 db/seeds.rb
 create mode 100644 docker-compose.yml
 create mode 100644 lib/assets/.keep
 create mode 100644 lib/tasks/.keep
 create mode 100644 log/.keep
 create mode 100644 package.json
 create mode 100644 public/404.html
 create mode 100644 public/422.html
 create mode 100644 public/500.html
 create mode 100644 public/apple-touch-icon-precomposed.png
 create mode 100644 public/apple-touch-icon.png
 create mode 100644 public/favicon.ico
 create mode 100644 public/robots.txt
 create mode 100644 test/application_system_test_case.rb
 create mode 100644 test/controllers/.keep
 create mode 100644 test/fixtures/.keep
 create mode 100644 test/fixtures/files/.keep
 create mode 100644 test/helpers/.keep
 create mode 100644 test/integration/.keep
 create mode 100644 test/mailers/.keep
 create mode 100644 test/models/.keep
 create mode 100644 test/system/.keep
 create mode 100644 test/test_helper.rb
 create mode 100644 tmp/.keep
 create mode 100644 vendor/.keep

Configure Database

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
 
# (snip)

次のように追加する

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: db
  username: postgres
  password:

# (snip)
diff --git a/config/database.yml b/config/database.yml
index 31181fd..385bb4d 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -20,6 +20,9 @@ default: &default
   # For details on connection pooling, see Rails configuration guide
   # http://guides.rubyonrails.org/configuring.html#database-pooling
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+  host: db
+  username: postgres
+  password:
 
 development:
   <<: *default

最初の Rails 起動

いよいよ Rails とその関連のコンテナ(PostgreSQL)を起動します。

$ docker-compose up
myapp_db_1 is up-to-date
Creating myapp_web_1 ... done
Attaching to myapp_db_1, myapp_web_1
db_1   | The files belonging to this database system will be owned by user "postgres".
db_1   | This user must also own the server process.
db_1   |
db_1   | The database cluster will be initialized with locale "en_US.utf8".
db_1   | The default database encoding has accordingly been set to "UTF8".
db_1   | The default text search configuration will be set to "english".
db_1   |
db_1   | Data page checksums are disabled.
db_1   |
db_1   | fixing permissions on existing directory /var/lib/postgresql/data ... ok
db_1   | creating subdirectories ... ok
db_1   | selecting default max_connections ... 100
db_1   | selecting default shared_buffers ... 128MB
db_1   | selecting dynamic shared memory implementation ... posix
db_1   | creating configuration files ... ok
db_1   | running bootstrap script ... ok
db_1   | performing post-bootstrap initialization ... ok
db_1   | syncing data to disk ... ok
db_1   |
db_1   | Success. You can now start the database server using:
db_1   |
db_1   |     pg_ctl -D /var/lib/postgresql/data -l logfile start
db_1   |
db_1   |
db_1   | WARNING: enabling "trust" authentication for local connections
db_1   | You can change this by editing pg_hba.conf or using the option -A, or
db_1   | --auth-local and --auth-host, the next time you run initdb.
db_1   | ****************************************************
db_1   | WARNING: No password has been set for the database.
db_1   |          This will allow anyone with access to the
db_1   |          Postgres port to access your database. In
db_1   |          Docker's default configuration, this is
db_1   |          effectively any other container on the same
db_1   |          system.
db_1   |
db_1   |          Use "-e POSTGRES_PASSWORD=password" to set
db_1   |          it in "docker run".
db_1   | ****************************************************
db_1   | waiting for server to start....2017-12-06 03:28:24.625 UTC [41] LOG:  listening on IPv4 address "127.0.0.1", port 5432
db_1   | 2017-12-06 03:28:24.625 UTC [41] LOG:  could not bind IPv6 address "::1": Cannot assign requested address
db_1   | 2017-12-06 03:28:24.625 UTC [41] HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
db_1   | 2017-12-06 03:28:24.627 UTC [41] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1   | 2017-12-06 03:28:24.636 UTC [42] LOG:  database system was shut down at 2017-12-06 03:28:24 UTC
db_1   | 2017-12-06 03:28:24.641 UTC [41] LOG:  database system is ready to accept connections
db_1   |  done
db_1   | server started
db_1   | ALTER ROLE
db_1   |
db_1   |
db_1   | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db_1   |
db_1   | 2017-12-06 03:28:24.759 UTC [41] LOG:  received fast shutdown request
db_1   | waiting for server to shut down....2017-12-06 03:28:24.761 UTC [41] LOG:  aborting any active transactions
db_1   | 2017-12-06 03:28:24.764 UTC [41] LOG:  worker process: logical replication launcher (PID 48) exited with exit code 1
db_1   | 2017-12-06 03:28:24.765 UTC [43] LOG:  shutting down
db_1   | 2017-12-06 03:28:24.789 UTC [41] LOG:  database system is shut down
db_1   |  done
db_1   | server stopped
db_1   |
db_1   | PostgreSQL init process complete; ready for start up.
db_1   |
db_1   | 2017-12-06 03:28:24.870 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1   | 2017-12-06 03:28:24.870 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1   | 2017-12-06 03:28:24.872 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1   | 2017-12-06 03:28:24.882 UTC [59] LOG:  database system was shut down at 2017-12-06 03:28:24 UTC
db_1   | 2017-12-06 03:28:24.886 UTC [1] LOG:  database system is ready to accept connections
db_1   | 2017-12-06 04:20:09.867 UTC [1] LOG:  received smart shutdown request
db_1   | 2017-12-06 04:20:09.870 UTC [1] LOG:  worker process: logical replication launcher (PID 65) exited with exit code 1
db_1   | 2017-12-06 04:20:09.871 UTC [60] LOG:  shutting down
db_1   | 2017-12-06 04:20:09.881 UTC [1] LOG:  database system is shut down
db_1   | 2018-02-03 11:55:12.200 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1   | 2018-02-03 11:55:12.200 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1   | 2018-02-03 11:55:12.203 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1   | 2018-02-03 11:55:12.216 UTC [24] LOG:  database system was shut down at 2017-12-06 04:20:09 UTC
db_1   | 2018-02-03 11:55:12.226 UTC [1] LOG:  database system is ready to accept connections
web_1  | => Booting Puma
web_1  | => Rails 5.1.4 application starting in development
web_1  | => Run `rails server -h` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 3.11.2 (ruby 2.4.2-p198), codename: Love Song
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop

ブラウザを開いて下記 URL を開く。

http://localhost:3000/

ターミナルには以下のようなログが流れるはずだ。

web_1  | Started GET "/" for 172.18.0.1 at 2018-02-03 12:52:26 +0000
web_1  | Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
web_1  | Processing by Rails::WelcomeController#index as HTML
web_1  |   Rendering /usr/local/bundle/gems/railties-5.1.4/lib/rails/templates/rails/welcome/index.html.erb
web_1  |   Rendered /usr/local/bundle/gems/railties-5.1.4/lib/rails/templates/rails/welcome/index.html.erb (5.0ms)
web_1  | Completed 200 OK in 502ms (Views: 17.9ms)
% docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
4fde53d3ee98        myapp               "bundle exec rails s…"   4 minutes ago       Up 5 minutes        0.0.0.0:3000->3000/tcp   myapp_web_1
a2eadc8592b8        postgres            "docker-entrypoint.s…"   About an hour ago   Up About an hour    5432/tcp                 amaknowledge_db_1
97a6bbebbe7e        postgres            "docker-entrypoint.s…"   8 weeks ago         Up About an hour    5432/tcp                 myapp_db_1
➜  myapp git:(master) ✗ docker exec -it myapp_web_1 bash
root@4fde53d3ee98:/myapp#

止めるときは Ctrl+C してねって書いてあるけど、 Docker コンテナの場合はこれやっちゃダメ。 この場合はアプリケーションサーバ Puma の強制停止に相当するので pid ファイルが残ってしまう。 終了するときは別ターミナル開いて docker-compose down するのが正しい。

正しい終了の仕方

$ docker-compose down
Stopping myapp_web_1 ... done
Stopping myapp_db_1  ... done
Removing myapp_web_1     ... done
Removing myapp_web_run_2 ... done
Removing myapp_web_run_1 ... done
Removing myapp_db_1      ... done
Removing network myapp_default

間違って終了してしまったとき

すでに Rails のプロセスが起動していると勘違いしてコンテナが異常終了してしまう。

(snip)
web_1  | => Booting Puma
web_1  | => Rails 5.1.4 application starting in development
web_1  | => Run `rails server -h` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 3.11.2 (ruby 2.4.2-p198), codename: Love Song
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop
Killing myapp_web_1 ... done
Killing myapp_db_1  ... done

$  myapp git:(master) ✗ docker-compose up
Starting myapp_db_1 ... done
Starting myapp_web_1 ... done
Attaching to myapp_db_1, myapp_web_1
db_1   | 2018-02-03 13:23:31.487 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1   | 2018-02-03 13:23:31.487 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1   | 2018-02-03 13:23:31.490 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1   | 2018-02-03 13:23:31.502 UTC [24] LOG:  database system was interrupted; last known up at 2018-02-03 13:21:33 UTC
db_1   | 2018-02-03 13:23:31.804 UTC [24] LOG:  database system was not properly shut down; automatic recovery in progress
db_1   | 2018-02-03 13:23:31.806 UTC [24] LOG:  redo starts at 0/16349D8
db_1   | 2018-02-03 13:23:31.806 UTC [24] LOG:  invalid record length at 0/1634B00: wanted 24, got 0
db_1   | 2018-02-03 13:23:31.806 UTC [24] LOG:  redo done at 0/1634AC8
db_1   | 2018-02-03 13:23:31.806 UTC [24] LOG:  last completed transaction was at log time 2018-02-03 13:21:33.703436+00
db_1   | 2018-02-03 13:23:31.819 UTC [1] LOG:  database system is ready to accept connections
web_1  | A server is already running. Check /myapp/tmp/pids/server.pid.
web_1  | => Booting Puma
web_1  | => Rails 5.1.4 application starting in development
web_1  | => Run `rails server -h` for more startup options
web_1  | Exiting
myapp_web_1 exited with code 1
^CERROR: Aborting.
You can also stop the application with Ctrl-C in the same shell in which you executed the docker-compose up. If you stop the app this way, and attempt to restart it, you might get the following error:

以下のようにする。

rm tmp/pids/server.pid

これで再度 docker-compose up すればいい。 終了するときは別ターミナル開いて docker-compose down 。忘れずに。

db 作成

$ docker-compose run web rake db:create
Creating network "myapp_default" with the default driver
Creating myapp_db_1 ... done
Created database 'myapp_development'
Created database 'myapp_test'

次は・・・

あとは普通の Rails の開発となんら変わりはしない。 docker-compose run web をプレフィックスとしてやっていけばいいだけ。

よく使うコマンドを列挙しておく。

docker-compose up
docker-compose down
docker-compose run web rake db:create
docker-compose run web rake db:migrate
docker-compose run web rails generate scaffold question title:string description:string

あとは Rails Guide を読んどけばいいでしょう。

Ruby on Rails Guides

tech/infra/docker/rails-development-with-docker/rails-development-with-docker.txt · Last modified: 2018/02/03 23:10 by wnoguchi