# 🇫🇷 Kit de survie Kubernetes pour les développeurs avec K3S - Partie 3: Automatiser le déploiement

Il est 5h du matin, avant de prendre la route pour les montagnes enneigées, je vais essayer d'écrire cette partie 3, qui va juste consister en trouver un moyen de nous simplifier la vie pour les déploiements de notre application (cela nous aidera pour les chapitres suivants).

👋 mais avant de commencer, chez fait quelques modifications dans les articles précédents suite à des remarques pertinentes de Louis Tournayre (opens new window)

# Addenda (🐛 fix) aux articles précédents

Dans l'article sur comment créer son cluster (opens new window), j'ai changé le script de création:

multipass launch -n ${node1_name} --cpus 2 --mem 2G
multipass launch -n ${node2_name} --cpus 2 --mem 2G
multipass launch -n ${node3_name} --cpus 2 --mem 2G
  • j'ai diminué la taille des nodes en cpu et en mémoire et j'ai mis les même valeurs aussi pour chacune des nodes (je pense que l'on peut faire moins, tout dépend de ce que vous allez déployez et de la puissance de votre machine)
  • par contre 😢 il va falloir supprimer votre cluster et le re-créer (une future version de multipass devrait permettre de le faire dynamiquement)

Dans l'article sur le déploiement (opens new window):

  • j'ai précisé que quand j'utilise k33g dans k33g/amazing-img:0.0.0, k33g c'est mon user sur le Docker Hub
  • et aussi que j'utilise un projet publique sur Docker Hub (pour pas avoir à gérer la notion de credentials dans le cluster)

Voilà, ça c'est fait. On peut passer à la suite. Mais avant "d'outiller" notre déploiement, nous allons procéder à quelques améliorations.

# Un peu de rangement: utilisons des namespaces

Un namespace Kubernetes, c'est comme un "cluster virtuel" au sein de votre cluster "physique". C'est un moyen de "rangement" de vos pods par projets par exemple (mais ça peut être par équipes, ou tout autre chose)

Pour cela, nous allons tout d'abord supprimer l'application que nous avons déployée, de la manière suivante:

cd amazing-web-app
export KUBECONFIG=../create-cluster/k3s.yaml # n'oubliez pas de préciser où est le fichier de conf de votre cluster
kubectl delete -f ./kube/deploy.yaml  

Ensuite, nous allons créer le namespace training:

kubectl create namespace training

Et maintenant, allons modifier notre fichier ./kube/deploy.yaml:

il suffit d'ajouter la ligne namespace: training dans chacune des rubriques metadata (au nombre de 3) du fichier (donc pour Service, Deployment et Ingress).

Votre nouveau fichier devrait ressembler à ceci:

---
# Service
apiVersion: v1
kind: Service
metadata:
  name: amazing-web-app
  namespace: training
spec:
  selector:
    app: amazing-web-app
  ports:
    - port: 80
      targetPort: 8080
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: amazing-web-app
  namespace: training
spec:
  replicas: 3
  selector:
    matchLabels:
      app: amazing-web-app
  template:
    metadata:
      labels:
        app: amazing-web-app
    spec:
      containers:
        - name: amazing-web-app
          image: k33g/amazing-img:0.0.1
          ports:
            - containerPort: 8080
          imagePullPolicy: Always
---
# Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: amazing-web-app
  namespace: training
spec:
  rules:
    - host: amazing-web-app.192.168.64.17.nip.io
      http:
        paths:
          - backend:
              serviceName: amazing-web-app
              servicePort: 80

Sauvegardez, mais ne déployez pas tout de suite.

# Fixer les ressources de vos pods

Il est aussi possible de fixer la taille mémoire et cpus de vos pod avec ceci (vous pouvez modifier les valeurs):

resources:
  limits:
    cpu: "1"
    memory: "128Mi"
  requests:
    cpu: "1"
    memory: "128Mi"

Vous devez ajouter ceci à la fin de la partie déploiement de votre fichier, dans la rubrique containers:

---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: amazing-web-app
  namespace: training
spec:
  replicas: 3
  selector:
    matchLabels:
      app: amazing-web-app
  template:
    metadata:
      labels:
        app: amazing-web-app
    spec:
      containers:
        - name: amazing-web-app
          image: k33g/amazing-img:0.0.1
          ports:
            - containerPort: 8080
          imagePullPolicy: Always
          resources:
            limits:
              cpu: "1"
              memory: "128Mi"
            requests:
              cpu: "1"
              memory: "128Mi"

Maintenant vous pouvez redéployer votre application:

export KUBECONFIG=../create-cluster/k3s.yaml
kubectl apply -f ./kube/deploy.yaml

Et vous pouvez voir que vos pods s'exécutent bien dans un nouveau namespace training

alt k9s

Vous trouverez la nouvelle version du fichier deploy.yaml par ici: https://gitlab.com/learn-k3s/amazing-web-app/-/blob/second-steps/kube/deploy.yaml (opens new window)

# Amélioration et bonne pratique: remarque d'Aurélie Vache (23/02/2020)

Aurélie Vache (opens new window) me disait sur Twitter: "c'est bien d'éviter de mettre le namespace dans les manifest yaml, Et du coup le préciser lors de l'apply avec l'option -n" et elle ajoutait aussi que cela permettait de déployer le même manifeste dans plusieurs namespaces, cela permet à la CD de piloter la destination.

Donc, je fais une dernière modification de mon fichier yaml et finalement j'enlève toutes les lignes namespace: training

Et maintenant j'utiliserais plutôt:

export KUBECONFIG=../create-cluster/k3s.yaml
kubectl apply -f ./kube/deploy.yaml -n training

👋 Pub: Aurélie Vache (opens new window) et Kevin Davin (opens new window) ont fait une superbe présentation sur le sujet Docker, Kubernetes & Istio (opens new window)

# Les redéploiements c'est c...

A chaque fois que vous modifiez votre application, il faut reconstruire l'image, incrémenter un tag, ... sauvegarder, etc... Personnellement, je trouve ceci pénible, je vous propose donc de mettre en place un système de template.

je suis preneur de toute solution différente bien sûr 🙂

Nous allons modifier notre fichier deploy.yaml en utilisant des variables d'environnement, et nous effectuerons les substitutions des valeurs à l'aide de l'utilitaire envsubst. Normalement sous Ubuntu envsubst est présent de base, pour les autres linux je ne sais pas, mais ça s'installe facilement; pour OSX, vous pouvez l'installer avec brew install gettext; brew link --force gettext (envsubst est présent dans le package gettext) et pour Windows, je ne saurais que vous conseiller d'utiliser le shell fournit avec l'installation de git (je n'ai pas de poste Windows, donc ami lecteur, si tu es sous Windows, n'hésite pas à me contacter... Ou attendez que je me décide à m'acheter une Surface).

Renommez le fichier ./kube/deploy.yaml en ./kube/deploy.template.yaml et changez son contenu avec ceci:

# second steps
---
# Service
apiVersion: v1
kind: Service
metadata:
  name: ${APPLICATION_NAME}
spec:
  selector:
    app: ${APPLICATION_NAME}
  ports:
    - port: ${EXPOSED_PORT}
      targetPort: ${CONTAINER_PORT}
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${APPLICATION_NAME}
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ${APPLICATION_NAME}
  template:
    metadata:
      labels:
        app: ${APPLICATION_NAME}
    spec:
      containers:
        - name: ${APPLICATION_NAME}
          image: ${IMAGE}
          ports:
            - containerPort: ${CONTAINER_PORT}
          imagePullPolicy: Always
          resources:
            limits:
              cpu: "1"
              memory: "128Mi"
            requests:
              cpu: "1"
              memory: "128Mi"
---
# Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ${APPLICATION_NAME}
spec:
  rules:
    - host: ${HOST}
      http:
        paths:
          - backend:
              serviceName: ${APPLICATION_NAME}
              servicePort: ${EXPOSED_PORT}

Donc par exemple pour déployer, nous pourrions donc faire quelque chose comme ceci:

export CONTAINER_PORT=8080
export EXPOSED_PORT=80
export APPLICATION_NAME="amazing-web-app"
export DOCKER_USER="k33g"
export NAMESPACE="training"
export IMAGE_NAME="amazing-img"
export TAG="0.0.1"
export IMAGE="${DOCKER_USER}/${IMAGE_NAME}:${TAG}"
export HOST="amazing-web-app.192.168.64.17.nip.io"

docker build -t ${IMAGE_NAME} .
docker tag ${IMAGE_NAME} ${IMAGE}
docker push ${IMAGE}

envsubst < ./deploy.template.yaml > ./kube/deploy.${TAG}.yaml

kubectl apply -f ./kube/deploy.${TAG}.yaml -n ${NAMESPACE}

Mais il faut encore gérer le TAG 😕. Mais si vous êtes bien élevé, votre application est dans un repository git (si ce n'est pas le cas faites-le: git init 😉), et:

  • si vous tapez git rev-parse --short HEAD (dans le répertoire de l'application) vous obtiendrez le commit SHA court (quelque chose comme ceci ace7f26)
  • si vous tapez git symbolic-ref --short HEAD vous obtiendrez la branche courante
  • si vous tapez basename $(git rev-parse --show-toplevel) vous obtiendrez le nom de votre projet git

Je vous propose donc de faire ceci:

export CONTAINER_PORT=8080
export EXPOSED_PORT=80
export APPLICATION_NAME=$(basename $(git rev-parse --show-toplevel)) # ✋
export DOCKER_USER="k33g"
export NAMESPACE="training"
export IMAGE_NAME="${APPLICATION_NAME}-img" # ✋
export TAG=$(git rev-parse --short HEAD) # ✋
export IMAGE="${DOCKER_USER}/${IMAGE_NAME}:${TAG}"
export CLUSTER_IP="192.168.64.17" # ✋
export BRANCH=$(git symbolic-ref --short HEAD) # ✋
export HOST="${APPLICATION_NAME}.${BRANCH}.${CLUSTER_IP}.nip.io" # ✋

docker build -t ${IMAGE_NAME} .
docker tag ${IMAGE_NAME} ${IMAGE}
docker push ${IMAGE}

envsubst < ./kube/deploy.template.yaml > ./kube/deploy.${TAG}.yaml

export KUBECONFIG=../create-cluster/k3s.yaml
kubectl apply -f ./kube/deploy.${TAG}.yaml -n ${NAMESPACE}
echo "http://${HOST}"

👋 Remarque au début j'avais variabilisé les réplicas, mais comme me l'a fait justement remarqué Kevin Davin (opens new window), c'est plus simple (et plus élégant) d'utiliser: kubectl scale deploy name --replicas=2

  • Exemple: kubectl scale --replicas=1 deploy amazing-web-app -n training

Donc modifiez votre code, fait un git add . puis un git commit -m "🚀 new deployment" et lancez les commandes ci-dessus.

La nouvelle url de votre webapp devrait ressembler à ceci: http://amazing-web-app.third-steps.192.168.64.17.nip.io (opens new window)

✋ j'utilise la branche, comme ça je peux faire facilement des previews de mes développements

Donc maintenant, il vous suffit de faire un commit et de re-utiliser les même commandes (pensez à supprimer les fichiers de déploiement générés)

✋ vous trouverez la nouvelle version ici: https://gitlab.com/learn-k3s/amazing-web-app/-/blob/third-steps/kube/deploy.template.yaml (opens new window)

# Un dernier pour la route: en faire un plugin kubectl

Il est très facile de créer un plugin pour kubectl: exemple de création de plugin avec un script shell (opens new window).

Nous allons donc transformer nos commandes précédentes en plugin kubectl

Dans un autre dossier (easy-deploy par exemple), créez un fichier kubectl-easy.sh

#!/bin/sh

if [[ "$1" == "version" ]]; then
  echo "1.0.0"
  exit 0
fi

if [[ "$1" == "config" ]]; then
  echo "$KUBECONFIG"
  exit 0
fi

if [[ "$1" == "deploy" ]]; then

  export CONTAINER_PORT=${CONTAINER_PORT:-8080}
  export EXPOSED_PORT=${EXPOSED_PORT:-80}
  export APPLICATION_NAME=$(basename $(git rev-parse --show-toplevel))
  export DOCKER_USER="${DOCKER_USER}"
  export NAMESPACE="${NAMESPACE}"
  export IMAGE_NAME="${APPLICATION_NAME}-img"
  export TAG=$(git rev-parse --short HEAD)
  export IMAGE="${DOCKER_USER}/${IMAGE_NAME}:${TAG}"
  export CLUSTER_IP="${CLUSTER_IP}"
  export BRANCH=$(git symbolic-ref --short HEAD)
  export HOST="${APPLICATION_NAME}.${BRANCH}.${CLUSTER_IP}.nip.io"

  docker build -t ${IMAGE_NAME} .
  docker tag ${IMAGE_NAME} ${IMAGE}
  docker push ${IMAGE}

  envsubst < ./kube/deploy.template.yaml > ./kube/deploy.${TAG}.yaml

  kubectl apply -f ./kube/deploy.${TAG}.yaml -n ${NAMESPACE}
  echo "🌍 http://${HOST}"

  exit 0
fi

Pour l'installer c'est simple:

sudo chmod +x ./kubectl-easy.sh
cp ./kubectl-easy.sh ./kubectl-easy
sudo mv ./kubectl-easy /usr/local/bin

Et maintenant pour déployer votre webapp c'est encore plus simple:

cd amazing-web-app
git add .
git commit -m "🎉 wow!"

export KUBECONFIG=../create-cluster/k3s.yaml
NAMESPACE="training" \
DOCKER_USER="k33g" \
CLUSTER_IP="192.168.64.17" \
kubectl easy deploy

👋 le code du plugin est ici: https://gitlab.com/learn-k3s/easy-deploy (opens new window)

Et c'est tout pour aujourd'hui 😃. N'hésitez pas à faire des remarques. Je dois aller charger le coffre de la voiture. A bientôt 👋 pour la suite.

Last Articles