#はじめに
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にあげた方がいいかもと思ったのですが、ここにべた書きしてしまいます(^^;
#!/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
で受け入れをしているので、ポート指定せずにアクセスできます。
無事に表示出来たら完了です。
ただ、バージョンなどを少しでもいじるとどこかでつまずく可能性があるので注意してください。
その場合は大人しくデバックに勤しむしかないです。。。(^^;
シェルの内容について
シェルスクリプト内にコメントを記載していますが、内部で行っていることについて補足していきたいと思います。
補足できていない部分や、詳細な処理内容について気になる方は、記事最下部に挙げている参考記事をご参照ください。
フォルダ階層
ホスト側のフォルダ階層は以下の通りです。
ルートディレクトリに配置している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ファイルです。
rails6
は webpacker
のインストールが必須となり、それに伴い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)