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を動かして開発します。
開発環境の構築
-
Visual Studio Codeをインストールする
- 「Windowsの機能の有効化または無効化」で、「Linux 用Windows サブシステム」と、「仮想マシンプラットフォーム」にチェックを入れる
- Linux カーネル更新プログラム パッケージをインストールする(ARM64 マシンの場合はARM64 パッケージを選ぶ)(https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-kernel)
- PowerShellまたはコマンドプロンプトでコマンド
wsl --set-default-version 2
を実行し、WSLのバージョンを2にする(事前にBIOSで仮想化の設定を変える必要があるかも) - Microsoft Storeから、Ubuntu 20.04 LTSをインストールする
- PCを再起動する
- Ubuntu 20.04 LTSを起動し、ユーザー名とパスワードを設定する
- パッケージのリポジトリを日本国内に変更する
cd /etc/apt
sudo sed -i.bak -e "s/http:\/\/archive\.ubuntu\.com/http:\/\/jp\.archive\.ubuntu\.com/g" sources.list
- インストールされているソフトを更新する
sudo apt update
sudo apt upgrade -y
- 日本語の言語パックをインストールし、日本語化する
sudo apt install language-pack-ja
sudo update-locale LANG=ja_JP.UTF-8
- Ubuntu 20.04 LTSを再起動する
- マニュアルを日本語化する
sudo apt install manpages-ja manpages-ja-dev
- Rubyに必要なソフトをインストールする
sudo apt install -y libssl-dev libreadline-dev zlib1g-dev
sudo apt install -y gcc g++ make
- rbenvをダウンロードする
cd
git clone https://github.com/sstephenson/rbenv.git .rbenv
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
- シェルの設定ファイルを編集し、変更を反映させる
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
- インストールできるRubyのバージョンを確認し、バージョンを指定してインストールする
rbenv install -l
rbenv install 3.0.2
rbenv rehash
rbenv global 3.0.2
- gemをアップデートする。そして、gemでライブラリをインストールしたときにドキュメントを生成しない設定にする
gem update --system
touch ~/.gemrc
echo "install: --no-document" >> ~/.gemrc
echo "update: --no-document" >> ~/.gemrc
- 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
- npmをインストールする
sudo apt install -y npm
sudo npm install n -g
sudo n stable
sudo apt purge nodejs npm
sudo apt autoremove
- 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 コードを起動します。
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」と表示されていることを確認します。
追加で以下の拡張機能をインストールするのがおすすめです。
- 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のページが表示されることを確認します。
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 スクリプトを追加する
- MongoDB 用の init.d スクリプトをダウンロードします。
curl https://raw.githubusercontent.com/mongodb/mongo/master/debian/init.d | sudo tee /etc/init.d/mongodb >/dev/null
- スクリプト実行可能ファイルのアクセス許可を割り当てます。
sudo chmod +x /etc/init.d/mongodb
- 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を記述します。
gem 'mongoid', '~> 7.0.5'
Ubuntu上で以下のコマンドを実行し、Gemライブラリをインストールします。
cd ~/projects/book-manager/rails-api
bundle install
mongoidの設定ファイルを作成します。
rails g mongoid:config
mongoidの設定ファイルに接続先のデータベース名などを記述します。
config/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以外のポートを指定します。
"scripts": {
"dev": "next dev -p 3001",
Ubuntu上で以下のコマンドを実行し、Next.jsアプリを起動します。
yarn dev
ブラウザでhttp://localhost:3001 にアクセスし、Next.jsのページが表示されることを確認します。
Next.jsサーバーを起動しているUbuntuのコンソールとは別に、もう一つUbuntuのコンソールを開くのがおすすめです。
APIの通信に使うライブラリaxiosをインストールします。
cd ~/projects/book-manager/nextjs
npm i axios
nextjsディレクトリに型定義ファイルbook.d.tsを作成します。
_idはMongoDBでデータを作成したときに自動付与されるIDです。
export type Book = {
title:string;
price:number;
_id:{
$oid: string;
};
};
nextjsディレクトリに、API通信の共通処理を記述した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に記述されているメソッドに対応しています。
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を以下のように編集します。
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を以下のように編集します。
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 にアクセスし、本の登録と一覧表示、削除ができることを確認します。
デプロイ前のコード修正
nextjs/http-commons.tsを以下のように編集します。
本番環境の時はHerokuにデプロイしたRailsアプリのURLにリクエストを送るようにします。
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の接続用の文字列を指定します。
production:
clients:
default:
uri: <%= ENV['MONGODB_URL'] %>
options:
server_selection_timeout: 5
rails-api/config/initializers/cors.rbを以下のように編集します。
本番環境で、VercelにデプロイしたNext.jsアプリからのリクエストを許可するようにします。
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」をクリックします。
無料プランであるSharedを選択します。
プロバイダとリージョンを選びます。
リージョンはTokyoにした方が良いと思ったのですが、Herokuの無料プランのリージョンはUnited StatesかEuropeしか選べないので、それに合わせてus-east-1リージョンを選んだ方が良い気がします。
作成して数分経つと利用できるようになります。
「Connect」をクリックし、接続できるIPアドレスとユーザー名、パスワードを設定します。
Choose a connection methodでは「Connect with the MongoDB Shell」を選びます。
「Select your mongo shell version」では4.4(できればUbuntuにインストールしたMongoDBのバージョン)を選びます。
表示された文字列を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」を選ぶと表示される接続用の文字列をメモしておきます。
Herokuへのデプロイ
Herokuのアカウントを作成します。
ログインが完了したら、「Create new app」をクリックします。
アプリ名を入力して「Create app」をクリックします。
アプリが作成できたら、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を開きます。
以下の環境変数を設定します。
環境変数名(キー) | 説明 |
---|---|
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」を選んでアプリを再起動します。
これでデプロイが完了しました!
Vercelにデプロイしたアプリが正常に動作しているか確認しましょう。
Herokuで稼働しているアプリをスリープさせないようにする方法
Herokuの無料プランでは、アプリに30分以上アクセスがないとスリープしてしまい、次にアクセスされてからアプリが起動するので応答が遅くなってしまいます。
対策として、cron-job.orgなどのサービスを利用して、定期的にアプリにアクセスさせる方法があります。
ただし、クレジットカード認証をしていない場合、月に550時間しか稼働させられません(認証をすると月1000時間)。深夜~早朝などの利用者が少ない時間帯は自動アクセスしないようにして、稼働時間を温存すると良いでしょう。
参考記事
今回作成したアプリのソースコード
GitLabに上げています。
Ruby on Railsアプリ
Next.jsアプリ