Knative, en douceur, sur K3S

Je commence aujourd'hui une nouvelle série d'articles parallèle à celle sur Kubernetes, dédiée à Knative que j'ai découvert il y a peu grâce à Sébastien Blanc (opens new window) (RedHat) qui a fait le mois dernier plusieurs présentations (opens new window) fort intéressantes.

# 🇫🇷 Knative, l'outil qui rend Kubernetes sympathique

Knative en une phrase: c'est une abstraction pensée pour rendre Kubernetes plus "développeur friendly".

Autrement dit, cet outil va vous permettre de simplifier votre workflow de développement ("je me fous de savoir comment ça marche, je veux juste déployer mes applications").

Knative est composé de deux éléments principaux:

  • Knative Serving pour le déploiements des applications (ce qui nous intéresse aujourd'hui)
  • Knative Eventing pour la gestion des événements (nous y reviendrons dans de futurs articles)

Dans les 1ers articles de cette série, je traiterais de Knative Serving.

# Installation

Comme pour les articles précédents, pour comprendre comment cela fonctionne, j'aime bien tout installer sur ma machine (👋 petit rappel important, tout est écrit avec une "vision développeur"), donc une fois de plus nous allons faire tourner tout ça en local et sur K3S en utilisant Multipass.

La documentation (opens new window) de Knative est plutôt bien faite, donc je me suis quasiment contenté de la suivre et de l'appliquer dans un contexte Multipass.

# Création du cluster K3S

Knative a sa propre couche de gestion de réseau, donc nous n'allons pas installer Traefik avec K3S

  • Créez un dossier 2k-cluster (par exemple)
  • Créez un dossier config dans 2k-cluster (c'est ici que nous stockerons la configuration k3s.yaml de notre cluster)
  • Ensuite dans le répertoire 2k-cluster, tapez les commandes suivantes:
vm_name="cluster-2k-vm"
vm_cpus=3
vm_mem="8G"
vm_disk="10GB"
knative_version="0.14.0"

# mount the config directory
multipass mount config ${vm_name}:config
# install k3s
multipass --verbose exec ${vm_name} -- sudo -- bash <<EOF
curl -sfL https://get.k3s.io | sh -s - --disable traefik
EOF

# get the IP address of the Multipass VM
IP=$(multipass info ${vm_name} | grep IPv4 | awk '{print $2}')

# get the cluster configuration file
multipass exec ${vm_name} sudo cat /etc/rancher/k3s/k3s.yaml > config/k3s.yaml
sed -i '' "s/127.0.0.1/$IP/" config/k3s.yaml

✋ je suis sous OSX, si vous êtes sous Linux, remplacez sed -i '' "s/127.0.0.1/$IP/" config/k3s.yaml par sed -i "s/127.0.0.1/$IP/" config/k3s.yaml. Ou alors utilisez les coreutils (opens new window) pour OSX.

Avant de continuer, soyez bien sûr que tous les pods système soient bien "en marche" avec la commande:

export KUBECONFIG=$PWD/config/k3s.yaml
kubectl get pods --namespace kube-system

Vous devriez avoir quelque chose comme ceci:

NAME                                      READY   STATUS    RESTARTS   AGE
local-path-provisioner-58fb86bdfd-zhc2l   1/1     Running   0          10m
metrics-server-6d684c7b5-4dw68            1/1     Running   0          10m
coredns-6c6bb68b64-2d6zk                  1/1     Running   0          10m

Donc nous avons maintenant un cluster sur lequel nous pouvons déployer Knative.

# Déployer Knative Serving

👋 Knative Serving a besoin d'une Ingress Gateway pour router les requêtes aux services. Normallement c'est Istio qui est utilisé, mais d'autres sont proposées et possible. Nous allons utiliser Kourier (opens new window) qui est plus léger et donc qui se comporte plutôt bien sur K3S.

multipass --verbose exec ${vm_name} -- sudo -- bash <<EOF
# Install Knative Serving
kubectl apply --filename "https://github.com/knative/serving/releases/download/v${knative_version}/serving-crds.yaml"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v${knative_version}/serving-core.yaml"

# Install and configure Kourier
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v${knative_version}/third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'
EOF

Vérifiez que tous les pods sont bien en "état de marche" (attendez bien qu'ils soient tous avec un statut Running):

kubectl get pods --namespace knative-serving
kubectl get pods --namespace kourier-system

Ensuite, vérifions que Kourier a bien récupéré une IP externe pour le LoadBalancer:

kubectl --namespace kourier-system get service kourier

Si tout va bien, vous aurez ceci:

NAME      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
kourier   LoadBalancer   10.43.126.243   192.168.64.70   80:32717/TCP,443:32167/TCP   62m

l'IP externe correspond à l'IP de votre cluster

Il est temps d'appliquer un dernier petit réglage. Pour gérer la partie DNS, nous allons utiliser la version "facile": le service xip.io (opens new window):

multipass --verbose exec ${vm_name} -- sudo -- bash <<EOF
# Configure xip.io DNS
kubectl apply --filename "https://github.com/knative/serving/releases/download/v${knative_version}/serving-default-domain.yaml"
EOF

Un dernier test:

kubectl get pods --namespace knative-serving

Vous devriez avoir le pod default-domain avec un statut Completed:

NAME                          READY   STATUS      RESTARTS   AGE
webhook-577576647-z5254       1/1     Running     0          87m
activator-65fc4d666-6c4nf     1/1     Running     0          87m
autoscaler-74b4bb97bd-x5m2d   1/1     Running     0          87m
controller-6b6978c965-jc79d   1/1     Running     0          87m
default-domain-rxfhj          0/1     Completed   0          21s

Voilà, maintenant nous avons un cluster K3S avec Knative, nous pouvons passer aux choses sympas.

🖐️ J'ai créé un projet pour facilement reproduire tout cela: https://gitlab.com/knative-on-k3s/2k-cluster (opens new window)

# Premier déploiement sur Knative

Nous allons créer une application NodeJS. Créez un nouveau dossier amazing-web-app avec les fichiers suivants:

  • index.js
  • package.json
  • Dockerfile
  • service.yaml

# index.js

const http = require('http')
const port = 8080

let index_page = `
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
        body {
          background-color: ${process.env.BACKGROUND_COLOR};
        }
        h1 {
          font-family: "Source Sans Pro"; font-weight: 300; font-size: 100px;
          display: block;
          color: #${process.env.COLOR};
        }
    </style>
  </head>
  <body>
    <h1>
      ${process.env.MESSAGE}
    </h1>
  </body>
</html>  
`

const requestHandler = (request, response) => {
  response.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'})
  response.end(index_page)
}

const server = http.createServer(requestHandler)

server.listen(port, (err) => {
  if (err) {
    return console.log('😡 something bad happened', err)
  }
  console.log(`🌍 server is listening on ${port}`)
})

# package.json

{
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  }
}

# Dockerfile

FROM node:12-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "npm", "start" ]

# service.yaml

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: k-amazing-web-app
  namespace: default
spec:
  template:
    spec:
      containers:
      - image: docker.io/k33g/k-amazing-web-app:latest
        env:
        - name: MESSAGE
          value: "I 💚 Knative"
        - name: BACKGROUND_COLOR
          value: "lemonchiffon"
        - name: COLOR
          value: "darkslateblue"

# Déploiement

Vous remarquez que notre manifeste yaml s'est grandement simplifié. C'est un des bons côtés de Knative et nous ferons encore mieux un peu plus loin.

Pour déployer notre webapp sur notre cluster, vous devez d'abord construire l'image Docker de celle-ci:

docker_user="your_docker_user"
docker_pwd="your_docker_password"
echo ${docker_pwd} | docker login --username ${docker_user} --password-stdin

docker build -t ${docker_user}/k-amazing-web-app .
docker push ${docker_user}/k-amazing-web-app

Et maintenant vous pouvez déployer:

kubectl apply --filename service.yaml

🎉 C'est tout!

Tapez la commande suivante:

kubectl get ksvc

Vous devriez obtenir ceci:

NAME                URL
k-amazing-web-app   http://k-amazing-web-app.default.192.168.64.70.xip.io

nous avons déployé dans le namespace default

Utilisez cette URL avec votre navigateur:

alt kantaive01

# Un déploiement encore plus sympa

Créez un nouveau répertoire sparkling-web-app et copiez les fichiers précédents suivant:

  • index.js
  • package.json
  • Dockerfile

🖐️ vous n'avez pas besoin du fichier service.yaml 🤔

Pour se passer du fichier yaml, nous allons utiliser kn la CLI de Knative.

Vous trouverez le projet de la CLI de Knative ici https://github.com/knative/client (opens new window) et pour l'installer c'est par ici: https://github.com/knative/client/blob/master/docs/README.md#installing-kn (opens new window).

# Déploiement (sans fichier yaml)

Vous devez bien sûr construire l'image de votre webapp:

echo ${docker_pwd} | docker login --username ${docker_user} --password-stdin

docker build -t ${docker_user}/sparkling-web-app .
docker push ${docker_user}/sparkling-web-app

Et maintenant vous pouvez déployer (nous allons créer un namespace cette fois-ci). Nous allons utiliser la commande kn service create:

kubectl create namespace k-apps

kn service create sparkling-web-app \
--namespace k-apps \
--env MESSAGE="I 💚 Knative" \
--env BACKGROUND_COLOR="mediumslateblue" \
--env COLOR="lightyellow" \
--image docker.io/k33g/sparkling-web-app

Vous allez obtenir une sortie de ce type:

Creating service 'sparkling-web-app' in namespace 'k-apps':

  0.294s The Route is still working to reflect the latest desired specification.
  0.461s Configuration "sparkling-web-app" is waiting for a Revision to become ready.
  6.200s ...
  6.270s Ingress has not yet been reconciled.
  6.458s Ready to serve.

Service 'sparkling-web-app' created to latest revision 'sparkling-web-app-cwxky-1' is available at URL:
http://sparkling-web-app.k-apps.192.168.64.70.xip.io

Utilisez cette URL avec votre navigateur:

alt kantaive02

🎉 c'est simple! 😉

# Re-déploiment (toujours sans fichier yaml)

Pour re-déployer nos modifications, nous allons utiliser la commande kn service update:

kn service update sparkling-web-app \
--namespace k-apps \
--env MESSAGE="I 💚🖐️ Knative" \
--env BACKGROUND_COLOR="crimson" \
--env COLOR="lightyellow" \
--image docker.io/k33g/sparkling-web-app

ici nous n'avons pas modifié le code, nous avons juste changé les valeurs des variables d'environnement.

Vous allez obtenir une sortie de ce type:

Updating Service 'sparkling-web-app' in namespace 'k-apps':

  5.301s Traffic is not yet migrated to the latest revision.
  5.382s Ingress has not yet been reconciled.
  5.721s Ready to serve.

Service 'sparkling-web-app' updated to latest revision 'sparkling-web-app-ltxwx-2' i
s available at URL:
http://sparkling-web-app.k-apps.192.168.64.70.xip.io

Utilisez cette URL avec votre navigateur:

alt kantaive03

😃 Vous voyez, l'utilisation de Kubernetes est grandement simplifiée avec Knative. Nous verrons d'autres qualités de cet outil dans de prochains article.

Vous pourrez trouver le code des 2 exemples par ici:

Last Articles