11
12

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.

【クレジットカード不要】無料の最強構成でWebアプリを作ってデプロイする【Vercel + Heroku + MongoDB Atlas】

Last updated at Posted at 2021-10-29

2021年10月時点でクレジットカード不要で利用できるサービスの中で最適だと思う組み合わせで、Webアプリ開発の環境構築からデプロイまで行います。

利用するホスティングサービス

Vercel

  • ReactのフレームワークNext.jsで作ったアプリをデプロイできる。
  • 日本のCDNを利用できるのでNetlifyに比べて応答が早いらしい。
  • 無料のHobbyプランは個人利用で非営利目的のみ(広告はOKらしい)。

Heroku

  • 今回はRuby on Railsで作ったアプリをデプロイしてAPIとして利用する。
  • アプリを稼働できるのは1つのアカウントにつき550時間/月。
  • PostgreSQLのアドオンはレコード数10000行の制限がある。

MongoDB Atlas

  • 無料プランでは容量512MBまで利用できる。

開発環境

OS: Windows 10 20H2

WindowsではRuby on Railsの動作が安定しないので、WSL2という機能を使ってWindows上でLinuxを動かして開発します。

開発環境の構築

  1. Visual Studio Codeをインストールする
    image.png
  2. 「Windowsの機能の有効化または無効化」で、「Linux 用Windows サブシステム」と、「仮想マシンプラットフォーム」にチェックを入れる
    image.png
  3. Linux カーネル更新プログラム パッケージをインストールする(ARM64 マシンの場合はARM64 パッケージを選ぶ)(https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-kernel)
  4. PowerShellまたはコマンドプロンプトでコマンドwsl --set-default-version 2を実行し、WSLのバージョンを2にする(事前にBIOSで仮想化の設定を変える必要があるかも)
  5. Microsoft Storeから、Ubuntu 20.04 LTSをインストールする
    image.png
  6. PCを再起動する
  7. Ubuntu 20.04 LTSを起動し、ユーザー名とパスワードを設定する
    image.png
  8. パッケージのリポジトリを日本国内に変更する
    cd /etc/apt
    sudo sed -i.bak -e "s/http:\/\/archive\.ubuntu\.com/http:\/\/jp\.archive\.ubuntu\.com/g" sources.list
  9. インストールされているソフトを更新する
    sudo apt update
    sudo apt upgrade -y
  10. 日本語の言語パックをインストールし、日本語化する
    sudo apt install language-pack-ja
    sudo update-locale LANG=ja_JP.UTF-8
  11. Ubuntu 20.04 LTSを再起動する
  12. マニュアルを日本語化する
    sudo apt install manpages-ja manpages-ja-dev
  13. Rubyに必要なソフトをインストールする
    sudo apt install -y libssl-dev libreadline-dev zlib1g-dev
    sudo apt install -y gcc g++ make
  14. rbenvをダウンロードするcd
    git clone https://github.com/sstephenson/rbenv.git .rbenv
    git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
  15. シェルの設定ファイルを編集し、変更を反映させる
    echo "# For rbenv" >> ~/.bash_profile
    echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
    echo 'eval "$(~/.rbenv/bin/rbenv init -)"' >> ~/.bash_profile
    source ~/.bash_profile
  16. インストールできるRubyのバージョンを確認し、バージョンを指定してインストールする
    rbenv install -l
    rbenv install 3.0.2
    rbenv rehash
    rbenv global 3.0.2
  17. gemをアップデートする。そして、gemでライブラリをインストールしたときにドキュメントを生成しない設定にする
    gem update --system
    touch ~/.gemrc
    echo "install: --no-document" >> ~/.gemrc
    echo "update: --no-document" >> ~/.gemrc
  18. yarnをインストールする
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
    sudo apt update
    sudo apt install -y yarn
  19. npmをインストールする
    sudo apt install -y npm
    sudo npm install n -g
    sudo n stable
    sudo apt purge nodejs npm
    sudo apt autoremove
  20. Railsをインストールする
    gem install rails

Ruby on Railsアプリ制作

今回は本のデータを管理する簡単なアプリを作ります。
作業するディレクトリを作成し、作成したディレクトリに移動します。

mkdir -p ~/projects/book-manager
cd ~/projects/book-manager

Railsプロジェクトを作成し、作成されたディレクトリに移動します。

rails new rails-api --api --skip-bundle --skip-active-record --skip-test --skip-system-test
cd rails-api

WindowsにインストールしたVisual Studio コードを起動します。

image.png

Visual Studio Codeに、以下の拡張機能をインストールします。

  • Japanese Language Pack for Visual Studio Code
  • Remote - WSL

Visual Studio Codeを閉じます。

Ubuntu上で以下のコマンドを入力し、Visual Studio Codeで現在のディレクトリを開きます。

code .

Visual Studio Codeのウィンドウ左下に「WSL: Ubuntu-20.04」と表示されていることを確認します。

image.png

追加で以下の拡張機能をインストールするのがおすすめです。

  • Ruby
  • VSCode Ruby
  • Rails
  • Ruby on Rails
  • endwise
  • Rinbow End
  • Material Icon Theme

Gemfileを開き、gem 'webpacker', '~> 5.x'を追記します。

Ubuntu上で以下のコマンドを実行し、Gemライブラリをインストールします。

bundle install

WebPackerの初期設定をします。

rails webpacker:install

Railsサーバーを起動します。

rails s

ブラウザでhttp://localhost:3000 にアクセスし、Railsのページが表示されることを確認します。

image.png

Railsサーバーを起動しているUbuntuのコンソールとは別に、もう一つUbuntuのコンソールを開いてください。

MongoDBのインストール

UbuntuにMongoDBをインストールします。

wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

サービスとして MongoDB を開始する init スクリプトを追加する

  1. MongoDB 用の init.d スクリプトをダウンロードします。 curl https://raw.githubusercontent.com/mongodb/mongo/master/debian/init.d | sudo tee /etc/init.d/mongodb >/dev/null
  2. スクリプト実行可能ファイルのアクセス許可を割り当てます。 sudo chmod +x /etc/init.d/mongodb
  3. MongoDBのサービスを起動します。 sudo service mongodb start

MongoDBのデータベース作成

MongoDBにログインします。

mongo

開発用とテスト用のデータベースとコレクションをそれぞれ作成します。

use book_manager_development;
db.createCollection('books');
use book_manager_test;
db.createCollection('books');

Ctrl + Cを押してログアウトします。

RailsでMongoDBを使うためのライブラリをインストールします。
Gemfileにmongoidを記述します。

Gemfile
gem 'mongoid', '~> 7.0.5'

Ubuntu上で以下のコマンドを実行し、Gemライブラリをインストールします。

cd ~/projects/book-manager/rails-api
bundle install

mongoidの設定ファイルを作成します。

rails g mongoid:config

mongoidの設定ファイルに接続先のデータベース名などを記述します。
config/mongoid.ymlを以下のように編集します。

mongoid.yml
development:
  clients:
    default:
      database: book_manager_development
      hosts:
        - localhost:27017
      options:
        server_selection_timeout: 1
test:
  clients:
    default:
      database: book_manager_test
      hosts:
        - localhost:27017
      options:
        read:
          mode: :primary
        max_pool_size: 1

Ubuntu上で以下のコマンドを実行し、Bookモデルと関連ファイルを作成します。

rails g scaffold Book title:string price:integer

Next.jsアプリ制作

Ubuntu上で以下のコマンドを実行して、Next.jsプロジェクトを作成します。

cd ~/projects/book-manager
npx create-next-app@latest --ts

プロジェクト名を聞かれるので、nextjsと入力します。

作成されたディレクトリに移動し、現在のディレクトリをVisual Studio Codeで開きます。

cd nextjs
code .

Railsアプリとポートが被らないように、package.jsonの"scripts"の"dev"のコードを修正し、3000以外のポートを指定します。

package.json(一部)
  "scripts": {
    "dev": "next dev -p 3001",

Ubuntu上で以下のコマンドを実行し、Next.jsアプリを起動します。

yarn dev

ブラウザでhttp://localhost:3001 にアクセスし、Next.jsのページが表示されることを確認します。

image.png

Next.jsサーバーを起動しているUbuntuのコンソールとは別に、もう一つUbuntuのコンソールを開くのがおすすめです。

APIの通信に使うライブラリaxiosをインストールします。

cd ~/projects/book-manager/nextjs
npm i axios

nextjsディレクトリに型定義ファイルbook.d.tsを作成します。
_idはMongoDBでデータを作成したときに自動付与されるIDです。

book.d.ts
export type Book = {
    title:string;
    price:number;
    _id:{
        $oid: string;
    };
};

nextjsディレクトリに、API通信の共通処理を記述したhttp-common.tsを作成します。

http-common.ts
import axios from "axios";

export default axios.create({
    baseURL:"http://localhost:3000",
    headers:{
        "Content-type":"application/json",
    },
});

nextjsディレクトリに、API通信処理を記述したBook.service.tsを作成します。
rails-api/app/controllers/books_controller.rbに記述されているメソッドに対応しています。

Book.service.ts
import http from "./http-common"
import { Book } from  "./Book";

class BookService {
    index() {
        return http.get<Book[]>("/books");
    }
    create(book: Book) {
        return http.post("/books", book);
    }
    update(book: Book) {
        return http.put("/books/" + book._id.$oid, book);
    }
    delete(id: string) {
        return http.delete("/books/" + id);
    }
}

export default new BookService();

nextjs/pages/index.tsxを以下のように編集します。

index.tsx
import { useEffect, useState } from 'react';
import type { NextPage } from 'next';
import { Book } from '../Book';
import BookService from '../Book.service';

const Home: NextPage = () => {
  const [books, setBooks] = useState<Book[]>([]);
  const [titleFormValue, setTitleFormValue] = useState("");
  const [priceFormValue, setPriceFormValue] = useState(0);

  useEffect(() => {
    getIndex();
  }, []);

  const getIndex = () => {
    BookService.index().then((res) => {
      if(res.status !== 200) return;
      setBooks(res.data);
    }).catch((reason) => {
      console.error(reason);
    });
  };

  const onCreateButtonClick = () => {
    BookService.create({title:titleFormValue, price:priceFormValue, _id:{$oid:""}}).then((res) => {
      if(res.status !== 201) return;
      setTitleFormValue("");
      setPriceFormValue(0);
      getIndex();
    }).catch((reason) => {
      console.error(reason);
    });
  };

  const onDeleteButtonClick = (id: string) => {
    BookService.delete(id).then((res) => {
      if(res.status !== 204) return;
      getIndex();
    }).catch((reason) => {
      console.error(reason);
    });
  };

  return (
    <>
    <h2>新しい本を登録</h2>
    タイトル
    <input type="text" value={titleFormValue} onChange={(e) => {setTitleFormValue(e.target.value)}} />
    価格
    <input type="number" value={priceFormValue} onChange={(e) => {setPriceFormValue(e.target.valueAsNumber)}}/>
    <button onClick={onCreateButtonClick}>登録</button>
    <h2>本リスト</h2>
    <table>
      <thead>
        <tr>
          <td>ID</td>
          <td>タイトル</td>
          <td>価格</td>
          <td>操作</td>
        </tr>
      </thead>
      <tbody>
        {books.map((book) => {
          return(
            <tr key={book._id.$oid}>
              <td>{book._id.$oid}</td>
              <td>{book.title}</td>
              <td>{book.price}</td>
              <td><button onClick={() => {onDeleteButtonClick(book._id.$oid)}}>削除</button></td>
            </tr>
          );
        })}
      </tbody>
    </table>
    </>
  );
};

export default Home;

CORSの設定

rails-api/Gemfileのgem 'rack-cors'のコメントアウトを外します。

bundle installします。

rails-api/config/initializers/cors.rbを以下のように編集します。

cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001'

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

Railsサーバーを再起動します。

ブラウザでhttp://localhost:3001 にアクセスし、本の登録と一覧表示、削除ができることを確認します。

image.png

デプロイ前のコード修正

nextjs/http-commons.tsを以下のように編集します。
本番環境の時はHerokuにデプロイしたRailsアプリのURLにリクエストを送るようにします。

http-common.ts
import axios from "axios";

export default axios.create({
    baseURL:
    process.env.NODE_ENV === "production"
    ? process.env.NEXT_PUBLIC_RAILS_API_URL
    : "http://localhost:3000",
    headers: {
        "Content-type":"application/json",
    },
});

rails-api/config/mongoid.ymlに以下のコードを追記します。
本番環境でのMongoDB Atlasの接続用の文字列を指定します。

mongoid.yml(末尾)
production:
  clients:
    default:
      uri: <%= ENV['MONGODB_URL'] %>
      options:
        server_selection_timeout: 5

rails-api/config/initializers/cors.rbを以下のように編集します。
本番環境で、VercelにデプロイしたNext.jsアプリからのリクエストを許可するようにします。

cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001', ENV['FRONTEND_URL'] || ''

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

デプロイ

MongoDB Atlas

MongoDB Atlasのアカウントを作成します。

ログイン後、Atlasでデータベースを作成します。
「Build a Database」をクリックします。

image.png

無料プランであるSharedを選択します。

image.png

プロバイダとリージョンを選びます。
リージョンはTokyoにした方が良いと思ったのですが、Herokuの無料プランのリージョンはUnited StatesかEuropeしか選べないので、それに合わせてus-east-1リージョンを選んだ方が良い気がします。

image.png

作成して数分経つと利用できるようになります。

image.png

「Connect」をクリックし、接続できるIPアドレスとユーザー名、パスワードを設定します。

image.png

Choose a connection methodでは「Connect with the MongoDB Shell」を選びます。
「Select your mongo shell version」では4.4(できればUbuntuにインストールしたMongoDBのバージョン)を選びます。

image.png

表示された文字列をUbuntu上で実行します。

mongo "mongodb+srv://cluster0.uhkba.mongodb.net/myFirstDatabase" --username username

ログインできたら、データベースとコレクションを作成します。

use book_manager_db;
db.createCollection('books');

もう一度MongoDB Atlasのページ上で「Connect」をクリックし、Choose a connection methodでは「Connect your application」を選びます。
DRIVERに「Ruby」、VERSIONに「2.5 or later」を選ぶと表示される接続用の文字列をメモしておきます。

image.png

Herokuへのデプロイ

Herokuのアカウントを作成します。

ログインが完了したら、「Create new app」をクリックします。

image.png

アプリ名を入力して「Create app」をクリックします。

image.png

アプリが作成できたら、Ubuntu上で以下のコマンドを実行し、Heroku CLIをインストールします。

curl https://cli-assets.heroku.com/install-ubuntu.sh | sh

gitのユーザー名とメールアドレスを設定します。

git config --global user.name "設定したい名前"
git config --global user.email "設定したいメールアドレス"

RailsプロジェクトでGitのコミットをします。

cd ~/projects/book-manager/rails-api
git add .
git commit -m "Initial commit"

Herokuにログインし、プッシュします。

heroku login
heroku git:remote -a アプリ名
git push heroku master

Vercelへのデプロイ

Vercelのアカウントを作成します。

Ubuntu上で以下のコマンドを実行して、Vercel CLIをインストールし、デプロイします。

cd ~/projects/book-manager/nextjs
sudo npm i -g vercel
vercel

環境変数の設定

Vercelでの環境変数の設定

Vercelのダッシュボードを開き、先ほどデプロイしたプロジェクトを選びます。

Settings > Environment Variablesを開きます。

image.png

以下の環境変数を設定します。

環境変数名(キー) 説明
NEXT_PUBLIC_RAILS_API_URL HerokuにデプロイしたRailsアプリのURL

環境変数を反映させるために、再度デプロイを行ってください。

Herokuでの環境変数の設定

Herokuのダッシュボードでプロジェクトを選び、settings > Config Varsより以下の環境変数を設定します。

環境変数名(キー) 説明
MONGODB_URL MongoDB Atlasの接続用の文字列
FRONTEND_URL Vercelでデプロイしたアプリのドメイン
接続用の文字列の例
mongodb+srv://ユーザー名:パスワード@cluster0.uhkba.mongodb.net/book_manager_db?retryWrites=true&w=majority

環境変数を反映させるため、「Restart all dynos」を選んでアプリを再起動します。

image.png

これでデプロイが完了しました!
Vercelにデプロイしたアプリが正常に動作しているか確認しましょう。

Herokuで稼働しているアプリをスリープさせないようにする方法

Herokuの無料プランでは、アプリに30分以上アクセスがないとスリープしてしまい、次にアクセスされてからアプリが起動するので応答が遅くなってしまいます。
対策として、cron-job.orgなどのサービスを利用して、定期的にアプリにアクセスさせる方法があります。
ただし、クレジットカード認証をしていない場合、月に550時間しか稼働させられません(認証をすると月1000時間)。深夜~早朝などの利用者が少ない時間帯は自動アクセスしないようにして、稼働時間を温存すると良いでしょう。

参考記事

今回作成したアプリのソースコード

GitLabに上げています。

Ruby on Railsアプリ

Next.jsアプリ

11
12
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
11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?