DockerとHeroku Container Registryを使ってAPI(Rails)とフロントエンド(Vue)を別プロジェクトにしたTODOアプリを作る①

やりたいことはタイトルの通りです。
MacOS上にDockerをインストールしてアプリを作成し、`git push heroku` ではなく `heroku container:push` を使用してコンテナごと Heroku にデプロイします。
なお、API は Ruby on Rails、フロントエンドは Nuxt.js (Vue.js) で実装します。

第一回目となる本記事ではAPIのデプロイまでを行います。

※ まずは詳しい説明はほとんど省いて、手順だけを載せた状態で一旦投稿します。追々説明等を追加していきます。

# Dockerのインストール
こちらのサイトから Docker for Mac をインストールします。Stable版で。
https://docs.docker.com/docker-for-mac/install/

# ディレクトリとファイルの準備
次のようにディレクトリとファイルを作成します。今回は Documents 配下に作成しますが、どこでも大丈夫です。各種ファイルの中身はとりあえずは空で。

```bash
mkdir ~/Documents/docker
mkdir ~/Documents/docker/api
mkdir ~/Documents/docker/front
touch ~/Documents/docker/docker-compose.yml
touch ~/Documents/docker/api/Dockerfile
touch ~/Documents/docker/api/Gemfile
touch ~/Documents/docker/api/Gemfile.lock
touch ~/Documents/docker/front/Dockerfile
touch ~/Documents/docker/front/.dockerignore
```
![スクリーンショット 2018-03-21 23.04.59.png](https://qiita-image-store.s3.amazonaws.com/0/237863/28080d6d-8692-e5ff-4131-54e7d4641c58.png)


# APIの作成
公式のクイックスタートガイドを参考に進めます
https://docs.docker.com/compose/rails/#define-the-project

Dockerfile, Gemfile, docker-compose.ymlを編集します。

```dockerfile:docker/api/Dockerfile
FROM ruby:2.5.0
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myApi
WORKDIR /myApi

COPY Gemfile /myApi/Gemfile
COPY Gemfile.lock /myApi/Gemfile.lock
RUN bundle install

COPY . /myApi

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

```ruby:docker/api/Gemfile
source 'https://rubygems.org'
gem 'rails', '5.1.5'
```

```dockerfile:docker/docker-compose.yml
version: '3'
services:
  db:
    image: postgres:10.3
    ports: 
      - "5432:5432"
    volumes:
      - ./api/tmp/db:/var/lib/postgresql/data
  api:
    build: ./api
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./api:/myApi
    ports:
      - "3000:3000"
    depends_on:
      - db
```

dockerを起動させてRailsプロジェクトを作成します。

```bash
# ディレクトリ移動
cd ~/Documents/docker

# コンテナを起動してRailsプロジェクト作成
docker-compose run api rails new . --force --database=postgresql --api
```
![スクリーンショット 2018-03-22 0.09.50.png](https://qiita-image-store.s3.amazonaws.com/0/237863/e58ba392-ff20-012a-8101-efd431d7d6a7.png)

ローカルの起動まで

```bash
# 権限の変更
sudo chown -R $USER:$USER ~/Documents/docker/api

# ディレクトリ移動
cd ~/Documents/docker

# ビルド
docker-compose build

# 起動
docker-compose up

# ブラウザで localhost:3000 にアクセスして動作確認
# 確認ができたら Ctrl + C でコンテナをストップ
```
![スクリーンショット 2018-03-22 0.29.26.png](https://qiita-image-store.s3.amazonaws.com/0/237863/d65d872d-e508-5499-ad99-74d5dd1dbaee.png)

いろいろ作業

```yml:docker/api/config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: myApi_development

test:
  <<: *default
  database: myApi_test

production:
  <<: *default
  database: myApi_production
  username: myApi
  password: <%= ENV['MYAPI_DATABASE_PASSWORD'] %>
  url: <%= ENV['DATABASE_URL'] %>
```

```bash
# scaffold
docker-compose run api rails g scaffold Todo title:string is_done:boolean
```

```ruby:docker/api/app/db/migrate/xxxxxxxxxxxxxx_create_todos.rb
class CreateTodos < ActiveRecord::Migration[5.1]
  def change
    create_table :todos do |t|
      t.string :title, null: false
      t.boolean :is_done, default: false, null: false

      t.timestamps
    end
  end
end
```

```ruby:docker/api/app/db/seeds.rb
4.times { Todo.create!(title: 'Sample Task') }
3.times { Todo.create!(title: 'Sample Task Done', is_done: true) }
```

```ruby:docker/api/config/routes.rb
Rails.application.routes.draw do
  namespace :api, format: 'json' do
    namespace :v1 do
      resources :todos
    end
  end
end
```

```bash
# ディレクトリの作成
mkdir -p ~/Documents/docker/api/app/controllers/api/v1

# ファイルの移動
mv ~/Documents/docker/api/app/controllers/todos_controller.rb ~/Documents/docker/api/app/controllers/api/v1
```

```ruby:docker/api/app/controllers/api/v1/todos_controller.rb
class Api::V1::TodosController < ApplicationController    # ここを修正
# POST /todos
  def create
    @todo = Todo.new(todo_params)

    if @todo.save
      render json: @todo, status: :created                # ここを修正 
    else
      render json: @todo.errors, status: :unprocessable_entity
    end
  end
end
```

```bash
# ディレクトリの移動
cd ~/Documents/docker

# DB作成
docker-compose run api rails db:create

# テーブルの作成
docker-compose run api rails db:migrate

# シードの反映
docker-compose run api rails db:seed

# 起動
docker-compose　up

# ブラウザで localhost:3000/api/v1/todos にアクセスして動作確認

# ブラウザで localhost:3000/api/v1/todos/1 にアクセスして動作確認

# ターミナルをもう一枚開きcurlで確認
curl localhost:3000/api/v1/todos
curl localhost:3000/api/v1/todos/1

# 登録の確認
curl -X POST -H "Content-Type: application/json" -d '{"todo": {"title": "todo from curl"}}' localhost:3000/api/v1/todos

# 更新の確認
curl -X PATCH -H "Content-Type: application/json" -d '{"todo": {"title": "todo updated"}}' localhost:3000/api/v1/todos/8

# 削除の確認
curl -X DELETE -H "Content-Type: application/json" localhost:3000/api/v1/todos/8
curl localhost:3000/api/v1/todos
```
![スクリーンショット 2018-03-22 1.04.38.png](https://qiita-image-store.s3.amazonaws.com/0/237863/fce3774c-9629-d93e-d6af-7840f5c3501c.png)

![スクリーンショット 2018-03-22 1.07.49.png](https://qiita-image-store.s3.amazonaws.com/0/237863/db50059f-8749-890b-238c-5daccca749d5.png)

# APIのデプロイ
Herokuのアカウントを作成します。
https://signup.heroku.com/login

デプロイします。

```bash
# heroku-cliのインストール
brew install heroku/brew/heroku

# heroku-cliのバージョン確認
heroku —version

# herokuにログイン
heroku login

# 鍵の追加
heroku keys:add

# コンテナ用のプラグインを追加
heroku plugins:install heroku-container-registry

# コンテナにログイン
heroku container:login

# アプリの作成（作成されたアプリ名称を控えておく）
heroku create

# herokuに設定を追加
SECRET_BASE_KEY=$(docker-compose run api rails secret)
heroku config:add SECRET_KEY_BASE=$SECRET_BASE_KEY -a {your_heroku_app_name}

# APIディレクトリに移動
cd ~/Documents/docker/api

# コンテナをデプロイ
heroku container:push web -a {your_heroku_app_name}

# PostgreSQLのアドオンを追加
heroku addons:create heroku-postgresql:hobby-dev -a {your_heroku_app_name}

# テーブルの作成
heroku run rails db:migrate -a {your_heroku_app_name}

# シードの反映
heroku run rails db:seed -a {your_heroku_app_name}

# ブラウザで確認
heroku open -a {your_heroku_app_name}
heroku open -a {your_heroku_app_name}/api/v1/todos

# curlで確認
curl　https://{your_heroku_app_name}.herokuapp.com/api/vi/todos
curl -X POST -H "Content-Type: application/json" -d '{"todo": {"title": "todo from curl to heroku"}}'
curl　https://{your_heroku_app_name}.herokuapp.com/api/vi/todos
```