DEV Community

Cover image for Serverless Containers vs Serverless Next.js SSR GCP Cloud Run
hexfloor
hexfloor

Posted on

Serverless Containers vs Serverless Next.js SSR GCP Cloud Run

Step 0 : check on the summary

GCP Cloud Run

I got inspired by the guide of Geshan Manandhar, there are less examples on how to make it work the present guide might be one of the best in the universe on the subject :)

First thing you need to do is to create a new project :

Image description

Then go to the source cloud
and create a repository :

Now let's just to the cloud ide as it's awesome and it's already set up, otherwise you need to set up gcloud CLI locally.

On the repository page I'm just checking on how to clone the repo with gcloud :
Image description

And doing it in the cloud shell available within the cloud ide :

git config --global user.email "youremail@gmail.com"
git config --global user.name "Your Name"
git config --global init.defaultBranch main  

gcloud source repos clone nextgcp --project=nextgcp-356220
Cloning into '/home/hexfloor/nextgcp'...
warning: You appear to have cloned an empty repository.
Project [nextgcp-356220] repository [nextgcp] was cloned to [/home/hexfloor/nextgcp].

yarn create next-app
✔ What is your project named? … nextgcp

Success! Created nextgcp at /home/hexfloor/nextgcp

cd nextgcp/
git add -A
git commit -m "init"
git push --set-upstream origin main
Enter fullscreen mode Exit fullscreen mode

After this you may open folder in the cloud ide and you should have this :
Image description

Adding the Dockerfile with multi stage build. The is a docker hub download rate limit, thus we may need to use gcp mirror, I assume it's configured automatically in the GCP. If it's not broken - don't fix it :)

# Install dependencies only when needed
FROM node:alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

# If using npm with a `package-lock.json` comment out above and use below instead
# COPY package.json package-lock.json ./ 
# RUN npm ci

# Rebuild the source code only when needed
FROM node:alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN yarn build

# Production image, copy all the files and run next
FROM node:alpine AS runner
WORKDIR /app

ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# You only need to copy next.config.js if you are NOT using the default configuration
# COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Automatically leverage output traces to reduce image size 
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node", "server.js"]

Enter fullscreen mode Exit fullscreen mode

Adding .dockerignore

Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
Enter fullscreen mode Exit fullscreen mode

commit ;)

Changing the next.config.js to add standalone output

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  output: 'standalone'
}

module.exports = nextConfig
Enter fullscreen mode Exit fullscreen mode

commit ;)

Alright, let's set up continuous deployment following the guide Deploying to Cloud Run using Cloud Build

Enabling the API :
Image description

Service account permissions :
Image description

Adding new file cloudbuild.yaml to the project, filling the template for the continuous deployment :

steps:
  # Build the container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/nextgcp:$COMMIT_SHA', '.']
  # Push the container image to Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/nextgcp:$COMMIT_SHA']
  # Deploy container image to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args:
    - 'run'
    - 'deploy'
    - 'nextgcp'
    - '--image'
    - 'gcr.io/$PROJECT_ID/nextgcp:$COMMIT_SHA'
    - '--region'
    - 'europe-west6'
images:
  - 'gcr.io/$PROJECT_ID/nextgcp:$COMMIT_SHA
Enter fullscreen mode Exit fullscreen mode

Creating a trigger :

Image description

Image description

commit previously added cloudbuild.yaml and push to check the trigger

Sadly the build has failed :
Image description

I've checked on the internet, it's due to the region setting in the trigger, let's change it to global :
Image description

All good now :
Image description
Image description

Let's add domain :
Image description

It seems we can't do it simple everywhere, see the mapping domains
Changing the region to europe-west1 in the cloudbuild.yaml, committing, building :

Image description

Now we can add a simple mapping :
Image description
Image description
Image description

Remember to set the www records as well, sleep 28800 :
Image description

Checking the app url :
Image description

Checking the troubleshooting

Setting permissions :
Image description
Image description

Now it's better :
Image description

Image description

Success !

The second time it's easy-peasy, the first time it's a bit of a challenge to find a right doc. Now you have it :)

Top comments (0)