t3 stackアプリをdockerを通して、cloud runにデプロイしてみる
この動画を参考にデプロイまで頑張る。
追記:バージョンの違いもあり、ところどころ自前で修正しました。
起動編
インストールできたので試しに起動してみる。
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に変更する。
- DISCORD_CLIENT_ID: z.string(),
- DISCORD_CLIENT_SECRET: z.string(),
+ DISCORD_CLIENT_ID: z.string().optional(),
+ DISCORD_CLIENT_SECRET: z.string().optional(),
任意にしたことでauth設定でエラーが出たのでここも直す。
- 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に貼り付け。
# 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!!!!
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は先ほどメモったパスワードをいれる)
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db~~~/postgres"
schema.prismaのDB設定を変更する(postgresqlになってたらそのままでOK)
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なはず。)
認証編
discordで認証を試してみる(さっきdiscord使わないって言ったけど使ってみます。)
discord側の設定
メニュー(左上の3本線)からOAuth2を押下。
Redirectsには
http://localhost:3000/api/auth/callback/discord
を設定し、CLIENT ID、CLIENT SECRETをコピー。
discord側の設定は完了。
.envの設定
以下を.envに追加(コピーしたCLIENT IDとCLIENT SECRET)
# Next Auth Discord Provider
DISCORD_CLIENT_ID="xxxxxx"
DISCORD_CLIENT_SECRET="xxxxxx"
これだけで認証自体はできてるはず。試す。
npm run dev
おお〜。出来たね。
Dockerで起動編
公式のドキュメントにもdocker起動の参考ある
基本はこれ通りに設定。
/**
* 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ファイルをルートディレクトリに作成
.env
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
##### 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の環境変数をこちらにも記述を追加。(" "はいらないよ)
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!!!!!
Cloud Runにデプロイ編
いよいよCloud Runにデプロイへ。
(Google Cloudにログインは各自しておいてね。)
ちなみに参考動画の通りに進めるとなぜかエラーになりました。(cloud runのGUIからデプロイする方法だとダメでした。たぶんちゃんと設定とか調べたらいけるんだろうけど。)
で、ググると下記記事に出会った。
いや〜先人の知恵ありがたや〜
ってことで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 のシークレット アクセサーを追加。
これで再度デプロイ。
ゆけ!!!!
いぇえええええええす!!!!!
とりあえず、デプロイまでいけましたので、クローズします。