CICDパイプラインからローカルで開発中のアプリをherokuにデプロイするの巻（Rails / postgresql / Docker / TravisCI）

前回、ローカル環境で開発中のアプリケーションをローカルのDockerに移行して動かしてみました。
今回は、TravisCIというCICDツールを使ってテストを行い、Herokuにデプロイします。

# 参考
前回はローカルで開発中のアプリをローカルのDocker環境に移行して動かしてみました。
　https://qiita.com/igat/items/9292ccae9f413170edda

# 全体構成

```
   Heroku      ... ④Dockerごとアプリをデプロイして公開する
     ^              ・Container2: Web(Ruby on Rails)
     |              ・HerokuPostgresql
   TravisCI    ... ③対象のアプリケーションがGithubにpushされるとテストを自動実行する。
     ^
     |
   Github      ... ②ローカルPCからDockerを含むアプリケーションのコードがpushされる
     ^
     |
   ローカルPC    ... ①Docker上でアプリケーションが動く
                    ・Container1: DB(postgresql)
                    ・Container2: Web(Ruby on Rails)

```

### Dockerfile作成
Dockerfileを作成します。
ローカルで使うDBをMySQLからコンテナ上のpostgresqlに移行するため、前回作成したものを書き換えています。
　・コメントアウト部分が、MySQL環境でのコードになります。
　・Travisでrubyのバージョンが不整合となったため、バージョンを変更しました。

```ruby:Dockerfile

## For MySQL running on host Environment.

# FROM ruby:2.5.1
# RUN apt-get update
# RUN apt-get install -y mysql-client nodejs vim --no-install-recommends
# RUN rm -rf /var/lib/apt/lists/*
# RUN mkdir /myproject
# WORKDIR /myproject
# ADD Gemfile /myproject/Gemfile
# ADD Gemfile.lock /myproject/Gemfile.lock
# RUN gem install bundler
# ADD . /myproject
# RUN rm /myproject/Gemfile.lock
# RUN bundle install


## For Postgresql running on container environment

FROM ruby:2.5.3
RUN apt-get update && apt-get install -y \
      build-essential \
      libpq-dev \
      nodejs \
      vim \
      postgresql-client \
      yarn
RUN rm -rf /var/lib/apt/lists/*

ENV APP_HOME /myproject
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
COPY Gemfile $APP_HOME
COPY Gemfile.lock $APP_HOME
RUN bundle install
```

### database設定修正

・postgresqlを利用します。
・postgresqlのパスワードは環境変数から取得します。

```ruby:config/database.yml

# MySQL. Versions 5.0 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.7/en/old-client.html
#


##########################################
#  Settings on Postgresql Environment
##########################################
default: &default
  adapter: postgresql
  encoding: unicode
  # host名はpostgresqlコンテナのServicesとして記載した名称を指定
  host: db
  user: postgres
  port: 5432
  # パスワードはコンテナに設定する環境変数を指定する
  password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

##########################################
#  Settings on MySQL Environment
##########################################
# default: &default
#   adapter: mysql2
#   encoding: utf8
#   pool: 5
#   username: root
#   password:
#   socket: /tmp/mysql.sock

development:
  <<: *default
  database: memo-space_v2_development

  ### Settings for host mysql database.
  # host: docker.for.mac.localhost

...

```

### docker-compose.yml

ファイル```docker-compose.yml``` 作成します。

```ruby:docker-compose.yml
version: '3'

# dbコンテナのボリュームに指定した名称を指定する。
volumes:
  db-data:

services:
  web:
    build: .
    ports:
      - '3000:3000'
      # Setting for only Mysql environment on host 
      # - '3306:3306'
    volumes:
      - '.:/myproject'
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=********'
    # コンテナの起動状態を保つための設定
    tty: true
    # コンテナの標準入力をオープンにして、ターミナルからの操作を可能にする。
    stdin_open: true
    #依存するコンテナを指定し、起動時の順序が決まる
    depends_on:
      - db
    #アクセス可能なコンテナを指定する。
    links:
      - db
  db:
    # docker-hubのpostgresのイメージを使う
    image: postgres
    # ホストのボリュームにDBデータを保管することでコンテナが消えてもデータが残るようにする
    volumes:
      - 'db-data:/var/lib/postgresql/data'
```

### コンテナ起動

dockerコンテナをビルドしてバックグラウンドで起動します。

```console:terminal
$ docker-compose up --build -d
```

コンテナのステータスを確認します。

```console:terminal

$ docker-compose ps           
       Name                      Command              State           Ports         
------------------------------------------------------------------------------------
memo-space_v2_db_1    docker-entrypoint.sh postgres   Up      5432/tcp              
memo-space_v2_web_1   irb                             Up      0.0.0.0:3000->3000/tcp
```
dbコンテナ(postgresql)とwebコンテナ(Ruby on Rails)が起動していることがわかります。

### データベース作成

起動しているwebコンテナに入ります。

```console:terminal
$ docker-compose exec web bash
```

```
root@0cd7c2024bc0:/myproject# rails db:create
root@0cd7c2024bc0:/myproject# rails db:migrate
root@0cd7c2024bc0:/myproject#  rails s -b '0.0.0.0'
```


ブラウザでアクセスできることを確認します。

 URL : localhost:3000

Dockerfileで指定したボリュームを確認できます。

```cosole:terminal
$ docker volume inspect memo-space_v2_db-data
[
    {
        "CreatedAt": "2020-07-18T14:24:32Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "memo-space_v2",
            "com.docker.compose.version": "1.25.5",
            "com.docker.compose.volume": "db-data"
        },
        "Mountpoint": "/var/lib/docker/volumes/memo-space_v2_db-data/_data",
        "Name": "memo-space_v2_db-data",
        "Options": null,
        "Scope": "local"
    }
]
```

### githubにpush

``` console:terminal
git add .
git commit -m 'commit'
git push origin master
```

### TravisCIアカウント設定

次のURLでアカウントを作成します。
https://travis-ci.org/

自分のGithubと同期させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564329/caef0faa-b913-0041-fbed-831e95f44d7c.png)

・ページ右上の「アカウントアイコン」をクリックし、「settings」を選択します。
・左ペインの「Sync account」をクリックする。
・自分のGithubのアカウント情報を入力する。

自分のGithubアカウントが同期される、RepositoriesにGithubレポジトリが表示されることを確認します。
・herokuにデプロイするアプリのレポジトリのボタンをクリックする。
・Dashboardボタンをクリックする。
・Active repositoriesに、選択肢たgithubレポジトリが表示されていることを確認する。

### Travisファイル記述

アプリケーションフォルダの直下に、ファイル```.travis.yml```を作成します。
Travisサーバで実行してテストする内容を記述します。

```ruby:.travis.yml
# 権限の設定
sudo: required

service: docker

# インストール前に実行する内容を記述する
before_install:
  # Dockerをビルドして起動
  - docker-compose up --build -d

# 実行したいスクリプトを記述する。
script:
  # Databaseを作成する（rails db:create）
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:create
  # tableを作成する（rails db:migrate）
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate
  # RSpecによるテストを実行する（bundle exec rspec）
  - docker-compose exec --env 'RAILS_ENV=test' web bundle exec rspec
```
※実行時にはコメント部分を削除しないとsyntaxエラーになるかもしれません。

### docker-compose.yml

TravisCI実行用の環境変数を記述します。

```ruby:docker-compose.yml

version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    ports:
      - '3000:3000'
      # Setting for only Mysql environment on host 
      # - '3306:3306'
    volumes:
      - '.:/myproject'
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=postgres'
    tty: true
    stdin_open: true
    depends_on:
      - db
    links:
      - db
  db:
    image: postgres
    # ホストのボリュームにDBデータを保管
    volumes:
      - 'db-data:/var/lib/postgresql/data'
    environment:
      # テスト環境でTravisCIから実行する際POSTGRESQLの認証を通すため
      - 'POSTGRES_HOST_AUTH_METHOD=trust'         # <- 追加
```

### テストを実行

githubにpushします。

``` console:terminal
git add .
git commit -m 'update travis and compose'  
git push origin master
```

ブラウザからTravisCIのWEBページを確認します。

joblog タブに実行内容が出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564329/0d28e69e-4781-e58b-4f11-59e85e381b77.png)

記述したテストが実行された結果が出力されます。

```console:joblog

Worker information
0.14s0.00s0.01s0.00s0.00s
system_info
Build system information
0.01s0.01s0.49s0.19s0.05s0.00s0.04s0.00s0.01s0.01s0.01s0.01s0.01s0.00s0.00s0.02s0.00s0.01s0.25s0.00s0.00s0.00s0.01s0.00s0.09s0.01s0.76s0.00s0.09s6.03s0.00s2.45s0.00s2.18s
docker_mtu
resolvconf
services
3.01s$ sudo systemctl start docker
git.checkout
0.77s$ git clone --depth=50 --branch=master https://github.com/igatai/memo-space_v2.git igatai/memo-space_v2
0.01s
Setting environment variables from repository settings
$ export HEROKU_USERNAME=_
$ export HEROKU_APP_NAME=memo-space-v2
$ export HEROKU_API_KEY=[secure]
rvm
0.61s$ rvm use default
$ export BUNDLE_GEMFILE=$PWD/Gemfile
ruby.versions
$ ruby --version
before_install.1
$ docker-compose up --build -d
before_install.2
0.52s$ docker login -u "$HEROKU_USERNAME" -p "$HEROKU_API_KEY" registry.heroku.com
install.bundler
178.44s$ bundle install --jobs=3 --retry=3 --deployment
2.42s$ docker-compose exec --env 'RAILS_ENV=test' web rails db:create
Created database 'memo-space_v2_test'
The command "docker-compose exec --env 'RAILS_ENV=test' web rails db:create" exited with 0.
2.39s$ docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate
== 20200206094020 DeviseCreateUsers: migrating ================================
-- create_table(:users)

...

== 20200603012332 CreateEvents: migrated (0.0089s) ============================
The command "docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate" exited with 0.
2.41s$ docker-compose exec --env 'RAILS_ENV=test' web bundle exec rspec
User
  #create
    nameがない場合は登録できないこと
    emailがない場合は登録できないこと
    passwordがない場合は登録できないこと
    password_confirmationがない場合は登録できないこと
    passwordとpassword_confirmationが一致しない場合は登録できないこと
    重複したemailが存在する場合は登録できないこと
Finished in 0.13822 seconds (files took 1.54 seconds to load)
6 examples, 0 failures
The command "docker-compose exec --env 'RAILS_ENV=test' web bundle exec rspec" exited with 0.

Done. Your build exited with 0.

```

### heroku設定

herokuアカウントにサインアップし、ログイン状態にします。
https://dashboard.heroku.com/apps

##### アプリケーションを登録
・トップページ右上の「New」ボタンをクリックします。
・プルダウンメニューから「create new app」をクリックします。
・「App name」にユニークなアプリケーション名を入力します。
・「Choose Regionは」デフォルトのまま
・「Create app」をクリックします

##### databaseを設定
・アプリケーション名をクリックします。
・「Resource」タブをクリックします。
・「Add-ons」フォームから postgresを入力し、 「Heroku Postgres」を探してクリックします。
・「plan name」から「Hobby Dev - free」を選択します。
・「provision」をクリックします。

##### 環境変数を設定
・「settings」タブをクリックします。
・「Reveal Config Vars」をクリックします。
・設定済み環境変数を確認します。（key / value）
　DATABASE_URL / postgres://xxxx...
   -> 後ほどアプリケーションのデータベース定義で、production環境部分に上記の環境変数を設定します。
・次の環境変数を設定します。（key / value）
　SECRET_KEY_BASE / xxxxxxxx　（config/master.keyに記述された値）

　※　master.keyがない場合
　　コンソールから以下を実行する。

```console:terminal
EDITOR="vi" rails credentials:edit
```

　エラーが出る場合は、以下を削除し、再度前述のコマンドを実行する。
　　```config/credentials.yml.enc```

deployタブを選択します。
・githubアイコンボタンをクリックし、デプロイ対象アプリケーションのリポジトリを同期させます。（connected状態）
・チェックボックス「wait for CI to pass before deploy」をクリックします。
・ボタン「Enable Automatic Deploy」をクリックします。

### データベース定義のproduction環境設定を修正
 データベースのproduction環境のurlとして、herokuに設定された環境変数```DATABASE_URL```を設定します。

```ruby:config/database.yml
# 以下のコメントアウトを外し、有効化する
  production:
    url: <%= ENV['DATABASE_URL'] %>

# 以下をコメントアウトする
# production:
#   <<: *default
#   database: memo-space_v2_production
#   adapter: postgresql
#   # database: postgresql
#   encording: unicorde
#   pool: 5
#   username: memo-space_v2
#   url: <%= ENV['DATABASE_URL'] %>
#   password: <%= ENV['MEMO-SPACE_DATABASE_PASSWORD'] %>
```



### .travis.ymlに追記

``` .travis.yml``` に本番環境の記述を追加します。

```ruby:.traviis.yml

sudo: required

service: docker

before_install:
  - docker-compose up --build -d
  - docker login -u "$HEROKU_USERNAME" -p "$HEROKU_API_KEY" registry.heroku.com #<-追記

script:
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:create
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate
  - docker-compose exec --env 'RAILS_ENV=test' web bundle exec rspec

#以下追記
# デプロイ時に実行する内容を記述
deploy:
  provider: script
  script:
    # Dockerfile.prodを元に、本番環境用のDockerImageをherokuのDockerレジストリにpushし、ビルドします。
    # -tオプションにより、レジストリ名を「registry.heroku.com/$HEROKU_APP_NAME/web」としてタグづけします。
    docker build -t registry.heroku.com/$HEROKU_APP_NAME/web -f Dockerfile.prod .;
    # pushします
    docker push registry.heroku.com/$HEROKU_APP_NAME/web;
    # runする際に db:migrateします。
    heroku run --app $HEROKU_APP_NAME rails db:migrate;
  # マージされたときに実行するブランチを指定します。
  on:
    branch:
      - master
      - DeployWithTravisCI
```

### TravisCIに環境変数を設定する。

macにheroku cliをインストールします。

```console:terminal
$ brew tap heroku/brew && brew install heroku
```
※https://devecnter.heroku.com/articles/heroku-cli

herokuトークンキーを取得します。

```console:terminal
$ heroku authorizations:create
Creating OAuth Authorization... done
Client:      <none>
ID:          xxxxxxxxxxxxxxxxxx
Description: Long-lived user authorization
Scope:       global
Token:       xxxxxxxxxxxxxxxxxx
Updated at:  Wed Jul 15 2020 12:10:07 GMT+0900 (GMT+09:00) (less than a minute ago)
```

・ブラウザでTravisCIのwebページにアクセスする。
・アプリケーションページから「More options」をクリックする。
・プルダウンメニューから「Settings」をクリックする。
・「Environment Variables」蘭にて以下を追加する。
  ``` HEROKU_USERNAME : _ ```
  ``` HEROKU_APP_NAME : herokuに設定したアプリケーション名 ```
  ``` HEROKU_API_KEY  :  確認したherokuトークンキーを設定します。 ```


### 本番環境用Dockerfileを作成する

```ruby:Dockerfile.prod
FROM ruby:2.5
RUN apt-get update && apt-get install -y \
      build-essential \
      libpq-dev \
      nodejs \
      postgresql-client \
      yarn
ENV APP_HOME /myproject
WORKDIR $APP_HOME
COPY Gemfile Gemfile.lock $APP_HOME
# RUN gem install bundler
RUN bundle install
# カレントディレクトリのファイルを本番環境にコピーする
COPY . .
# railsサーバスタート
CMD ["rails","s"]
```

gitにpushします。

```
$ git add .
$ git commit -m 'add deploy code'
$ git push origin master
```

### TravisCIを確認
TravisCIのwebページ（デプロイするアプリケーションページのcurrentタブのjob log）から以下を確認します。
・テストを実行してパスすること
・デプロイがされていること

※初回実行時にherokuに各種rails環境変数が自動追加されるが、それが反映されずエラーが出る場合があります。
　その場合、２回目にデプロイを実行したときに正常に実行できます。

### デプロイしたアプリの確認
herokuから「open app」をクリックし、ブラウザでアクセスします。