Rubyと筋肉とギターとわたし

筋トレが仕事です

【雑記】Nestjs/Prisma/GraphQLをPlanetScaleとFly.ioへデプロイできるリポジトリを作った

どうもてぃ。

作りました。興味がある人は気軽に触ってみてください。

当該リポジトリ

github.com

デプロイ手順

前提として pscaleコマンド、flyctlコマンドが実行できるようにしておいてください。npm(yarn)が入ってないのは論外で。

planetscale.com

fly.io

PlanetScaleの作成

$ pscale auth login

$ pscale database create nestjs-sample --region ap-northeast

$ pscale branch create nestjs-sample dev
$ pscale branch promote nestjs-sample main

$ pscale password create nestjs-sample dev <name> # nameは識別子なのでなんでもおk

pscale password listでも取得できますが、DATABASE_URLの形式に書き換えるのだるいのでPlanetScaleのダッシュボードで確認してください。

データベース → connectで表示されます。

passwordがまだ作成できてない場合は New passwordを押しましょう。DATABASE_URLが出来上がったらコピーしておいてください。flyctlで使用します。

Fly.ioの作成

$ flyctl auth login

# databaseはPlanetScaleを使用するのでNo
# Dockerfileを上書きしないように注意
# Dockerfileがあるため最後にデプロイするかどうか聞かれますがNoで(DATABASE_URLを設定してないため)
# fly.tomlは上書きしてください
$ flyctl launch

# DATABASE_URLは上でコピーしたやつ
# 後ろのパラメータ大事、絶対忘れるな
$ flyctl secrets set DATABASE_URL="mysql://~~~~~~~~~~~~~~~~~~~~~~~~~/nestjs-sample?sslaccept=strict?sslcert=/etc/ssl/certs/ca-certificates.crt"

$ flyctl deploy

特に問題なければデプロイできているでしょう、きっとね。

苦労したこと

WLS2環境で認証画面が立ち上がらない

最初 pscaleコマンドでPlanetScaleへschema.prismaを反映させようとしていました。

が、まず pscale auth loginではまった。

chromeが立ち上がらない。ctrl + cでキャンセルすると認証画面のURLが表示されるが、アクセスしても無効化されている(そりゃそうだ)。

根本的にはまだ解決できていませんが、いったんsudoで実行し生成したアクセストークンを実行ユーザーのディレクトリに移動させることでうまくいきました。

解決したのでこのissueにコメントしときました。OSS活動じゃん。

根本解決は↓を今度ためしてみる予定。

github.com

ちなみに、pscaleコマンドだけでなくflyctlコマンドでもCLI認証する必要があり、同じようにスーパーユーザーで実行し、アクセストークンを移動させるというムーブをしました。

くっそだる。

MacLinux使ってる人は関係ないのでスルーでおkです。

Fly.ioでPrismaInitializationErrorが発生

Fly.ioにデプロイするタイミングでPlanetScaleにスキーマ構造を反映させているのですが、その際 PrismaInitializationErrorが発生しデプロイに失敗していました。

長いですが以下

~/github.com/planetscale.nestjs-graphql-prisma main*
% sudo flyctl deploy
[sudo] password for motty:
Update available 0.0.446 -> v0.0.451.
Run "flyctl version update" to upgrade.
==> Verifying app config
--> Verified app config
==> Building image
Remote builder fly-builder-bitter-wave-2664 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon  191.9kB
[+] Building 57.6s (18/18) FINISHED
 => [internal] load remote build context                                                                                                                                                                                                                                 0.0s
 => copy /context /                                                                                                                                                                                                                                                      0.1s
 => [internal] load metadata for docker.io/library/debian:bullseye                                                                                                                                                                                                       2.0s
 => [stage-1 1/8] FROM docker.io/library/debian:bullseye@sha256:534da5794e770279c889daa891f46f5a530b0c5de8bfbc5e40394a0164d9fa87                                                                                                                                         0.0s
 => CACHED [builder 2/7] RUN apt-get update; apt install -y curl python-is-python3 pkg-config build-essential                                                                                                                                                            0.0s
 => CACHED [builder 3/7] RUN curl https://get.volta.sh | bash                                                                                                                                                                                                            0.0s
 => CACHED [builder 4/7] RUN volta install node@18.8.0 yarn@1.22.19                                                                                                                                                                                                      0.0s
 => CACHED [builder 5/7] WORKDIR /app                                                                                                                                                                                                                                    0.0s
 => [builder 6/7] COPY . .                                                                                                                                                                                                                                               0.0s
 => [builder 7/7] RUN yarn install &&     yarn prisma generate &&     yarn build                                                                                                                                                                                        49.7s
 => CACHED [stage-1 2/8] WORKDIR /app                                                                                                                                                                                                                                    0.0s
 => CACHED [stage-1 3/8] COPY --from=builder /root/.volta /root/.volta                                                                                                                                                                                                   0.0s
 => CACHED [stage-1 4/8] COPY --from=builder /app/node_modules ./node_modules/                                                                                                                                                                                           0.0s
 => [stage-1 5/8] COPY --from=builder /app/dist ./dist                                                                                                                                                                                                                   0.1s
 => [stage-1 6/8] COPY --from=builder /app/.fly ./.fly                                                                                                                                                                                                                   0.2s
 => [stage-1 7/8] COPY --from=builder /app/package.json ./                                                                                                                                                                                                               0.1s
 => [stage-1 8/8] COPY --from=builder /app/yarn.lock ./                                                                                                                                                                                                                  0.0s
 => exporting to image                                                                                                                                                                                                                                                   0.1s
 => => exporting layers                                                                                                                                                                                                                                                  0.1s
 => => writing image sha256:393032d20e1b39441cedd0abc72eff5352edb0a377daedd8e0dabf6c293d7aa3                                                                                                                                                                             0.0s
 => => naming to registry.fly.io/nestjs-app:deployment-01GR1MYTD32RT3HDPMPYEGNK86                                                                                                                                                                                        0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/nestjs-app]
1e854d6563d3: Pushed
1d6767cc453b: Pushed
f7e90164f428: Pushed
fbf3a65b1a93: Pushed
5d3c3bdc31ec: Layer already exists
5b1800ba7f88: Layer already exists
1b526d844305: Layer already exists
a9099c3159f5: Layer already exists
deployment-01GR1MYTD32RT3HDPMPYEGNK86: digest: sha256:0e210bfe9ec945b3e5c9cd95571ca559e3db8eaa140cba21b0fdec2cba066698 size: 1996
--> Pushing image done
image: registry.fly.io/nestjs-app:deployment-01GR1MYTD32RT3HDPMPYEGNK86
image size: 847 MB
==> Creating release
--> release v3 created

--> You can detach the terminal anytime without stopping the deployment
==> Monitoring deployment
Logs: https://fly.io/apps/nestjs-app/monitoring

 1 desired, 1 placed, 0 healthy, 1 unhealthy [restarts: 2] [health checks: 1 total, 1 critical]
Failed Instances

Failure #1

Instance
ID              PROCESS VERSION REGION  DESIRED STATUS  HEALTH CHECKS           RESTARTS        CREATED
cca60ea1        app     3       nrt     run     failed  1 total, 1 critical     2               46s ago

Recent Events
TIMESTAMP               TYPE            MESSAGE
2023-01-30T15:34:25Z    Received        Task received by client
2023-01-30T15:34:25Z    Task Setup      Building Task Directory
2023-01-30T15:34:51Z    Started         Task started by client
2023-01-30T15:34:57Z    Terminated      Exit Code: 1
2023-01-30T15:34:57Z    Restarting      Task restarting in 1.14956903s
2023-01-30T15:35:04Z    Started         Task started by client
2023-01-30T15:35:10Z    Terminated      Exit Code: 1
2023-01-30T15:35:10Z    Restarting      Task restarting in 1.058345s
2023-01-30T15:35:17Z    Started         Task started by client
2023-01-30T15:35:23Z    Terminated      Exit Code: 1
2023-01-30T15:35:23Z    Not Restarting  Exceeded allowed attempts 2 in interval 5m0s and mode is "fail"
2023-01-30T15:35:23Z    Alloc Unhealthy Unhealthy because of failed task

2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] AppModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] PostsModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] AuthorsModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] GraphQLModule dependencies initialized +1ms
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [RoutesResolver] AppController {/}: +199ms
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [RouterExplorer] Mapped {/, GET} route +4ms
2023-01-30T15:35:21Z   [info]prisma:info Starting a mysql pool with 3 connections.
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [PrismaService] info: Starting a mysql pool with 3 connections.
2023-01-30T15:35:21Z   [info]/app/node_modules/@prisma/client/runtime/index.js:24780
2023-01-30T15:35:21Z   [info]          throw new PrismaClientInitializationError(error2.message, this.config.clientVersion, error2.error_code);
2023-01-30T15:35:21Z   [info]                ^
2023-01-30T15:35:21Z   [info]PrismaClientInitializationError: Error opening a TLS connection: error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:0A000086:SSL routines:func(0):certificate verify failed:../deps/openssl/openssl/ssl/statem/statem_clnt.c:1896: (unable to get local issuer certificate)
2023-01-30T15:35:21Z   [info]    at startFn (/app/node_modules/@prisma/client/runtime/index.js:24780:17)
2023-01-30T15:35:21Z   [info]    at async Proxy.onModuleInit (/app/dist/src/prisma/prisma.service.js:40:9)
2023-01-30T15:35:21Z   [info]    at async Promise.all (index 0)
2023-01-30T15:35:21Z   [info]    at async callModuleInitHook (/app/node_modules/@nestjs/core/hooks/on-module-init.hook.js:43:5)
2023-01-30T15:35:21Z   [info]    at async NestApplication.callInitHook (/app/node_modules/@nestjs/core/nest-application-context.js:178:13)
2023-01-30T15:35:21Z   [info]    at async NestApplication.init (/app/node_modules/@nestjs/core/nest-application.js:96:9)
2023-01-30T15:35:21Z   [info]    at async NestApplication.listen (/app/node_modules/@nestjs/core/nest-application.js:158:33)
2023-01-30T15:35:21Z   [info]    at async bootstrap (/app/dist/src/main.js:11:5) {
2023-01-30T15:35:21Z   [info]  clientVersion: '4.9.0',
2023-01-30T15:35:21Z   [info]  errorCode: 'P1011'
2023-01-30T15:35:21Z   [info]}
2023-01-30T15:35:21Z   [info]Node.js v18.8.0
2023-01-30T15:35:21Z   [info]error Command failed with exit code 1.
2023-01-30T15:35:21Z   [info]info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
2023-01-30T15:35:21Z   [info]Starting clean up.
--> v3 failed - Failed due to unhealthy allocations - no stable job version to auto revert to and deploying as v4

--> Troubleshooting guide at https://fly.io/docs/getting-started/troubleshooting/
Error abort

Dockerfileに関しては flyctl launchした際に生成されるDockerfileで少しばかりカスタマイズしています。

似た問題のIssueを見つけました。

github.com

sslcert pathを設定しないといけない…と。しかもそれがPrisma公式に書いてあった。

www.prisma.io

PlanetScaleにもdebianだったらこれ設定しろよって書いてました。

planetscale.com

そしてflyctl launchして生成されるDockerfileのdebian:bullseye imageには、なんと/etc/sslディレクトリがなかった。証明書が存在しなかったのです…。

これはコンテナを立てて確認済みです。

正しくはbuilder imageではcurlをいれてるので証明書ca-certificates.crtはあるのですが、実行image には存在してませんでした。

そのため、実行imageの方にCOPYしてあげる必要があった…と(apt installする手もありますが、実行imageが肥大化するので無し)。

FROM debian:bullseye as builder

ARG NODE_VERSION=18.8.0
ARG YARN_VERSION=1.22.19

ENV NODE_ENV development
ENV VOLTA_HOME /root/.volta
ENV PATH /root/.volta/bin:$PATH

RUN apt update && \
    apt install -y curl python-is-python3 pkg-config build-essential apt-transport-https
RUN curl https://get.volta.sh | bash
RUN volta install node@${NODE_VERSION} yarn@${YARN_VERSION}

WORKDIR /app

COPY . .

RUN yarn install && \
    yarn prisma generate && \
    yarn build



FROM debian:bullseye

ENV NODE_ENV production
ENV PATH /root/.volta/bin:$PATH
LABEL fly_launch_runtime="nodejs"

WORKDIR /app

COPY --from=builder /root/.volta /root/.volta
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/.fly ./.fly
COPY --from=builder /app/package.json ./
COPY --from=builder /app/yarn.lock ./
COPY --from=builder /etc/ssl /etc # これが必要!!!!!!

CMD ["sh", ".fly/start.sh"]

あとDATABASE_URLにパラメータを設定したものをsecretsに反映させます。

$ flyctl secrets set DATABASE_URL='mysql://~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/nestjs-sample?sslaccept=strict?sslcert=/etc/ssl/certs/ca-certificates.crt'

これで無事認証も通り flyctl deployPrisma schemaをPlanetScaleへ反映させることができました。

おめでとう。

おわり

私はいつも会社で新規プロジェクトが始まると、新しい技術を取り入れようとし一瞬で作れるリポジトリ作成マンになります。

自分にとっても会社にとっても他のエンジニア様たちにとってもメリットしかないので今後も続けていきてぇっすね。

ぜひぜひ使ってみてね。

これであなたもモダンなインフラを扱えるようになりますよぉ。

次はRenderかRailwayでデプロイする記事書こうかな。