This is an example and tutorial to build and run your Rails app on Docker.
Replace `pwd` with %CD% in your Command Prompt or PowerShell.
Otherwise you would see an error like following:
docker: Error response from daemon: create `pwd`/app: “
pwd/app” includes invalid characters for a local volume name, only “[a-zA-Z0-9][a-zA-Z0-9_.-]” are allowed. If you intended to pass a host directory, use absolute path.
You need followings:
- Docker installed
Clone this repository.
$ git clone git@github.com:ginpei/docker-rails-example.git
$ cd docker-rails-example/Up Docker container.
$ docker-compose upOpen another console and run this to initialize database.
$ cd docker-rails-example/
$ docker-compose exec rails rake db:create db:migrate db:seedOpen in browser.
To stop, in the console where docker-compose is running, hit Ctrl + C and wait.
Gracefully stopping... (press Ctrl+C again to force)
Stopping docker-rails-example_rails_1 ... done
Stopping docker-rails-example_db_1 ... done
$- Prepare a directory for the app
- Run
rails newin Docker - Describe a Docker image for your app where gems are installed
- Database configuration
- Prepare Docker component file with database specification
- Ignore database files
- Up them
- Stop them (once)
- Open another console
- Initialize database
- Open in browser
We name the app "my-great-app". Please do not forget replace this with your great app's name.
Directory structure would be this:
my-great-app/
+ app/
| - (Rails app files like `config.ru`)
+ db/
| - (Database files)
+ .gitignore
+ docker-compose.yml
+ Dockerfile
+ README.md
$ mkdir my-great-app
$ cd my-great-appWe will be working here.
This step can be broken down into smaller steps.
- Start docker container
rails new- Create empty
Gemfile.lock - Expose created files to your host computer
- Exit from docker container
First of all, run a docker container:
$ mkdir app
$ docker run --rm -ti -v `pwd`/app:/app rails:5.0.1 bash5.0.1 is a tag of the image, as Rails version. Find available tags here:
This should take long time when you try at the first time since Docker downloads a Docker image of Rails. From next time, it would up soon.
Then you'll see you are in docker container. You may want to check the rails version:
root@b4227fbcb3b1:/# rails --version
Rails 5.0.1
root@b4227fbcb3b1:/#Note that, inside a Docker container, you cannot use Ctrl-P. This is a special key for Docker.
In the container, run rails new with some options.
root@b4227fbcb3b1:/# rails new my-great-app --skip-bundle --database=mysql
create
create README.md
create Rakefile
create config.ru
...
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.keep
remove config/initializers/cors.rb
root@b4227fbcb3b1:/#--skip-bundle option, as you read, skips bundle install.
The installation will happen later so you don't do that for now.
--database option is up to you.
In this tutorial, we chose MySQL as the DBMS for our Rails app.
Since we skipped bundle install, we need empty lock file for after operation.
$ touch /my-great-app/Gemfile.lockTo expose result to you host computer, move the generated files to the shared volume. And exit.
root@b4227fbcb3b1:/# cp -rT my-great-app/* /app
root@b4227fbcb3b1:/# exit-T option of cp is to copy dot files (.gitignore).
(The reason we didn't generate files directly in /app is Rails generates files using the directory name with template. Please let me know if you know the way to do that directly.)
Check the files exist on your computer outside Docker container.
$ ls -a app/
. .gitignore Gemfile.lock Rakefile bin config.ru lib public tmp
.. Gemfile README.md app config db log test vendorCreate a new file named Dockerfile, which has no extension:
FROM rails:5.0.1
RUN mkdir /app
WORKDIR /app
COPY ./app/Gemfile /app/Gemfile
COPY ./app/Gemfile.lock /app/Gemfile.lock
RUN bundle install
CMD rm /app/tmp/pids/server.pid ; rails sAlthough we haven't got database ready yet, modify rails configuration a little.
Change host: localhost to host: db in app/config/database.yml:
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password:
host: dbThis db will be used in the next step as a service name in docker-compose.yml.
Create a new file named docker-compose.yml as following:
version: "3"
services:
rails:
build: ./
ports:
- "3000:3000"
volumes:
- ./app:/app
depends_on:
- db
db:
image: mysql
volumes:
- ./db:/var/lib/mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"This prepares a database for root user without password. It's unsafe in some cases but you know this is only for development.
If you use git, create .gitignore and ignore database files:
/db/
$ docker-compose upThis takes longer time for the first time too.
First, it builds an image from your Dockerfile for your project. This process includes bundle install.
Second, after starting, MySQL creates database files in db.
Take your time until your console gets calm down.
Try to stop the containers by holding Ctrl-C in the console. Closing process may take time. Be patient.
Gracefully stopping... (press Ctrl+C again to force)
Stopping docker-rails-example_rails_1 ... done
Stopping docker-rails-example_db_1 ... done
$If you get how to up and end it, up again and move on to the next.
Keep the Docker containers running.
While they are running, you need to open a new console to work.
In the 2nd console just you opened, type following line to create database while running docker-compose up in the 1st one.
$ docker-compose exec rails rake db:create
Created database 'my-great-app_development'
Created database 'my-great-app_test'Open http://localhost:3000/ and look at what you have done! Yay! You’re on Rails!
Now it's simple.
$ docker-compose upAnd open http://localhost:3000/.
You may need the 2nd console to run commands like rails g scaffold.
You may want to run rails, rake or any other commands. For those cases, the most basic idea is running docker-compose exec rails xxx.
Let's say you want to echo here. You would use this:
$ docker-compose exec rails echo Hello from Docker container!Please remember you have to type this command into your 2nd console, next the 1st one where your docker-compose up is running.
$ docker-compose exec rails rails g scaffold post title:string body:text
invoke active_record
create db/migrate/20180806212720_create_posts.rb
create /models/post.rb
...
create /assets/stylesheets/posts.scss
invoke scss
create /assets/stylesheets/scaffolds.scssDon't forget migration! (See next chapter.)
$ docker-compose exec rails rake db:migrate
== 20180806212720 CreatePosts: migrating ======================================
-- create_table(:posts)
-> 0.0766s
== 20180806212720 CreatePosts: migrated (0.0769s) =============================$ docker-compose exec rails rake test
Run options: --seed 52703
# Running:
.......
Finished in 1.302024s, 5.3762 runs/s, 6.9123 assertions/s.
7 runs, 9 assertions, 0 failures, 0 errors, 0 skipsWhen you update Gemfile, you used to run bundle install.
With Docker, you need to rebuild your image instead. This process includes bundle install and updates Gemfile.lock.
To rebuild, give --force-recreate option.
$ docker-compose.exe up --buildOtherwise you would see an error like this (you could miss it because the log is too long to read):
rails_1 | /usr/local/lib/ruby/gems/2.3.0/gems/bundler-1.13.7/lib/bundler/resolver.rb:366:in `block in verify_gemfile_dependencies_are_found!': Could not find gem 'carrierwave' in any of the gem sources listed in your Gemfile or available on this machine. (Bundler::GemNotFound)
rails_1 | from /usr/local/lib/ruby/gems/2.3.0/gems/bundler-1.13.7/lib/bundler/resolver.rb:341:in `each'
...
rails_1 | from /usr/local/bundle/bin/rails:15:in `<main>'
These steps should be described in the project's README documentation.
- Clone the repository
- Up Docker containers
- Initialize database
- Start your job
$ git clone ...
$ cd xxx$ docker-compose upHold Ctrl-C to stop.
Open another console and run:
$ docker-compose exec rails rake db:create db:migrate db:seedOpen http://localhost:3000/ (and another console maybe) and start your job.
Let's rock!
