Next Js with Minikube (Kubernetes)
In this Article, I will discuss about how the NextJS can be deployed into Minikube(Kubernetes) environment. I also discuss about the how to build the NextJS Application, How we can use multi stage layer build feature for Docker, How can we deploy the site with Ingress Controller. So later reader can use it as reference to deploy into any cloud service (Aws, Azure, gcloud) kubernetes environment.
Prerequisite
- Sample Next JS Application
- Docker Install into your PC (You can follow https://docs.docker.com/engine/install/)
- Docker Hub Account (You can follow https://hub.docker.com/signup)
- Minikube installed into your PC (You can follow https://minikube.sigs.k8s.io/docs/start/)
- Install Yarn (You can follow https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)
Sample Application creation
> npx create-next-app@latest nextjs-minikube-integration
> cd nextjs-minikube-integration
> yarn dev
Build Docker Image
I use standalone
NextJS build mode and multi staging
build for docker to build our docker image.
First enable the standalone
build mode.
Open next.config.js
file and add attribute (output:'standalone')
to file
Run build command to create standalone package
> yarn build
Create Docker file into root of the application directory <app_root>/(Dockerfile)
# Start (Block 1) Dependency Solving Stage
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else echo "Lock file not found." && exit 1; \
fi
# End (Block 1)
# Start (Block 2) Build the Application Stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
# End (Block 2)
# Start (Block 3) Create the Application Runner
FROM node:18-alpine AS runner
WORKDIR /app
# Declare the NODE_ENV
ENV NODE_ENV production
# Add the Group and User to Container
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy the next js application form builder image
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
# Expose the Application Port
EXPOSE 3000
# Add the Environment Port Variable
ENV PORT 3000
# Run the Application with "server.js" using node, which is
# Auto Generated by Standalone mode
CMD ["node", "server.js"]
# End (Block 3)
The following code for docker, I am using multi stage build to create the image and run the next js application with standalone mode using custom server in container.
Block 1, This layer using for solve all the dependency of the application, also check lock file is available into in application folder, because we are using the same application dependency, which we use at development time.
Block 2, This layer is using for building the next js application, it tooks the code source from block 1 (dependency layer), then copy the application path and build the next js application.
Block 3, This layer is using for run the application into container, setup the environment variable, create the group and user to run the application, copy the build source from Build layer (Block 2), then expose the port and run the application with custom server for Next JS application.
And then Run Docker Build command to create the docker image
> docker build -t tariqulislam/nextjs-kube-integration .
Check the Image is created successfully by following command
> docker images
Then run the following docker command to check the application is deployed into container successfully
# docker run -d -p <hostport>:<containerport> image name
> docker run -d -p 3002:3000 tariqulislam/nextjs-kube-integration
Check on Web Browser application application in running on specific port
Publish and Push the image to Docker Hub
Log in to Docker Hub and Push the image to it
# <username> please check on your docker hub account
> docker login --username=<username>
# docker tag <IMAGE ID> <REPOSITORY NAME>
> docker tag 2abc11c4f67e tariqulislam/nextjs-kube-integration:latest
# Push the image
> docker push tariqulislam/nextjs-kube-integration:latest
Tagging the image is important before push the image to docker registry.
After running the push command docker is successfully pushed into database.
For Minikube Deployment, I will save the repository location info `docker.io/tariqulislam/nextjs-kube-integration`
Configure the minikube
Start Minikube in docker:
minikube start --driver=docker
To make docker the default driver:
minikube config set driver docker
Add Ingress addons
minikube addons enable ingress
kubectl get pods -n ingress-nginx
> minikube addons enable metrics-server
Minikube Dashboard Enable
> minikube dashboard
> minikube dashboard --url
Deploy the application to Minikube(Kubernetes)
apiVersion: apps/v1 #line 1 (required field)
kind: Deployment #line 2 (required field)
metadata: #line 3 (required field)
name: nextjs-kube-app
labels:
app: nextjsweb
spec: #line7 (required field)
replicas: 3 #line8 How many Replica we want to create in cluster
strategy: #line9
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1 #line13
selector:
matchLabels:
app: nextjsweb #line16 matching with labels name of from metadata
template: #line17 Templeting the Deployment and declare the spec
metadata:
labels:
app: nextjsweb #line20 matching with label name of metadata
spec: #line21
containers:
- name: nextjsclient #line23
image: tariqulislam/nextjs-kube-integration:latest #line24
imagePullPolicy: IfNotPresent #line25
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 3000 #line31
restartPolicy: Always #line32
Important parts of Deployment file
apiVersion
, metadata
, kind
, spec
, are required filed for kubernetes deployment
apiVersion: delare which api version of kubernetes using for create the object
kind: which kind of kubernetes object we want to create
metadata: its helps to uniquely identify the object name in cluster after deployment
spec: using for control the state of object and provide desire configuration during deployment
line 9 to line 13, contains the deployment strategy of the application. line 17 to 32 is the template section where we will create the spec of container configuration and where to get the images to deploy the site.
#line23: provide the unique container name to kubernetes cluster
#line24: application image location, where to get the image and deploy to cluster
#line25: image pulling policy,
IfNotPresent: if image available in locally is will not pull from docker registry.
Never: it will not pull the image from remote registry, only using locally build image
.
Always: it will pull the image always from remote repository
#line31, container port will be same as application web server running port for next js
Apply the deployment
> kubectl apply -f deployment.yaml
# Check the status of the pod
> kubectl get pod
Expose the service
apiVersion: v1
kind: Service #line2
metadata:
name: nextjs-kube-service #line4
spec:
selector:
app: nextjsweb #line7
ports:
- port: 3000 #line8
targetPort: 3000 #line9
type: NodePort #line10
Important Points of Service file
line2
Using the Service Kind to expose Service for deployed
line4
name of the service, which is be later identify the other services and other kind
line7,
app name should be same as the label of deployment metadata label name, service will be discover the deployment by this name.
line8 port
which port the service will be exposed to cluster
line9 targetPort,
this will be application deployment port mapping, which port the application is deployed by deployment.yaml
file.
line10, type
, service type
clusterIP
: it is default service type. its provide the load balanced IP address. one and more pods with same label selection can froward the traffice to specific IP address.
NodePort
: it expose the port for each node. kube-proxy can use 30000- 42767 range of ports to expose the service.
LoadBalancer
: create the service like ClusterIP and Open the Port to every node like NodePort.
So, For primary purpose of minikube, i just use the NodePort
to expose the service for minikube.
Apply the service
> kubectl apply -f service.yaml
# check the Service is running
> kubectl get svc
# Get Service url from minikube by following command
> minikube service --urlo nextjs-kube-service
To get better routing access, functionality, I use the ingress to serve our application
Ingress for handling the application routing
First add the some fake domain into /etc/hosts
file
*.next-kube.com
apiVersion: networking.k8s.io/v1 # Api Version
kind: Ingress #Service type: ingress
metadata: #line 3
name: ingress-nextjs-service
labels:
name: ingress-nextjs-service #line 6
annotations: #line7
nginx.ingress.kubernetes.io/rewrite-target: /$1 #line8
spec:
ingressClassName: nginx #line10
rules:
- host: "*.next-kube.com" #line12
http:
paths:
- path: /?(.*) #line15
pathType: Prefix #line16
backend: #line17
service:
name: nextjs-kube-service #line19
port:
number: 3000 #line21
- Kubernetes ingress Nginx is used as a load balancer.
- It can make some smarter routing decision based on the rules provided
- Run the command manually on the Kubernetes master for deployments
line3:
meta data which contains the service name and label to kuberntens can identity the service
line7 and line8, add the annotation so it will load with the nginx rules when server started, i add the extra configuration to rewrite the serving path of the next js application
line12, add the host name where to point out the service with virtual hosting
line15 and line16, how the http request are and page of the next js application will be serve
line 17 to 21, which service it will be mapped with, of this application it connects with
nextjs-kube-service with 3000 port
Apply the ingress
> kubectl apply -f ingress.yaml
# Get Ingress deployment service
> kubectl get ingress
Because we are using Minikube in docker so we have to tunnel it with host by following below command
> minikube tunnel
Try again with domain i added before, so we can see the output on this browser
All the codebase you can find into
And the Docker Hub Image you can find into
https://hub.docker.com/repository/docker/tariqulislam/nextjs-kube-integration/general