Deploy della tua applicazione Next.js su Kubernetes
Portare la tua applicazione Next.js su Kubernetes? Non è mai stato così facile e a costo (quasi) zero, grazie al servizio Managed Kubernetes di Aruba.
Per questo articolo, useremo un’applicazione di esempio con una semplice TODO list e un cluster Aruba Managed Kubernetes: il codice dell’applicazione è disponibile qui, mentre per creare il cluster, puoi seguire questo articolo.
Tip: se invece volessi partire con un’applicazione Next.js da zero, basta eseguire il seguente comando:
npx create-next-app@latest
>>>
Need to install the following packages:
create-next-app@14.2.5
Ok to proceed? (y) y
√ What is your project named? ... my-app
√ Would you like to use TypeScript? ... No / Yes
√ Would you like to use ESLint? ... No / Yes
√ Would you like to use Tailwind CSS? ... No / Yes
√ Would you like to use `src/` directory? ... No / Yes
√ Would you like to use App Router? (recommended) ... No / Yes
√ Would you like to customize the default import alias (@/*)? ... No / Yes
Creating a new Next.js app in C:\Users\serena.sensini\WebstormProjects\next-demo\my-app.
Using npm.
Initializing project with template: app-tw
Installing dependencies:
- react
- react-dom
- next
Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
...
Una volta che avremo pronta l’applicazione, sarà necessario occuparsi del Dockerfile.
Quello riportato di seguito lo andiamo a definire seguendo queste istruzioni: per eseguire il deploy tramite un container, possiamo partire da un’immagine Node.js in versione recente come la 20 (gennaio 2025) e minimale basata su Alpine, un sistema operativo molto leggero e orientato alla sicurezza (vedi riga 1).
Sempre all’interno del Dockerfile, creiamo poi la cartella di lavoro app
, all’interno della quale andremo a copiare il codice sorgente dell’applicazione Next.js (riga 2).
Si passa a questo punto alla copia del file package.json
(riga 3) che ci permetterà di installare tutte le dipendenze del progetto nella cartella di lavoro (riga 4), per poi copiare il resto dei file (riga 5).
Può essere utile sottolineare che, come spesso succede, la cartella node_modules
di un progetto non deve essere copiata dall’istruzione COPY, ma piuttosto generata grazie all’installazione dei pacchetti tramite npm. Per questo, avere un file .containerignore all’interno del progetto con specificata la cartella può essere utile per evitare che la copia richieda troppo tempo e vada a creare conflitti nelle dipendenze.
Una volta copiati i file, è possibile eseguire la build dell’applicazione Next.js (riga 5), specificare quale porta utilizzare per esporre e raggiungere l’applicazione (riga 6) e infine fornire il comando per avviare l’applicazione tramite npm
(riga 7).
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY ../../draft .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
Con il Dockerfile pronto, potremo costruire il nostro container e testarlo: per farlo, eseguiamo i seguenti comandi sfruttando Docker o Podman.
docker build -t nextjs-app .
docker run -p 3000:3000 nextjs-app
Se riusciamo a raggiungere correttamente http://localhost:3000, vuol dire che l’immagine è stata creata correttamente e il container sta funzionando a dovere.
E ora? Ora possiamo passare a Kubernetes: dal momento che la nostra applicazione è stata creata per essere stateless, ci sarà sufficiente creare un Deployment che possa gestirla, e un Service che ci permette di configurarne la raggiungibilità.
Partiamo dalla definizione del primo: il Deployment.
L’unica cosa che ci servirà, prima di scrivere lo YAML, è un registry che possa ospitare l’applicazione, da cui Kubernetes dovrà eseguire il pull. Questo può essere quay.io, DockerHub o qualsiasi altro registry!
Un esempio di come eseguire il push di un’immagine su un registry pubblico è riportato in questo articolo.
Per la definizione del Deployment, usiamo il seguente file YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs-app
labels:
app: nextjs
spec:
replicas: 1
selector:
matchLabels:
app: nextjs
template:
metadata:
labels:
app: nextjs
spec:
containers:
- name: nextjs-container
image: ssensini/nextjs-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
I campi che devono essere modificati sono quelli relativi all’immagine ed eventualmente la porta, se la nostra applicazione venisse esposta su una porta differente dalla 3000.
Questo Deployment andrà a creare una sola replica dell’applicazione Next.js che sarà esposta tramite un Service definito come segue:
apiVersion: v1
kind: Service
metadata:
name: todo-app
spec:
type: LoadBalancer
selector:
app: todo-app
ports:
- port: 80
targetPort: 3000
In questo caso, il Service è tipo LoadBalancer
con un reindirizzamento della porta 3000 del container verso la porta 80 esposta dal Pod: in questo modo, l’utente potrà contattare l’applicazione sfruttando le risorse di rete messe a disposizione dal cluster, che vedremo a breve.
Infatti, un Load Balancer distribuisce il traffico utente su più istanze di un’applicazione e viene normalmente usato nei servizi che offrono cluster gestiti per ridurre il rischio che le tue applicazioni riscontrino problemi di prestazioni e consentire alle aziende di gestire meglio le proprie risorse cloud e di aumentare la disponibilità delle proprie applicazioni.
A questo punto, non ci resta che collegarci al cluster Aruba: per farlo, sarà sufficiente andare sulla dashboard di Aruba Cloud che riporta i cluster attivi, e cliccare sui 3 punti a lato per scaricare il file di configurazione che contiene le info relative alla connessione verso il cluster.
Se ti sei già collegato/a ad un cluster Kubernetes da locale, avrai un file .kube/config
nel tuo sistema che contiene alcune informazioni sui cluster che hai utilizzato; in caso contrario, puoi installare kubectl
e creare questo file con il nome config
sotto il path C:\Users\UTENTE.kube.
Se vuoi sapere come unire i due file di configurazione per Kubernetes, trovi le indicazioni fornite in questo articolo.
Quando sarà riuscito il collegamento al cluster, sarà sufficiente eseguire i seguenti comandi per creare le risorse definite in precedenza all’interno di uno dei namespace del cluster e attenderne il deploy:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Ci siamo, ultimo step: il Load Balancer funge da punto di smistamento del traffico, ma per rendere l’applicazione accessibile dall’esterno, abbiamo bisogno di configurare un Elastic IP.
Per farlo, torniamo sul dettaglio del cluster nella dashboard di Aruba Cloud e selezioniamo la sezione networking:
A questo punto, procediamo con l’acquisto: il costo si aggira sui 3 euro al mese circa (gennaio 2025) e la configurazione è molto semplice. Assegnamo un nome e seguiamo le istruzioni della pagina:
L’indirizzo IP verrà creato in pochissimo tempo e sarà disponibile nella pagina dedicata alla configurazione di rete:
Se la creazione del Service visto in precedenza è andata a buon fine, probabilmente vedremo che l’indirizzo IP risulterà in stato “In uso”: vuol dire che il cluster avrà associato automaticamente l’Elastic IP al Load Balancer e che potremo usarlo per raggiungere l’applicazione. In caso contrario, è necessario prima procedere alla configurazione dell’IP e poi alla creazione del Service.
Ci siamo: digitiamo nel browser l’indirizzo IP, et voilà!
Se avessi dubbi o problemi durante l’esecuzione di qualche step, non esitare a scriverci!