Home>
I want to build Rails using Docker

I am trying to build a local server with Rails by setting the following 4 files.
Docker version is v19.03.12

Dockerfile

FROM ruby:2.5.0
ENV LANG C.UTF-8
RUN apt-get update -qq&&apt-get install -y build-essential mysql-client nodejs
RUN gem install bundler
WORKDIR /tmp
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
RUN bundle install
ENV APP_HOME /app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD .$APP_HOME


docker-compose.yml

version: '2'
services:
  web:
    build:
      context:.
      dockerfile: ./docker/Dockerfile
    container_name: "app_web"
    command: bash -c "rake db:create&&rake db:migrate&&bundle exec rails s"
    ports:
      -"3000:3000"
    volumes:
      -.:/app
    environment:
      RAILS_ENV: development
      DOCKER_HOST_IP: host.docker.internal
    # DOCKER_HOST_IP: docker.for.mac.host.internal
    tty: true
    stdin_open: true


docker-compose2.yml

version: '2'
services:
  app:
    build:
      context:.
      dockerfile: ./Dockerfile
    entrypoint: /wait-for-it.sh -t 180 -s db:3306 -s db-test:3306 -
    command: bash -c "rake db:create&&rake db:migrate&&bundle exec rails s"
    ports:
      -${APP_PORT}:3000
    volumes:
      -.:/app
      -./docker/wait-for-it.sh:/wait-for-it.sh
    environment:
      RAILS_ENV: development
      MYSQL_HOST: db
      MYSQL_TEST_HOST: db-test
      MYSQL_USER: user
      MYSQL_PASSWORD: passwordMYSQL_DATABASE: app
    env_file:
      -.env
    tty: true
    stdin_open: true
  db:
    image: mysql:5.7
    # volumes:
    #-./tmp/mysql-data:/var/lib/mysql
    ports:
      -${DB_PORT}:3306
    environment:
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: app
      TZ: Asia/Tokyo
    env_file:
      -.env
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  db-test:
    image: mysql:5.7
    ports:
      -${DB_TEST_PORT}:3306
    environment:
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: app_test
      TZ: Asia/Tokyo
    env_file:
      -.env
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


docker-start.sh

rm tmp/pids/server.pid
docker-compose up -d
docker start app_web
docker attach app_web


config/database.yml

default:&default
  adapter: mysql2
  encoding: utf8mb4
  pool:<%= ENV.fetch("RAILS_MAX_THREADS") {5} %>username:<%= ENV.fetch("MYSQL_USER") {'root'} %>password:<%= ENV.fetch("MYSQL_PASSWORD") {''} %>host:<%= ENV.fetch('MYSQL_HOST') {ENV['DOCKER_HOST_IP']} %>development:
<<: *default
  database:<%= ENV.fetch('MYSQL_DATABASE') {'app_development'} %>test:
<<: *default
  host:<%= ENV.fetch('MYSQL_TEST_HOST') {ENV['DOCKER_HOST_IP']} %>database: app_test
production:
<<: *defaulthost: app.cgbybcmani5q.ap-northeast-1.rds.amazonaws.com
  database: app
  username: root
  password:<%= ENV['APP_DATABASE_PASSWORD'] %>
error

I should be able to build it by running sh docker-start.sh from the command.
I am having trouble with Mysql related errors.

Error message

% sh docker-start.sh
rm: tmp/pids/server.pid: No such file or directory
Starting app_web ... done
app_web
Can't connect to MySQL server on'host.docker.internal' (111 "Connection refused")
Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf8mb4", "pool"=>5, "username"=>"root", "password"=>nil, "host "=>"host.docker.internal", "database"=>"app_development"}
rake aborted!
Mysql2::Error: Can't connect to MySQL server on'host.docker.internal' (111 "Connection refused")
/usr/local/bundle/gems/mysql2-0.4.10/lib/mysql2/client.rb:89:in `connect'
/usr/local/bundle/gems/mysql2-0.4.10/lib/mysql2/client.rb:89:in `initialize'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/mysql2_adapter.rb:22:in `new'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/mysql2_adapter.rb:22:in `mysql2_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:809:in `new_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:853:in `checkout_new_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:832:in `try_to_checkout_new_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:793:in `acquire_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:521:in `checkout'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:380:in `connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:1008:in `retrieve_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_handling.rb:118:in `retrieve_connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/connection_handling.rb:90:in `connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/mysql_database_tasks.rb:6:in `connection'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/mysql_database_tasks.rb:14:in `create'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:119:in `create'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:139:in `block in create_current'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:316:in `block in each_current_configuration'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:313:in `each'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:313:in `each_current_configuration'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/tasks/database_tasks.rb:138:in `create_current'
/usr/local/bundle/gems/activerecord-5.2.0.rc1/lib/active_record/railties/databases.rake:29:in `block (2 levels) in<main>'
/usr/local/bundle/gems/rake-12.3.0/exe/rake:27:in `<top (required)>'
Tasks: TOP =>db:create
(See full trace by running task with --trace)
What i did

When I check the error, it seems that there is no connection between mysql2 and Docker locally.
In the first place, since I am using the image of Mysql, I don't think it's relevant to local, but I set environment variables in zshenv.
Is it also necessary to set it?

Addendum

When running docker-compose -f docker-compose.yml up, the above error message is output

The execution log of% docker-compose -f docker-compose2.yml up is as follows.
%docker-compose -f docker-compose2.yml run --rm app bash -c "rake db:create&&rake db:migrate&&bundle exec rails s"
Will be the same log

WARNING: The APP_PORT variable is not set.Defaulting to a blank string.
WARNING: The DB_PORT variable is not set.Defaulting to a blank string.
WARNING: The DB_TEST_PORT variable is not set.Defaulting to a blank string.
ERROR: The Compose file'./docker-compose2.yml' is invalid because:
services.app.ports is invalid: Port ranges don't match in length
services.db.ports is invalid: Port ranges don't match in length
services.db-test.ports is invalid: Port ranges don't match in length
  • Answer # 1

    Probably first

    docker-compose -f docker-compose2.yml up

    Such asdocker-compose2.ymlAfter starting the container for the service defined in
    docker-start.shI'm running
    It will be an answer on that assumption

    Cause

    User:root, Password: trying to connect without
    Login to the database fails because it doesn't match the database password:

    Can't connect to MySQL server on'host.docker.internal' (111 "Connection refused")
    Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf8mb4", "pool"=>5, "username"=>"root", "password"=>nil, "host "=>"host.docker.internal", "database"=>"app_development"}
    Explanation

    In the log

    "database"=>"app_development"

    Is output,config/database.ymlOf the settings
    developmentSettings are used

    The following settings are used for database connections:

    default:&default
      adapter: mysql2
      encoding: utf8mb4
      pool:<%= ENV.fetch("RAILS_MAX_THREADS") {5} %>username:<%= ENV.fetch("MYSQL_USER") {'root'} %>password:<%= ENV.fetch("MYSQL_PASSWORD") {''} %>host:<%= ENV.fetch('MYSQL_HOST') {ENV['DOCKER_HOST_IP']} %>development:
    <<: *default
      database:<%= ENV.fetch('MYSQL_DATABASE') {'app_development'} %>

    Most of these settings are
    If the environment variable is not set it is defined to use the default value

    docker-start.shStart withdocker-compose.ymlofwebService
    The environment variables only define the following:

      environment:
          RAILS_ENV: development
          DOCKER_HOST_IP: host.docker.internal

    Therefore, the database connection settings areconfig/database.ymlDefault value of is used
    User:root, Password: will try to connect without

    However, the service of the connected database is
    dbEvendb-testEven
    User:rootPassword isMYSQL_ROOT_PASSWORD: rootpasswordAs
    rootpasswordIs set:

    db:
        image: mysql:5.7
        # volumes:
        #-./tmp/mysql-data:/var/lib/mysql
        ports:
          -${DB_PORT}:3306
        environment:
          MYSQL_USER: user
          MYSQL_PASSWORD: password
          MYSQL_ROOT_PASSWORD: rootpassword
          MYSQL_DATABASE: app
          TZ: Asia/Tokyo
        env_file:
          -.env
        command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
      db-test:
        image: mysql:5.7
        ports:
          -${DB_TEST_PORT}:3306
        environment:
          MYSQL_USER: user
          MYSQL_PASSWORD: password
          MYSQL_ROOT_PASSWORD: rootpassword
          MYSQL_DATABASE: app_test
          TZ: Asia/Tokyo
        env_file:
          -.env
        command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

    Because of this, the connection fails

    By the way, I think it's almost impossible,
    ./docker/DockerfileAt the innerENVThis does not apply if you specify MySQL password etc. in the command

    Solution

    if,appService andwebUnless you have a specific reason to separate services,
    You can run the migration with the following command

    docker-compose -f docker-compose2.yml run --rm app bash -c "rake db:create&&rake db:migrate&&bundle exec rails s"

    Then you don't have to have multiple Compose files,
    By command-fYou no longer need to specify a Compose file as an option

  • Answer # 2

    Isn't there any problem in the order in which the containers start up?

    version: '2'
    services:
      app:
        build:
          context:.
          dockerfile: ./Dockerfile
        entrypoint: /wait-for-it.sh -t 180 -s db:3306 -s db-test:3306 -
        command: bash -c "rake db:create&&rake db:migrate&&bundle exec rails s"
        ports:
          -${APP_PORT}:3000
        volumes:
          -.:/app
          -./docker/wait-for-it.sh:/wait-for-it.sh
        environment:
          RAILS_ENV: development
          MYSQL_HOST: db
          MYSQL_TEST_HOST: db-test
          MYSQL_USER: user
          MYSQL_PASSWORD: password
          MYSQL_DATABASE: app
        env_file:
          -.env
        tty: true
        stdin_open: true
    + depends_on:
    +-db

    Control startup and shutdown order in Compose | Docker Documentation