Docker(Rails6Api + PostgreSQL + Nuxt.js).push(heroku.yml) = Hello!!SPA


## この記事の一部が動画になりました

[Rails × Nuxt.js = HelloAPIApp - Youtube](https://www.youtube.com/watch?v=lXM-977Cj8M&list=PLZ7rMmopvM9lGpIpR93Fvp6vxMHR_iQq4)

## この記事を完走すると作れるもの

Dockerを使ったRails6ApiとNuxt.jsのアプリケーションを構築します。

Nuxt.jsのボタンを押すとRailsから「Hello」が返されます。

**完成イメージ**

![2020-06-13_11-23-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/e5daff89-76b6-4a32-92de-44cf3fb112be.png)




### 作業手順

1. Dockerを使ってRails6とNuxt.jsの開発環境を構築します。
2. Rails6とNuxt.jsがAPI通信をするアプリケーションを作成します。
3. heroku.ymlを使ってRailsをHerokuにデプロイします。
4. heroku.ymlを使ってNuxt.jsをHerokuにデプロイします。



### デモアプリURL

> このURLは予告なく変更・削除する場合があります。

[https://nuxt-rails-v3-front.herokuapp.com/](https://nuxt-rails-v3-front.herokuapp.com/)



### 仕様

- OS ... macを使用しています。
- Docker for Mac ... インストール済みとします。
- 使用しているDockerのバージョン ... v 19.03.8
- Composeファイルに`version: 3.8`を指定するので、Dockerはv 19.03以上を使用してください。

> 参考 [Compose file version 3 reference - Docker docs](https://docs.docker.com/compose/compose-file/)




## 1.開発環境構築

**開発環境の全体像**

![003_Docker_dev_intro.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/289e5f6f-ed58-c339-b0e2-fe4cc7a46e4c.png)




### 作業ディレクトリの作成

```terminal
$ mkdir nuxt_rails_v3_dockerfile_test && cd $_

root $ git init

root $ mkdir {api,front}

root $ cd api

api $ touch {Dockerfile,Gemfile,Gemfile.lock}

api $ ls
Dockerfile	Gemfile		Gemfile.lock
```



**api/Dockerfile**

```dockerfile
FROM ruby:2.7.1-alpine

ARG WORKDIR

ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata postgresql-dev postgresql git" \
    DEV_PACKAGES="build-base curl-dev" \
    HOME=/${WORKDIR} \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

# ENV test（このRUN命令は確認のためなので無くても良い）
RUN echo ${HOME}

WORKDIR ${HOME}

COPY Gemfile* ./

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    bundle install -j4 && \
    apk del build-dependencies

COPY . .

CMD ["rails", "server", "-b", "0.0.0.0"]
```
> Dockerfile参考
> [Nuxt.js + Rails(API) on DockerのHello Worldするべ！](https://qiita.com/at-946/items/08de3c9d7611f62b1894)


**api/Gemfile**

```ruby

source 'https://rubygems.org'
gem 'rails', '~> 6.0.0'
```



### frontディレクトリの作業

```terminal
api $ cd ../front

front $ touch Dockerfile
```



**front/Dockerfile**

```dockerfile
FROM node:14.4.0-alpine

ARG WORKDIR
ARG CONTAINER_PORT

ENV HOME=/${WORKDIR} \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo \
    HOST=0.0.0.0

# ENV check（このRUN命令は確認のためなので無くても良い）
RUN echo ${HOME}
RUN echo ${CONTAINER_PORT}

WORKDIR ${HOME}

EXPOSE ${CONTAINER_PORT}
```



### rootディレクトリの作業

```terminal
front $ cd ..

root $ touch {.gitignore,.env,docker-compose.yml}

root $ ls -a
.			.git			docker-compose.yml
..			.gitignore		front  .env			api
```



**.gitignore**

```tex
/.env
```



**.env**

```tex
# commons
WORKDIR=app
CONTAINER_PORT=3000
API_PORT=3000
FRONT_PORT=8080

# db
POSTGRES_PASSWORD=password
```



**docker-compose.yml**

```yaml
version: '3.8'

services:
  db:
    image: postgres:12.3-alpine
    environment:
      TZ: UTC
      PGTZ: UTC
      POSTGRES_PASSWORD: $POSTGRES_PASSWORD
    volumes:
      - ./api/tmp/db:/var/lib/postgresql/data

  api:
    build:
      context: ./api
      args:
        WORKDIR: $WORKDIR
    environment:
      POSTGRES_PASSWORD: $POSTGRES_PASSWORD
    volumes:
      - ./api:/$WORKDIR
    depends_on:
      - db
    ports:
      - "$API_PORT:$CONTAINER_PORT"

  front:
    build:
      context: ./front
      args:
        WORKDIR: $WORKDIR
        CONTAINER_PORT: $CONTAINER_PORT
    command: yarn run dev
    volumes:
      - ./front:/$WORKDIR
    ports:
      - "$FRONT_PORT:$CONTAINER_PORT"
    depends_on:
      - api
```



### Dockerイメージを作る

```terminal
root $ docker-compose build

# 成功
Successfully built af6dbf3f1f30
Successfully tagged nuxt_rails_v3_blogtest_front:latest

# 確認
root $ docker images
```



### Railsアプリの作成

```terminal
root $ docker-compose run --rm api rails new . -f -B -d postgresql --api
```

- run <任意オプション> <サービス名> <コマンド>... サービスに対してコマンドを実行する
- --rm (runオプション) ...  コンテナstop時に自動でコンテナを削除する
- rails new . ... 今いるディレクトリ内でrailsアプリを作成する（新たなディレクトリを作成しない）
- -f (rails newオプション) ... ファイルが存在する場合に上書きする（Gemfileを対象としている）
- -B (rauls newオプション) bundle installをしない（後で再buildするので）
- -d (rails newオプション) ... 使用するデータベースを指定する
- --api ... APIモードのアプリケーションを作成する



> 参考
>
> [Dockerコマンドライン・リファレンス](http://docs.docker.jp/compose/reference/run.html)
>
> [Railsドキュメント](https://railsdoc.com/rails)
>
> [Railsガイド](https://railsguides.jp/api_app.html#アプリケーションを新規作成する)





- Gemfileが書き変わり、ruby v2.7.1、Rails 6.0.0系が使用されていることを確認する

```ruby
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.1'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
...
```



- Gemfileが書き変わったのでもう一度apiのDockerイメージを再ビルドする
- 公式ドキュメントにあるようにGemfileまたはDockerfileを書き換えた場合は再buildする、と覚えておきましょう。

  > 参考
  >
  > *新しいGemfileを入手したので、イメージを再度ビルドする必要があります。（これと、GemfileまたはDockerfile への変更は、再構築が必要な唯一の場合です。）*
  >
  > [Docker docs](https://docs.docker.com/compose/rails/)

  

```termianl
root $ docker-compose build api
```



### Rails DB設定

**api/config/database.yml**

```yaml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db            # 追加
  username: postgres  # 追加(デフォルトの名前)
  password: <%= ENV["POSTGRES_PASSWORD"] %>  # 追加
	...
```



- 設定が完了したらDB作成コマンドを実行する（初回のみ）

```terminal
root $ docker-compose run --rm api rails db:create

# 成功
Created database 'app_development'
Created database 'app_test'
```



### Railsの起動を確認する

```terminal
root $ docker-compose up api
```

- ブラウザで確認 http://localhost:3000

- コンテナの停止

```terminal
「Control」 + 「C」
root $ docker-compose ps

            Name                          Command              State     Ports  
--------------------------------------------------------------------------------
nuxt_rails_v3_blogtest_api_1   rails server -b 0.0.0.0         Exit 1           
nuxt_rails_v3_blogtest_db_1    docker-entrypoint.sh postgres   Up       5432/tcp
```

- 停止したコンテナを削除する
- `-f`オプション ... 削除しますか?の確認なしで削除する

```terminal
root $ docker-compose rm -f
root $ docker-compose ps   

           Name                          Command              State    Ports  
------------------------------------------------------------------------------
nuxt_rails_v3_blogtest_db_1   docker-entrypoint.sh postgres   Up      5432/tcp
```

- DBコンテナが削除できていない
- こんな時は`down`コマンドを実行する
- `docker-compose down` ... コンテナの停止と削除を同時に行う

```terminal
root $ docker-compose down
root $ docker-compose ps

Name   Command   State   Ports
------------------------------
```



### 【コラム】postgreSQLのパスワードを変更したい場合

- コンテナを起動

```terminal
docker-compose up -d db
```

- postgreSQLの中に入る

```terminal
 docker-compose exec -u <ユーザー名> db psql
```

- ここのユーザー名は指定がない限り「postgres」となります。

  > 参考 
  >
  > [PsotgreSQL - Docker Hub](https://hub.docker.com/_/postgres)



- ユーザーのパスワードを変更する

```terminal
postgres=# ALTER USER <ユーザー名> WITH PASSWORD '新たなパスワード';

# 例
ALTER USER postgres WITH PASSWORD 'password';
```

- パスワードがセットされているか確認する

```terminal
postgres=# SELECT * FROM pg_shadow;

# OK
 usename  | usesysid |               passwd               |
 postgres |       10 |md532e12f215ba27cb750c9e093ce4b5127 |

```

- postgreSQLから抜ける

```terminal
postgres=# \q
```

- 起動コンテナの停止・削除

```terminal
 docker-compose down
```

- 変更後はRailsのパスワードも変更しておきましょう

**config/database.yml**

```yaml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db          
  username: postgres
  password: <新たなパスワード>
  ...
```

【コラム】終わり

### Nuxt.jsアプリの作成

- Nuxt.jsアプリ作成コマンドの実行

```temrinal
root $ docker-compose run --rm front yarn create nuxt-app


Q,アプリ名はこれで良いですか? (Yes: Enter)
? Project name (app) 

Q,アプリの説明はこれで良いですか? (Yes: Enter)
? Project description (My splendid Nuxt.js project) 

Q,アプリの製作者はこれで良いですか? (自分の名前を記述 + Enter)
? Author name () andou

Q,JavaScriptとTypeScriptどっち使う? (JavaScript: Enter)
? Choose programming language (Use arrow keys)
❯ JavaScript 
  TypeScript 

Q,パッケージマネージャーはどちらですか? (Yarn: Enter)
? Choose the package manager (Use arrow keys)
❯ Yarn 
  Npm 

Q,CSSフレームワークはどれにしますか? (とりあえず使わない: None選択 + Enter)
? Choose UI framework (Use arrow keys)
❯ None 
  Ant Design Vue 
	...

Q,サーバーフレームワークはどれにしますか? (使わない: None選択 + Enter)
? Choose custom server framework (Use arrow keys)
❯ None (Recommended) 
  AdonisJs 
	...

Q,導入するモジュールを選択してください。 (Axios: Axios選択中にスペースkey + Enter)
? Choose Nuxt.js modules 
❯◯ Axios
 ◯ Progressive Web App (PWA) Support
 ◯ DotEnv
 
Q,文法チェックツールを選択してください。 (ESLint: ESLint選択中にスペースkey + Enter)
? Choose linting tools 
❯◯ ESLint
 ◯ Prettier
 ◯ Lint staged files
 ◯ StyleLint

Q,テストフレームワークを選択してください。 (使わない: None選択 + Enter)
? Choose test framework (Use arrow keys)
❯ None 
  Jest 
  AVA 
  
# レンダリングモードをお選びください。 (Single Page App: Single Page Appに矢印 + Enter)
? Choose rendering mode (Use arrow keys)
  Universal (SSR) 
❯ Single Page App 
  
Q,開発ツールをお選びください。 (使わない: そのままEnter)
Choose development tools
n)
❯◯ jsconfig.json (Recommended for VS Code)
 ◯ Semantic Pull Requests
 
# アプリ作成開始...
.
.
# アプリ完成
🎉  Successfully created project app
```



- アプリの起動確認

```terminal
root $ docker-compose up front
```

- ブラウザで確認 http://localhost:8080

- コンテナの停止・削除

```terminal
front停止:「Control」 + 「C」
api/db停止: docker-compose stop
削除: docker-compose rm -f
確認: docker-compose ps
```



<!-- [TODO]GitHubにpushするチャプター -->





## 2.Rails x Nuxt API



### 下準備

- docker-compose.ymlに環境変数を用意する

**docker-compose.yml**

```yaml
  api:
    build:
      context: ./api
      args:
        WORKDIR: $WORKDIR
    environment:
      POSTGRES_PASSWORD: $POSTGRES_PASSWORD
      API_DOMAIN: "localhost:$FRONT_PORT"       # 追加

  ...

	front:
    build:
      context: ./front
      args:
        WORKDIR: $WORKDIR
        CONTAINER_PORT: $CONTAINER_PORT
        API_URL: "http://localhost:$API_PORT"   # 追加
```



- forntのDockerfileに環境変数を受け取る設定をする

**front/Dockerfile**

```dockerfile
FROM node:14.4.0-alpine

ARG WORKDIR
ARG CONTAINER_PORT
# 追加
ARG API_URL

ENV HOME=/${WORKDIR} \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo \
    # \ 追記
    HOST=0.0.0.0  \
    # 追加
    API_URL=${API_URL}

# ENV check（このRUN命令は確認のためなので無くても良い）
RUN echo ${HOME}
RUN echo ${CONTAINER_PORT}
# 追加
RUN echo ${API_URL}

WORKDIR ${HOME}

EXPOSE ${CONTAINER_PORT}
```



- Dockerfileを書き換えたので再ビルトする

```terminal
root $ docker-compose build front
```



- これでRails、Nuxt.js共にAPI用の環境変数を参照できるようになった



### RailsにHelloを返すAPIを作成

- helloコントローラーを作成する

```terminal
root $ docker-compose run --rm api rails g controller api::v1::hello

# 成功
create  app/controllers/api/v1/hello_controller.rb
invoke  test_unit
create    test/controllers/api/v1/hello_controller_test.rb
```

- `api::v1::hello` ... 子ディレクトリのapi/v1内にherlloコントローラーを作成する

- テストファイルを作成しない`--skip-test-framewor`オプションは無くなった模様。

  > 参考
  >
  > [Railsドキュメント](https://railsdoc.com/page/rails_generate_controller)



- コントローラーに"Hello"を返すアクションを作成

**api/app/controllers/api/v1/hello_controller.rb**

```ruby
class Api::V1::HelloController < ApplicationController
  # 追加
  def index
    render json: "Hello"
  end
end
```



- rootを追加する

**api/config/routes.rb**

```ruby
Rails.application.routes.draw do
  # 追加
  namespace :api do
    namespace :v1 do
      # api test action
      resources :hello, only:[:index]
    end
  end
end
```



- アクションを確認する

```terminal
root $ docker-compose up api
```

- ブラウザにアクセス

  http://localhost:3000/api/v1/hello



- コンテナの停止・削除・確認

```terminal
「Control」 + 「C」
root $ docker-compose down
root $ docker-compose ps
```



- Gitにpushしとく



### Nuxt.js Axiosの設定を行う

- Axiosの共通設定ファイルを作成する

```terminal
root $ cd front

front $ touch plugins/axios.js
```



**front/plugins/axios.js**

```javascript
export default ({ $axios }) => {
  // リクエストログ
  $axios.onRequest((config) => {
    console.log(config)
  })
  // レスポンスログ
  $axios.onResponse((config) => {
    console.log(config)
  })
  // エラーログ
  $axios.onError((e) => {
    console.log(e.response)
  })
}
```



- このファイルを読み込むように設定

**front/nuxt.config.js**

```javascript
...
plugins: [
  'plugins/axios' // 追加
],
```



- ~~本番用にaxiosのベースURLを設定する~~

> 2020年6月23日 訂正
> baseURLは、環境変数のAPI_URLを優先するため、ここの設定は必要ありませんでした。
> baseURL: process.env.API_URLを削除しました。

**front/nuxt.config.js**

```javascript
...
axios: {
  // サーバーサイドで行うリクエストに使用されるURL
  // baseURL: process.env.API_URL
  // クライアントサイドで行うリクエストに使用されるURL(デフォルト: baseURL)
  // browserBaseURL: <URL>
},
```

- Nuxt.jsのSAPモードでAPI通信を行う場合は、クライアントからリクエストを飛ばすだけなので`browserBaseURL`にAPI_URLを設定します。
- デフォルトのURLは`baseURL`です。
- ~~そこで今回は`baseURL`にAPI_URLを設定し、`browserBaseURL`の値を変更するように設定しています。~~

  > 参考 [options - Axios ](https://axios.nuxtjs.org/options.html#baseurl)



### Nuxt.jsにAPIリクエストボタンを作成

**front/pages/index.vue**

```html
<template>
  <div>
    <button
      type="button"
      name="button"
      @click="getMsg"
    >
      RailsからAPIを取得する
    </button>
    <div
      v-for="(msg, i) in msgs"
      :key="i"
    >
      {{ msg }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      msgs: []
    }
  },
  methods: {
    getMsg () {
      this.$axios.$get('/api/v1/hello')
        .then(res => this.msgs.push(res))
    }
  }
}
</script>
```



- 準備完了。コンテナ起動

```temrinal
front $ cd ..

root $ docker-compose up
```

- http://localhost:8080にアクセス
- ボタンを押すとCORSエラーが

![2020-06-12_17-47-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/0b039c9b-c2b5-718f-ad19-4d7be7077ebf.png)



- これはhttp://localhost:8080 から http://localhost:3000 へ、別URLにリクエストを飛ばしているため起こるエラーです。
- Railsもいきなり知らないURLからリクエストが来たのでびっくりしてエラーを吐いたんでしょうね。
- Rails側にこのドメインのリクエストを許可する設定をしましょう。

> Nuxt.jsのproxy設定でCORSエラーを回避できるようですが、コンテナ経由だとうまく設定できませんでした。今回はサーバー側に設定を行います。



- コンテナの停止・削除

```terminal
root $  docker-compose down
```





### RailsのCORSエラーに対応

**api/Gemfile**

```ruby
# 26行目のコメント外す
gem 'rack-cors'
```



- GemfileはDockerfileに関連するファイルなので、再ビルドします。
- Gemfileを書き換えたら再ビルドと覚えておきましょう。

```terminal
root $ docker-compose build api

# 確認
root $ docker-compose run --rm api bundle info rack-cors

# OK
* rack-cors (1.1.1)
 ...
```



- 設定ファイルを編集
- コメントを外して、`origins`の部分を書き換えます。

**api/config/initializers/cors.rb**

```ruby
# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins ENV["API_DOMAIN"] || ""

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```



- 以上で完了。確認しよう

```terminal
root $ docker-compose up
```



- レスポンスの成功を表す200が返ってきました。Helloも表示されています。

![2020-06-12_18-17-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/a1d7537b-cc78-89b8-4ae4-d15ea29053c0.png)





- コンテナの停止・削除

```terminal
「Control」+「C」
root $ docker-compose down
```



- api、fornt、root全てをgit にpush



## 3.Herokuにpush

**本番環境の全体像**

![production_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/9d2aa835-885f-5cf4-302e-f56661016e80.png)




### Herokuに新規登録

> ここから
> [https://signup.heroku.com/login](https://signup.heroku.com/login)

- First name ... 名前

- Last name ... 名字

- Email address ... メール

- Company name ... 無記入でOK

- Role ... Herokuを利用する目的
  迷ったら、Hobbyist（趣味）、Professional Developer（プロフェショナル開発者）のどちらかでOK

- Country ... Japan

- Primary development language ... 今回はRubyを選択

- 「Create Free Account」をクリック

- メールフォルダを開きHerokuからのメールを確認

- メールのリンクをクリックしてメール認証完了

- 次はパスワードの設定
  文字、数字、記号を含む8文字以上のパスワード



### Heroku CLIのインストール

> ここから
> [https://devcenter.heroku.com/articles/heroku-cli](https://devcenter.heroku.com/articles/heroku-cli)

- macOSの「Download the Installer」のボタンをクリック

- こんな注意事項が出てきた場合

![2020-05-20_16-55-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/447f32b6-7f53-d8d1-d5c5-26b186d58f27.png)




- macのシステム環境設定を開き => 「セキュリティとプライバシー」

- "heroic.pkg"は開発元を確認できないため、使用がブロックされました。の横「このまま開く」のボタンをクリック

![2020-05-20_17-00-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/8340b6d6-05f6-492f-7c29-ce3f27f78560.png)



- あとは手順に沿ってインストール

- イントールできたか確認
- どのディレクトリにいてもOKなので下記コマンドを実行

```terminal
% heroku --version (もしくは -v)

heroku/7.39.5 darwin-x64 node-v12.16.2
```





### Heroku用にpumaの設定

**api/config/puma.rb**

```ruby
workers Integer(ENV.fetch("WEB_CONCURRENCY") { 2 })

max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

preload_app!

rackup      DefaultRackup
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }

on_worker_boot do
  ActiveRecord::Base.establish_connection
end
```

> 参考 [Puma Web Server を使用した Rails アプリケーションの展開 - Heroku](https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server)



### Rails用のheroku.yml

- api直下のheroku.ymlを作成

```termianl
root $ touch api/heroku.yml
```



**api/heroku.yml**

> 2020年6月24日 訂正
> HerokuCLIのプラグイン「manifest」を導入しないため、setupの記述は必要ありません。
> 削除しました。
> 「manifest」によるHerokuアプリケーションの作成は、[こちら](http://blog.cloud-acct.com/posts/u-setup-herokuyml-deploy)の記事をご覧ください。

```yaml
# setup:
#   addons:
#     - plan: heroku-postgresql
#       as: DATABASE
build:
  docker:
    web: Dockerfile
  config:
    WORKDIR: app
run:
  web: bundle exec puma -C config/puma.rb
```



>  参考 [heroku.ymlを使用したDockerイメージの構築 - Heroku](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml)



### Herokuアプリの作成

- apiディレクトリに移動

```terminal
root $ cd api
```

- Herokuにログイン

```temrinal
api $ heroku login

# Enterキー
heroku: Press any key to open up the browser to login or q to exit:

# 成功
Logging in... done
Logged in as <Heroku email>
```

- appの作成

```terminal
api $ heroku create <app name>

# 例) heroku create my-app

# 成功
Creating ⬢ <app name>... done
https://<app name>.herokuapp.com/ | https://git.heroku.com/<app name>.git
```

- 確認

```temrinal
api $ heroku open
```

- 成功
![2020-06-13_08-51-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655003/0a75db02-5b7c-222b-ab28-33c71864af76.png)




### Herokuに基本的な環境変数をセット

- コンテナを使わないRailsデプロイの場合、以下の環境変数が初期値として設定されている

- LANG: en_US.UTF-8
  - ランゲージ（言語）
- RACK_ENV: production
  - ラックエンブ
  - Rackへ現在の環境を示す変数
  - rack ... Ruby言語で開発されたhttp送受信処理を担当するモジュールのこと
  - [https://railsguides.jp/rails_on_rack.html](https://railsguides.jp/rails_on_rack.html)
- RAILS_ENV: production
  - レイルズ エンブ
  - Railsへ現在の環境を示す変数
- RAILS_LOG_TO_STDOUT: enabled
  - ログ エスティディ（スタンダート） アウト
  - logを標準で出力するかどうかのフラグ
- RAILS_SERVE_STATIC_FILES: enabled
  - サーバー スタティック ファイルズ
  - publicディレクトリからの静的ファイルの提供の有無を切り替えるフラグ
  - [https://devcenter.heroku.com/changelog-items/617](https://devcenter.heroku.com/changelog-items/617)

- コンテナ経由の場合、自分で環境変数を設定する必要がある。
- LANG、TZについてはDockerfileで設定しているため、Herokuに自動で設定される。
- ここではそれ以外を設定する。

```terminal
api $ heroku config:set RACK_ENV=production RAILS_ENV=production RAILS_LOG_TO_STDOUT=enabled RAILS_SERVE_STATIC_FILES=enabled
```

- 確認

```terminal
api $ heroku config

# 成功
RACK_ENV:                 production
RAILS_ENV:                production
RAILS_LOG_TO_STDOUT:      enabled
RAILS_SERVE_STATIC_FILES: enabled
```



### RailsアプリをHerokuにデプロイ

- Gitにコミット

```terminal
api $ git add -A
api $ git commit -m "add_hello_api"
```

- heroku.yml経由でデプロイする場合はHerokuのstackをコンテナに変更する必要がある

```terminal
api $ heroku stack:set container
```

- 確認

```terminal
api $ heroku stack

# 成功
  cedar-14
* container
  heroku-16
  heroku-18
```

- push

```temrinal
api $ git push heroku master
```



### HerokuDB設定

- PostgreSQLのアドオンを追加

```terminal
api $ heroku addons:create heroku-postgresql:hobby-dev
```

- 確認

```temrinal
api $ heroku addons

Add-on                                            Plan       Price  State  
────────────────────────────────────────────────  ─────────  ─────  ───────
heroku-postgresql (postgresql-dimensional-xxxxx)  hobby-dev  free   created
```

- HerokuDB初期化

```terminal
api $ heroku run rails db:migrate
```

### DB初期化時のエラー

```terminal
ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`
```

- これは「暗号化された`secret_key_base`が見れないぞ！開くための鍵をくれ。」というエラーです。

- 鍵は「api/config/master.key」にあります。

- この「master.key」はGitの管理下に置かれていないので、本番環境になったとたんエラーとなったんですね。
- このように、開発環境で動くのに本番環境ではエラーになることはこれからの開発で多々あります。まずGitの管理外ファイル周りを疑ってみてください。



### Herokuにマスターキーを追加する

- 環境変数としてマスターキーを追加します。
- api/config/master.keyの値をコピーして`RAILS_MASTER_KEY`という変数に値をセットしてください。

```terminal
api $ heroku config:set RAILS_MASTER_KEY=<master key>

# 間違った場合は値を入れ替えてもう一度setすればOK
api $ heroku config:set RAILS_MASTER_KEY=<new master key>
```

> 参考 [Rails 5.2 で ActiveSupport::MessageEncryptor::InvalidMessage - Qiita](https://qiita.com/scivola/items/cc06ddbfd94d3118f693)

- もう一度DBの初期化を行う

```terminal
api $ heroku run rails db:migrate

# 成功
Running rails db:migrate on ⬢ ...
```



### Herokuタイムゾーンの確認

- Dockerfileで指定したタイムゾーンがHerokuに設定されているか確認

```terminal
# Herokuアプリに入る
api $ heroku run sh

~$ date

# ISTになっていれば日本時間
Fri Jun  5 09:17:55 JST 2020

# 抜ける
~$ exit
```



### Heroku PostgreSQLのタイムゾーンの確認

- PostgreSQLの名前確認

```terminal
api $ heroku pg:info

...
# 名前
Add-on: postgresql-dimensional-xxxxx
```

- PostgreSQLに入る

```temrinal
api $ heroku pg:psql <PostgreSQLの名前>

# 例) heroku pg:psql postgresql-dimensional-xxxxx

# タイムゾーン確認
:DATABASE=> show timezone;

# OK
TimeZone 
----------
 Etc/UTC
(1 row)

# 抜ける
:DATABASE=> \q
```

> Heroku postgresqlのタイムゾーンの考え方
>
> [Heroku Postgresのタイムゾーン設定を変更するにはどうすればよいですか？- Heroku](https://help.heroku.com/XKRPVR53/how-to-change-the-timezone-setting-for-heroku-postgres)



### Herokuアプリの確認

```terminal
api $ heroku open
```

- トップページは「ページが見つかりません」になると思うので「/api/v1/hello」にアクセスする
- helloが表示されていれば成功



## 4.Nuxt.jsをHerokuにデプロイ



### デプロイ用のDockerfileに変更

- デプロイ用にDockerfileを編集

**front/Dockerfile**

```dockerfile
FROM node:14.4.0-alpine

ARG WORKDIR
ARG CONTAINER_PORT
ARG API_URL

ENV HOME=/${WORKDIR} \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo \
    HOST=0.0.0.0  \
    API_URL=${API_URL} \
    # 追加
    NPM_CONFIG_PRODUCTION=false

# ENV check（このRUN命令は確認のためなので無くても良い）
RUN echo ${HOME}
RUN echo ${CONTAINER_PORT}
RUN echo ${API_URL}

WORKDIR ${HOME}

# 追加
COPY package*.json ./
RUN yarn install

COPY . .

RUN yarn run build
# ここまで

EXPOSE ${CONTAINER_PORT}
```

- Nuxt.jsのSPAモードでは、`heroku config:set` で設定した環境変数を参照できない。そこでDockerイメージ内で環境変数を設定し、`nuxt build`することでNuxt.jsに環境変数を渡す

- `NPM_CONFIG_PRODUCTION=false` ... devDependenciesのパッケージもインストールするための環境変数
  > 参考 [Heroku へデプロイするには？ - Nuxt.js](https://ja.nuxtjs.org/faq/heroku-deployment/)

- NODE_ENVは自動でproductionに切り替わるので設定の必要なし

- `COPY package*.json ./` ... ローカルのpackageから始まる全てのファイルを、作業ディレクトリの直下にコピー
 package.jsonとpackage-lock.jsonが対象になります。



- `RUN yarn install` ...
  ちょっと豆知識。筆者がコピペで間違えたトコ。
  ネットでは、Alpineベースのnodeイメージで`yarn`をインストールしている記事があります。
  当初は何も疑わずyarnをインストールしていましたが、**Alpineベースのnodeイメージには最初からyarnが入っています。**
  （この記事を含む）ネットの情報はすぐに当てにせず、自分のDockerfileと見比べて何が必要か考える癖をつけましょう。
  > 助けられた記事 [alpine-linuxにyarnを入れる](https://scrapbox.io/keroxp/alpine-linuxにyarnを入れる)





### Nuxt.js用のheroku.ymlを設定

- frontディレクトリに移動

```terminal
api $ cd ../front
```

- heroku.ymlファイルの作成

```terminal
front $ touch heroku.yml
```

**front/heroku.yml**

```yaml
build:
  docker:
    web: Dockerfile
  config:
    WORKDIR: app
    API_URL: <RailsアプリのURL>
    # 例) API_URL: https://my-app.herokuapp.com
run:
  web: yarn run start
```



### Herokuアプリの作成

- Herokuにログイン

```temrinal
front $ heroku login

# Enterキー
heroku: Press any key to open up the browser to login or q to exit:

# 成功
Logging in... done
Logged in as <Heroku email>
```

- appの作成

```terminal
front $ heroku create <app name>
```

### Nuxt.jsアプリをHerokuにpush

- Gitの初期化
- Nuxt.jsアプリは.gitディレクトリが無いので初期化が必要

```terminal
front $ git init
```

- Gitにコミット

```terminal
front $ git add -A
front $ git commit -m "add_index.vue_hello_api"
```

- heroku.yml経由でデプロイする場合はHerokuのstackをコンテナに変更する必要がある

```terminal
api $ heroku stack:set container
```

- エラーが出た場合

```terminal
 ›   Error: Missing required flag:
 ›     -a, --app APP  app to run command against
 ›   See more help with --help
```

- GitとHerokuアプリの紐付けがうまくできていないエラー。参照先が分からない。アプリ名を指定してくれ！と言われている。

- アプリ名を指定するのも良いが、ここでは`git remote`にHerokuのURLを追加することでも解決する。

- HerokuのGitURLを調べる

```terminal
front $ heroku info -a <Herokuのアプリ名>
 
# 例) heroku info -a my-app-front
 
# このURLをコピー
Git URL:        https://git.heroku.com/<Herokuのアプリ名>.git
```

- Git remoteに追加する

```temrinal
front $ git remote add heroku https://git.heroku.com/<Herokuのアプリ名>.git
```

- 確認する

```terminal
front $ git remote -v

# 成功
heroku	https://git.heroku.com/<Herokuのアプリ名>.git (fetch)
heroku	https://git.heroku.com/<Herokuのアプリ名>.git (push)
```

- もう一度stackコマンドを実行

```terminal
front $ heroku stack:set container
```

- 確認

```temrinal
front $ heroku stack

# 成功
  cedar-14
* containe
```

- Nuxt.jsをHerokuにpush

```terminal
front $ git push heroku master
```

- アプリを開く

```terminal
front $ heroku open
```



### RailsにAPIドメインをセットする

- Nuxt.jsアプリのボタンを押すとCORSエラーが返ってくる。
- これは、Rails側のcors設定に、本番用のドメインが入っていないために起こるエラー。
- そこでRails環境変数をセットする

- apiディレクトリに移動

```terminal
front $ cd ../api
```

- 環境変数API_DOMAINをセット

```temrinal
api $ heroku config:set API_DOMAIN=<Nuxt.jsアプリのドメイン>

# 例) heroku config:set API_DOMAIN=my-app.herokuapp.com
```

- 確認（最終的なRailsの環境変数）

```terminal
api $ heroku config

API_DOMAIN:               <Nuxt.jsアプリのドメイン>
DATABASE_URL:             <databaseのURL>
RACK_ENV:                 production
RAILS_ENV:                production
RAILS_LOG_TO_STDOUT:      enabled
RAILS_MASTER_KEY:         <マスターキー>
RAILS_SERVE_STATIC_FILES: enabled
```



## 5.完成

これでDcokerを使ったRails6Api +  Nuxt.jsのAPIアプリケーションが完成しました。

お疲れ様でした。

<a href="https://twitter.com/esegrammer" target="_blank" rel="noopener noreferrer">Twitter</a>