Deploy a Next.js application on AWS using CDK & Fargate

Deploy a Next.js application on AWS using CDK & Fargate

What is Fargate ?

Capture d’écran 2021-08-12 à 14.18.30.png

Why Next.js on AWS ?

The Next.js documentation says :

The easiest way to deploy Next.js to production is to use the Vercel platform from the creators of Next.js

Well, if you are just getting started with your project, i will say go for Vercel.

But in my case, i had all my projects on AWS using CDK, and i don't wanted to have to manage my Next.js app on another platform.

Let's get started

Let's create the main folder of the project

mkdir nextJsOnAws
cd nextJsOnAws

NextJS part

npx create-next-app

and name it the way you want (i named it front)

We are going to create a Dockerfile ( i am using the official one from the Vercel documentation)

# Install dependencies only when needed

FROM node:alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

# Rebuild the source code only when needed

FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline

# Production image, copy all the files and run next

FROM node:alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001


COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

USER nextjs

EXPOSE 3000

CMD ["yarn", "start"]

CDK part

Let's create a new CDK project on a dedicated folder

mkdir cdkPart
cd cdkPart
cdk init --language typescript

Then let's install the package we need

npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs @aws-cdk/aws-ecs-patterns

In the lib folder, a file was created called cdk_part-stack, open it and complete it like this

import * as cdk from '@aws-cdk/core';
import * as ec2 from "@aws-cdk/aws-ec2";
import * as ecs from "@aws-cdk/aws-ecs";
import * as ecs_patterns from "@aws-cdk/aws-ecs-patterns";
import { DockerImageAsset } from "@aws-cdk/aws-ecr-assets";

export class CdkPartStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

 const APP_PORT = 3000
 const pathToDockerFile = "../front"

    const vpc = new ec2.Vpc(this, "MyVpc", {
      maxAzs: 2,
    });

    const taskDefinition = new ecs.FargateTaskDefinition(this, "MyTaskDefinition", {
      memoryLimitMiB: 512,
      cpu: 256,
    });

    const dockerFile = new DockerImageAsset(this, 'DockerFileAsset', {
      directory: pathToDockerFile,
      file: 'Dockerfile',
    });

    // cdk will build it and push it to en ecr repository
    const image = ecs.ContainerImage.fromDockerImageAsset(dockerFile);

    const container = taskDefinition.addContainer("MyContainer", {
      image,
      // store the logs in cloudwatch 
      logging: ecs.LogDriver.awsLogs({ streamPrefix: "myexample-logs" })
    });

    container.addPortMappings({
      containerPort: APP_PORT, 
    });

    const cluster = new ecs.Cluster(this, "MyECSCluster", {
      clusterName: "MyECSCluster",
      containerInsights: true,
      vpc,
    });

    const securityGroup = new ec2.SecurityGroup(this, `My-security-group`, {
      vpc: vpc,
      allowAllOutbound: true,
      description: 'My Security Group'
    });

    securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(APP_PORT));

    const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'MyFargateService', {
      cluster,
      publicLoadBalancer: true,
      cpu: 256,
      desiredCount: 1,
      memoryLimitMiB: 512,
      taskDefinition,
      securityGroups: [securityGroup]
    })

    const scalableTarget = fargateService.service.autoScaleTaskCount({
      minCapacity: 1,
      maxCapacity: 2
    })

    scalableTarget.scaleOnCpuUtilization('cpuScaling', {
      targetUtilizationPercent: 70
    })
  }
}

Now time for deploy

cdk synth

Then after that :

cdk deploy

The deploy can be a little long , so be patient :)

Let's go to the console

After the deploy is completed, go to your AWS Console > ECS (for Elastic Container Service)

Capture d’écran 2021-08-18 à 19.32.59.png

Click on MyECSCluster

Capture d’écran 2021-08-18 à 19.34.07.png

Click on the service

Capture d’écran 2021-08-18 à 19.34.53.png

Select the Task Tab

Capture d’écran 2021-08-18 à 19.35.03.png

Click on the Task

Capture d’écran 2021-08-18 à 19.37.35.png

You can see that your container is ... RUNNING :)

Let's see the app !!!

Go to the console > EC2 > LoadBalancer and copy the DNS name of your load balancer

Capture d’écran 2021-08-18 à 19.38.43.png

Paste it in your browser and ...

Capture d’écran 2021-08-18 à 19.40.10.png

Congrats ! You deployed your Next.js app with Fargate and it's running :)

Cleaning

Don't forget to clean after this tutorial, i don't want you to have a huge bill on AWS!

cdk destroy

I hope you liked this tutorial, i will try my best to post some articles about AWS.

If you want, you can follow me on Twitter :)

Did you find this article valuable?

Support Sonia Manoubi by becoming a sponsor. Any amount is appreciated!