# 🇫🇷 J'utilise K3S comme un PaaS - Partie 1 - un Workflow plus simple
Aujourd'hui, j'attaque une nouvelle série autour de K3S. La série précédente est née de mon besoin d'apprendre Kubernetes et avoir un environnement sur mon poste pour pouvoir tester mes développements (webapp et microservices) - et toujours avec une "vision développeur", l'objectif n'étant pas de devenir un admin Kube, mais plutôt un utilisateur "éclairé", ce qui ne peut avoir qu'un impact bénéfique sur ma façon de coder.
Après cette 1ère série, donc, mon workflow local de développeur ressemble à ceci:
Mais j'aimerais quelque chose de plus "GitOps", où je pourrais faire abstraction de mon Dockerfile
(et de ma phase de build locale), quelque chose un peu plus "à la Clever Cloud" ou "à la Heroku": je "pousse" mon code, et je lance le déploiement. Mon workflow ressemblerait à ceci:
la commande
git push
implique qu'il nous faudra un serveur git
C'est donc à partir de ce cas d'utilisation que va commencer cette nouvelle série "K3S as a PaaS"
Remerciements une fois de plus à Louis Tournayre (opens new window) pour la très instructive conversation de Samedi matin qui m'a permis de démarrer cette nouvelle série.
# Quelle est ma solution ? Les spécifications
👋 Disclaimer: c'est un POC qui date d'hier, tout est bien sûr perfectible - cet article à uniquement pour vocation d'expérimenter et apprendre.
Je disais donc que la commande git push
implique qu'il nous faudra un serveur git. Je vais au hasard utiliser GitLab.com (opens new window), mais tout autre serveur git ferait l'affaire (même installé en local, mais soyez juste sûr que votre cluster puisse "voir" votre serveur git local). Pour arriver à cela, voici un schéma de la "mécanique" associée à mon workflow (j'utilise une application ExpressJS pour mon exemple):
Dorénavant, je vais "pousser" (git push
) mon code dans un projet sur GitLab.com (opens new window). Et c'est lorsque j'aurais lancé la commande kubectl apply -f etc...
que je vais déléguer la partie "chiante" à mon cluster (ou plus précisément à un pod)
- Lorsque le pod va se "charger" et se "construire", il va le faire à partir d'une image de container qui est construite à partir d'une image nodejs (
node:13.12-slim
). - Ensuite le pod va faire un
git clone
du projet sur GitLab.com (opens new window). - Une fois le projet "récupéré", le pod va lancer un
npm install
pour récupérer les dépendances. - Et enfin, le pod exécute un
npm start
Et la webapp est déployée 🎉
Il y a certainement d'autres façons de faire (et je suis curieux de les connaître). Là je suis parti au plus simple.
# Quelle est ma solution ? La réalisation
# Dockerfile
Pour faire cela, tout va se passer dans le manifest de déploiement. Mais avant cela nous devons créer notre image de "container runtime". Mon Dockerfile
est tout simple:
FROM node:13.12-slim
WORKDIR /home/app
CMD /bin/bash -c "npm install; npm start"
Le rôle du container sera de télécharger les dépendances et d'exécuter l'application.
Donc buildez votre image et "poussez" là sur une registry. Vous avez différentes solutions:
Ou vous buildez et poussez sur Docker Hub:
DOCKER_USER="votre_docker_user"
IMAGE_NAME="node-js-runtime"
TAG="0.0.0"
IMAGE="${DOCKER_USER}/${IMAGE_NAME}:${TAG}"
docker build -t ${IMAGE_NAME} .
docker tag ${IMAGE_NAME} ${IMAGE}
docker push ${IMAGE}
Et vous utiliserez quelque chose comme ceci: votre_docker_user/node-js-runtime:0.0.0
Où vous buildez et poussez sur votre registry privée:
DOCKER_REGISTRY="acme-registry.docker.nimbo:5000"
IMAGE_NAME="node-js-runtime"
TAG="0.0.0"
IMAGE="${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}"
docker build -t ${IMAGE_NAME} .
docker tag ${IMAGE_NAME} ${IMAGE}
docker push ${IMAGE}
Et vous utiliserez quelque chose comme ceci: acme-registry.docker.nimbo:5000/node-js-runtime:0.0.0
Où (la solution la plus simple) vous utilisez mon image Docker toute prête: registry.gitlab.com/bots-garden/funky/funky-node-js-runtime:latest
Pour la suite de l'article c'est ce que j'utiliserai, libre à vous d'adapter bien sûr.
- le code du runtime est ici: https://gitlab.com/bots-garden/funky/funky-node-js-runtime (opens new window)
- si vous voulez apprendre à builder cette image comme moi, j'ai fait un tuto ici: How to use GitLab Docker Container Registry on GitLab.com (opens new window)
# L'application ExpressJS
vous pouvez utilisez tout autre framework JavaScript bien sûr
L'application en elle-même n'a que peu d'intérêt, vous trouverez son code ici: https://gitlab.com/bots-garden/funky/yo (opens new window).
Le plus important est le fichier (le manifeste) de déploiement. Comme pour la série précédente, je crée un template de manifeste, et je substitue les variables d'environnement avec envsubst
.
# Le manifeste de déploiement
Pour faire exécuter un git clone
de mon projet à mon pod, je vais utiliser ce que l'on appelle un init Container (qui est monté temporairement) et un volume de type EmptyDir
Il sera donc décrit/créé de cette manière:
initContainers: # 1️⃣
- name: git-repository-container
image: alpine/git # 2️⃣
imagePullPolicy: Always
args:
- clone # 3️⃣
- -b
- ${BRANCH}
- ${GIT_REPOSITORY}
- /home/app # 4️⃣
volumeMounts: # 5️⃣
- name: git-repository-${TAG} # 6️⃣
mountPath: /home/app
- L'init container est monté avant le container du pod
- J'utilise l'image
alpine/git
cf. https://hub.docker.com/r/alpine/git/ (opens new window) qui va me permettre d'exécuter des commandesgit
dans le "init container" - J'exécute donc un
git clone
, - Dans le répertoirs
/home/app
(cf leWORKDIR
de monDockerfile
) - Et cela sera monté/copié dans le volume de type
EmptyDir
- Je lui donne un nom:
git-repository-${TAG}
(TAG
est calculé comme ceci:$(git rev-parse --short HEAD)
comme cela j'ai la garantie que le contenu du volume est renouvelé)
Ensuite je définis mon container "principal":
containers:
- name: ${APPLICATION_NAME}
image: ${RUNTIME_IMAGE}
ports:
- containerPort: ${CONTAINER_PORT}
imagePullPolicy: Always
volumeMounts:
- name: git-repository-${TAG} # 1️⃣
mountPath: /home/app
volumes: # 2️⃣
- name: git-repository-${TAG}
emptyDir: {}
- Mon container applicatif partage donc le volume monté sur
/home/app
avec l'init container - Et enfin, je monte mon volume de type
emptyDir
# Et finalement on déploie
Les commandes pour déployer seront les suivantes:
à adapter en fonction de votre contexte
export KUBECONFIG=../../cluster/config/k3s.yaml
export SUB_DOMAIN="192.168.64.33.nip.io"
export RUNTIME_IMAGE="registry.gitlab.com/bots-garden/funky/funky-node-js-runtime:latest"
export GIT_REPOSITORY="https://gitlab.com/bots-garden/funky/yo.git"
git add .; git commit -m "👋 update and 🚀 deploy"; git push
export CONTAINER_PORT=${CONTAINER_PORT:-8080}
export EXPOSED_PORT=${EXPOSED_PORT:-80}
export APPLICATION_NAME=$(basename $(git rev-parse --show-toplevel))
export TAG=$(git rev-parse --short HEAD)
export BRANCH=$(git symbolic-ref --short HEAD)
export HOST="${APPLICATION_NAME}.${BRANCH}.${SUB_DOMAIN}"
export NAMESPACE="funky-apps-${BRANCH}"
kubectl describe namespace ${NAMESPACE}
exit_code=$?
if [[ exit_code -eq 1 ]]; then
echo "🖐️ ${NAMESPACE} does not exist"
echo "⏳ Creating the namespace..."
kubectl create namespace ${NAMESPACE}
else
echo "👋 ${NAMESPACE} already exists"
fi
rm ./kube/*.yaml
envsubst < ./deploy.template.yaml > ./kube/deploy.${TAG}.yaml
kubectl apply -f ./kube/deploy.${TAG}.yaml -n ${NAMESPACE}
# Un peu de simplification
Je vous engage à scripter ces commandes bien sûr.
✋ Vous pouvez vous inspirer de mon script de déploiement: https://gitlab.com/bots-garden/funky/yo/-/blob/master/deploy (opens new window)
Maintenant, ma commande de déploiement ressemble à ça:
export KUBECONFIG=../../cluster/config/k3s.yaml
export SUB_DOMAIN="192.168.64.33.nip.io"
./deploy "✨ feature"
# Hop hop hop! Ce n'est pas fini
Vous l'aurez sans doute compris, vous pouvez vous créer votre propre runtime pour votre techno préférée.
Quand je ne fais pas de JavaScript, je fais du Vert.x avec du Kotlin 😍. J'ai donc créé un runtime pour pouvoir faire un build maven et exécuter mon application Vert.x.
- Le code est ici: https://gitlab.com/bots-garden/funky/funky-java-runtime (opens new window)
- Si vous souhaitez utiliser l'image:
registry.gitlab.com/bots-garden/funky/funky-java-runtime:latest
Le Dockerfile
est tout simple:
# build
FROM maven:3.6.0-jdk-8-alpine
WORKDIR /home/app
CMD /bin/bash -c "mvn clean package; cp target/*-fat.jar app.jar; java -jar ./app.jar"
Voici le build maven "dans le pod":
Et voici une application pour faire vos tests: https://gitlab.com/bots-garden/funky/ping-vert-x (opens new window)
🎉 Amusez vous (bien j'espère) avec ça et 👋 à bientôt pour la suite. Nous traiterons de monitoring (un PaaS sans monitoring, n'est pas un PaaS 😉)
Pour rappel: Précédents blog posts de la série "Kit de survie K8S pour les dévs avec K3S":
- Partie 1: Intro et création du cluster (opens new window)
- Partie 2: Le déploiement (opens new window)
- Partie 2bis: Le déploiement: quelques améliorations (opens new window)
- Partie 3: Automatiser le déploiement (opens new window)
- Partie 4: Utiliser une registry privée (unsecure) (opens new window)
- Partie 5: Les Volumes (opens new window)
- Partie 6: Redis (opens new window)
- Partie 7: Vert-x (opens new window)
- Partie 8: Un peu de réseau (opens new window)
Last Articles
- 🇫🇷 Type Result en Kotlin | 2020-10-31 | Kotlin
- 🇫🇷 Type Result en Kotlin | 2020-10-31 | Kotlin
- 🇬🇧 Every GitLab Page deserves a real CI/CD | 2020-07-23 | GitLab CI
- 🇫🇷 Lit-Element, commencer doucement | 2020-07-20 | WebComponent
- 🇬🇧 Build quickly and host easily your Docker images with GitLab and GitLab CI | 2020-06-02 | GitLab CI
- 🇬🇧 Deploy quickly on Clever Cloud with GitLab CI | 2020-05-31 | GitLab CI
- 🇫🇷 Borg Collective, mes jouets pour apprendre Knative | 2020-05-30 | Knative
- 🇬🇧 Borg Collective, Toys to learn Knative | 2020-05-30 | Knative
- 🇫🇷 M5Stack, une petit device IOT bien sympathique, programmable en Python | 2020-05-09 | IOT
- 🇫🇷 Knative, l'outil qui rend Kubernetes sympathique | 2020-05-02 | kubernetes