React su Kubernetes
Hai creato la tua fantastica app in React e ora vuoi renderla disponibile sul tuo cluster Kubernetes, giusto?
Con l’aiuto di Kubernetes (e Docker) puoi farlo facilmente.
In questo post vedremo come prendere la tua app React e distribuirla in Kubernetes, rendendola accessibile tramite il tuo cluster. Per farlo, useremo Docker Desktop, che nel 2018 introduce il supporto all’integrazione di Kubernetes e permette di installare localmente un cluster di sviluppo single-node.
A scopo di esempio, andiamo a creare un’app React da zero usando i seguenti comandi:
npx create-react-app my-sample-app
cd my-sample-app
npm install
npm start
>>>
Starting the development server...
Compiled successfully!
You can now view react-kubernetes in the browser.
Local: http://localhost:3000
On Your Network: http://172.29.64.1:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
webpack compiled successfully
Questo farà sì che abbiamo a disposizione un’app React pronta all’uso. Per vederla in azione, è sufficiente aprire il browser all’indirizzo localhost:3000:
App React su localhost
A questo punto, possiamo iniziare a sporcarci le mani: partiamo con un Dockerfile che ci permetta di descrivere tutto ciò che serve per far partire l’applicazione, e quindi dipendenze, porte e comandi:
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Nella prima istruzione specifichiamo come immagine base Node in versione 16, necessaria per avviare l’applicazione React. Nella seconda riga andiamo a creare una cartella di lavoro chiamata app che ospiterà i file che andremo a copiare tramite le istruzioni presenti nella terza e quinta riga.
In particolare, nella terza riga, chiediamo a Docker di copiare il file package.json ed eventualmente package-lock.json, i quali contengono le indicazioni circa le dipendenze dell’applicazione, che poi installiamo tramite il comando npm install specificato nella quarta riga.
Infine, tramite la sesta riga specifichiamo quale sarà la porta su cui verrà esposta l’applicazione, e nell’ultima indichiamo il comando per avviare l’app.
Ora siamo pronti/e per testare l’immagine: lanciamo i comandi seguenti per costruire il container ed eseguirlo, e verifichiamo che l’applicazione sia ancora funzionante e visibile tramite browser, sempre all’indirizzo localhost:3000:
docker build -t my-react-app . # build dell'immagine con il tag "my-react-app"
docker run -p 3000:3000 my-react-app # esecuzione dell'immagine e mapping della porta
App React in esecuzione tramite Docker
Perfetto, tutto è andato liscio come l’olio! Passiamo a Kubernetes.
Dopo aver verificato che questo sia abilitato tramite Docker Desktop (o che il cluster in uso sia attivo), andiamo a definire le risorse Kubernetes che sono necessarie affinché l’applicazione possa essere eseguita e sia raggiungibile in rete.
Per prima cosa, andiamo ad utilizzare un oggetto come il Deployment, che ci permette di definire il contenitore della nostra applicazione e tutte le specifiche del container:
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-app-deployment
spec:
replicas: 2
selector:
matchLabels:
app: react-app
template:
metadata:
labels:
app: react-app
spec:
containers:
- name: react-app-container
image: my-react-app:latest
ports:
- containerPort: 3000
All’interno di questo file YAML indichiamo:
- il numero di repliche dei Pod da eseguire (replicas)
- il nome del container da utilizzare (name)
- il nome dell’immagine (image)
- le porte (ports>containerPort)
Fatto questo, passiamo al Service, ossia l’oggetto che ci permette di creare un livello di astrazione per mettere in comunicazione l’utente finale con l’applicazione.
Esistono diversi tipi di Service: in questo caso, quello più semplice è il LoadBalancer, che ci permette di bilanciare il traffico nel caso si abbiano più repliche attive dello stesso Pod. Andiamo a definire qual è la porta in ascolto del container (targetPort) con quella effettivamente esposta dal Service (port).
Inoltre, è fondamentale ricordarsi di utilizzare dei selector in entrambe le risorse, così da indicare il legame tra le due.
apiVersion: v1
kind: Service
metadata:
name: react-app-service
spec:
selector:
app: react-app
ports:
- protocol: TCP
port: 3000
targetPort: 3000
type: LoadBalancer
Ci siamo, possiamo testare il lavoro fatto finora: lanciamo i seguenti comandi e apriamo il browser!
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Ops, errore inatteso. Non viene visualizzato nulla… Come mai?
Dando un’occhiata ai pod attualmente attivi, notiamo un piccolo dettaglio…
kubectl get pods
>>>
NAME READY STATUS RESTARTS AGE
react-app-deployment-cbcfd484b-7bts5 0/1 ErrImagePull 0 6s
Ma come, non trova l’immagine? Certo, perché la cerca su un registry pubblico (a seconda di quello configurato, potrebbe essere DockerHub). In questo caso, invece l’immagine è in locale: come fare?
Semplice: indichiamo al Deployment di non fare il pull dell’immagine aggiungendo il campo imagePullPolicy:
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-app-deployment
spec:
replicas: 1
selector:
matchLabels:
app: react-app
template:
metadata:
labels:
app: react-app
spec:
containers:
- name: react-app-container
image: my-react-app:latest
imagePullPolicy: Never
ports:
- containerPort: 3000
Eseguiamo nuovamente i comandi kubectl apply e apriamo il browser:
kubectl apply -f .\deployment.yaml
>>>
deployment.apps/react-app-deployment configured
kubectl get pods
>>>
NAME READY STATUS RESTARTS AGE
react-app-deployment-f85c88887-nv4kn 0/1 ContainerCreating 0 1s
App React su Kubernetes
That’s it! L’applicazione React all’interno del cluster Kubernetes è in esecuzione e raggiungibile.