Stable development environment construction manual for "Rails6" with "Docker-compose"

Purpose of this article

When I was a beginner of Docker / Rails and used Docker-compose to build the environment of Rails6 We were able to build the environment safely with the information provided by a knowledgeable person. However, it is very difficult for me as a beginner to understand what is being executed in the construction contents. Unlike when building the environment with Rails5, if you made a mistake in the processing items, you could not build the environment. I think there are multiple points in building the environment.

In this article, based on the matters that you consulted and answered

--Disassemble and explain the code at the time of environment construction and perform your own learning and knowledge fixation --Use as a memorandum of your own and a manual when building an environment in the future ――Intention that other people can use it as a reference material as much as possible.

I summarized it for the above purpose.

There may be mistakes because you are a beginner, but if you notice something, I would appreciate it if you could point out. If you find it helpful, I would be grateful if you could ** LGTM </ font> **.

Environment created in this article

Docker-compose

  • Web
    • Ruby version:2.7〜
    • Rails version:6.0.3.4〜
  • db
    • PostgreSQL version: 12.4〜

Those who want to use it for the time being

Those who want to use it as soon as possible and build it are published in the GitHub repository below. The procedure is described in the README, but please execute the following commands in order.

  1. Execute git clone <Project name> from the README on the following site
  2. Move the project directory with cd <Project name>
  3. Execute the ./qs setup command
  • Please refer to the README on the following site and execute. Rails6-QS

It takes about 20 minutes to complete the command. Please be patient. (Lol) When you're done, hit localhost: 3000 in your browser and you'll see the familiar Rails initial screen. Docker's Dashboard should also have web, db containers in the project name.

What to create in this article

  • Dockerfile
  • Docker-compose.yml --Shell script (sample.bash)
  • The public repository is named qs (short for quick start).
  • .gitignore
  • Gemfile
  • database.yml

① Create Dockerfile

First, create from the Dockerfile. When creating a Dockerfile

--Official site
Docker Quickstart

The explanation of the above site is easy to understand.

Containers created with images defined in Dockerfile should be ephemeral if possible. Our "ephemeral" means that it can be stopped and destroyed, and is clearly built and usable with minimal setup.

As quoted from the above site, Dockerfile aims to be built with the minimum setup. In the following, we will explain the Dockerfile created this time for each item.

File structure

Ruby version specification Version: 2.7

FROM ruby:2.7

Required to use Japanese on rails console

ENV LANG C.UTF-8

Warning hidden / listen port setting

  1. Settings to eliminate DEBCONF warnings
    [Ubuntu manual] http://manpages.ubuntu.com/manpages/bionic/man7/debconf.7.html --To prevent warnings issued by the apt-key command --The file generated in the mount area of Docker is
    There are cases where you do not have write permission for the cache directory and throw an error.
    XDG_CACHE_HOME specifies a directory with write permission, but
    Apparently it doesn't happen when using Rails only.
    If there is no problem, you can remove it. --The port on which the container listens for connections
ENV DEBCONF_NOWARNINGS yes
ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE yes
ENV XDG_CACHE_HOME /tmp
EXPOSE 3000

Container package installation

  1. You can use the latest version of the package with apt-get and apt-get install -y without caching. --Installing build tools --Required for PostgreSQL support. --installing vim * --Specified to add less command (linux). * --To use the open source tool package graphviz *

If you don't use the part marked with *, you can delete it.

RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    vim \
    less \
    graphviz

Yarn installation

Since Webpack has become the standard since Rails6, an error will occur without yanr installation.

  1. The package is installed with ʻapt-transport-https` to deal with https. --The command after that is to install yarn.
RUN apt-get install apt-transport-https
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn

Creating a work directory

# setting work directory
RUN mkdir /app
WORKDIR /app

Environment variable settings

--To use when entering a script or container

ENV HOME /app

Create Gemfile

COPY Gemfile /app/Gemfile

If you describe all of the above, it will be as follows. (* Some comments have been added.)

# use ruby version 2.7
FROM ruby:2.7

# using japanese on rails console
ENV LANG C.UTF-8

# remove warn
ENV DEBCONF_NOWARNINGS yes
ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE yes
ENV XDG_CACHE_HOME /tmp
EXPOSE 3000

# install package to docker container
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    vim \
    less \
    graphviz

# install yarn
RUN apt-get install apt-transport-https
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn

# setting work directory
RUN mkdir /app
WORKDIR /app

# setting environment value
ENV HOME /app

# executing bundle install
COPY Gemfile /app/Gemfile

② Create Docker-compose.yml file

version specification of docker-compose

The current situation is 3.

version: '3'

service creation

  • image
    Specify the database, web, etc.
    I specified db`` redis chrome`` web: & app spring`` solagraph which is likely to be used this time.
    I hope you can correct it by deleting it if necessary.
  • port
    Port settings (Created with generally set port contents)
  • volume
    Mount settings. It is a specification of the data storage destination that should not be deleted after deleting the Docker container such as db.
  • build
    It shows the path where Dockerfile etc. is located, but basically it is . and it is the current directory.

Supplementary matters

  • shm_size
    When using selenium-chrome, memory may run out, so I increased it a little.
  • .:
    It is used to speed up the loading of volumes with the docker-compose function.
  • <<: *app
    The web service settings are inherited by spring.
  • tty: true
    Settings to keep the container running --About port settings
    The port on the left side is the port on the host side, and the port on the right side is the port on the container side.

This is also summarized below by describing all of the above.

version: '3'
services:
  db:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - data:/var/lib/postgresql/data:cached
    environment:
      POSTGRES_PASSWORD: postgres
  redis:
    image: redis
    ports:
      - "6379:6379"
    command: redis-server --appendonly yes
    volumes:
       - redis-data:/data:cached
  chrome:
    image: selenium/standalone-chrome
    ports:
      - "4444:4444"
  web: &app
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app:cached
      - bundle:/usr/local/bundle:cached
      - /app/.git
    environment:
      HOME: /app
      RAILS_ENV: development
    ports:
      - "3000:3000"
    tty: true
    links:
      - db
  spring:
    <<: *app
    command: bundle exec spring server
    ports: []
  solargraph:
    <<: *app
    command: bundle exec solargraph socket --host=0.0.0.0 --port=7658
    ports:
      - "8091:7658"
    links: []

volumes:
  bundle:
    driver: local
  data:
    driver: local
  redis-data:
    driver: local

③ Script creation

Summarize the execution contents with a shell script. By doing this, you can realize that Dockerfile can be stopped / destroyed and can be built and used with obviously the minimum setup. Shell scripts are those used to execute Unix commands.

Basic excerpt of shell script

Although it is not a commentary article on the shell script, I will extract and describe the basics so that I can understand the target shell script. I referred to the following site for article creation and understanding of shell operation. [Introduction of basic commands for beginner shell scripts] https://qiita.com/zayarwinttun/items/0dae4cb66d8f4bd2a337 [Shell Script Reference] https://shellscript.sunone.me/

--Declaration #!/bin/bash Required to declare shell scripts. There is also a way to write #! / Bin / sh, but if you use the unique function of bash, sh will be NG. Referenced site: https://sechiro.hatenablog.com/entry/20120806/1344267619 Unless you are particular about it, you can use #! / Bin / bash.

--Comment out Just add # at the beginning.

--Output Output with ʻecho`

--if condition --The basic way to write ʻif is ʻif [condition] then command fi. --If the condition is true, execute the following command with then. --For different conditions Check ʻelif [Condition 2] . --If there is no true condition, execute the following command of ʻelse to exit. --If there is no else`, it will end as it is.

--How to take arguments When accessing a variable, put $ before the variable name. Or put $ and enclose the variable in {}.

  • -e Escape special text

  • $* All arguments are processed as one. There is $ @ in a similar process. If you do not enclose it in double quotes, it is the same as $ @, but if you enclose it, it will be output for each argument, There is a difference in whether to output all at once with one argument.

  • $1 1st argument

  • $0 Script name

--Function In shell scripts you can write and quote functions Function () { echo }

  • End processing To end the script prematurely, give a number to the ʻexit` command.

  • Swith The basic way to write a switch is the case variable in condition / value) command ;; esac. If the condition / value matches the variable, execute the following command.

Overview of shell script configuration

  1. First, specify the docker-compose command for dc with the full path.
  2. If docker is not installed, you will be prompted to install it.
  3. Store the value in each variable (rm, app, db, app_name)
  4. Execute command value of $ 1 with echo
  5. $ 1 is in the form of processing the given command given the value given by the function (expanded by switch syntax depending on the function).
  6. The script name is written in $ 0 so that it can be used as help.

If you create a script file that contains various commands and settings with the above contents, it will be as follows.

qs.bash


dc=$(which docker-compose) # docker-compose command with full path

if [[ -x "$dc" ]]; then
  :
else
  echo "Please install Docker before run this command."
  exit 2
fi

rm="--rm" # To destroy a container

app="web" # describe $application service name from docker-compose.yml

db="db" # describe database service name from docker-compose.yml

app_name=$(pwd | awk -F "/" '{ print $NF }') # get project dir name

# define container name
app_container="${app_name}_${app}_1"
db_container="${app_name}_${db}_1"

echoing() {
  echo "========================================================"
  echo "$1"
  echo "========================================================"
}

rm_pids() {
  if [ -f "tmp/pids/server.pid" ]; then
    rm -f tmp/pids/server.pid
  fi
}

create_project() {
  echoing "Exec Bundle Install for executing rails new command"
  compose_build $app
  bundle_cmd install

  echoing "Exec rails new with postgresql and webpack"
  bundle_exec rails new . -f -d=postgresql $*

  echoing "Update config/database.yml"
  mv database.yml config/database.yml

  echoing "Exec db create"
  bundle_exec rails db:create

  echoing "docker-compose up"
  compose_up $app

  echo "You can access to localhost:3000"
}

init_services() {
  echoing "Building containers"
  $dc down -v
  $dc build --no-cache $app

  bundle_cmd install

  if [ "--webpack" == "$1" ]; then
    run_yarn install
  fi

  rails_cmd db:migrate:reset
  rails_cmd db:seed

  rm_pids

  $dc up $app
}

compose_up() {
  echoing "Create and start containers $*"
  rm_pids
  $dc up -d "$1"
}

compose_down() {
  echoing "Stop and remove containers $*"
  $dc down $*
}

compose_build() {
  echoing "Build containers $*"
  $dc build $*
}

compose_start() {
  echoing "Start services $*"
  rm_pids
  $dc start $*
}

compose_stop() {
  echoing "Stop services $*"
  $dc stop $*
}

compose_restart() {
  echoing "Restart services $*"
  $dc restart $*
}

compose_ps() {
  echoing "Showing running containers"
  $dc ps
}

logs() {
  echoing "Logs $*"
  $dc logs -f $1
}

invoke_bash() {
  $dc run $rm -u root $1 bash
}

invoke_run() {
  renv=""
  if [ -n "$RAILS_ENV" ]; then
    renv="-e RAILS_ENV=$RAILS_ENV "
  fi

  if [ -n "$TRUNCATE_LOGS" ]; then
    renv="$renv -e TRUNCATE_LOGS=$TRUNCATE_LOGS "
  fi

  dbenv=""
  if [ -n "$DISABLE_DATABASE_ENVIRONMENT_CHECK" ]; then
    dbenv="-e DISABLE_DATABASE_ENVIRONMENT_CHECK=$DISABLE_DATABASE_ENVIRONMENT_CHECK "
  fi

  $dc run $rm ${renv}${dbenv}$*
}

run_app() {
  invoke_run $app $*
}

run_db() {
  invoke_run $db $*
}

run_spring() {
  $dc exec spring $*
}

run_solargraph() {
  invoke_run solargraph $*
}

rails_server() {
  rm_pids
  # $dc run $rm ${renv}--service-ports $app rails s -p 3000 -b 0.0.0.0
  $dc run $rm --service-ports $app bundle exec foreman start -f Procfile.dev
}

rails_db() {
  case "$1" in
  set)
    rails_cmd db:migrate
    ;;
  up)
    rails_cmd db:migrate:up VERSION="$2"
    ;;
  down)
    rails_cmd db:migrate:down VERSION="$2"
    ;;
  reset)
    rails_cmd db:reset
    ;;
  *)
    rails_cmd db:migrate:status
    ;;
  esac
}

spring_db() {
  case "$1" in
  set)
    spring_cmd rake db:migrate
    ;;
  up)
    spring_cmd rake db:migrate:up VERSION="$2"
    ;;
  down)
    spring_cmd rake db:migrate:down VERSION="$2"
    ;;
  reset)
    spring_cmd rake db:reset
    ;;
  *)
    spring_cmd rake db:migrate:status
    ;;
  esac
}

spring_dive() {
  $dc exec spring bash
}

rails_cmd() {
  bundle_exec rails $*
}

rake_cmd() {
  bundle_exec rake $*
}

rspec_cmd() {
  $dc start chrome
  bundle_exec rspec $*
}

test_cmd() {
  bundle_exec test $*
}

bundle_cmd() {
  run_app bundle $*
}

bundle_exec() {
  run_app bundle exec $*
}

rubocop_cmd() {
  bundle_exec rubocop $*
}

rails_console() {
  bundle_exec rails c $*
}

spring_cmd() {
  run_spring spring $*
}

solargraph_cmd() {
  run_solargraph solargraph $*
}

rake_reset_db() {
  echoing "Running reset db"
  compose_stop $app
  DISABLE_DATABASE_ENVIRONMENT_CHECK=1 rake_cmd "db:reset"
  rake_cmd "db:fdw:setup"
  RAILS_ENV=test rake_cmd "db:fdw:setup"
  compose_up $app
}

db_console() {
  # from config/database.yml
  database="development"
  username="postgres"
  port="5432"

  run_db psql -h $db_container -p $port -U $username $database
}

db_dump() {
  # from config/database.yml
  database="development"
  username="postgres"
  port="5432"

  tm=$(date +\%Y\%m\%d-\%H\%M)
  dump_file=tmp/dbdump-${dbname}-${tm}.dump

  echoing "Dump database $dbname data to $dump_file"

  run_db pg_dump -h $db_container -p $port -U $username --disable-triggers $database >$dump_file
  echo "done"
}

run_yarn() {
  run_app bin/yarn $*
}

run_npm() {
  run_app npm $*
}

run_webpack() {
  run_app webpack $*
}

cmd=$1
shift
case "$cmd" in
setup)
  create_project $* && exit 0
  ;;
init)
  init_services $* && exit 0
  ;;
ps)
  compose_ps && exit 0
  ;;
up)
  compose_up $* && compose_ps && exit 0
  ;;
build)
  compose_build $* && exit 0
  ;;
start)
  compose_start $* && exit 0
  ;;
stop)
  compose_stop $* && exit 0
  ;;
restart)
  compose_restart $* && exit 0
  ;;
down)
  compose_down $* && exit 0
  ;;
logs)
  logs $*
  ;;
bash)
  invoke_bash $*
  ;;
run)
  invoke_run $*
  ;;
server)
  rails_server $*
  ;;
rails)
  rails_cmd $*
  ;;
db)
  rails_db $*
  ;;
cons)
  rails_console $*
  ;;
rake)
  rake_cmd $*
  ;;
rspec)
  rspec_cmd $*
  ;;
test)
  test_cmd $*
  ;;
bundle)
  bundle_cmd $*
  ;;
rubocop)
  rubocop_cmd $*
  ;;
reset-db)
  rake_reset_db
  ;;
psql)
  db_console $*
  ;;
db-dump)
  db_dump $*
  ;;
yarn)
  run_yarn $*
  ;;
npm)
  run_npm $*
  ;;
webpack)
  run_webpack $*
  ;;
spring)
  spring_cmd $*
  ;;
sdb)
  spring_db $*
  ;;
sdive)
  spring_dive $*
  ;;
solargraph)
  solargraph_cmd $*
  ;;
*)
  read -d '' help <<-EOF
Usage: $0 command

Service:
  setup    Create new rails application
  init     Initialize backend services then run
  ps       Show status of services
  up       Create service containers and start backend services
  down     Stop backend services and remove service containers
  start    Start services
  stop     Stop services
  logs     [options] default: none. View output from containers
  bash     [service] invoke bash
  run      [service] [command] run command in given container

App:
  server   Run rails server
  rails    [args] Run rails command in application container
  rake     [args] Run rake command in application container
  db       [args] Run rails db command you can use set(migrate), up, down, reset, other is status
           ex: ./qs db set #running rails db:migrate
               ./qs db up 2019010101 #running rails db:migrate:up VERSION=2019010101
  rspec    [args] Run rspec command in application container
  test     [args] Run Minitest command in application container
  bundle   [args] Run bundle command in application container
  cons     Run rails console
  rubocop  [args] Run rubocop
  yarn      Run yarn command in application container
  npm       Run npm  command in application container
  webpack   Run webpack  command in application container

Spring
  spring    Exec spring command in Spring container
  sdive     Into spring container
  sdb       [args] Run rails db command you can use set(migrate), up, down, reset, other is status
             ex: ./qs db set #running rails db:migrate
                 ./qs db up 2019010101 #running rails db:migrate:up VERSION=2019010101

Solargraph
  solargraph Run solargraph command in Spring container

DB:
  reset-db  reset database in DB container
  psql      launch psql console in DB container
  pg-dump   dump database data as sql file in DB container
EOF
  echo "$help"
  exit 2
  ;;
esac

Other created files

Although it is another setting file, the explanation is omitted because it is created by general construction. As a supplementary matter, PostgreSQL is selected as the DB. The reason is that MySQL may cause a validator error from Rails 6.0 and it is necessary to add settings. Regarding this point, if you use MySQL, you can set it, but Since PostgreSQL does not require any settings, this is used as the DB. Please refer to the following site for details. What to do if you get a warning "Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1." in Rails 6.0

database.yml


default: &default
  adapter: postgresql
  encoding: utf8
  min_messages: WARNING
  host: db
  port: 5432
  username: postgres
  password: postgres
  pool: 5
  timeout: 5000
  stats_execution_limit: 10

development:
  <<: *default
  database: development

test:
  <<: *default
  database: test

production:
  <<: *default
  database: production

Gemfile

source 'https://rubygems.org'

gem 'rails', '~> 6.0'

.gitignore

.bash_history
.bundle
.solargraph
.atom
.vscode
.viminfo

Afterword

Build a development environment for Rails 6 locally while using rbenv etc. with homebrew It seems that it did not take much man-hours, but building a development environment in a stable container is Even on the Docker official website, it was described in Rails 5, and it seemed difficult to build in Rails 6.

Also, on the official website Quick Start, the execution command is described in the Dockerfile, If you want to write with the smallest possible setup, it is better to separate it into a shell script It is considered good to include maintainability as a development environment.

  • The production environment includes the possibility that the Dockerfile may change.

Thank you for reading this article. I hope it helps in some development.

Finally, we would like to thank you for your cooperation and advice in compiling this article. Thank you very much.

Reference site / source

Quickstart Compose and Rails Dockerfile best practices Bash script execution sample New Linux textbook SB Creative

Recommended Posts

Stable development environment construction manual for "Rails6" with "Docker-compose"
Rails6 development environment construction [Mac]
[Docker] Rails 5.2 environment construction with docker
Environment construction of Rails5 + MySQL8.0 + top-level volumes with docker-compose
Environment construction for Servlet application development
Environment construction with Docker for beginners
[Environment construction with Docker] Rails 6 & MySQL 8
[Procedure 1 for beginners] Ruby on Rails: Construction of development environment
Rails application development environment construction with Docker [Docker, Rails, Puma, Nginx, MySQL]
Rails environment construction with Docker (personal apocalypse)
Laravel development environment construction with Docker (Mac)
Ruby on Rails development environment construction with Docker + VSCode (Remote Container)
Procedure for building a Rails application development environment with Docker [Rails, MySQL, Docker]
Rails API server environment construction using docker-compose
[Docker] Development environment construction Rails6 / Ruby2.7 / MySQL8
"Rails 6 x MySQL 8" Docker environment construction procedure for sharing with teams
Rails & React & Webpacker & MySQL Environment Construction Manual
Build debug environment on container --Build local development environment for Rails tutorial with Docker-
Build a local development environment for Rails tutorials with Docker (Rails 6 + PostgreSQL + Webpack)
Build a development environment for Docker + Rails6 + Postgresql
[Jakarta EE 8 application development with Gradle] 1. Environment construction
Environment construction procedure for using PowerMock with JUnit
[Ubuntu 18.04] Environment construction for using PyTorch with RTX3090
Rails6 [API mode] + MySQL5.7 environment construction with Docker
Rails development environment created with VSCode and devcontainer
Ruby on Rails development environment construction on M1 Mac
Django development environment construction using Docker-compose (personal memorandum)
Wordpress local environment construction & development procedure with Docker
Rails Docker environment construction
java development environment construction
[Rails] Development with MySQL
[Environment construction] Ruby on Rails 5.2 system development environment construction [within 1 hour]
[Java] Environment construction procedure for developing struts 1.3 with Eclipse
[Environment construction] Build a Java development environment with VS Code!
Java + Spring development environment construction with VirtualBox + Ubuntu (Xfce4)
Rails engineer environment construction ruby2.7.1
docker-compose command list (for Rails)
Preparation for developing with Rails
[Rails / MySQL] Mac environment construction
React environment construction with Docker
I made a development environment with rails6 + docker + postgreSQL + Materialize.
Web application development environment construction in Java (for inexperienced people)
exited with code 1 error resolution with docker-compose up in rails environment
[Environment construction] Get the Ruby on Rails 6 development environment within 1 hour
Java web application development environment construction with VS Code (struts2)
How to build Rails, Postgres, ElasticSearch development environment with Docker
[Java development environment construction] Install OpenJDK 11 (Java 11) on macOS with Homebrew.
Building an environment for creating apps with Rails and Vue
Build a local development environment for Rails tutorials with Docker-Introduce Bootstrap and Font Awesome with Webpack-
Procedure for migrating Rails application development environment to Docker even if you are inexperienced (Rails5 + MySQL8.0 + docker-compose)
Node.js environment construction with Docker Compose
Ruby on Rails --From environment construction to simple application development on WSL2
Prepare Java development environment with Atom
Rails 6 (API mode) + MySQL Docker environment creation by docker-compose (for Mac)
[Unity] Android development environment construction procedure
Build environment with vue.js + rails + docker
Build Rails environment with Docker Compose
How to install Pry after building Rails development environment with Docker
Ruby on Rails 6.0 environment construction memo
Build Java development environment (for Mac)
Rails on Docker environment construction procedure