Railsプロジェクトをdocker-composeで環境ごとに構築

Railsのプロジェクト(Redmine)をdocker-composeを用いて、	
環境ごとに管理していきます	
今回はdockerを用いた環境構築を主軸としたため、
webサーバーなどの詳しい説明は省きます

作成：2020年11月24日
## 環境
Ubuntu18.04	
Rails 5	
Ruby 2.6.5
Redmine 4.0	
nginx 1.15.8
Docker 19.03.13	
docker-compose 3
MySQL 5.7

## 必要なもの
Dockerとdocker-compsoeが動く環境(今回はUbuntu18.04で実行)
Redmineのソース	
やる気

## ざっくりとした方法
環境ごとにcomposeファイルを作成
環境変数COMPOSE_FILEを環境ごとに指定する

## コンテナ構成
- 開発環境	
	app(rails), db(mysql)
- 本番環境	
	app(rails, puma), db(mysql), web(nginx)

## フォルダ構成
docker-compose.develop.ymlが開発環境とテスト環境	
docker-compose.prod.ymlが本番環境となる	
開発環境のDockerファイルはDockerfile
本番環境はDockerfile.prodとなる

```
redmine(プロジェクトroot)
├── app
├── bin
├── etc....
├── config
│	├── database.yml(追加)
│	├── puma.rb(追加)
│	└── etc...
├── container(追加)
│ 	├── app
│ 	│ 	├── Dockerfile
│ 	│ 	└── Dockerfile.prod
│ 	├── db
│ 	│	└── multibyte.cnf
│ 	└── web
│ 		├── Dockerfile.prod
│ 		└── nginx.conf
├── docker-compose.develop.yml(追加)
└── docker-compose.prod.yml(追加)
```

## 初期設定

### 環境に応じて、環境変数を設定する  
docker-composeの仕様により、
COMPOSE_FILE環境変数を設定すると、設定した環境すべてにおいて、	
docker-composeコマンドに適応されるので注意。	
direnvの導入をおすすめする

```bash
 開発環境
$ export COMPOSE_FILE=docker-compose.develop.yml
 本番環境
$ export COMPOSE_FILE=docker-compose.prod.yml
```

### Gemfile修正
pumaを本番環境に適応させる

```ruby:Gemfile
group :test, :production do 
  gem 'puma', '~> 3.7' # 追加
end
group :test do
  gem "rails-dom-testing"
  gem 'mocha', '>= 1.4.0'
  gem "simplecov", "~> 0.14.1", :require => false
  # For running system tests
  gem "capybara", '~> 2.13'
  gem "selenium-webdriver"
  # gem 'puma', '~> 3.7' 削除
end
```

### puma.rb作成

```ruby:config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
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
```

### database.yml作成(DB設定)
redmine公式ドキュメントに基づきdatabase.yml.exampleをもとに	
database.ymlを作成

```yml:database.yml
	# Default setup is given for MySQL with ruby1.9.
	# Examples for PostgreSQL, SQLite3 and SQL Server can be found at the end.
	# Line indentation must be 2 spaces (no tabs).

	production:
	  adapter: mysql2
	  database: redmine
	  host: db
	  username: hoge
	  password: "fugafuga_1"
	  encoding: utf8

	development:
	  adapter: mysql2
	  database: redmine_development
	  host: db
	  username: root
	  password: ""
	  encoding: utf8

	# Warning: The database defined as "test" will be erased and
	# re-generated from your development database when you run "rake".
	# Do not set this db to the same as development or production.
	test:
	  adapter: mysql2
	  database: redmine_test
	  host: db
	  username: root
	  password: ""
	  encoding: utf8
	  
	# 省略、、、、、
```

本番環境以外、パスなしrootで良ければproduction関連以外編集しなくてもよい

## 各ファイル詳細
### 開発、本番環境共用
実際の本番環境ではDBサーバーを別途用意するとは思いますが、、	

```cnf:cotainers/db/multibyte.cnf
# 文字コードを設定しないとエラーとなるため
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
```

### 開発環境
#### appコンテナ

```docker:containers/app/Dockerfile
FROM ruby:2.6.5
RUN apt-get update -qq && apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs
RUN mkdir /redmine
WORKDIR /redmine
COPY . /redmine
RUN gem install bundler && bundle install
```

#### compose

```yml:docker-compose.develop.yml
# DBを永続化したい場合、コメントを外す
version: '3'
services:
  app:
    build: 
      context: ./
      dockerfile: containers/app/Dockerfile
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    volumes:
      - .:/redmine
    ports:
      - "3000:3000"
    depends_on:
      - db  

  db:
    image: mysql:5.7
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    ports:
      - 3306:3306
    volumes:
      - ./containers/db/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      #- db-store:/var/lib/mysql
 

#volumes:
  #db-store:
```

### 本番環境

#### appコンテナ

```docker:containers/app/Dockerfile.prod
FROM ruby:2.6.5
RUN apt-get update -qq && apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs
RUN mkdir /redmine
WORKDIR /redmine
COPY . /redmine
RUN gem install bundler && bundle install --without development test
```

#### webコンテナ

```docker:Dockerfile.prod
FROM nginx:1.15.8
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/myapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/myapp.conf
```


```conf:containers/db/nginx.conf
user  root;
worker_processes  1;

events{
    worker_connections  512;
}

# ソケット接続
http {
  upstream redmine{
    server unix:///redmine/tmp/sockets/puma.sock;
  }
  server { # simple load balancing
    listen 80;
    server_name localhost;

    #ログを記録しようとするとエラーが生じます
    #root /redmine/public;
    #access_log logs/access.log;
    #error_log logs/error.log;

    location / {
      proxy_pass http://redmine;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
    }
  }
}
```

#### compose

```yml:docker-compose.prod.yml
# DBコンテナはdatabase.ymlをもとに設定
# DBを永続化したい場合、コメントを外す
version: '3'
services:
  app:
    build:
      context: ./
      dockerfile: containers/app/Dockerfile.prod
    command: bash -c "rm -f /redmine/tmp/pids/server.pid && bundle exec puma -e production -C config/puma.rb"
    volumes:
      - .:/redmine
      - public-data:/redmine/public
      - tmp-data:/redmine/tmp
    depends_on:
      - db
  
  web:
    build: 
      context: containers/web/
      dockerfile: Dockerfile.prod
    ports:
      - 80:80
    volumes:
      - public-data:/redmine/public
      - tmp-data:/redmine/tmp
    depends_on:
      - app

  db:
    image: mysql:5.7
    environment:
      MYSQL_USER: hoge
      MYSQL_PASSWORD: fugafuga_1
      MYSQL_ROOT_PASSWORD: rootdayo
      MYSQL_DATABASE: redmine
    ports:
      - 3306
    volumes:
      - ./containers/db/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      #- db-store:/var/lib/mysql
 
volumes:
  #db-store:
  public-data:
  tmp-data:
```

## 実行手順
### 開発環境

```bash
$ docker-compose build
# DBを永続化した場合は最初だけ----------
# db作成(appはコンテナ名)
$ docker-compose run app rake db:create
# テーブル設定
$ docker-compose run app rake db:migrate
# ---------------------------------------
# 起動
$ docker-compose up -d
# http://localhost:3000
```

### 本番環境

```bash
$ docker-compose build

# Redmine公式ドキュメントに基づき、シークレットトークン作成
# 実行は最初の一回だけでよい
$ docker-compose run -e RAILS_ENV=production app bundle exec rake generate_secret_token
# DBを永続化した場合は最初だけけ---------
# テーブル設定
$ docker-compose run -e RAILS_ENV=production app bundle exec rake db:migrate
# ---------------------------------------
# $ 起動
$ docker-compose up -d
```

## 参考
Redmine GitHub
https://github.com/redmine/redmine
Redmineインストール	
http://guide.redmine.jp/RedmineInstall/	
docker-compose.ymlが環境別に複数ある場合はCOMPOSE_FILEを定義しておくと幸せになれる	
https://suin.io/535

## GitHub
ソースをgithubにあげているので良かったら参考にしてください
https://github.com/kinako555/redmine_managed_docker.git