Closed7

t3 stackアプリをdockerを通して、cloud runにデプロイしてみる

pandanosepandanose

起動編

インストールできたので試しに起動してみる。

npm run dev

すると....

> t3-google-cloud@0.1.0 dev
> next dev

❌ Invalid environment variables: {
  DISCORD_CLIENT_ID: [ 'Required' ],
  DISCORD_CLIENT_SECRET: [ 'Required' ]
}
file:///Users/xxx/projects/tutorial/t3-google-cloud/node_modules/@t3-oss/env-core/dist/index.js:29
        throw new Error("Invalid environment variables");
              ^

Error: Invalid environment variables
    at onValidationError (file:///Users/xxx/projects/tutorial/t3-google-cloud/node_modules/@t3-oss/env-core/dist/index.js:29:15)
    at createEnv (file:///Users/xxx/projects/tutorial/t3-google-cloud/node_modules/@t3-oss/env-core/dist/index.js:35:16)
    at createEnv (file:///Users/xxx/projects/tutorial/t3-google-cloud/node_modules/@t3-oss/env-nextjs/dist/index.js:12:12)
    at file:///Users/xxx/projects/tutorial/t3-google-cloud/src/env.js:4:20
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

Node.js v18.17.0

環境変数がうまく設定できていないと怒られた。(ディスコの設定がなんで必須なんだよ....)
ディスコの設定をちゃんと入れてもいいが、今回ディスコで認証したいわけでもないので、仕方なく手動でoptionalに変更する。

/src/env.js
-    DISCORD_CLIENT_ID: z.string(),
-    DISCORD_CLIENT_SECRET: z.string(),
+    DISCORD_CLIENT_ID: z.string().optional(),
+    DISCORD_CLIENT_SECRET: z.string().optional(),

任意にしたことでauth設定でエラーが出たのでここも直す。

/src/server/auth.ts
-      clientId: env.DISCORD_CLIENT_ID,
-      clientSecret: env.DISCORD_CLIENT_SECRET,
+      clientId: env.DISCORD_CLIENT_ID ?? "",
+      clientSecret: env.DISCORD_CLIENT_SECRET ?? "",

あと、たぶんエラーに出てないから多分大丈夫なんだろうけど、一応NEXTAUTH_SECRETもkeyを入れておく。
下記コマンドで適当にkeyを作成して、

openssl rand -base64 32
3dQfeajlfejf/nm0jkrRlGGKflekamdy0Vw=

これをNEXTAUTH_SECRETに貼り付け。

.env
# NEXTAUTH_SECRET=""
NEXTAUTH_SECRET="3dQfeajlfejf/nm0jkrRlGGKflekamdy0Vw="

これでもう一度npm run devで起動

> t3-google-cloud@0.1.0 dev
> next dev

   ▲ Next.js 14.1.0
   - Local:        http://localhost:3000
   - Environments: .env

 ✓ Ready in 10.7s
 ○ Compiling / ...
 ✓ Compiled / in 4.3s (362 modules)
 ✓ Compiled /api/trpc/[trpc] in 224ms (112 modules)
 ✓ Compiled (119 modules)

yes!!!!

pandanosepandanose

supabase編

supabaseでDB作成

supabaseでstart your project

Name、password、refionを設定し、次へ

  • passwordはGenerate a password押せば勝手に作ってくれるよ。
  • このpasswordは後で使うのでちゃんとメモっとく。

その後、planを選ばさせれるけど、テストで使用するだけならfreeで問題なし。
作成したら「project settings」の「Database」に行き、Connection stringからURIをコピー。

.envにDBを設定

.envでデータベースを設定(YOUR-PASSWORDは先ほどメモったパスワードをいれる)

.env
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db~~~/postgres"

schema.prismaのDB設定を変更する(postgresqlになってたらそのままでOK)

schema.prisma
generator client {
    provider      = "prisma-client-js"
+    binaryTargets = ["native", "linux-musl-openssl-3.0.x", "debian-openssl-3.0.x"]
}
datasource db {
-    provider = "sqlite"
+    provider = "postgresql" 
    // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below
    // Further reading:
    // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema
    // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string
    url      = env("DATABASE_URL")
}

prismaをgenerate

npx prisma db push
npx prisma generate

これでDBはOK。試しにstudioでカラムができているか確認

npx prisma studio

http://localhost:5555 でstudio起動できればOK。
(画像は試しにデータ入れているけど本来は全部0なはず。)

pandanosepandanose

認証編

discordで認証を試してみる(さっきdiscord使わないって言ったけど使ってみます。)

discord側の設定

https://discord.com/developers/applications
ログインしたら、「new application」を押下し適当にnameを入力、create。

メニュー(左上の3本線)からOAuth2を押下。

Redirectsには

http://localhost:3000/api/auth/callback/discord

を設定し、CLIENT ID、CLIENT SECRETをコピー。

discord側の設定は完了。

.envの設定

以下を.envに追加(コピーしたCLIENT IDとCLIENT SECRET)

.env
# Next Auth Discord Provider
DISCORD_CLIENT_ID="xxxxxx"
DISCORD_CLIENT_SECRET="xxxxxx"

これだけで認証自体はできてるはず。試す。

npm run dev





おお〜。出来たね。

pandanosepandanose

Dockerで起動編

公式のドキュメントにもdocker起動の参考ある
基本はこれ通りに設定。

next.config.js
/**
 * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
 * for Docker builds.
 */
await import("./src/env.js");

/** @type {import("next").NextConfig} */
const config = {
  reactStrictMode: true,
+  output: "standalone",

  /**
   * If you are using `appDir` then you must comment the below `i18n` config out.
   *
   * @see https://github.com/vercel/next.js/issues/41980
   */
  i18n: {
    locales: ["en"],
    defaultLocale: "en",
  },
};

export default config;

.dockerignoreファイルをルートディレクトリに作成

.dockerignore
.env
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
dockerfile
##### DEPENDENCIES

FROM --platform=linux/amd64 node:20-alpine AS deps
- RUN apk add --no-cache libc6-compat openssl1.1-compat
+ RUN apk add --no-cache libc6-compat --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing openssl1.1-compat
WORKDIR /app

# Install Prisma Client - remove if not using Prisma

COPY prisma ./

# Install dependencies based on the preferred package manager

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./

RUN \
    if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
    elif [ -f package-lock.json ]; then npm ci; \
    elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
    else echo "Lockfile not found." && exit 1; \
    fi

##### BUILDER

FROM --platform=linux/amd64 node:20-alpine AS builder
ARG DATABASE_URL
ARG NEXT_PUBLIC_CLIENTVAR
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# ENV NEXT_TELEMETRY_DISABLED 1

RUN \
    if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \
    elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \
    elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \
    else echo "Lockfile not found." && exit 1; \
    fi

##### RUNNER

FROM --platform=linux/amd64 gcr.io/distroless/nodejs20-debian12 AS runner
WORKDIR /app

ENV NODE_ENV production

# ENV NEXT_TELEMETRY_DISABLED 1

COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
ENV PORT 3000

CMD ["server.js"]

dockerfileも公式からほぼコピペだけど、そのままだとopenssl1.1-compatのapt addでつまづくので、上記修正。どうやらalpinev3.19にバージョンアップされて3.19だとopenssl1.1-compatがないらしい。
参考記事によるとtest用のパッケージだからすぐに削除される可能性があるっぽいけど、とりあえずその場しのぎ。

 => ERROR [app deps 2/6] RUN apk add --no-cache libc6-compat openssl1.1-compat                                                      3.6s
------
 > [app deps 2/6] RUN apk add --no-cache libc6-compat openssl1.1-compat:
0.191 fetch https://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/APKINDEX.tar.gz
0.621 fetch https://dl-cdn.alpinelinux.org/alpine/v3.19/community/x86_64/APKINDEX.tar.gz
3.418 ERROR: unable to select packages:
3.418   openssl1.1-compat (no such package):
3.418     required by: world[openssl1.1-compat]
------
failed to solve: process "/bin/sh -c apk add --no-cache libc6-compat openssl1.1-compat" did not complete successfully: exit code: 1

あとはdocker-compose.yml。.envの環境変数をこちらにも記述を追加。(" "はいらないよ)

docker-compose.yml
version: "3.9"
services:
  app:
    platform: "linux/amd64"
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_CLIENTVAR: "clientvar"
    working_dir: /app
    ports:
      - "3000:3000"
    image: t3-app
    environment:
-      - DATABASE_URL=database_url_goes_here
+      - DATABASE_URL=xxxx
+      - NEXTAUTH_SECRET=xxxx
+      - NEXTAUTH_URL=xxxx
+      - DISCORD_CLIENT_ID=xxxx
+      - DISCORD_CLIENT_SECRET=xxxx

これで設定完了。

docker compose upで起動


yes!!!!!

pandanosepandanose

Cloud Runにデプロイ編

いよいよCloud Runにデプロイへ。
Google Cloudにログインは各自しておいてね。)

ちなみに参考動画の通りに進めるとなぜかエラーになりました。(cloud runのGUIからデプロイする方法だとダメでした。たぶんちゃんと設定とか調べたらいけるんだろうけど。)

で、ググると下記記事に出会った。
https://zenn.dev/gtca/scraps/b44612b44b97ff
ふむふむ、Google Cloud SDKでデプロイするとすんなりいけるとな。
いや〜先人の知恵ありがたや〜

Google Cloud SDK参考

ってことでGoogle Cloud SDK公式にある通りCLIをインストールする。(以下、インストールした前提で記述。)

imageをデプロイ

Cloud Buildを使用してコンテナイメージをデプロイ。
(PROJECT-IDはコンソールのヘッダー > プロジェクトを押下したところから見れるよ。)

gcloud builds submit --tag gcr.io/PROJECT-ID/{適当にコンテナ名} --project PROJECT-ID

Cloud Runにデプロイ

無事終われば、デプロイしたコンテナイメージを使って、CloudRunにデプロイ。

gcloud run deploy --image gcr.io/PROJECT-ID/{適当にコンテナ名} --project PROJECT-ID --platform managed --allow-unauthenticated
✓ Deploying... Done.
  ✓ Creating Revision...
  ✓ Routing traffic...
  ✓ Setting IAM Policy...
Done.
Service [t3-cloudrun-deploy] revision [t3-cloudrun-deploy-00006-57b] has been deployed and is serving 100 percent of traffic.
Service URL: https://t3-cloudrun-deploy-vtxfzocuva-an.a.run.app/

い、いけたっぽいな。
末尾のService URLにアクセスしてみる。

yesssssss
ただこの状態だと環境変数が入っていないのでauth認証がエラーになってしまう。

Cloud Runに環境変数を設定

コンソールに環境変数を設定



ついでに最大スケール数も100→4に変更。
デプロイ。

権限エラーになった場合

環境変数を設定してデプロイすると何やらエラーが。

Revision 't3-cloudrun-deploy-00002-m6f' is not ready and cannot serve traffic. spec.template.spec.containers[0].env[0].value_from.secret_key_ref.name: Permission denied on secret: projects/1234567890/secrets/t3-cloudrun-deploy-database-url/versions/latest for Revision service account 1234567890-compute@developer.gserviceaccount.com. The service account used must be granted the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level. spec.template.spec.containers[0].env[1].value_from.secret_key_ref.name: Permission denied on secret: projects/1234567890/secrets/t3-cloudrun-deploy-nextauth-secret/versions/latest for Revision service account 1234567890-compute@developer.gserviceaccount.com. The service account used must be granted the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level. spec.template.spec.containers[0].env[2].value_from.secret_key_ref.name: Permission denied on secret: projects/1234567890/secrets/t3-cloudrun-deploy-discord-client-id/versions/latest for Revision service account 1234567890-compute@developer.gserviceaccount.com. The service account used must be granted the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level. spec.template.spec.containers[0].env[3].value_from.secret_key_ref.name: Permission denied on secret: projects/1234567890/secrets/t3-cloudrun-deploy-discord-client-secret/versions/latest for Revision service account 1234567890-compute@developer.gserviceaccount.com. The service account used must be granted the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level.

あれこれ怒られたけどようはdeveloperアカウントに権限がないって。(1234567890-compute@developer.gserviceaccount.comのこと。※一応数字は変えてます。)
「IAM」に移動して、対象のアカウントのロールにSecret Manager のシークレット アクセサーを追加。

これで再度デプロイ。



ゆけ!!!!

いぇえええええええす!!!!!
とりあえず、デプロイまでいけましたので、クローズします。

このスクラップは2ヶ月前にクローズされました