docker run ruby:3.0.0
$ docker run -i -t --rm -v ${PWD}:/usr/src/app ruby:3.2.2 bash > root@b6bfd08833a4:/# root@b6bfd08833a4:/# cd /usr/src/app root@b6bfd08833a4:/# gem install rails root@b6bfd08833a4:/usr/src/app# rails new myapp --skip-test --skip-bundle root@b6bfd08833a4:/usr/src/app# exit $ sudo chown $(id -u):$(id -g) -R .
Create Dockerfile:
FROM ruby:3.2.2 RUN apt-get update -yqq RUN apt-get install -yqq --no-install-recommends nodejs COPY . /usr/src/app WORKDIR /usr/src/app RUN bundle install
Build Image
$ docker build [options] path/to/build/directory for example: $ docker build .
Running a Rails Server
$ docker run -p 3000:3000 image_id bin/rails s -b 0.0.0.0
$ docker tag 848010a87d2b railsapp $ docker tag 848010a87d2b railsapp:1.0
Dockerfile
FROM ruby:3.2.2 RUN apt-get update -yqq RUN apt-get install -yqq --no-install-recommends nodejs COPY . /usr/src/app WORKDIR /usr/src/app RUN bundle install CMD ["bin/rails", "s", "-b", "0.0.0.0"]
$ docker build -t railsapp .
Now we can start server:
$ docker run -p localhost_port:container_port container_name
Create .dockerignore
/# Git .git .gitignore /# Logs log/* /# Temp files tmp/* /# Editor temp files *.swp *.swo
Rebuild image
$ docker build -t railsapp .
Dockerfile
RUN apt-get update -yqq && apt-get install --yqq --no-install-recommends \ nodejs \ vim
Rebuild
$ docker build -t railsapp .
Dockerfile
FROM ruby:3.2.2 RUN apt-get update -yqq && apt-get install --yqq -no-install-recommends \ nodejs \ vim - COPY . /usr/src/app/ + COPY Gemfile* /usr/src/app/ WORKDIR /usr/src/app RUN bundle install + COPY . /usr/src/app/ CMD ["bin/rails", "s", "-b", "0.0.0.0"]
Rebuild
$ docker build -t railsapp .
LABEL <key>=<value> for example: LABEL maintainer="msypniewski511@gmail.com"
Create docker-compose.yml file.
services: web: build: . ports: - "4000:3000"
Lunch app
$ docker-compose up
Mounting a local volume
docker-comose.yml
services: web: build: . ports: - " 4000:3000" volumes: - .:/usr/src/app
Relaunch:
$ docker-compose up -d
In docker-compose.yml file
services: web: build: . ports: - " 4000:3000" volumes: - .:/usr/src/app + redis: + image: redis
Start Redis server
$ docker-compose up -d redis
Connect to Redis
$ docker-compose run -rm redis redis-cli -h redis
Connect app to redois
$ docker-compose stop web
Add redis gem in Gemfile
gem 'redis' , '~> 4.0'
$ docker-compose build web
Starting all
$ docker-compose stop $ docker-compose up -d
Add in docker-compose.yml
services: web: build: . ports: - "4000:3000" volumes: - .:/usr/src/app redis: image: redis + database: + image: postgres + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: some-long-secure-password + POSTGRES_DB: myapp_development
Start postgres
$ docker-compose up -d database
Connecting Rails App to Postgres
in Gemfile file change gem 'sql' to gem 'pg', '~>1.0'
rebuild image
$ docker-comose stop web $ docker-compose build web
config/database.yml
default: &default adapter: PostgreSQL encoding: unicode host: <%= ENV.fetch('DATABASE_HOST') %> username: <%= ENV.fetch('POSTGRES_USER') %> password: <%= ENV.fetch('POSTGRES_PASSWORD') %> database: <%= ENV.fetch('POSTGRES_DB') %> pool: 5 variables: statement_timeout: 5000 development: <<: *default test: <<: *default database: myapp_test production: <<: *default
$ mkdir -p .env/developmentCreate web and database files.
web fileDATABASE_HOST=database
database file
POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=myapp_development
docker-compose.yml
fileservices: web: build: . ports: - "4000:3000" volumes: - .:/usr/src/app env_file - .env/development/database - .env/development/web redis: image: redis database: image: postgres env_file: - .env/development/database
$ docker-compose run --rm web rails db:create
$ docker-compose up -d --force-recreate web
$ docker-compose exec web rails g scaffold User first_name:string last_name:string $ sudo chown <your_user>:<your_group> -R . $ docker-compose exec web rails db:migrate
Decoupling data from the container
docker-compose.yml
services: web: build: . ports: - "4000:3000" volumes: - .:/usr/src/app env_file - .env/development/database - .env/development/web redis: image: redis database: image: postgres env_file: - .env/development/database volumes: - db_data:/var/lib/postgresql/data volumes: db_data:
$ docker-compose stop database $ docker-compose rm -f database $ docker-compose up -d database Recreate database in service $ docker-compose exec web rails db:create db:migrarateTo check where actually is stored our data:
$ docker volume inspect --format '{{ 'Mountpoint' }} myapp_db_data
Gemfile
filegroup :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 6.0.0' end
$ docker-compose stop web $ docker-compose build web $ docker-compose up -d --force-recreate web # install RSpec $ docker-compose exec web rails generate rspec:install # check installation $ docker-compose exec web rspec
In Gemfile
add capybara gem
group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 6.0.0' gem 'capybara' end
Rebuild image:
$ docker-compose build web $ docker-compose stop web $ docker-compose up -d --force-recreate web
Create a folder for system/feature tests
$ mkdir spec/system
Edit spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' require 'capybara/rails' require 'capybara/rspec' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # You can uncomment this line to turn off ActiveRecord support entirely. # config.use_active_record = false # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and # `post` in specs under `spec/controllers`. # # You can disable this behaviour by removing the line below, and instead # explicitly tag your specs with their type, e.g.: # # RSpec.describe UsersController, type: :controller do # # end # # The different available types are documented in the features, such as in # https://rspec.info/features/6-0/rspec-rails config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") config.before( :each, type: :system) do driven_by :rack_test end end
# check installation $ docker-compose exec web rspec spec/system
In Gemfile
file add selenium-webdriver gem:
group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 6.0.0' gem 'capybara' gem 'selenium-webdriver', '~> 4.4' end
Rebuild image:
$ docker-compose build web $ docker-compose stop web $ docker-compose up -d --force-recreate web
Edit docker-compose.yml
file:
selenium_chrome: image: selenium/standalone-chrome logging: driver: none ports: - "5900:4444"
Start selenium service:
$ docker-compose up -d selenium_chrome
Configure Capybara to use Chrome spec/support/capybara.rb
:
Capybara.register_driver :selenium_chrome_in_container do |app| Capybara::Selenium::Driver.new( app, browser: :remote, options: Selenium::WebDriver::Options.chrome, url: 'http://selenium_chrome:4444/wd/hub' ) end
Configuring RSpec System Tests - spec/rails_helper.rb
:
# This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' require 'capybara/rails' require 'capybara/rspec' require_relative './support/capybara.rb' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # You can uncomment this line to turn off ActiveRecord support entirely. # config.use_active_record = false # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and # `post` in specs under `spec/controllers`. # # You can disable this behaviour by removing the line below, and instead # explicitly tag your specs with their type, e.g.: # # RSpec.describe UsersController, type: :controller do # # end # # The different available types are documented in the features, such as in # https://rspec.info/features/6-0/rspec-rails config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") config.before( :each, type: :system) do driven_by :rack_test end config.before(:each, type: :system, js: true) do driven_by :selenium_chrome_in_container Capybara.server_host = "0.0.0.0" Capybara.server_port = 4002 Capybara.app_host = 'http://web:4002' end end
Update web services with new ports:
web: build: . ports: - "4444:3000" - "4001:4002"
# Pick up change $ docker-compose stop web $ docker-compose up -d --force-recreate web
To replay you need to login. Don't have an account? Sign up for one.