T3 Stack で作成したアプリを Fly.io にデプロイしてみた

# はじめに
## T3 Stackとは？

https://github.com/t3-oss/create-t3-app

**simplicity（シンプルさ）**, **modularity（モジュール性）**, and **full-stack typesafety（フルスタックの型安全）** に重点をおいて、以下の技術要素で構成されています。
* Next.js
* tRPC
* Tailwind CSS
* Typescript
* Prisma
* NextAuth.js

tRPCは最近目にするようになってきましたが、その他はポピュラーな構成だと思います。
`npx create-t3-app@latest` を実行して、必要なモジュールを選択するとプロジェクトの雛形が作成されます。

## Fly.ioとは？

https://fly.io/

いわゆる PaaS です。同様のサービスでは Heroku が有名ですね。
無料でアプリをデプロイできて、必要であれば追加のリソースを従量課金で購入できます。
永続ボリュームストレージが使用できるため、 sqlite を配置してデータベースを利用したアプリ開発もできます。


# T3 Stackのアプリを作成する

create-t3-app コマンドを実行し、質問に回答します。
```bash:回答例
$ npx create-t3-app@latest
 
# プロジェクト名を決めます
? What will your project be called?
t3-app-example
 
# TypeScriptしか勝たん
? Will you be using JavaScript or TypeScript?
o JavaScript
* TypeScript
 
# 今回は認証を実装しないのでnextAuthを外しました
? Which packages would you like to enable?
o nextAuth
* prisma
* tailwind
* trpc
 
# git repositoryは自分で用意するのでNoを選択
? Initialize a new git repository?
No
 
# Yesでnpm installも実行
? Would you like us to run npm install?
Yes
 
# install成功ログ
Using: npm
 
✔ t3-app-example scaffolded successfully!
 
Installing packages...
✔ Successfully installed prisma
✔ Successfully installed tailwind
✔ Successfully installed trpc
✔ Successfully installed envVariables
 
Next steps:
  cd t3-app-example
  npx prisma db push
  npm run dev
```

次に、上記の Next steps に表示されたコマンドを実行します。
```bash
$ cd t3-app-example
$ npx prisma db push
$ npm run dev
```

http://localhost:3000/ にアクセスすると画面が表示されました。
![t3-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25044/b1c4dc88-b0c1-3c4f-b4d5-0824ca4b9c9e.png)

あとは生成されたコードを修正して、お好きなアプリを作ってみてください！:muscle_tone2:

## 開発Tips
### Prismaで新しいテーブルを作成する
schema.prisma に model を追加して、 `npx prisma db push` を実行すると新しいテーブルがDBに作成されます。

```prisma/schema.prismaに追記
model Counter {
    id String @id
    count Int
}
```

```bash
$ npx prisma db push
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": SQLite database "db.sqlite" at "file:./db.sqlite"
 
Your database is now in sync with your Prisma schema. Done in 42ms
 
✔ Generated Prisma Client (4.3.1 | library) to .\node_modules\@prisma\client in 111ms
```

### tRPCに新しいクエリを作成する

まずはサーバーサイドから実装します。src/server/router 配下に新しいルーティングを追加します。
今回は Prisma を使用して、アクセスカウンターを実装してみました。

```js:src/server/router/example.tsに追記
  .query("countup", {
    async resolve({ ctx }) {
      const pv = await ctx.prisma.counter.upsert({
        where: { id: 'pv' },
        create: { id: 'pv', count: 1 },
        update: { count: {increment: 1}},
      });
      return pv.count;
    },
  })
```

次に、クライアントサイドを実装します。作成したクエリを呼び出す処理を追加しました。
```js:src/page/index.tsxに追記
 const pv = trpc.useQuery(["example.countup"]);
 console.log(pv.data);
```

これでページリロードする度に、pv.data がカウントアップされるようになりました。


# Fly.ioにデプロイする
## CLI tool（flyctl）のインストール

ローカル環境に合わせてインストールコマンドを実行します。公式サイトをご確認ください。

https://fly.io/docs/hands-on/install-flyctl/

```bash:Windowsの場合
> iwr https://fly.io/install.ps1 -useb | iex
```

## アカウントにログイン
ログインコマンドを実行するとWebブラウザにログインページが表示されます。
Sign Inに成功すると、以降そのアカウントに対して操作を実行します。

```bash
$ fly auth login
```
Githubアカウントがある方は連携するとすぐにログインできます。
無料枠内で使用する場合には、クレジットカードの登録などは必要ないようです。
![t3-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25044/48514e85-4b28-70b7-e147-8c726b3d50c0.png)


## デプロイの初期設定

`fly launch` を実行して質問に回答します。
回答が終わると Dockerfile と fly.toml（サーバー構成定義）ファイルが出力されます。
```bash:回答例
$ fly launch
Creating app in t3-app-example
Scanning source code
 
# 自動でNextJSアプリとして検出されました
Detected a NextJS app
 
# デプロイするアプリ名を入力します
? App Name (leave blank to use an auto-generated name):
t3-app-example
 
# リージョンは近い方が良いので東京を選択
? Select region:
nrt (Tokyo, Japan)

# すぐにデプロイするか問われますが、 Dockerfileを編集したいのでNoを選択
? Would you like to deploy now?
No
 
Your app is ready. Deploy with `flyctl deploy`
```

作成されたDockerfileを修正します。
* 個人的にnpmを使いたいので、yarnコマンドはコメントアウトして、npmコマンドのコメントアウトを解除
* 環境変数の PORT を fly.toml の internal_port と同じポート番号(8080)に修正

```bash: Dockerfileを修正
#RUN yarn install --frozen-lockfile
RUN npm ci
 
#RUN yarn build
RUN npm run build
 
#ENV PORT 3000
ENV PORT 8080
 
#CMD ["yarn", "start"]
CMD ["npm", "run", "start"]
```

## デプロイ

`fly deploy` コマンドを実行すると、先ほど作成した fly.toml の内容に従ってデプロイされます。
```bash
# remoteのbuilderが調子が悪かったので、localでビルドするオプションを追加
$ fly deploy --local-only
```

管理画面にホスト名が表示されているので、こちらをクリックしてデプロイされたアプリを確認します。
![t3-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25044/620f2ad3-78d6-b5ef-f6bd-27e00bb179a4.png)
![t3-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25044/1ec4909b-feed-a142-6268-324a0c9311c2.png)

## トラブルシューティング
### Error failed to fetch an image or build from source: error connecting to docker: failed building options: failed probing "personal": context deadline exceeded

`fly deploy` 実行時にエラーになったので下記ページを参考に `fly wireguard websockets enable` を実行しました。

https://community.fly.io/t/deployments-not-working-error-connecting-to-docker/3992/57

### Error failed to fetch an image or build from source: error connecting to docker: remote builder app unavailable

何度かデプロイしているとリモートの builder が動かなくなりましたが、管理画面から builder を一度削除することで解決しました。また、`--local-only` オプションでローカルビルドできたので、途中からはこちらを使用しました。
![t3-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25044/cf49a34a-f1e6-54eb-36c1-06830d78e539.png)

### Failed due to unhealthy allocations - no stable job version to auto revert to and deploying as v1

何度デプロイしてもヘルスチェックでエラーになっていましたが、しばらく時間をおくとヘルスチェックが通るようになりました。原因は不明です…:disappointed_relieved:


# DBのデータを永続ボリュームに保存する

マニュアルを参考に、永続ボリュームを storage という名前でアプリと同じ東京リージョンに1GBで作成しました。

https://fly.io/docs/reference/volumes/

```bash
$ fly volumes create storage --region nrt --size 1
        ID: vol_xxxxxxxxxxx
      Name: storage
       App: t3-app-example
    Region: nrt
      Zone: d8a3
   Size GB: 1
 Encrypted: true
Created at: 22 Sep 22 07:30 UTC
```

続けて fly.toml に永続ボリュームのマウント設定と、 sqlite の保存場所を追記します。

```toml:fly.tomlに追記
[mounts]
  source="storage"
  destination="/storage"
 
[env]
  DATABASE_URL="file:/storage/db.sqlite"
```

アプリ起動時にマイグレーションが走るようにします。
```bash
# マイグレーションファイルを作成
$ npx prisma migrate dev
```
```json: package.jsonに追記
    "prestart": "prisma migrate deploy",
```

再デプロイし、アプリを再起動してもデータが永続化されていることを確認しました。
```bash
# 再デプロイ
$ fly deploy --local-only
 
# 再起動
$ fly restart t3-app-example
```

# おわりに

* 最初デプロイが上手くいかずに困りましたが、最終的にはデプロイできるようになったので良かったです。
* 永続ボリュームに直接アクセスする方法が見つけられなかったので、 sqlite のダウンロードは自分で実装する必要があるかもしれません。
* Prisma, tRPCは初めて触りましたが、使いやすそうでした。
* アクセスが少ないサイトであれば無料で始められそうなので、今後活用していきたいです。
* 今回作成したコードのサンプルです。https://github.com/proyuki02/t3-flyio-sqlite-example