6
4

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 1 year has passed since last update.

next.jsアプリをfly.ioにリリースしたので作業をまとめておく

Last updated at Posted at 2022-11-27

何の記事?

herokuが有料化されてから、アプリのホスティングをどこにするかと悩まれている方も多いはずの今日この頃。
かくいう私も趣味サイトのデプロイをどこにしようか迷っていたところ、fly.ioが要件をいい感じに満たしてくれそうだったので、色々と遊びながら、デプロイまで漕ぎ着けてみました。

今回は、fly.ioにデプロイするまでの過程や注意点を放流🐟していこうと思います。

アプリ構成

  • FW: next.js
  • ホスティング: fly.io
  • データベース: postgres
  • ドメイン: muumuu-domain
    (重要な部分だけ)

前提

fly.ioの会員登録は終わっている
flyctlはインストール済み(ちな、brewではbrew install flyctlで一発
next.jsはデプロイできる状態まで準備済み
prisma使ってる

作業ディレクトリは、next.jsのroot

❯ pwd # -> /Users/🐱/Desktop/dev/myapp

実装

fly.ioでアプリを動かすには、flyctlコマンドを通じてデプロイまで操作していきます。
デプロイまでの手順としては、アプリの基礎情報の作成、環境変数の設定、データベースの設定、そしてドメインの設定といったステップで進めます。

アプリの基礎情報

flyctl launchコマンドを実行すると、fly.ioにアプリをアップロードするまでに必要な情報をプロンプトに従って入力することで、デプロイまでを一気に行うことができます。

❯ flyctl launch
Creating app in /Users/🐱/Desktop/dev/myapp
Scanning source code
Detected a Dockerfile app
? Choose an app name (leave blank to generate one): myapp # ? Select Organization: my-super-secret@email.com (personal) # ? Choose a region for deployment: Tokyo, Japan (nrt) # Created app myapp-37 in organization personal
Wrote config file fly.toml
? Would you like to set up a Postgresql database now? No # ? Would you like to set up an Upstash Redis database now? No # ? Would you like to deploy now? No # Your app is ready! Deploy with `flyctl deploy`

さて、今回はローカルのnext.jsアプリディレクトリからflyctl launchを実行して、上記のように設定を行いました。

設定を行うと以下の3つのファイルが作成されます
.dockerignore
Dockerfile
fly.toml

①アプリ名の設定

fly.ioに展開するアプリ名です。これはfly.ioで、ホスト名として利用される為、他の人と被らないようにユニークにする必要があるので要注意。

アプリ名がhogeであればhttps://hoge.fly.devというurlが発行されます。

②組織名

この組織名に関しては、fly.ioのダッシュボードから予めorganizationを作成してあれば、personal以外にも選択が可能になります。今回は、特に指定がないので、脳死で先に進みます。

後々、origanizationを変更したいという時は、flyctl apps move -a myapp -o cat-lovers-organizationとflyctlから変更が可能です。

③リージョン

リージョンは、今回はtokyoを選択。

④postgresql

launchコマンド実行と同時に、postgresのセットアップを一緒に行うかを尋ねられます。
デフォルトのオプションでは以下のように、選択項目が表示されますが、今回は後々設定して作りたいのでNを選択。

Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
  Production - Highly available, 2x shared CPUs, 4GB RAM, 40GB disk
  Production - Highly available, 4x shared CPUs, 8GB RAM, 80GB disk
  Specify custom configuration

⑤Redis

今回は不要なのでN

⑥直ぐにデプロイするか

後述しますが、環境変数の設定をDockerfileに行わないといけないのでN

環境変数の設定

fly.ioでnext.js運用時は、利用する環境変数の種類に応じて設定方法が異なってきます。

secretな環境変数

クライアントに表示したくない秘匿性の高い環境変数は、flycltから値をセットします。

❯ flyctl secrets set -a myapp MY_SUPER_SECRET_KEY="this is my super secret key" MY_ANOTHER_SUPER_SECRET_KEY=1234

publicな環境変数

next.jsではクライアントでも参照可能な環境変数にはNEXT_PUBLIC_とprefixをつけてあげるのが必須になっています。(仮にパブリック環境変数と呼ぶことにします。

このパブリック環境変数は前項目(secretな環境変数)で行ったようなflyctlから環境変数に値をセットしても動きません。パブリック環境変数はDockerイメージをビルド時に値として渡しあげる必要があるので、Dockerfileとfly.tomlファイルを編集することで対応していきます。

今回は、NEXT_PUBLIC_FRUITというパブリック環境変数があると仮定し、以下のように各ファイルへ編集を行います。

# rootディレクトリに作成されたfly.toml
# ...
[build]
  [build.args]
    NEXT_PUBLIC_EXAMPLE = "Value goes here"
    NEXT_PUBLIC_FRUIT = "APPLE"
    #...
    ARG NEXT_PUBLIC_FRUIT

データベースの設定

今回はデータベースを操作するのにPrismaを利用していると仮定します。
Prismaのデータベースの接続先の値では、schema.prismaファイルで設定してありDATABASE_URLという環境変数を参照している。今回はfly.io上で稼働しているpostgresサーバーを利用するので、postgresサーバーのホスト名を取得する必要があります。
その為、postgresサーバーの作成->urlの取得といった手順を踏んでいきます。

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

DBアプリの作成

今回は先ほど、postgresの作成をスキップしたので、flyctlからコマンドを実行してpostgres用マシーンの作成をします。

❯ fly postgres create
? Choose an app name (leave blank to generate one): my-sample-db # ? Select Organization: my-super-secret@email.com (personal) # ? Select region: Tokyo, Japan (nrt) # ? Select configuration:  [Use arrows to move, type to filter]
  Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk # 1
  Production - Highly available, 2x shared CPUs, 4GB RAM, 40GB disk # 2
  Production - Highly available, 4x shared CPUs, 8GB RAM, 80GB disk # 3
> Specify custom configuration # ④
  ? Initial cluster size 2
  ? Select VM size: shared-cpu-1x - 256
  ? Volume size 1

①、②、③に関してはnext.jsのでflyctl launchを実行した時と同じなので説明は省略

④設定

postgresマシンスペックの設定項目では、デフォルトで4つの選択項目が存在します。

developmentとproductionの一番大きな違いは、スペック以外にも読み込み、読み書きと専用のクラスターが提供されるということです。(development(1)はクラスターが1つ。production(2)では2つ。(3)は未確認)

さて、どれだけ小規模なアプリでも本番運用を想定すれば、productionを選択したいところですが、ここで選択できるほどのスペックを必要としない場合、無駄にコストがかってしまうのでカスタムを選択することになります。

今回は以下のように設定を進めました。

...
? Select configuration: Specify custom configuration
? Initial cluster size 2
? Select VM size: shared-cpu-1x - 256
? Volume size 1
Creating postgres cluster in organization personal
Creating app...
Setting secrets on app my-sample-db...
Provisioning 1 of 2 machines with image flyio/postgres:14.4
Waiting for machine to start...
Machine 3d8d1adwced189 is created
Provisioning 2 of 2 machines with image flyio/postgres:14.4
Waiting for machine to start...
Machine 1781774a932489 is created
==> Monitoring health checks
  Waiting for 3d8d1adwced189 to become healthy (started, 3/3)
  Waiting for 1781774a932489 to become healthy (started, 3/3)

Postgres cluster my-sample-db created
  Username:    postgres
  Password:    mysupersecretpassword
  Hostname:    my-sample-db.internal
  Proxy port:  5432
  Postgres port:  5433
  Connection string: postgres://postgres:mysuperpassword@my-sample-db.internal:5432

また、クラスターを複数作成で選択すると「読み書き、読み込みのみ(レプリカ)」といい感じに動作モードを設定してくれるようです。

最後に表示されるユーザー名とパスワードは、大事に保管しておきましょう。
補足しておくと、psql接続以外にもflyctlコマンドでpostgresには接続可能で、自動的にユーザーがpostgresで選択された状態で接続可能です。

動作確認

今回はクラスターを2個としたので、動作確認で作成したデータベースに接続し、読み書きを両方で行ってみます。

今回作成したサーバーの情報

--- id --- --- モード --- --- ポート ---
3d8d1adwced189 読み書き 5432
1781774a932489 読みのみ 5433

flyctlではflyctl postgres connect -a <postgres-app-name>のコマンドでも接続が可能ですが、flyctlでproxyを通すことで、psql接続も可能になります。

今回は、せっかくなのでproxyを通して接続してみたいと思います。

test_dbというのを予め作成、fruitテーブルを準備してあります。

# terminal 1
# ローカルのdbポートと喧嘩するので適当に5455にバインドしてます

❯ flyctl proxy 5455:5432 -a my-sample-db
# terminal 2

❯ psql postgres://postgres:mysupersecretpassword@localhost:5455/test_db
...

psql (14.6 (Homebrew), server 14.4 (Debian 14.4-1.pgdg110+1))
Type "help" for help.
test_db=# insert into fruit values ('lemon');
INSERT 0 1
test_db=# select * from fruit;
 name  
-------
 lemon
(1 row)

問題なく書き込みができていることが確認できました。
では、次にもう一つのサーバーの動作を確認してみましょう。ポート5433に接続し書き込み失敗すれば意図した通りの動作です。

# terminal 1
❯ flyctl proxy 5455:5433 -a my-sample-db
Proxying local port 5455 to remote [my-sample-db.internal]:5433
# terminal 2
❯ psql postgres://postgres:mysupersecretpassword@localhost:5455/test_db
psql (14.6 (Homebrew), server 14.4 (Debian 14.4-1.pgdg110+1))
Type "help" for help.
...
test_db=# insert into fruit values('apple');
ERROR:  cannot execute INSERT in a read-only transaction
test_db=# select * from fruit;
 name  
-------
 lemon
(1 row)

前途した通り動作していることが確認できました。

prismaから繋がるように

Prismaからpostgresサーバーに繋がるようにするには、前途した通りDATABASE_URLにpostgresのアドレスを指定しないと動作しません。このアドレスですが、基本的には先ほどのDBを作成した際に出力されるHostname: my-sample-db.internalで出力されたホストを指定することになります。

❯ flyctl secrets set -a myapp DATABASE_URL="postgres://postgres:mysupersecretpassword@my-sampledb.internal:5432/test_db"

さて、ここまで来ればflyctl deployコマンドを実行してアプリをデプロイしておきましょう。

ドメインの適用

今回は大昔から運用している個人アプリで、ドメインをmuumuuドメインで取得していたので、muumuuドメインを例に説明しています。

デプロイしたnext.jsアプリのip取得

fly.ioのダッシュボードから確認することも可能ですが、flyctlでサクッとDNS設定に必要なipを表示します。

❯ flyctl ips list -a myapp
VERSION IP                      TYPE    REGION  CREATED AT           
v4      123.456.789.xx          public  global  2022-11-26T12:29:27Z
v6      xxxx:xxxx:z::xxxx       public  global  2022-11-26T12:29:32Z

ここで取得したものを、DNSレコードに適応しておきます。ちなみに、muumuuドメインはこんな感じのフォームが用意されてます。

サブドメイン 種別 内容 優先度
A 123.456.789.xx
AAAA xxxx:xxxx:z::xxxx

設定を反映したら、あとはflyctlから証明書を発行するだけです。ちなみに、2つ目のコマンドは証明書が発行されたかどうか確認するコマンドなのでやらなくても良いです。

❯ flyctl certs create -a myapp example.com
❯ flyctl certs show example.com

あとは、反映されればfin!

おまけ

サーバー側で何かしらする時に権限エラーが出る

例えば一時ファイルを作った時とか。
サーバーのコード実行時のユーザーはnodeなのでDockerイメージ生成時のファイルコピー時に、nodeに権限を付与しておきましょう。

# ファイルコピー時にnodeに権限付与
COPY --from=builder --chown=node:node /app ./

デプロイできない

色々遊んでいると"ENOSPC: no space left on device, write"的なエラーが出たりして、デプロイできない問題にちょくちょくぶち当たる。これは、fly-builder-xxxという名前のビルド専用アプリが、過去に作ったdockerイメージの容量で圧迫されて限界がきて発生しているっぽいです。

ということで、このビルド専用インスタンスを作り直す(もしくは、アプリに接続して不要なイメージを削除🤔?。 シンプルに削除するには、ダッシュボードからアプリ一覧を開くとfree builderタグがついてるものがあるので、その設定からdeleteを実行する。

また、このビルド専用アプリはflyctl deployコマンドを実行すると勝手に作成されるので、特別、自身で作成しないといけない。なんてことはないのでご安心ください。

6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?