LoginSignup
2
3

More than 1 year has passed since last update.

Rails6の環境構築を自動でしてくれるシェルスクリプトを作ってみた (1) ~ローカル起動編~

Last updated at Posted at 2020-05-25

#はじめに

Rails6 を Docker のコンテナ上で動作させて
自動で開発環境を構築してくれるシェルスクリプトを作ってみました。

フォルダやDockerファイルもスクリプト内で生成するので、
Dockerとdocker-composeさえ導入されていればどんな環境でもコマンド一つで構築できると思います
(多分。。。)

私自身の環境は以下の通りです。

OS 環境
Windows 10 Home 64bit Ubuntu 18.04(WSL2)

構築する環境

以下構成をdocker上で動作させて、ローカルで初期画面が見れるところをゴールとします。

項目    使用したもの バージョン
言語 ruby 2.6.6
フレームワーク rails 6.0.2
データベース  MySQL 8.0
Webサーバ Nginx 1.17.10
APサーバ puma 4.3.5

コンテナ構成は以下の通りです。

appコンテナ
ruby
rails
puma
dbコンテナ
MySQL
webコンテナ
Nginx

できること

まず実際にできることを提示してから、その後中身について記載していきたいと思います。

シェルの内容

今回作成したシェルスクリプトが以下の通りです。
結構長いのでGitHubにあげた方がいいかもと思ったのですが、ここにべた書きしてしまいます(^^;

docker_rails_template.sh
#!/bin/bash

####################################################
# 以下環境をdockerで構築するためのシェルスクリプト     #
# プログラミング言語:Ruby / Ruby on rails           #
# データベース      :mysql                          #
# webサーバ         :Nginx                         #
####################################################

RUBY_VERSION="2.6.6"
RAILS_VERSION="6.0.2"
MYSQL_VERSION="8.0"
NGINX_VERSION="1.17.10"
APP_ADDLESS="app"

if [ $# != 1 ]; then
  echo "引数の指定が間違っています。"
  echo "ex: $0 sample"
  exit 1
fi

APP_NAME=$1
APP_DIR=`pwd`/${APP_NAME}

# 作業フォルダを作成
echo "----- Make working directories -----"
echo "mkdir -p ${APP_DIR}"
mkdir -p ${APP_DIR}
cd ${APP_DIR}
echo ""

# Dockerfile 配置用のフォルダを作成
echo "----- Make docker directories -----"
echo "mkdir -p docker/app"
mkdir -p docker/app
echo "mkdir -p docker/web"
mkdir -p docker/web
echo ""

# ソースコード配置用のフォルダを作成
echo "----- Make source directories -----"
echo "mkdir -p src/tmp/sockets"
mkdir -p src/tmp/sockets
echo ""

# 各種ファイル作成
echo "----- Make source directories -----"
# app用のDockerfile 作成
echo "make app Dockerfile"
cat << EOF > docker/app/Dockerfile
FROM ruby:${RUBY_VERSION}
RUN apt-get update -y && apt-get install -y default-mysql-client nodejs npm && \
    npm install -g -y yarn
RUN mkdir /myapp
WORKDIR /myapp
COPY ./src/Gemfile Gemfile
COPY ./src/Gemfile.lock Gemfile.lock
RUN bundle install
COPY ./src /myapp
EOF

# web用のDockerfile 作成
echo "make web Dockerfile"
cat << EOF > docker/web/Dockerfile
FROM nginx:${NGINX_VERSION}
RUN rm -f /etc/nginx/conf.d/*
COPY ./docker/web/${APP_NAME}.conf /etc/nginx/conf.d/${APP_NAME}.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
EOF

# Gemfile 作成
echo "make Gemfile"
cat << EOF > src/Gemfile
source 'https://rubygems.org'
gem 'rails', '${RAILS_VERSION}'
EOF

# Gemfile.lock 作成
echo "make Gemfile"
touch ${APP_DIR}/src/Gemfile.lock

# nginx.conf 作成
echo "make ${APP_NAME}.conf"
cat << EOF > docker/web/${APP_NAME}.conf
upstream ${APP_NAME} {
  server unix:///myapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  
  server_name ${APP_ADDLESS};

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /myapp/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  \$uri/index.html \$uri @${APP_NAME};
  keepalive_timeout 5;

  location @${APP_NAME} {
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header Host \$http_host;
    proxy_pass http://${APP_NAME};
  }
}
EOF

# docker-compose.yml 作成
echo "make docker-compose.yml"
cat << EOF > docker-compose.yml
version: '3'

volumes:
  db_data:

services:
  db:
    image: mysql:${MYSQL_VERSION}
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - db_data:/var/lib/mysql
    ports:
      - 3306:3306

  app:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    command: bundle exec puma -C config/puma.rb
    volumes:
      - ./src:/myapp
    depends_on:
      - db

  web:
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    volumes:
      - ./src/public:/myapp/public
      - ./src/tmp:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - app

EOF

echo ""

# rails new 実行
echo "----- Set application -----"
echo "docker-compose run app rails new . --force --database=mysql"
docker-compose run app rails new . --force --database=mysql

# database.yml を編集
echo "edit database.yml"
cat src/config/database.yml | sed 's/password:$/password: password/' | sed 's/host: localhost/host: db/' > __tmpfile__
cat __tmpfile__ > src/config/database.yml
rm __tmpfile__

# puba.rb を編集
cat << EOF > src/config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
EOF

echo ""

echo "----- container start -----"
# イメージをビルド
echo "docker-compose build"
docker-compose build

# 初回マイグレーション
echo "docker-compose run app rails db:create"
docker-compose run app rails db:create

# 一度コンテナをリセット
echo "docker-compose down"
docker-compose down

# コンテナ起動
 echo "docker-compose up"
 docker-compose up

実行結果

こちらのスクリプトを適当な場所において、以下のコマンドを実行します。
引数で渡している sample は作成したいアプリの名前です。

sudo ./docker_rails_template_ver1.0.sh sample

完了まで 10分ほどかかります。
以下状態になったら実行完了です。

app_1  | * Listening on tcp://0.0.0.0:3000
app_1  | * Listening on unix:///myapp/tmp/sockets/puma.sock
app_1  | Use Ctrl-C to stop

ブラウザでlocalhostにアクセスしてみます。
railsのみだったらポート3000を指定してアクセスすることが多いと思いますが、
今回はWebサーバ(Nginx)経由であり、ポート80で受け入れをしているので、ポート指定せずにアクセスできます。

2020-05-25.png

無事に表示出来たら完了です。

ただ、バージョンなどを少しでもいじるとどこかでつまずく可能性があるので注意してください。
その場合は大人しくデバックに勤しむしかないです。。。(^^;

シェルの内容について

シェルスクリプト内にコメントを記載していますが、内部で行っていることについて補足していきたいと思います。
補足できていない部分や、詳細な処理内容について気になる方は、記事最下部に挙げている参考記事をご参照ください。

フォルダ階層

ホスト側のフォルダ階層は以下の通りです。
ルートディレクトリに配置しているdocker-compose.ymlからdockerフォルダ配下のDockerfileを参照しにいき、コンテナ生成などを行っています。
また、railsのソースコードはsrcに格納しています。
このsrcフォルダがappコンテナとの共有ボリュームとして機能します。

sample
├── docker
│   ├── app
│   │   └── Dockerfile
│   └── web
│       └── Dockerfile
├── docker-compose.yml
└── src

src フォルダ生成について

# ソースコード配置用のフォルダを作成
echo "----- Make source directories -----"
echo "mkdir -p src/tmp/sockets"
mkdir -p src/tmp/sockets
echo ""

srcフォルダ作成で、src/tmp/socketsまでフォルダを作成している理由について補足します。
アプリケーションサーバはpumaを利用していますが、puma起動時にsrc/tmp/sockets/puma.sockというファイルが自動生成されます。
しかし、自動生成時にフォルダ階層までは生成してくれないので、事前に作成しています。

app用Dockerファイルについて

ruby + rails6 + puma が格納されるコンテナ用のDockerファイルです。
rails6webpacker のインストールが必須となり、それに伴いyarnが必要となるので注意です。

nginx.conf 作成について

Nginx用の設定ファイルになります。
upstrem内で///myapp/tmp/sockets/puma.sockを参照しておりますが、こちらがappコンテナ内のpumaと連携する部分になります。
webコンテナにてポート80でリクエストを受け入れ、appコンテナへつないでいます。

docker-compose.yml作成について

DBコンテナ、appコンテナ、webコンテナをdocker-composeで操作するためのファイルです。
#####dbコンテナ
/var/lib/mysqlでdockerのlocalボリュームであるdb_dataを永続ボリュームとしてマウントしています。
パスワードは面倒なので固定で「password」としています。
#####appコンテナ
/myappでホスト側のsrcをマウントしています。
起動時には毎回以下コマンドでwebコンテナからの受け入れを開始します。

bundle exec puma -C config/puma.rb

また、dbコンテナとdepends_onにて関連付けを行っています。

#####webコンテナ
src内のフォルダをマウントして、各デフォルト表示用のhtmlやソケット通信用のpuma.sockをappコンテナと共有します。
ポートは80を受け入れています。

その後の処理について

その後はrails newを実行し、生成されたファイルを編集したのち、イメージビルド⇒コンテナ実行をしています。(雑)

アプリケーションのソースをsrcにてホスト側で編集できるため、都度イメージビルドをする必要がなく、開発環境に使えるのではないでしょうか。

以上になりますが、何かの参考になれば幸いです。

参考記事

以下記事を参考にさせて頂きました。ありがとうございましたm(_ _)m

丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)

Rails+PostgreSQL+Docker+AWSで作成したポートフォリオの概要

docker-compose コマンドまとめ

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3