BNBのプログラミング勉強記録

ガチのプログラミング初心者が駆け上がっていくブログ

ECSで作ったサイトが超重かった原因はLB設定ミスでした

何があった

https://tagfoto.link/
というプライベートな画像アップローダーをRailsで構築してFargateでECSにデプロイしたんですよ。そしたら超重いんですよ。原因を探りました。

結論

ロードバランサーの設定ミスでした。あ、タイトルにも書いてるのでここに書く意味もあまりなかった。

もう少し詳細に書くと、VPCの2つのサブネットの内1つがパブリックではなくプライベートでした。これをパブリックのものに変更し、改善されました。マジで原因の特定に時間掛かった。誰にも聞けないし、Googleだと関係ない記事ばかり引っかかるしで苦労しました。AWSのログの見方を、まずは理解する必要があると思いました。(ただAWSのログに触れてる記事って例外なく糞ムズいんですよね。そしてAWSって公式のドキュメント自体が超ムズいし。萎える・・。が、後々絶対役に立つはず)

公開までの流れ

色々な記事を参考に、下記を実施しました。やった通りの順番にしてますので、「本来なら先にこっちをやるべき」というのもありますがそこはご愛嬌ということで。

  1. 作ったRailsアプリをコンテナ化
  2. VPC作成
  3. RDS作成
  4. AWS CLI使えるようにする
  5. ECRにプッシュ
  6. FargateでECSにデプロイ
  7. Route53でドメイン取得、レコード設定
  8. 証明書発行
  9. ターゲットグループ作成
  10. ロードバランサー作成
  11. ECSのタスク定義更新(ロードバランサー設定)
  12. もっかいECSデプロイ

ここまでやって、ようやく「ローカル環境にてDockerで動かしていたWebサービスを、自分の好きなドメインでみんなに使ってもらえる」状態になります。で、実際そうなったんですよ。「あ、動く動く!」って。

ポートフォリオサイトにした

で、これも現在進行系(なおかつこのブログのURLも先方に伝えてる)なんですがね、Web開発系の会社に応募しまして。そして応募書類に、上記サイトをポートフォリオとして記載している訳です。

立ち上がりが遅いぞ

応募書類をメールで送ってしばらくして、ふともう一度サイトにアクセスしたんです。「万が一止まってたらスキル疑われるよな〜」って。そしたら、読み込まないんです。いや、正確に言うと「アクセスして1分くらい、全くなんの反応もなく、その後何事もなかったかのように動き出す、その後しばらくはサクサク動く、でも時間を置くと(30分くらい?)また同じように、アクセスしたらしばらく止まる」という症状なんです。

応募書類は、金曜に送っています。土日に直すしかないですよね。動きの早い会社なら、もうサイト見てますよね。「あれ?遅え!」ってなりますよね。俺、終わった・・。ってなった訳です。この「遅いのに気づいた」のがこの金曜の夜(2023年8月18日 金曜日 23時)の話。変な汗が止まりません

改善に向けて何をやったか

ここが、俺の要改善ポイントだと思うんですが、ログにそこまであたってないんですよ。結果論(いや、一般論か?)ですが、AWSの色んなログっぽいものを見まくったらもう少し早めに直ってたと思います。やったことと結果は下記です。

  1. ECSの正常性のグラフ → 異常なし
  2. RDSのグラフ → 異常なし
  3. Googleで「ECS Fargate アクセス 遅い」で検索 → デプロイの遅さに言及する記事が目立つものの、ECSタスク定義のCPU、メモリのアップによる速度改善について触れているものもあった → やってみたが効果なし
  4. ECSのパブリックIPでアクセス → 遅くない → ということはRoute53のレコード設定か証明書発行あたりか? → 再度、参考にした記事を確認 → その通りできている
  5. ChatGPTに聞いてみる → あまり参考にならず(最下段の参考1参照)
  6. ロードバランサーか?とふと思いつき「aws load balancer 遅い」で調べる → それっぽい記事発見 → VPCのサブネットとルートテーブルの紐づけミスだったので俺とは違うな → 診断サイトの存在を知る
  7. 診断サイトやる → 「77秒(おい!)です。99%はSSHが原因です。」とのこと
  8. またもChatGPT → 回答の2つ目に、ロードバランサーの設定ミスと書いている(最下段の参考2参照)
  9. ECSのコンソールにて、作成したLBの「サブネットの編集」を見る → なんか「プライベートだけどいいの?」的な警告文出てる → パブリックのサブネットに変更 → 改善

今後

VPCロードバランサーあたり知識がなさすぎるので勉強する。あと、何よりログの読み方!

参考1(ChatGPTとのやり取り)

質問

Rails(DBはRDS)で作ったサービスをfargateでECSに置いているが、URLでアクセスすると、とにかく遅い。ただ、正常に表示された後はサクサクどのページに行っても動く。しかし、しばらくアクセスしないと、再度アクセスしたときに同じように遅い。ちなみにデプロイにかかる時間は標準。下記アドバイスください

1 ECS,RDS等の何のグラフを見れば遅い原因が分かるか。
2 遅い原因は何が考えられるか。

回答

遅いパフォーマンスの問題に対処するためのアドバイスを以下に示します。

1.ECS、RDSのパフォーマンスの監視:
ECSとRDSのコンソールで、以下のグラフやメトリクスをチェックしてみてください。

  • CPU 使用率: サービスやデータベースのCPU使用率が高い場合、パフォーマンスに影響を及ぼす可能性があります。
  • メモリ使用率: メモリ使用率が高い場合、システムのスワップや遅延が発生する可能性があります。
  • データベースのクエリパフォーマンス: RDSのクエリログやパフォーマンスメトリクスを確認して、クエリの遅延がないか確認します。

2.遅い原因の考えられる要因:

  • コールドスタートの影響: Fargateはコンテナを必要に応じて起動するため、初回アクセス時にコールドスタートの遅延が発生する可能性があります。
  • リソース制約: ECSコンテナやRDSが十分なリソースを確保しているか確認してください。不十分なリソースはパフォーマンスの低下を引き起こす可能性があります。
  • データベースクエリの最適化: RDSのクエリが効率的でない場合、データベースの負荷が増加し、遅延の原因となる可能性があります。
  • キャッシュの活用: データベースやアプリケーションの結果をキャッシュして、頻繁なアクセスの高速化を図ることができます。

上記のステップに従って、問題の特定と解決策の導入を検討してみてください。状況によっては、ボトルネックやルート原因を特定するのに時間がかかるかもしれませんが、障害排除のプロセスを進めることでパフォーマンスを向上させることができるでしょう。


参考2(ChatGPTとのやり取り)

質問

ECSのサイトの遅い原因を、解析サイトで調べると「Webサーバーの接続受付(SSL)」が99%の原因だった。時間は77秒。対処法を教えてほしい。。ちなみにECS,fargate,route53,ロードバランサーを使っている。

回答

Webサーバーの接続受付(SSL)が遅いという問題に対処するためのいくつかの対策を以下に示します。

  1. 使用するSSL/TLS証明書の確認: SSL/TLS証明書の選択や設定に問題がないか確認してください。証明書の更新が必要な場合は、最新の証明書を使用してください。
  2. ロードバランサーの設定を最適化: 使用しているロードバランサーの設定を確認し、適切に設定されていることを確認してください。必要に応じてロードバランサータイムアウト値やヘルスチェックの設定を調整してみてください。
  3. ネットワーク帯域幅の拡張: ECSタスクやFargateコンテナのネットワーク帯域幅が不足している場合、サーバーの応答が遅くなることがあります。必要に応じて帯域幅を増やしてみてください。
  4. アプリケーションの最適化: Webサーバーのコードや設定が最適化されているか確認してください。余分な処理や遅延の原因となる要因を特定し、改善を行ってください。
  5. CDNの使用: Amazon CloudFrontなどのCDN(コンテンツデリバリーネットワーク)を導入することで、コンテンツの配信を高速化し、サーバーへの負荷を軽減できる場合があります。
  6. コンテナのスケーリング: サーバーへの接続が遅い場合、タスク数を増やすことでトラフィックの処理を分散することができます。Fargateのスケーリング設定を調整して、トラフィックの増加に対応できるようにしてみてください。
  7. ボトルネックの特定: ロードバランサーやコンテナ、インフラストラクチャの各部分でボトルネックが発生していないか詳細に調査し、特定してください。

このような対策を検討し、問題の根本原因を特定してから対処を行うことで、Webサーバーの接続受付の遅延を軽減することができるかと思います。

Railsルーティングのresources分解

全部のっけ

resources :posts

get '/posts', to: 'posts#index'
get '/posts/new', to: 'posts#new', as: 'new_post'
post '/posts', to: 'posts#create'
get '/posts/:id', to: 'posts#show', as: 'post'
get '/posts/:id/edit', to: 'posts#edit', as: 'edit_post'
patch '/posts/:id', to: 'posts#update'
delete '/posts/:id', to: 'posts#destroy'

絞る

resources :posts, only: [:index, :show]

もしくは

resources :posts, only: %i[ index show ]

get '/posts', to: 'posts#index'
get '/posts/:id', to: 'posts#show', as: 'post'

除く

resources :posts, except: [:destroy, :show, :edit]

もしくは

resources :posts, except: %i[ destroy show edit ]

get '/posts', to: 'posts#index'
get '/posts/new', to: 'posts#new', as: 'new_post'
post '/posts', to: 'posts#create'
patch '/posts/:id', to: 'posts#update'

JavaScript俺用逆引きリファレンス

基本

再代入可能

let

再代入不可

const
リファレンス本にも「基本はconstを使い、再代入が必要な場合のみletにすると分かりやすくなる」的な事が書かれていた。

if

if(foo){
    bar
}else{
    baz
};

変数展開

`${foo}`

文字列

両端の空白を削除

foo.trim()

文字列が最初に現れる位置を返す

indexOf(foo)
0始まりで返す

文字列が最初に現れる位置を返す(正規表現

foo.search(正規表現)
同上

含まれてるかを真偽値で返す

foo.includes(bar)

切り出し

foo.slice(bar)

置換

foo.replace('foo1','bar1')

置換(全部)

foo.replace(/bar/g,'baz')

文字列がパターンにマッチするか

/正規表現/.test(foo)
真偽値で返す



配列

配列化

foo.split('区切る文字')
''にしたら1文字ずつ区切る

複数キーのある配列

const foo=[
    {bar:1,baz:'a'},
    {bar:2,baz:'b'}
];

配列の各要素を書き出す

上記の配列を元に。

foo.forEach( (data)=>{
    console.log(data.bar);
    console.log(data.baz);
}

配列の各要素を書き出す その2

for(const data of foo){
    console.log(data.bar);
    console.log(data.baz);
}

イベント

クリックしたら関数呼び出し

const foo=document.querySelector('.foo');

foo.addEventListener('click',bar)

function bar(){
    baz
};

クリックしたら関数呼び出し(アロー関数)

もちろん、アロー部分をfunction()と書いてもOK

const foo=document.querySelector('.foo');

foo.addEventListener('click',()=>{
    bar
});

要素を全て取得し、各要素をクリックしたときの処理

document.querySelectorAll('.foo').forEach( (foo)=>{
    foo.addEventListener('click',()=>{
        bar
    });
});

スワイプ関係まとめて(touchstart,touchmove,touchend)

これが最低限の左右スワイプです。今作った(2023/08/21)。Googleで上位に出てくるやつ、変な挙動が多い。ほんとに使ってるのかと問い詰めたい。

const imageTouch=document.querySelector('.image_touch');
let startX = 0;
let endX = 0;

imageTouch.addEventListener('touchstart', (e) =>  {
  startX = e.touches[0].pageX;
});

imageTouch.addEventListener('touchmove', (e) =>  {
  endX = e.changedTouches[0].pageX;
});

imageTouch.addEventListener('touchend', (e) =>  {
  if(endX == 0){
    console.log('何も起こらない1');
  }else if(startX - endX <= -30){
    console.log('左へ');
  }else if(startX - endX >= 30){
    console.log('右へ');
  }else{
    console.log('何も起こらない2');
  };

  startX = 0;
  endX = 0; 

});

リンク、URL

普通のリンク

// onclick="foo()"
function foo() {
  window.location.href = https://www.example.com
}

新規タブで開く

// onclick="foo()"
function foo() {
  window.open(https://www.example.com/)
}

DOMの読み取り・書き換え

【超重要】Railsで自作JavaScriptを読み込む、動かす

以下Rails7.0.5での導入です。

1.app/javascriptにfoo.js(使いたいjsファイル)を置く
2.config/importmap.rbに「pin "foo"」を追加
3.application.html.erbのヘッダの一番下に以下を書く。Railsがデフォルトで読み込むjsの順番があるので、一番下に書く。そうしないと動かない。注意。

<%= yield(:head) %>

4.自作JavaScriptを使うerbに以下を書く(これによりこのページだけヘッダに2つの要素が挿入される)。特に、reloadのやつは、turboがonになってる時(デフォルトでonなんですけどね)に書かないといけないっぽい。

<% content_for :head do %>
  <meta name="turbo-visit-control" content="reload">
  <%= javascript_import_module_tag "foo" %>
<% end %>

5.ちなみに自作JavaScriptでforEachする時はこれの中に書かないと動かない(検証した)。マジで、罠が多すぎるって!

window.onload=function(){
    bar
};

DOMにアクセスできるタイミングで処理を実行

リファレンス本に書いてて、便利そう!ってメモしていた。forEachが動かないのでこれを入れてみたが動かず。上記のonloadでようやく動いた。

window.addEventLintener('DOMContentLoaded',()=>{
    foo
});

IDを読み取る

const foo=document.getElementById('bar');

読み取ったIDのvalue

上の続き。
foo.value

特定のタグを読み取る(1つ)

const foo=document.querySelector('img');

特定のクラスを読み取る(1つ)

const foo=document.querySelector('.foo');

特定のクラスを読み取る(全て)

const foo=document.querySelectorAll('.foo');

HTML解析後にJavaScript実行

<script src="foo.js" defer></script>

クラスを付けたり外したり(toggle)

foo.classList.toggle('bar');
ちなみに足す引くはadd,remove

要素を作成

この場合、bodyの末尾に追加。

const foo=document.createElement('div');
foo.innerHTML='bar';
document.body.appendChild(foo);

ノード内を引っ張る(テキストのみ。タグは含まない。)

foo.textContent

ノード内を引っ張る(タグも含める)

foo.innerHTML

要素を引っ張る

foo.getAttribute('href')

要素を書き換え

foo.setAttribute('src','bar.png')

特定のクラスを持っていたら

if( foo.classList.contains('bar') == true ){
  baz; 
};

次の要素を引っ張る

これすごいよ。imgタグの下にinputタグ置くとするやん?img視点で見ると、お隣さんを引っ張っこれるので、例えば「クリックした画像にだけ、タグを付ける」みたいな事が可能になる。これを駆使したやつをテストページに置いてみたのでご参考に。思った通りに動作して、脳汁出まくった。(苦労した分余計に。)
https://blue-night-blue.github.io/js_playground/

foo.nextElementSibling

2023/08/28追記
あまりいい方法ではなかったので、結局下記のやり方に落ち着きました。念のため。
https://github.com/blue-night-blue/tagfoto/blob/main/app/javascript/tagToMultipleImage.js

あるクラスの任意の要素を読み取る

document.querySelector(".foo [bar='buz']");

あるクラスの任意のタグを読み取る

document.querySelector(".foo bar");

俺用目次

言語・フレームワーク Rails(Progate)Rails(それ以降)Rails(ルーティング)JavaScriptLaravel
外部サービス S3heroku
基礎 コマンドラインGitDocker
GithubQiita質問履歴はてなのプロフィール記事一覧
その他 tagfoto(制作したWebサービス)色々実験ページ

S3での画像投稿機能(導入・圧縮・exif削除・サムネイル)

主旨

私的メモです。これらをまとめた記事がないので自分用に作りました。なのでかなり省略して書いています。

  1. S3を入れる
  2. 画像を圧縮しexif削除してアップする
  3. サムネイル自動で作る

(いずれも、herokuにデプロイする前提)

前提

  • scaffoldで作ってるとします。
  • git pushやherokuデプロイの記載は省略します。(もしこの記事が参考になる人がいたとしたら、どこでやるべきか分かってるはず。俺がそうなので。)

その1:active storageとs3の導入

導入すると、自分の作ったRailsのサイトに画像をアップロードできます。感動します。そしてherokuでデプロイする度に画像が消えません。(Fileでやると消えるのよ)。下記が手順です。なお、s3に登録する部分は俺の書いた別記事を見てください
https://blue-night-blue.hatenablog.com/entry/2023/06/14/195424

  1. ターミナル:rails active_storage:install
  2. ターミナル:rails db:migrate
  3. モデルrb:has_one_attached :image
  4. コントローラー:permitに:imageを足す
  5. ビュー:フォームに「form.file_field :image,direct_upload: true」
  6. ビュー:図1
  7. storage.yml:コメントアウト解除して自分のS3の情報を入力
  8. ターミナル:EDITOR=vim rails credentials:edit
  9. s3:キー情報をコピー
  10. ターミナル上のvimコメントアウト解除、前項を貼付け、保存
  11. production.rb:config.active_storage.service = :amazon
  12. development.rb:同上
  13. ターミナル:heroku config:set RAILS_MASTER_KEY=`cat config/master.key`

図1

画像をクリックするとオリジナルのデカい画像へ飛ぶ仕様にしていますので、お好みでlink_toの部分は消してください。ちなみにリンク先をS3じゃなくて自分の所のURLにするにはAWSのcloud frontっていうのを使えばできるっぽい。(cloud frontって「URLを変える」というよりは、「先読みでサイトを軽くする」用のサービスのようですが)

<% if post.image.attached? %>
      <%= link_to post.image do %>
        <%= image_tag post.image %>
      <% end %>
 <% end %>

その2:画像を圧縮かつexif削除してS3にアップロード

これは、「最大幅・高さを指定」「exif削除」という、俺が思う「Webサービスに画像アップした際に、サービス側で最低限やってほしい処理」を入れている。だってさあ、顔写真アップして、鼻毛見えたら嫌じゃん。剃るのもめんどいし。俺はPHILIPSの鼻毛マシーンを使っている。

  1. ターミナル:(初回だけ)brew install vips
  2. ターミナル:(初回だけ)brew install imagemagick
  3. ジェム:image_processingのコメントアウト解除
  4. ターミナル:bundle install
  5. コントローラー:図2を、コントローラーの下の方にコピペ
  6. コントローラー:post_paramsをimage_resize(post_params)に修正

図2

MiniMagickで出来ることに関しては、下記を参照されたし。
https://github.com/janko/image_processing/blob/master/doc/minimagick.md

    def image_resize(params)
      if params[:image]
        params[:image].tempfile = ImageProcessing::MiniMagick
          .source(params[:image].tempfile)
          .resize_to_limit(1024, 768)
          .strip
          .call
      end
      params
    end 

その3:サムネイル自動作成

1つのページの中に画像が大量にある場合、サムネイルが必要と思います。以下をやると、なんと自動でサムネイルを成形してくれます。すごいね。

  1. モデルrb:上記で書いたやつを、図3のように書き換え
  2. ビュー:図4のように書くと、サムネイルが作れるぞ
  3. ファイル作成:アプリのフォルダ直下にAptfileというカラのファイルを作る(ジェムファイルみたいなやつです)
  4. Aptfile:図5をコピペ
  5. ターミナル:heroku buildpacks:add --index 1 https://github.com/heroku/heroku-buildpack-apt
  6. ターミナル:heroku buildpacks:add --index 2 https://github.com/brandoncc/heroku-buildpack-vips

図3

数字をいじるとサムネ画像の大きさも変わるぞ

has_one_attached :image do |attachable|
  attachable.variant :thumb, resize_to_limit: [300, 300]
end

図4

サムネを使う場所にこう書こう。ちなみにprocessedは、まずサムネイルが作られてなかったら自動で作る、以降は「サムネイルを過去に作ってるか」を確認し、作ってたらそれを持ってきてくれる。気の利く後輩とでもいいましょうか。

 <%= image_tag post.image.variant(:thumb).processed.url %>

図5

libglib2.0-0
libglib2.0-dev
libpoppler-glib8

railsで画像投稿機能を作ってherokuにデプロイする人へ

結論

railsのactieve storageという機能を使い、それにAWSのS3を連携させればできます

何があった

  • Progateのrailsレッスンで、画像をアップロードするやり方を習うんですが、それってFile.binwriteを使う方法なんですよ。
  • で、普通にその機能を使って画像投稿掲示板みたいなのを作ってherokuにデプロイしてみると普通に画像のアップはできるんですよ。
  • でも削除ができんのですよ。パーミッションエラーになる。ほんとはできるのかも分からんが、rails歴1ヶ月の俺は一旦保留に。
  • もっと問題なのは、その後色々コードいじって再度herokuにデプロイすると、画像が全部消えてる。
  • なんで消えるかというと、heroku上でbinwriteでpublicに画像を格納したとしても、それはgithub上に格納してる訳ではない→デプロイ時に、データベース以外はgithubのモノにまるっと置き換わっちゃうので消えちゃう→あーあ、という流れ。うまく説明できてるか?
  • という訳で色々ググると、railsの場合、actieve storageという機能にAWSってやつのS3ってサービスを連携させるのが一般的なようで。

active storageとは

rails組み込まれとる追加できる機能なんですがね、例えば、画像を初めてアップロードした際、専用のデータベースみたいなやつに「この画像はこのモデルのこのIDのやつですよ、あと画像データはここにあるっすよ」みたいな情報をこう、ちょちょいと自動で書き込んでくれるっていうのかね。あー駄目だ。解説記事とか見ないと、俺の理解の解像度ってこんなもんだわ。ま、いいや。

AWSのS3とは

AWSっていうのはアマゾン・ウェブ・サービスの略で、なんでもある、なんでもできるレンタルサーバーっぽいやつです。AWSのサービスの一つがS3というファイル置き場みたいなやつです。前項の「データはここにあるっすよ」の「ここ」を、S3に指定することができます。S3以外にGoogleとかも同じようなサービスをやってますが、ネット上の情報量がS3の方が多かったのでそっちにした。

※余談。非エンジニアからしたら、なんか数年前からAWSという文字をはてブとか本屋とか色んな場所で目にするようになって、当時は「え!みんな通販始めんの!すごい時代やん!」とかマジで思ってましたが、アマゾン潜入体験記みたいな本を買って読んでたら(前に読んだユニクロ潜入記がおもろかったので)AWSに触れてる箇所があって、あ〜、アマゾンのノウハウをうまいこと活かしてなおかつそれなりの収益を出していてすごいなあ、と思った記憶があります。

導入方法

全体感

これを読んでください。この通りにやったら突破できます。ただし、次項以降の「ハマりポイント」に注意してください。
https://qiita.com/hmmrjn/items/7cc5e5348755c517458a

ハマりポイント1

AWS周りで登録する箇所は下記URLを参考にしてください(つってもこれがまた完全に参考にできる訳でもないので微妙な所なんですが。AWS、マジでムズいって。Progateやったばかりの人間には酷ですよ。鋭意勉強中ですが、基本何書いてるか訳分からん状態)。上記qiitaの記事は2018年6月(これ書いてる時から5年も前)に書かれてますが、AWS周りの登録関係、死ぬほど画面が変わってるのでそこだけは止まりました。逆に言うと、それ以外は行けました。超感謝!
IAM周り
https://techtechmedia.com/terraform-tutorial/
アクセス権限周り
https://it-web-life.com/aws_s3_how_to_bucket_all_public/

ハマりポイント2

credentialをいじる箇所でおれがハマった部分があります。YAMLという設定ファイルなのですが、これ、書き方が厳密なので。半角スペース2つなのは色んなサイトに書いてるので覚えていたのですが、コロンのあとにスペースがないと、ハマります。まあ、「yaml 書き方」でググらなかった俺が悪いんですがね。ちなみにハマって具体的にどうなるかというと、herokuで環境変数を設定するとデプロイ後に動かなくなります(設定を消すと動く)。「環境変数マスターキーが指定した先のcredentialにs3の鍵が入ってないぞ〜お〜いどこだ〜どこに隠れとる〜?」状態になるので。マジで注意。

ハマりポイント3

Progateでrailsやったばかりの人、scaffoldの存在知らんでしょ。S3導入時は、scaffoldをググってある程度理解した上で、rails g scaffold〜で始めてください。active storageとs3でググってるとそれを基準に書いたviewとコントローラーばかり引っかかるので、「何これ」状態になること間違いなしです。

感想

罠が多い。多すぎる。

herokuを使いましょう、railsで作ったやつが動いてるのを公開したかったら。

結論

herokuを使うとgithubにpushしたやつが実際に動いて感動する

なんだこの記事

初心者が初心者向けに書いてます。Progateのrailsクリアして「うお〜作るぞ〜」ってなってる人向け。

herokuとは

railsで書いたコードをgithubにpushしても、localhost:3000で動いてるものをそのままお届けできませんがherokuならできます。ただ月額5ドルかかります。「無料に近い所もあるけどherokuが圧倒的に使いやすい、というツイートを紹介している記事」を見たので、俺は払います。ちなみにその記事は下記です。
https://blog.jnito.com/entry/2022/10/04/104100

使い方

下記参照。これで突破できた。最初からこの記事を発見できていればどれだけ楽だっただろうか。
https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39

俺用に要約しとこ。

  1. rails newとrails g scaffoldでとりあえずテキトーに作り、ルーティングでrootをindexに設定しとく
  2. gemfile:sqlite3をコメントアウト、下の方のdev,testのgroupにgem 'sqlite3'を追加(図1のように)、最下部に図2を追加(下記参照)
  3. ターミナル:bundle install --without production
  4. database.yml:図3参照
  5. production.rb:config.assets.compile をtrueにする
  6. ターミナル:bundle lock --add-platform x86_64-linux ※なんでこれがいるかは、https://universato.hatenablog.com/entry/2021/07/14/030424 を参照
  7. githubリポジトリ作っとく
  8. ターミナル:gitの一連のコマンド(add,commit,push)
  9. herokuのサイトへ行き新規のアプリを作る
  10. サイト上でgithubリポジトリと連携させる
  11. ターミナル:heroku git:remote -a herokuで設定したアプリ名(ミスったらgit remote rm heroku)
  12. ターミナル:git push heroku main
  13. ターミナル:heroku run rails db:migrate

図1

group :development, :test do
  gem 'sqlite3' 
end

図2

group :production do
  gem 'pg'
end

図3

production:
#半角スペース2個分の空白を開ける
  <<: *default
  adapter: postgresql
  encoding: unicode
  pool: 5

便利コマンド

ターミナルでheroku logsするとログを見られる。ただ、herokuのサイト上でも閲覧可能。vscode+chrome+サブディスプレイという環境なら、ターミナルアプリを立ち上げてアプリのフォルダに行ってheroku logsをメインディスプレイ、chromeをサブディスプレイにするのがいいと思うけどプロの皆さんはどうしてるんでしょうか。