［メモ］Dockerの環境構築＋Rails APIモードのまとめ


##初めに

最近はDockerとRailsのAPIモードを学習してるので、
学んだことをまとめてメモしておきます。



##まずはDocker環境構築から

Dockerの環境構築は主に以下の記事の参考で作成していきます。

https://qiita.com/kodai_0122/items/795438d738386c2c1966

https://docs.docker.com/samples/rails/

最初はまず作業用のファイルを用意します

```
$ mkdir myapp
```

###Dockerfile作成

rubyのバージョンは自分が今使ってる2.7.2に変更してみます。

```ruby:Dockerfile
FROM ruby:2.7.2
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - && \
apt-get install nodejs

RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```
P.S.[Docker 入門 １項目ずつ理解する](https://qiita.com/gyu_outputs/items/8fd21053bdb680bd81cc)では、Dockerfileの書き方について分かりやすく説明していますので、是非ご参考ください。

###Gemfile作成

```ruby:myapp/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'
```

### Gemfile.lock 作成
```
$ touch Gemfile.lock
```
中身は空のままでオッケー

### entrypoint.sh作成

```
$ touch entrypoint.sh
```

```ruby:myapp/entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

###docker-compose.yml 作成

```
$ touch docker-compose.yml
```

```ruby:myapp/docker-compose.yml 
version: "3.9"
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_HOST_AUTH_METHOD: 'trust'  
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```
P.S.[Docker 入門 １項目ずつ理解する](https://qiita.com/gyu_outputs/items/8fd21053bdb680bd81cc)

###これで下準備完了

ファイルの中はこんな感じ：

```
myapp
- Dockerfile
- Gemfile
- Gemfile.lock
- entrypoint.sh
- docker-compose.yml
```

###いよいよRails new

今回RailsのAPI開発モードを利用していますので、`--api`オプションを使います。
`-T`はデフォルトのテスティングフレームワーク「Minitest」をオフにするオプションです。
APIモード使わない場合は`--api`を自由に削除してください。

```ruby
$ docker-compose run web rails new . --force --no-deps --database=postgresql --api -T
```



`ls -l `コマンドでみると。。。

```
$ ls -l
total 64
-rw-r--r--   1 tsuki_  staff   831 Apr 24 23:02 Dockerfile
-rw-r--r--   1 tsuki_  staff  1397 Apr 24 23:48 Gemfile
-rw-r--r--   1 tsuki_  staff  4022 Apr 24 23:48 Gemfile.lock
-rw-r--r--   1 tsuki_  staff   374 Apr 24 23:48 README.md
-rw-r--r--   1 tsuki_  staff   227 Apr 24 23:48 Rakefile
drwxr-xr-x   8 tsuki_  staff   256 Apr 24 23:48 app
drwxr-xr-x   7 tsuki_  staff   224 Apr 24 23:48 bin
drwxr-xr-x  16 tsuki_  staff   512 Apr 24 23:48 config
-rw-r--r--   1 tsuki_  staff   160 Apr 24 23:48 config.ru
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 db
-rw-r--r--   1 tsuki_  staff   366 Apr 24 23:29 docker-compose.yml
-rw-r--r--   1 tsuki_  staff   201 Apr 24 23:23 entrypoint.sh
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 lib
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 log
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 public
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 storage
drwxr-xr-x   7 tsuki_  staff   224 Apr 24 23:48 tmp
drwxr-xr-x   3 tsuki_  staff    96 Apr 24 23:48 vendor
```
無事にRailsが生成されてますね！

###bundle install
```
$ docker-compose build
```

ぬぬ！？エラーが出ました

```
...[前略]
------
 > [ 9/12] RUN bundle install:
#14 0.294 /usr/local/lib/ruby/2.7.0/rubygems.rb:277:in `find_spec_for_exe': Could not find 'bundler' (1.17.2) required by your /myapp/Gemfile.lock. (Gem::GemNotFoundException)
#14 0.294 To update to the latest version installed on your system, run `bundle update --bundler`.
#14 0.294 To install the missing version, run `gem install bundler:1.17.2`
#14 0.296 	from /usr/local/lib/ruby/2.7.0/rubygems.rb:296:in `activate_bin_path'
#14 0.296 	from 
------
...[後略]
```

```
run `gem install bundler:1.17.2`
```
と書いたので、追加してみます。

```ruby:Dockerfile
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
########追加###########
RUN gem install bundler:1.17.2
######################
RUN bundle install
COPY . /myapp
```

はい、`$ docker-compose build`

ぬぬ！？また新しいエラーが出てきました。

```
...[前略]
------
 > [10/13] RUN bundle install:
#14 0.734 Your Ruby version is 2.7.2, but your Gemfile specified 2.6.5
------
...[後略]
```
そっか、GemfileのRubyバージョンは2.6.5だったのか
はい、変更！

```ruby:Gemfile
＃変更前
ruby '2.6.5'
#変更後
ruby '2.7.2'
。。。
```
はい、`$ docker-compose build`！

```
...[前略]
Successfully built.....
...[後略]
```

今度こそ無事にできたんですね！

###DBへの接続設定

Railsの`config/database.yml`ファイルを、下記のように書き換えます。

```ruby:database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test
```

###Dockerコンテナの起動

では、Dockerを起動してみます。

```
$ docker-compose up
```

したら、安心する画面ができましたね！

```ruby
...[前略]
web_1  | => Booting Puma
web_1  | => Rails 6.1.3.1 application starting in development 
web_1  | => Run `bin/rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Puma version: 5.2.2 (ruby 2.7.2-p137) ("Fettisdagsbulle")
web_1  | *  Min threads: 5
web_1  | *  Max threads: 5
web_1  | *  Environment: development
web_1  | *          PID: 1
web_1  | * Listening on http://0.0.0.0:3000
web_1  | Use Ctrl-C to stop
```

`http://0.0.0.0:3000`にアクセスしてみると
ぬぬ！？またエラーの画面が出てきました。

```ruby
...[前略]
web_1  | ActiveRecord::NoDatabaseError (FATAL:  database "myapp_development" does not exist
web_1  | ):
...[後略]
```
そかそかdatebaseの問題ね！

では一旦

```
$ docker-compose down
```
P.s.筆者はもう一つターミナルを開いて実行していますが、
こちらの[【Docker超入門 #7】Docker ComposeでRailsを構築しよう](https://www.youtube.com/watch?v=ltDdZAJli8c&t=0s)の19:55からのところは、
`docker-compose up`をバックグラウンドで実行してくれることについて説明がありますので、
必要な方は是非参考してみてください。

###DB作成

```
$ docker-compose run web rails db:create
```
そして

```
$ docker-compose up
```

無事に環境構築できたそうですね！！

![Yay! You’re on Rails!.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/ad843f8b-75d6-db5e-7f81-bd12f5ec6076.png)

##Dockerの環境でAPIモードでAPIを作ってみよう

https://qiita.com/k-penguin-sato/items/adba7a1a1ecc3582a9c9

この記事の参考で、Docker内でAPIを作っていきます。

###テスト環境

まずはテスト環境を先に用意していきます。今回はRspecを使います。

```ruby:Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #⇩追加
  gem 'rspec-rails'
end
```

そして`docker-compose build`でbundle install

次はこちらの記事：[dockerでよく使うコマンド一覧【Rails】](https://qiita.com/gyu_outputs/items/5178a539ba0c08b8b36e)で学んだDockerのコマンドで
Rspecのインストールを行います。

```
$ docker-compose run web bundle exec rails g rspec:install 
```
それでspecファイルが生成されます。

###モデル．コントローラの作成

では今回は簡単なタスク機能のアプリを作っていきます。

```
$ docker-compose run web bundle exec rails g model post title:string content:text
$ docker-compose run web bundle exec rails g controller posts
$ docker-compose run web bundle exec rails db:create
$ docker-compose run web bundle exec rails db:migrate
```

###ルーティングの設定


```ruby:config/routes.rb
Rails.application.routes.draw do
  namespace 'api' do
    namespace 'v1' do
      resources :posts
    end
  end
end
```

###コントローラの設定

ルートで設定した名前空間に合わせてディレクトリの構成を以下のようにします。

```ruby:controllers/api/v1/posts_controller.rb
module Api
  module V1
    class PostsController < ApplicationController
      before_action :set_post, only: [:show, :update, :destroy]

      def index
        posts = Post.order(created_at: :desc)
        render json: { status: 'SUCCESS', message: 'Loaded posts', data: posts }
      end

      def show
        render json: { status: 'SUCCESS', message: 'Loaded the post', data: @post }
      end

      def create
        post = Post.new(post_params)
        if post.save
          render json: { status: 'SUCCESS', data: post }
        else
          render json: { status: 'ERROR', data: post.errors }
        end
      end

      def destroy
        @post.destroy
        render json: { status: 'SUCCESS', message: 'Deleted the post', data: @post }
      end

      def update
        if @post.update(post_params)
          render json: { status: 'SUCCESS', message: 'Updated the post', data: @post }
        else
          render json: { status: 'SUCCESS', message: 'Not updated', data: @post.errors }
        end
      end

      private

      def set_post
        @post = Post.find(params[:id])
      end

      def post_params
        params.require(:post).permit(:title,:content)
      end
    end
  end
end
```

###初期データ作成

今回はseedで作ってみます。
seedの書き方はこの記事の参考しています→[railsのseedの書き方いろいろ](https://qiita.com/takehanKosuke/items/79a66751fe95010ea5ee)

```ruby:config/db/seeds
15.times do |i|
  Post.create!(
    title: "title#{i+1}",
    content: "テキスト#{i+1}" * 10
  )
end
```

seed書いたら

```
$ docker-compose run web rails db:seed
```
エラーが出ないので、無事にできたようです。

##postmanを使用して動作の確認

データはseedで生成したので
Railsのサーバーを動かしてみます。

```
$ docker-compose up
```

次は[Postman](https://www.postman.com)を開きます。
###GET

まずはGETの方にhttp://localhost:3000/api/v1/posts
、もしくは　http://0.0.0.0:3000/api/v1/posts
と記述していきます。

下の方にidの1〜15番のデータを取得することができました。

![PostmanGET.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/acfeeb50-ee07-88ab-5fca-dc60a4ceb576.png)

今度は`id`を指定します。

![PostmanGET：id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/824c73d5-2ad9-f8cd-b467-4e9d0a115183.png)

###POST
次はデータ作成してみましょう。
`JSON`のデータ形式で渡します。
Sendするとちゃんとid16が表示されました。

![PostmanPOST.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/ec256ee8-7068-030e-f9a8-4422b91ccba0.png)

###PUT
今回はURLをidを16に指定します
 http://0.0.0.0:3000/api/v1/posts/16
登録したデータをupdate更新してみます。
`PUT`の選択と`JSON`形式の設定を忘れないように

![PostmanPUT.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/bcd020a2-b464-2f93-6744-f47c26bec122.png)

###DELETE

最後に削除（Destroy）してみます。
同じURLをidを16に指定します
 http://0.0.0.0:3000/api/v1/posts/16
`DELETE`の選択で実行すれば、
無事に削除できたようですね

![PostmanDELETE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1371341/f979cf0e-c1b8-92a8-2f0c-ec43d2a04e44.png)

##まとめ
以上、Dockerの環境構築＋RailsのAPIモードのまとめでした。

今回Rspecのテストは書かないのですが、
こちらの記事：[【Rails】APIテストの書き方](https://qiita.com/k-penguin-sato/items/defdb828bd54729272ad)がありますので、興味がある方はご参考ください

もし内容の誤りがありましたら、ご指摘いただければ幸いです。

最後まで読んでいただき、ありがとうございました。