13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CODEBASE okinawaAdvent Calendar 2019

Day 18

SinatraアプリをHerokuへデプロイする手順

Last updated at Posted at 2019-12-31

はじめに

CODE BASE OKINAWAでのプログラミング教室ではSinatraを使ってWebApp開発を行うカリキュラムを採用している。
ただ、それを外部に公開する手順はちょっと複雑であり、教室のカリキュラム内でもあまり触れていない内容なので、今回記事にするか! ・・・となったわけだ。

対象

  • Gitの基礎がわかる人 (init, add, commit, push程度)
  • Sinatraの基礎を理解できている人
  • DBを使ったことがある人
  • (あると望ましい)Herokuを触った事がある。

流れ

今回は 2通りのSinatraアプリ をデプロイする。
なぜ2つなのか? それは・・・
簡単なWebAppを外部公開する手順を通して、Herokuへのデプロイの流れを掴む。

そして次に、DB画像保存場所を外部に用意したパターンのアプリを作成していくなかで、よりHerokuを理解していって欲しいから。

Herokuとは

Heroku createというコマンドを打つと、Herokuサーバーに空間を用意してくれる。
その空間にWebAppのコードをPushすると、勝手にビルドして起動してくれる。
めちゃくちゃ良いサービス。

※詳しくは説明しない。他に分かりやすい記事があるはずだ。

簡単なWebApp

さて1つ目のアプリを軽く作っていこう!

DBや画像保存の機能が付いていないWebAppであれば簡単にデプロイできる。
まずは、Sinatraによって作成したAppをHerokuを使ってどうデプロイするのか知って欲しい。

ざっくり

  1. Gitで管理する (git init)
  2. ローカルで動作する簡単なWebAppを作成
  3. 使うgemはGemfileにまとめる
  4. Procfile作成 (Herokuにデプロイする為に必要な手順)
  5. Heroku create
  6. Herokuへデプロイ (git push)

ではやっていこう!

簡単なWebAppを作る

フォルダを作る!

今回は適当に deploy-sample という名前にした。

gitで管理する

$ git init

さて、今回は沖縄の天気を表示するWebAppを作成してみる

app.rbを作成

require 'sinatra'
require 'sinatra/reloader'
require 'json'
require 'open-uri'

get '/' do
  res = OpenURI.open_uri('http://weather.livedoor.com/forecast/webservice/json/v1?city=471010')
  @res = JSON.parse(res.read)
  erb :index
end

viewsフォルダ作成

app.rbと同じ階層にviewsフォルダを作成する
※Sinatraの基本的な使い方は省略する

views/layout.erbを作成

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Deploy-Sample</title>
</head>
<body>
  <%= yield %>
</body>
</html>

views/index.erbを作成

<div id="res">
  <p>
    <%= @res['location']['prefecture'] %>
    (<%= @res['location']['city'] %>)
  </p>
  <p>
    天気:
    <span id="weather-res">
      <%= @res['description']['text'] %>
    </span>
  </p>
</div>

動作チェック

$ ruby app.rb

localhost:4567
ブラウザ上にて沖縄県那覇市の周辺天気が情報として表示されればOK!

コミットする

$ git add *
$ git commit -m "取り敢えず完成"

これでアプリは完成!とする。

使うgemはGemfileにまとめる

使うgemは、Gemfileへまとめる。
bundleの機能を使う。
※bundle, Gemfileのことは知っている前提で進める。

※本来は、最初からbundleを使いながら開発した方が良いが、一応今回はbundleを使ってないパターンからの想定をしたかったので・・・

$ bundle init
これで、Gemfileが作成される

今回使うgemは、sinatra, sinatra/reloader, open-uri, jsonなので・・・

Gemfileの中身

source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'sinatra'
gem 'json'

group :development do
  gem 'sinatra-contrib'
end

そして

$ bundle install

それに合わせてapp.rbを編集

require "bundler/setup"
Bundler.require
require 'open-uri'

if development?
  require 'sinatra/reloader'
end

get '/' do
  res = OpenURI.open_uri('http://weather.livedoor.com/forecast/webservice/json/v1?city=471010')
  @res = JSON.parse(res.read)
  erb :index
end

再度、ローカル環境で動作するのかチェック
$ ruby app.rb

さっきと同様に動作したら成功

コミットしとく
$ git add *
$ git commit -m "bunldeを使うように変更"

Procfile作成

HerokuがPushされたデータを元にどう起動するのか基本的なルールを書いとく。

app.rbと同じ階層にProcfileという名前のファイルを作成

  web: bundle exec ruby app.rb -p $PORT

Herokuを使っていく

$ heroku create
これで、Herokuサーバーに空間が作られる。

かつ、gitのremoteとして、herokuが登録される。
試しに確認したければ、以下を打つ。(やらなくも良い)
$ git remote -v
リモートリポジトリ情報が表示されるはず。Herokuという文字があればまぁ大丈夫!

よしコミットする
$ git add *
$ git commit -m "Herokuへのデプロイ準備"

Herokuへデプロイ (git push)

Pushしよう!
$ git push heroku master

これでPushされて、
ビルドされて、
アプリが起動してくれるはず!

動作チェック

ちゃんと動作するか確認する
$ heroku open

正しく動作していれば成功!!
おめでとう!

簡単だったでしょ!

ちょっと複雑なWebApp

先の説明で、分かった通り

「Herokuへのpushには
gitを使い、
そして使うgemはGemfileにまとめ、
Procfileを作成するだけ。」

簡単。

ただ、Herokuにはデメリットがある。
それは、画像保存はできるけど、少し時間が経つと削除される。
-> 理由は簡単で、Herokuは定期的にPushされた時の状態に戻す仕組みが動いているから。つまりは、仕様って事だね!

よって、画像の保存は外部のサーバーに対して行った方が良い!
-> 以下で採用するのは、AWSのS3

併せて、DBを使うWebAppであればこれも同じように、外部のDBサーバーへアクセスする仕組みを採用すべし!
-> 以下ではHerokuが提供しているHeroku Postgresを使う!

まとめ

WebAppサーバー(Webサーバー兼)と、DBサーバーはHeroku。
静的ホスティングサービスはAWS。

こんな感じ

2つ目のToDoアプリ

ざっくり

  1. Gitで管理する (git init)
  2. ローカル上で動作するサンプルWebAppを作る (Todoアプリ)
  3. 使うgemはGemfileにまとめる
  4. Procfile作成 (Herokuにデプロイする為に必要な手順)
  5. 画像保存はAWSにしたい。 まずはgemインストール。
  6. ブラウザにて新規のS3バケットを作成
  7. 作成したS3へアクセスできるI AMユーザーを作成
  8. I AMユーザー作成した時に生成したトークンを環境変数として使う
  9. アプリの画像保存の記述をすべてS3への保存処理で置き換える
  10. Heroku create
  11. Heroku PostgreSQLを有効化 (ブラウザ上から行う)
  12. 有効時に入手した接続権限を使って、コード修正
  13. ローカルで動作チェックしつつ・・・
  14. Herokuへデプロイ (git push)

ちょっと複雑なWebApp

簡単なToDoアプリを作っていく。

と言っても覚えるデータは、

  1. タイトル
  2. そのタスクを実行したかどうか
    の2種類。

一応、編集と削除の機能も付ける。

ユーザーのアイコン画像を編集できる機能も付ける。
↑ あんまりToDoアプリには必要ない機能だけど、今回は画像保存の機能を持つWebAppを例として作成したかったので、無理やりつけた。。。

フォルダ作成とgit init

今回はdeploy-sample-2って名前にした。

$ git init

Todoアプリを作成

作る手順を解説するのも面倒なので、
事前に僕がGitHubへあげた・・・

リポジトリ
DLリンク

リポジトリのリンク先からcloneしても良いが、
一応DLリンクを上に記載したのでそれをクリックしてもよい。
cloneより簡単だしね。

※masterブランチがローカルで動作する時点のアプリのコード。この後、デプロイの為にソースをいじる。それはまた別のdeployというブランチで作業している。

必要なSQL文を実行する

まずは、ローカルで動作させてみたい。

今回はPostgreSQLと連携したWebAppを作成した。
ので、PCのPostgreSQLコンソールへアクセスして・・・

以下 1行ずつ を実行

create database heroku_deploy_sample_2;

\c heroku_deploy_sample_2;

create table users(id serial, name varchar, pass varchar, primary key(id));

create table todos(id serial, creater_id int, title varchar, status bool);

Gemfileうんぬん

$ bundle update

動作確認

$ ruby app.rb

localhost:4567
ブラウザにて、ToDoアプリが無事に動作しているなら大丈夫!

Procfile作成

さっきと同じ様に・・・
Procfileを作成し、中身は以下。

  web: bundle exec ruby app.rb -p $PORT

ここまでで、さっきまでのHerokuへのPush準備は整った。
が、このWebAppはDBと画像保存機能があるので、、、

その為に色々と変更するのだ!

画像保存はAWSにしたい。 まずはgemインストール。

Gemfileの gem 'pg の下に追記

gem 'aws-sdk'

$ bundle update

バケット作成

※AWSのアカウント作成は既に終わっている前提。。。
↑ 作っていない人は作って欲しい。この記事ではアカウント作成の話はしない。もっと良いAWSの解説記事が他にあるはずだ・・・

01.png

バケット名はなんでも良いですが、僕は
deploy-sample-2にしました。

02.png

作る時に、
パブリックアクセスをすべてブロック
のチェックは今回外して欲しいです。
※本当は良くないけど、、、まぁ今回はテストアプリなので許して。。。

作成したS3へアクセスできる IAMユーザー を作成

AWS マネジメントコンソールで
IAM
と検索して、ページに移動して・・・

ユーザーを追加して行きましょう!

ユーザー名はなんでも良いけど、、、僕は
deploy-sample-2-s3としました。

プログラムによるアクセスにチェックを入れる。

03.png

で、権限を与える時に、、、グループを作成する必要があるので、、、
今回は、このIAMユーザーにはS3への全アクセスを与える事にします!

グループ名は適当で良いです。
が、S3へのフルアクセス権限は与えてくださいね!

04.png 05.png

あとは、、、特に変更せず、、、そのまま進んで・・・

さて、
ユーザーを追加
が終わったら、

06.png
  • アクセスキー
  • シークレットアクセスキー

が表示される。
これはメモっておく。

※このキーは盗まれない様に配慮してくださいね!

.envファイルうんぬん

さて作成したIAMユーザーのキーを
WebAppで使える様にする

ルートディレクトリ直下に、
.env ファイルを作成

中身

AWS_S3_ACCESS_KEY_ID='XXXXXXXXXXXXXXXXXXXX'
AWS_S3_SECRET_ACCESS_KEY='XXXXXXXXXXXXXXXXXXXX'
AWS_S3_BUCKET = 'XXXXXXXXXXX'

この
XXXXXXXXXXXXXXXXXXXX
の部分は、さっきIAM作成した時に教えられた
文字列に置き換える!

BUCKET名は、さっき作成したS3のバケット名を当てる。

さて、WebAppで、この.envファイルの内容を読み込みたいので・・・

app.rbの

if development?
  require 'sinatra/reloader'
end

の中身に、、

下を追加

  require 'dotenv'
  Dotenv.load ".env"

あと、Gemfileの中身を編集

group :development do
  gem 'sinatra-contrib'
end

の中に

  gem 'dotenv'

を追加

$ bundle update

さて、これで
AWSのS3を利用する準備と権限の読み込みは大丈夫!!

※env、環境変数についての詳しい説明は省略する。

ただ.envの中身は流出してはいけないので・・・
.gitignoreファイルの末尾に、

.gitignore
.env

を追記。

アプリの画像保存の記述をすべてS3への保存処理で置き換える

app.rbを編集する

enable :sessions
の下あたりに追記

# AWS S3 への接続クライアント
def s3
  @s3 ||= Aws::S3::Client.new(
    :region => 'us-east-2',
    :access_key_id => ENV['AWS_S3_ACCESS_KEY_ID'],
    :secret_access_key => ENV['AWS_S3_SECRET_ACCESS_KEY']
  )
end

併せて、、、
今まで画像保存処理の記述である

FileUtils.mv(params[:up_image][:tempfile], "./public/images/user_icon/#{session[:user_id].to_s}.jpg")

をコメントアウトして、以下に置き換える ↓

object_key = "images/user_icon/#{session[:user_id].to_s}.jpg"
# 保存処理
s3.put_object(
  bucket: ENV['AWS_S3_BUCKET'],
  key: object_key,
  body: params[:up_image][:tempfile],
  content_type: "image/jpegput",
  metadata: {}
)
# アクセスを公開に設定する
s3.put_object_acl({
  acl: "public-read",
  bucket: ENV['AWS_S3_BUCKET'],
  key: object_key,
})

これで、画像の保存がAWSのS3にされる。

それに併せて、画像のsrc元も変更する

<img id="preview" src="/images/user_icon/<%= session[:user_id].to_s %>.jpg" onerror="this.src='/images/user_icon/default.png';">

を消して・・・

<img id="preview" src="https://deploy-sample-2.s3.us-east-2.amazonaws.com/images/user_icon/<%= session[:user_id].to_s %>.jpg" onerror="this.src='/images/user_icon/default.png';">

に変更する。

動作チェック

$ ruby app.rb

これで、アイコンの画像保存と表示がうまくいけば安心。

よし!
画像の保存は成功!

DBも外部に!

まず、
$ heroku create
する

これで、Herokuサーバーがpushされる空間を用意してくれる。

で、Pushするまえに、、、
Herokuの Postgres機能を使いたいので、
ブラウザ上から有効化する!!

Herokuのapp詳細画面で、
Configure Add-ons
をクリックして、、、機能を有効化していく!
07.png

Add-ons
の検索で、
Heroku Postgres
と打ち込んで、有効化する。

08.png

有効化したら、
Heroku Postgres
をクリックして詳細画面へ!

settingsの
Heroku Postgres
から、有効化したDBへのアクセス権限を見る事が出来る。

09.png

それを、メモる。

有効時に入手した接続権限を使って、コード修正

.envに追記

.env
DATABASE_URL = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'

このxxxxxxの部分には、
先のブラウザ上で表示された
URIの文字列を貼り付ける。

そして、
app.rbも変更する。

def db
  @db ||= PG.connect(
    dbname: 'heroku_deploy_sample_2'
  )
end

を削除して、、、

以下に置き換え

def db
  uri = URI.parse(ENV['DATABASE_URL'])
  @client ||= PG::connect(
    host: uri.hostname,
    dbname: uri.path[1..-1],
    user: uri.user,
    port: uri.port,
    password: uri.password
  )
end

DBに最低限必要なテーブルを作成する。

$ heroku psql
で有効化したDBへアクセスできる。

ローカルで先程実行した様に、このToDoアプリが最低限動作するため必要なテーブルを作成する。

create table users(id serial, name varchar, pass varchar, primary key(id));

create table todos(id serial, creater_id int, title varchar, status bool);

※ここまでの状態をGitHubのリポジトリ(さっきのリンク)の、deployブランチにcommitしてますので、参考にどうぞ。

動作チェック

$ ruby app.rb
ユーザーやToDoの登録が無事に動作するなら、DBへのアクセスは良好!
やったね!

デプロイ

さて、外部に静的ホスティングサービス・DBを用意した。
そして、それらを使う様にアプリのコードも修正し、、、
動作チェックもしたと。。。

うん。
デプロイしよう!

$ git push heroku master

最後に、ちょっと。。。

で、1つ目のAppと同じ様に、、、

Gemfile.lockの最後の行を編集

Gemfile.lock
BUNDLED WITH
  1.17.2

をして。
さらに・・・

実はローカルでは、
.envファイルに、トークン(アクセス権限)を保存しました。
で、これって流出しては危険だからという理由で、.gitignoreファイルに.envと書いて、gitの管理対象外にしましたよね。

ってことは、Herokuサーバーがソースコードを元に、WebAppサーバーを構築し、起動する際に、この.envの中身が分からないので動作できません!!

つまり、
ローカルでは、.envにトークンを記した様に、
Herokuにも、トークンを設定する必要がある!

やる。

ブラウザで
Appの管理画面へ!

settingsの
Config Vars
に、

今回必要な

  • AWS_S3_ACCESS_KEY_ID
  • AWS_S3_SECRET_ACCESS_KEY
  • AWS_S3_BUCKET

と、その中身の文字列を覚えさせる。

10.png

これで大丈夫なはず。

動作チェック

デプロイしたアプリが正しく動作しているかテストしよう!
$ herou open

ローカル環境下と同じ様に
ちゃんと動いていれば成功!

お疲れ様でした!!

13
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?