Docker で padrino の開発環境を作ってみた

Docker で `Ruby on Rails` を動かす記事はよく見かけるが、 `padrino` はあまり見ないので勉強がてら試してみたときの手順メモ
`padrino g project` で強制的にサブディレクトリが作られるため、Docker イメージの作成方法が少々面倒になっている

# 検証環境

```
Windows10 Home Edition
VirtualBox 6.1.10

# Docker Host OS (CoreOS)
$ uname -a
Linux default 4.9.93-boot2docker #1 SMP Thu Jul 19 18:29:50 UTC 2018 x86_64 GNU/Linux

Docker version 18.06.1-ce, build e68fc7a
docker-compose version 1.20.1, build 5d8c71b
```

# インストール手順の概要

1. 最初に `padrino g project` で作成された初期プロジェクトが入った Docker イメージを作成する ( `base` イメージと呼ぶ)
2. `base` イメージからコンテナを作成し、初期プロジェクトのフォルダを `docker cp` でホストOS側にコピーする
3. `base` イメージを元にホストOS側のプロジェクトを上書きするイメージを作成する ( `app` イメージと呼ぶ)
4. 以後の開発作業はホストOS上のプロジェクトのソースコードを変更し、 `app` イメージを作り直すこととなる

`padrino g project` でサブディレクトリが作成される関係で、プロジェクト作成の前後で `WORKDIR` が変化するため、上記のような手順をとっている

# 初期のディレクトリ構成

初期は `padrino` フォルダの中に `docker-compose.yml` と `Dockerfile` のみ

```
padrino
|   docker-compose.yml
|   Dockerfile
```

## `docker-compose.yml`

`base` イメージを作成するための内容

```yaml:docker-compose.yml
version: "3.4"
services:
  base: 
    build:
      context: .
      dockerfile: ./Dockerfile
      target: "padrino-base"
    image: takaya030/padrino-base
    ports: 
      - "3000:3000"
    working_dir: /workspace/myapp
    command: "bundle exec padrino s -h 0.0.0.0"
```

## `Dockerfile`

こちらも `base` イメージを作成するための内容

```dockerfile:Dockerfile
FROM ruby:2.7.1 as padrino-base
LABEL maintainer "takaya030"

RUN apt-get update -qq && \
    apt-get install -y build-essential libpq-dev nodejs && \
	apt-get clean && \
	rm -rf /var/lib/apt/lists/*

# insall padrino
RUN mkdir /workspace
WORKDIR /workspace
RUN bundle init && \
	echo 'gem "padrino"' >>Gemfile && \
	bundle config set path 'vendor/bundle' && \
	bundle install && \
	bundle exec padrino g project myapp -t rspec -e erb -c sass -s jquery -d activerecord && \
	rm -rf Gemfile Gemfile.lock .bundle vendor

# create project
WORKDIR /workspace/myapp
RUN bundle config set path 'vendor/install' && \
	bundle install

CMD ["true"]
```

# `base` イメージのビルド

```bash
$ cd padrino
$ docker-compose build base
```

# `base` イメージの動作確認

Docker コンテナの起動

```bash
$ docker-compose up -d
```

web ブラウザで http://192.168.99.100:3000/ にアクセスして下のように表示されれば正常に動作しています

![padrino_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321059/db216373-c7ac-cd73-ab19-71bd472c3acf.png)


一旦コンテナを停止します

```bash
$ docker-compose stop
```

# プロジェクトフォルダをホストOS側へコピー

停止させたコンテナの中のプロジェクトフォルダをホストOS側へコピーします
これが実際の開発環境になります

```bash
$ docker cp padrino_base_1:/workspace/myapp .

# "padrino_base_1" は先ほど停止させたコンテナの名前
```

コピーが完了すると `padrino/myapp/` フォルダ以下に開発環境が作成されます

```
padrino
|   docker-compose.yml
|   Dockerfile
|   
\---myapp
```

コピーが完了したらコンテナを削除します

```bash
$ docker-compose rm
```

# `app` イメージ作成のための設定追加

## `docker-compose.yml`

下記の内容に変更します

```yaml:docker-compose.yml
version: "3.4"
services:
  base: 
    build:
      context: .
      dockerfile: ./Dockerfile
      target: "padrino-base"
    image: takaya030/padrino-base
  app: 
    build:
      context: .
      dockerfile: ./Dockerfile
      target: "padrino-app"
    image: takaya030/padrino-app
    ports: 
      - "3000:3000"
    volumes:
      - ./myapp:/workspace/myapp
    working_dir: /workspace/myapp
    command: "bundle exec padrino s -h 0.0.0.0"
```

## `Dockerfile`

下記の内容に変更します

```dockerfile:Dockerfile
FROM ruby:2.7.1 as padrino-base
LABEL maintainer "takaya030"

RUN apt-get update -qq && \
    apt-get install -y build-essential libpq-dev nodejs && \
	apt-get clean && \
	rm -rf /var/lib/apt/lists/*

# insall padrino
RUN mkdir /workspace
WORKDIR /workspace
RUN bundle init && \
	echo 'gem "padrino"' >>Gemfile && \
	bundle config set path 'vendor/bundle' && \
	bundle install && \
	bundle exec padrino g project myapp -t rspec -e erb -c sass -s jquery -d activerecord && \
	rm -rf Gemfile Gemfile.lock .bundle vendor

# create project
WORKDIR /workspace/myapp
RUN bundle config set path 'vendor/install' && \
	bundle install

CMD ["true"]

#=========================================
FROM padrino-base as padrino-app

COPY ./myapp /workspace/myapp
WORKDIR /workspace/myapp

CMD ["bundle","exec","padrino","s","-h","0.0.0.0"]
```

# `app` イメージのビルド

```bash
$ docker-compose build app
```

# `app` イメージの動作確認

Docker コンテナの起動

```bash
$ docker-compose up -d
```

web ブラウザで http://192.168.99.100:3000/ にアクセスして先ほどと同じ画面が表示されることを確認します

# ソースコード変更の反映確認

コンテナを起動させた状態で `myapp/app/app.rb` を下記の通り変更します

```diff:myapp/app/app.rb
--- a/app.rb      Sat Jun 06 19:19:23 2020
+++ b/app.rb      Sat Jun 06 22:30:27 2020
@@ -62,5 +62,10 @@
     #     render 'errors/500'
     #   end
     #
+
+    get '/' do
+      "Hello World"
+    end
+
   end
 end
```

web ブラウザで再度 http://192.168.99.100:3000/ にアクセスして下の画像のように表示されば正常動作しています

![padrino_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321059/9bc0b51e-f5ad-78e4-f18a-cf9c7ad61bcc.png)

# gem パッケージの追加方法

現在インストールされているパッケージを確認

```bash
$ docker-compose run --rm app bundle list
Gems included by the bundle:
  * activemodel (6.0.3.1)
  * activerecord (6.0.3.1)
  * activesupport (6.0.3.1)
  * concurrent-ruby (1.1.6)
  * diff-lcs (1.3)
  * erubi (1.9.0)
  * ffi (1.13.0)
  * i18n (1.8.3)
  * mail (2.7.1)
  * mime-types (3.3.1)
  * mime-types-data (3.2020.0512)
  * mini_mime (1.0.2)
  * minitest (5.14.1)
  * moneta (1.1.1)
  * mustermann (1.1.1)
  * padrino (0.15.0)
  * padrino-admin (0.15.0)
  * padrino-cache (0.15.0)
  * padrino-core (0.15.0)
  * padrino-gen (0.15.0)
  * padrino-helpers (0.15.0)
  * padrino-mailer (0.15.0)
  * padrino-support (0.15.0)
  * rack (2.2.2)
  * rack-protection (2.0.8.1)
  * rack-test (1.1.0)
  * rake (13.0.1)
  * rb-fsevent (0.10.4)
  * rb-inotify (0.10.1)
  * rspec (3.9.0)
  * rspec-core (3.9.2)
  * rspec-expectations (3.9.2)
  * rspec-mocks (3.9.1)
  * rspec-support (3.9.3)
  * ruby2_keywords (0.0.2)
  * sass (3.7.4)
  * sass-listen (4.0.0)
  * sinatra (2.0.8.1)
  * sqlite3 (1.4.2)
  * thor (0.20.3)
  * thread_safe (0.3.6)
  * tilt (2.0.10)
  * tzinfo (1.2.7)
  * zeitwerk (2.3.0)
Use `bundle info` to print more detailed information about a gem
```

`myapp/Gemfile` に追加したいパッケージを記述

```diff:myapp/Gemfile
--- a/Gemfile     Sat Jun 06 19:19:23 2020
+++ b/Gemfile     Sun Jun 07 21:57:33 2020
@@ -22,6 +22,10 @@
 gem 'activerecord', '>= 3.1', :require => 'active_record'
 gem 'sqlite3'

+# OAuth
+gem "omniauth"
+gem "omniauth-twitter"
+
 # Test requirements
 gem 'rspec', :group => 'test'
 gem 'rack-test', :require => 'rack/test', :group => 'test'
```

下記のコマンドでパッケージをインストール

```bash
$ docker-compose run --rm app bundle install
```

正常にインストールされたか確認
( `omniauth` 関連のパッケージが追加されているのを確認)

```bash
$ docker-compose run --rm app bundle list
Gems included by the bundle:
  * activemodel (6.0.3.1)
  * activerecord (6.0.3.1)
  * activesupport (6.0.3.1)
  * concurrent-ruby (1.1.6)
  * diff-lcs (1.3)
  * erubi (1.9.0)
  * ffi (1.13.0)
  * hashie (4.1.0)
  * i18n (1.8.3)
  * mail (2.7.1)
  * mime-types (3.3.1)
  * mime-types-data (3.2020.0512)
  * mini_mime (1.0.2)
  * minitest (5.14.1)
  * moneta (1.1.1)
  * mustermann (1.1.1)
  * oauth (0.5.4)
  * omniauth (1.9.1)
  * omniauth-oauth (1.1.0)
  * omniauth-twitter (1.4.0)
  * padrino (0.15.0)
  * padrino-admin (0.15.0)
  * padrino-cache (0.15.0)
  * padrino-core (0.15.0)
  * padrino-gen (0.15.0)
  * padrino-helpers (0.15.0)
  * padrino-mailer (0.15.0)
  * padrino-support (0.15.0)
  * rack (2.2.2)
  * rack-protection (2.0.8.1)
  * rack-test (1.1.0)
  * rake (13.0.1)
  * rb-fsevent (0.10.4)
  * rb-inotify (0.10.1)
  * rspec (3.9.0)
  * rspec-core (3.9.2)
  * rspec-expectations (3.9.2)
  * rspec-mocks (3.9.1)
  * rspec-support (3.9.3)
  * ruby2_keywords (0.0.2)
  * sass (3.7.4)
  * sass-listen (4.0.0)
  * sinatra (2.0.8.1)
  * sqlite3 (1.4.2)
  * thor (0.20.3)
  * thread_safe (0.3.6)
  * tilt (2.0.10)
  * tzinfo (1.2.7)
  * zeitwerk (2.3.0)
Use `bundle info` to print more detailed information about a gem
```

# 参考

- [Padrinoの始め方 - Qiita](https://qiita.com/y-sugasawa/items/3dd1a45ef13f8ba85622)
- [環境をよごさないでPadrino使ってみる - Qiita](https://qiita.com/gravitonMain/items/d9360dca3a0bf052e7bf)
- [アプリケーションサーバに引きこもるDBを自立させてDocker Composeで開発環境を作る - Qiita](https://qiita.com/upscent/items/8d9ebd1f4f4cc11b12a6)
- [Padrino - Basic Projects](http://padrinorb.com/guides/getting-started/basic-projects/)