Kubernetes est un sujet qui passionne les foules. Je le vois tous les jours avec la playlist k8s. C’est pourquoi j’ai décidé de me centrer sur quelques outils assez courants pour les personnes qui administrent ou utilisent le célèbre orchestrateur de conteneurs. Même si je continue encore la formation ansible, je commence en parallèle une série de tutoriels dédiés à Helm.
Là encore un cours complet pour se former à son rythme que ce soit pour débuter ou aller plus loin. Pour cela on va reprendre toutes les bases pour monter petit à petit en niveau.
Aujourd’hui c’est la première donc du coup on va commencer par poser la première brique à savoir l’introduction à Helm et essayer de répondre à quelques questions : pourquoi ? comment ? intérêts ?…
L’instanciation, une problématique kubernetes
A l’heure actuelle, beaucoup d’entreprises passe à tort ou à raison sur kubernetes. Un bel outil qui permet de lancer des services en décrivant des ressources. L’outil étant assez élaboré, pour lancer une petite application, il est souvent nécessaire d’installer différents objets. En outre nous devons souvent coordonner les objets les uns par rapport aux autres à défaut de variables mutualisées en dehors des configmaps.
Or il est assez courant d’avoir une infrastructure plus ou moins à base de microservices, ces derniers se ressemblant fortement voir appelant de même lignes de codes/images ou nécessitant un déploiement similaire. Mais alors faut-il faire des copier/coller de toutes ces ressources avecs de répertoires et faire évoluer tout cela ensemble avec des difficultés pour modifier dans des manifestes communs plusieurs fois le même élément.
Prenons un exemple avec wordpress, pour créer une instance wordpress, il faut :
2 deployments
1 ingress
2 pvc/pv
2 services…
Créons ensuite une deuxième instance… rebelote et ainsi de suite. Faire cela dans un outil comme kubernetes cela nous fait penser que l’on a loupé quelque chose. Vaut mieux arrêter de travailler et compter les mouches, surtout le jour où il faudra faire évoluer tout ça.
C’est un des points majeurs que Helm permet de corriger.
Helm permet de templatiser des ressources et de stocker toutes les variables définies dans un seul fichier : Values.yaml.
On a donc fait un grand pas.
Mais ce n’est pas le seul intérêt pour Helm
Bien sûr Helm va bien plus loin que cela.
Finalement, une fois que l’on commence à templatiser, on s’aperçoit que l’on dispose de briques cohérentes de ressources. Il ne manque qu’une chose c’est la possibilité de les partager et faciliter leur installation et leur versionning.
Du coup, imaginons que Helm puisse être un gestionnaire de paquets façon apt, yum ou autres. Avec des dépôts distants, cela peut permettre de partager ces packages que l’on appelle simplement des Charts.
Et comme tous les packages, Helm permet de versionner tout cela pour garder des briques cohérentes, éviter les bugs et faciliter l’installation des objets kubernetes. Intéressant n’est-ce pas ?
Ajoutons à cela que Helm a su évoluer d’une version 2 un peu plus complexe vers une version 3 qui permet de se connecter facilement à votre cluster kubernetes.
Conclusion sur Helm
Bref se former à Helm ou l’adopter c’est un bon point pour votre carrière car vous serez peut être amené à le rencontrer et pourquoi pas le tester pour le faire entrer dans votre société. Donc suivre une formation Helm est certainement un bon investissement de votre temps.
Nous en reparlerons plus tard mais un dépôt Helm peut se limiter à un dépôt git assez épuré.
Ainsi vous pourrez trouver des intérêts :
meilleures capacités d’itération
amélioration du travail en équipe pour vos déploiements
utilisation de charts déjà développées par la communauté
Après ne nous cachons pas qu’il faut une petite période d’adaptation. Mais comme vous êtes là c’est que vous voulez apprendre à utiliser Helm et vous former donc je ne me fais pas de soucis pour vous.
Se former est une approche importante pour adopter ou devenir devops. Et n’oubliez pas de vous abonner à la chaine xavki 😉
Petit à petit, nous allons rentrer dans l’utilisation de ansible. En effet, il me semble logique de ne pas brûler les étapes pour apprendre mais surtout comprendre l’outil et ses principes.
Ansible est de plus en plus au coeur des orchestrations dans un esprit de devops. Il permet à partir de choses simples et descriptives de configurer et déployer des infrastructures mais aussi des applicatifs.
Dans cet article dédié à la formation à ansible, je vous propose de découvrir le module user. C’est un peu une base car il permet comme son nom l’indique de créer des users… et l’installation de serveurs ne se fait pas sans créer d’utilisateurs.
Ce module se base principalement sur les commandes : useradd, userdel, usermod…
Comme je le rappelle dans cette page, il prend de nombreux paramètres. Vous povuez aussi vous rapprocher de la documentation en anglais et plus complète du site de ansible.
Débutons par la mise en place et le premier run du module user
Le cas le plus simple pour commencer est de se créer un utiliseurs de démo. Pour cela il vous faut quelques serveurs (je vous invite à regarder mon article précédent pour vous créer des conteneurs docker similaires à des VM).
Ensuite bien sûr vous devez disposer aussi d’un inventaire et d’un playbook mais maintenant vous commencez à connaître.
Pour créer un user sans droits ni contraintes particulières, nous pouvons commencer par cette tâche.
- name: création de xavki
user:
name: xavki
state: present
password: "{{ 'password' | password_hash('sha512') }}"
Nous utilisons bien « user » et lui passons 3 paramètres :
le nom : celui de l’utilisateur que nous voulons créer
le statut : est-ce que l’on souhaite qu’il soit créé ou supprimé (present/absent en anglais pour ansible)
le password : petite particularité c’est que celui-ci ne peut pas être complètement en clair (en théorie il ne doit pas l’être du tout mais nous n’avons pas encore vu dans cette formation ansible-vault). Donc nous utilisons le filtre jinja password_hash et lui précisons que nous voulons un hash de type sha512.
Et bien sûr nous lançons notre playbook :
ansible-playbook -i 00_inventory.yml playbook.yml
Ajoutons quelques paramètres pour mieux configurer notre utilisateur
Bien c’est un premier pas et vous pouvez vérifier cela en vous connectant sur une machine distante avec une simple commande comme :
id xavki
Mais admettons que nous souhaitions ajouter ce user à un groupe existant. On parle bien ici de l’ajouter et non pas d’écraser son groupe. Dans notre exemple, ajoutons-le au groupe sudo pour plus de liberté ;).
- name: création de xavki
user:
name: xavki
state: present
groups: sudo
append: yes
password: "{{ 'password' | password_hash('sha512') }}"
On ajoute donc le paramètre group et le paramètre append à yes. Ce dernier assure que nous ajoutions bien le groupe au user et pas l’écrasement de son appartenance aux autres groupes.
Cependant, nous aimons associer une clef ssh à un utilisteur. Pour cela ansible permet différentes situations. Ici nous allons l’ajouter et voir le retour de la tâche de création user grâce à un debug.
Lors de l’article précédent, nous avions découvert l’installation de la stack prometheus/grafana. Continuons pour découvrir aujourd’hui quelques rudiments du promql. Vous pouvez vous rendre sur cette page si vous souhaitez consulter toute cette formation prometheus/grafana gratuite.
Nous avions vu le principe de label dans prometheus à travers le langage promql. Ainsi, chaque ligne de métriques dispose d’autant de cardinalités qu’elles disposent de labels différents. Par exemple, si la métrique node_load_5 prend un label instance et un label datacenter, nous avons autant de cette métrique que d’instance pour l’ensemble des datacenters. C’est pourquoi certaines métriques peuvent être très lourdes en matière de traitement des métriques (des load-balancers par exemple). Et lorsque l’on utilise ces métriques il est nécessaires de les filtrer pour les exploiter.
Pour filtrer les métriques, il suffit de placer des requêtes sur les labels de la métrique en question. Par exemple sur le datacenter ou l’instance spécifiquement.
Ainsi on pourra utiliser des expressions régulières de cette manière :
Le premier cas s’attache à une équivalence stricte.
Le second à une regex qui permettra de rappatrier toutes les métriques node_network_receive_bytes_total mais dont le label device commence par « eth » et est suivi d’au moins un caractère ou plus.
Le troisième dans le cas où pour la même métrique on souhaite rappartier soit les devices « eth0 » ou « lo ».
Enfin la différence en appliquant une regex.
D’autres opérations : le BY
Commençons par le regroupement grâce à l’opérateur « by » qui permet de faire l’équivalent de « GROUP BY » en SQL. Ainsi sur certaines fonctions comme les sommes, les comptages… vous pourrez regrouper ce calcul par un ou plusieurs labels. Par exemple pour compter le nombre de lignes de la métrique node_cpu_seconds_total par cpu, nous procéderons de la sorte :
count(node_cpu_seconds_total) by (cpu)
Ce qui nous permet, après avoir engendré un résultat d’une ligne par cpu, la possibilité de calculer le nombre de CPU :
count(count(node_cpu_seconds_total) by (cpu))
Et donc pour compter cela par serveur, il faudra ajouter le label « instance » à notre calcul
count(count(node_cpu_seconds_total) by (cpu,instance)) by (instance)
On compte donc dans un premier temps le nombre de ligne par cpu unqiuement pour regrouper cette métrique disposant de plusieurs lignes. Puis on compte le nombre de fois où l’on retrouve cette ligne regroupée pour avoir le nombre de cpu par instance.
Le facteur temps : offset et vector
Parmi les principaux éléments de temps importants dans prometheus, il en existe 2 :
le vector : c’est la plage de temps sur laquelle certains calculs vont être réalisés (une somme, une moyenne, une dérivée…)
l’offset : il s’agit du point de référence auquel vous souhaitez réaliser le calcul. Par exemple, il peut être intéressant de se placer 24h en arrière pour comparer avec les métriques actuelles…
Le range vector est intéressant pour les calculs mais aussi pour commencer pour voir le nombre de valeur sur lesquels on réalise un calcul. Ainsi si on passe un range vector de 3m à partir de maintenant sur une métrique on l’écrit comme ceci :
node_cpu_seconds_total[3m]
Cette requête va vous permettre d’afficher toutes les valeurs de la métrique node_cpu_seconds au cours de 3 dernières minutes. Plus le scrape est fréquent et plus vous avez de valeurs.
Les valeurs de cette requête sont classées par timestamp en format epoch. Vous pouvez néanmoins les convertir en format « humain » grâce à la commande date :
date -d "@1574340307.27"
Ainsi, pour calcculer une moyenne au cours des 3 dernières minutes, vous pouvez le faire de cette manière :
avg_over_time(node_cpu_seconds_total[3m])
Pour les offset, il suffit d’ajouter le mot offset. Prenons un exemple. Nous voulons comparer le nombre de requêtes actuelles avec celui d’il y a 5 minutes. Comme nous avons besoin de 2 courbes, nous allons faire deux requêtes.
La première qui collecte l’information sur le moment :
sum(http_requests_total)by (code)
Ensuite la seconde en demandant un offset placé 5 minutes en arrière :
sum(http_requests_total offset 5m)by (code)
Simple non ?
Les opérateurs
Dans les éléments simples à connaître pour débuter avec prometheus, il y a les opérateurs. Il s’agit de simple test conditionnels.
Par exemple, nous voulons savoir si une métrique est supérieur à 0.
Ainsi parmi les résultats, comme pour les labels, prometheus ne retournera que les métriques correspondantes. Et bien cûr cela peut se faire sur des résultats de fonctions (calculs).
Et on pourra aussi combiner les tests. Voici la liste des opérateurs promql disponibles :
== (equal)
!= (not-equal)
> (greater-than)
< (less-than)
>= (greater-or-equal)
<= (less-or-equal)
and (intersection)
or (union)
unless (complement)
Je vous propose un exemple simple :
node_load1 > 1.0 and node_load1<1.6
Dans ce cas nous retenons uniquement les métriques node_load inférieures à 1.6 et supérieure à 1.0. nous aurions aussi pu l’écrire de la manière suivante :
Ce script n’a presque pas pris une ride depuis ces premiers pas sur le chaine youtube. Eh oui c’était la première vidéo de la chaine xavki. Je l’ai légèrement toiletté et j’ai bien sûr mis à jour l’image docker avec une version plus récente de Debian (10).
Lorsque j’ai créé ce script, j’avais besoin de tester régulièrement les rôles que je développais. J’utilisais déjà un peu vagrant et je trouvais que lancer une machine virtuelle demandais du temps. Et comme pour bien développer du ansible, il me semble très important de tout casser pour tout refaire, je me suis réorienté vers des conteneurs docker. Après coup, LXD/LXC serait encore plus optimal, ces conteneurs étant des conteneurs systèmes.
Bref, à l’époque j’ai creusé et cherchais comment avoir des conteneurs avec SSH et systemD. Finalement cela n’est pas très compliqué après avoir installé systemd, il suffit de supprimer quelques répertoires et de monter ceux de la machine en dessous.
Cependant attention, cela signifie aussi de lancer ces conteneurs en privileged. Donc tout cela cumulé, rend ce concept très défaillant d’un point de vue sécurité et non sécurisable. Il faut donc l’utiliser et supprimer les conteneurs après utilisation. En tout cas c’est ce que je recommande fortement.
On permet une élévation de privilège de notre conteneur sur notre hosts sur les capabilities NET_ADMIN et SYS_ADMIN.
On publie tous les ports pour pouvoir utiliser les applicatifs sur l’ip du conteneur sans se poser de question.
On monte un répertoire d’échange entre le host et le conteneur pour /srv/data.
On monte en lecture seul /sys/fs/cgroup.
Maintenant on automatise le lancement des conteneurs avec un script bash
L’enjeu est tout de même de pouvoir créer à la volée des conteneurs. Et d’essayer de gagner du temps dans le développement ansible. Au passage, bien sûr, ce script est utilisable dans d’autres cas d’utilisation (testing, dev…).
Voici le script que je vous propose et que vous pourrez personnaliser et compléter à votre goût.
#!/bin/bash
############################################################
#
# Description : déploiement à la volée de conteneur docker
#
# Auteur : Xavier
#
# Date : 28/12/2018 - V2.0
#
###########################################################
# Functions #########################################################
help(){
echo "
Options :
- --create : lancer des conteneurs
- --drop : supprimer les conteneurs créer par le deploy.sh
- --infos : caractéristiques des conteneurs (ip, nom, user...)
- --start : redémarrage des conteneurs
- --ansible : déploiement arborescence ansible
"
}
createNodes() {
# définition du nombre de conteneur
nb_machine=1
[ "$1" != "" ] && nb_machine=$1
# setting min/max
min=1
max=0
# récupération de idmax
idmax=`docker ps -a --format '{{ .Names}}' | awk -F "-" -v user="$USER" '$0 ~ user"-debian" {print $3}' | sort -r |head -1`
# redéfinition de min et max
min=$(($idmax + 1))
max=$(($idmax + $nb_machine))
# lancement des conteneurs
for i in $(seq $min $max);do
docker run -tid --privileged --publish-all=true -v /srv/data:/srv/html -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name $USER-debian-$i -h $USER-debian-$i priximmo/buster-systemd-ssh
docker exec -ti $USER-debian-$i /bin/sh -c "useradd -m -p sa3tHJ3/KuYvI $USER"
docker exec -ti $USER-debian-$i /bin/sh -c "mkdir ${HOME}/.ssh && chmod 700 ${HOME}/.ssh && chown $USER:$USER $HOME/.ssh"
docker cp $HOME/.ssh/id_rsa.pub $USER-debian-$i:$HOME/.ssh/authorized_keys
docker exec -ti $USER-debian-$i /bin/sh -c "chmod 600 ${HOME}/.ssh/authorized_keys && chown $USER:$USER $HOME/.ssh/authorized_keys"
docker exec -ti $USER-debian-$i /bin/sh -c "echo '$USER ALL=(ALL) NOPASSWD: ALL'>>/etc/sudoers"
docker exec -ti $USER-debian-$i /bin/sh -c "service ssh start"
echo "Conteneur $USER-debian-$i créé"
done
infosNodes
}
dropNodes(){
echo "Suppression des conteneurs..."
docker rm -f $(docker ps -a | grep $USER-debian | awk '{print $1}')
echo "Fin de la suppression"
}
startNodes(){
echo ""
docker start $(docker ps -a | grep $USER-debian | awk '{print $1}')
for conteneur in $(docker ps -a | grep $USER-debian | awk '{print $1}');do
docker exec -ti $conteneur /bin/sh -c "service ssh start"
done
echo ""
}
createAnsible(){
echo ""
ANSIBLE_DIR="ansible_dir"
mkdir -p $ANSIBLE_DIR
echo "all:" > $ANSIBLE_DIR/00_inventory.yml
echo " vars:" >> $ANSIBLE_DIR/00_inventory.yml
echo " ansible_python_interpreter: /usr/bin/python3" >> $ANSIBLE_DIR/00_inventory.yml
echo " hosts:" >> $ANSIBLE_DIR/00_inventory.yml
for conteneur in $(docker ps -a | grep $USER-debian | awk '{print $1}');do
docker inspect -f ' {{.NetworkSettings.IPAddress }}:' $conteneur >> $ANSIBLE_DIR/00_inventory.yml
done
mkdir -p $ANSIBLE_DIR/host_vars
mkdir -p $ANSIBLE_DIR/group_vars
echo ""
}
infosNodes(){
echo ""
echo "Informations des conteneurs : "
echo ""
for conteneur in $(docker ps -a | grep $USER-debian | awk '{print $1}');do
docker inspect -f ' => {{.Name}} - {{.NetworkSettings.IPAddress }}' $conteneur
done
echo ""
}
# Let's Go !!! ###################################################################""
#si option --create
if [ "$1" == "--create" ];then
createNodes $2
# si option --drop
elif [ "$1" == "--drop" ];then
dropNodes
# si option --start
elif [ "$1" == "--start" ];then
startNodes
# si option --ansible
elif [ "$1" == "--ansible" ];then
createAnsible
# si option --infos
elif [ "$1" == "--infos" ];then
infosNodes
# si aucune option affichage de l'aide
else
help
fi
Plusieurs options sont prévues :
–create : la création de conteneurs en ajoutant à cette option un nombre pour indiquer le nombre de docker souhaités
–drop : pour supprimer les conteneurs lancés avec ce script
–start : en cas d’arrêt et de relance de votre machine, les conteneurs seront stoppés, cette options permet de les relancer à la volée
–ansilbe : créé un répertoire dédié aux développements ansible avec notamment un inventory déjà rempli et complété par les ip de chacun des conteneurs
–infos : pour lister les ip des conteneurs créés
Bien sûr on organise tout cela avec des fonctions bash. La plus fournie étant la fonction de création qui notamment :
créé un user du même nom du user qui lance le script
lui définit un password par défaut à « password »
lui copie votre clef publique dans le authorized_keys
lance ssh (cf dans notre dockerfile on ne lançait que systemd et pas sshd)
Voilà, j’ai déjà eu pas mal de retour positifs ou de gens qui ont customisé ce script. Certains ont ajouté d’autres OS également. A vous de voir et de vous faire plaisir en personnalisant tout cela et en l’adaptant au mieux à votre cas d’utilisation.
Pour débuter avec kubernetes, il faut aimer la ligne de commande et on peut y prendre un certain plaisir au fur et à mesure du temps. D’ailleurs, je ne saurais trop vous recommander si vous débuter de ne pas avoir recours aux alias et autres tools qui facilitent l’utilisation ou by-pass la CLI.
Eh oui personnellement je considère qu’il faut passer un peu de temps avec cette cli avant de passer aux tools. D’ailleurs, il y a tellement de tools qu’il vaut mieux se contenter de quelques uns (kubectx, kubens, k9s…).
Commençons
Pour débuter, nous avons bien retenu que pour lister tout objet/ressource dans kubernetes il faut utiliser kubectl get. Maintenant vous pouvez lister tous les objets de bases en utilisant :
kubectl get all
Et pour aller plus loin et récupérer tous les objets communs pour tous les namespaces :
kubectl get all –all-namespaces
Mais comment connaît les ressources justement ?
kubectl api-resources -o wide
Remarquez le -o wide qui permet d’avoir le maximum d’informations sur chacun des objets. On peut utiliser assez souvent cette option, par exemple sur les pods etc.
Et pour connaître l’état des composants principaux de votre cluster (etcd, kubelet etc), vous pouvez réaliser un :
kubectl get componentstatuses
Debugger pour un débutant ?
Débuguer dans un cluster kubernetes demande d’avoir des réflexes. Je vous en livre quelques uns pour commencer et petit à petit vous trouverez vos automatismes.
Là encore la ligne de commande est votre meilleure amie. Bien sûr tout commence par un :
kubectl get pods
Puis une fois le pod identifié, on va pouvoir d’une part regarder l’état de l’objet en question et ses caractéristiques :
kubectl describe pods <id_du_pod>
Et si finalement l’erreur mentionnée dans les events du bas de la page vous laisse penser qu’il s’agit d’un problème applicatif, vous pouvez vous référez aux logs du pods.
Si celui ne possède qu’un conteneur :
kubectl logs -f <id-pod>
Mais si vous voulez obtenir les logs d’un conteneur spécifiquement :
Vous pouvez également lister les évènements d’un pod :
kubectl get events <id_pod>
Et pour identifier le contenu du manifest du pod :
kubectl edit pod <id_pod>
Mais souvent il vous faudra revenir à l’objet qui a créé le pod à savoir un deployment, un statefulset ou un cronjob et vous pourrez visualiser aussi son manifest avec la commande edit :
kubectl edit deployment <nom_deployment>
Et pour récupérer le yaml du déploiement dans un fichier au format yaml :
kubectl get deployment <nom_deployment> -o yaml > monfichier.yaml
Mais si vous avez un doute sur la rédaction d’un objet/ressource utilisez explain. Par exemple pour la documentation sur les pods
Lors d’un précédent article, nous avions découvert les principes de kubernetes et quelques définitions. Il est temps pour nous de débuter dans les commandes kubernetes.
Si vous le souhaitez, vous pouvez retrouver plus de 70 vidéos gratuites dans cette playlist que je mets à votre disposition. Cette formation kubernetes vous permet de découvrir l’outil tranquillement à votre rythme et de bénéficier du code de chacun des tutos pour reproduire cela de votre côté sur votre laptop.
Premier pod et premier run
La couche logique nommée pod qu’apporte k8s est inévitable lorsque vous utilisez cet orchestrateur. Si vous avez pratiqué docker ou un autre conteneur runtime, imaginez un pod comme une couche supérieure. Cette couche dispose de la description des conteneurs qui vont être lancés et de leur environnement.
Nous le verrons plus tard, les objets k8s sont souvent lancés via des manifests. Ces derniers sont des fichiers descriptifs en format yaml. Mais pour débuter souvent on apprend dans un premier temps à lancer ces éléments à partir de la ligne de commande.
Alors comment lancer le plus simplement du monde un pod dans kubernetes ?
Suivant la version de kub qu evous utilisez vous pourrez réaliser un simple « kubectl run » à la manière d’un docker run de cette manière :
kubectl run mycontainer --image busybox
De cette manière nous venons de lancer un pod qui va lancer un conteneur dont l’image source est une busybox. Alors comme pour un run docker, vous pouvez aussi passer des commandes et par exemple vous connecter à un terminal :
kubectl run anothershell -it --image busybox -- sh
Là nous venons de lancer un pod et nous entrons directement dans le conteneur en question avec un shell à notre disposition. Mais nous pouvons également venir nous connecter à un shell d’un conteneur existant. Pour cela et comme pour docker, nous pouvons lancer un kubectl exec :
kubectl exec -ti <monpod> -- sh
Tout cela est assez facile à retenir si vous avez déjà pratiqué un peu de docker.
Comment lister les pods ?
kubectl get pods
Je vous invite à bien retenir la combinaison « kubectl get » qui revient en permanence sur kub. Elle permet de lister les objets d’un type donné.
Si vous êtes sur un cluster kubernetes, il est intéressant d’en revenr aux fondamentaux qu’il faut toujours garder en tête et notamment : kubernetes est un orchestrateur de conteneurs.
Ainsi, je vous invite à faire un :
kubectl get pods -o wide
L’option -o wide permet d’avoir plus d’information et notamment les les noeuds sur lesquels les conteneur ont été déployés. Ainsi poru aller plus loin vous pouvez vous rendre sur le serveur des pods que nous venons de lancer et lancer un simple
docker ps | grep "mycontainer\|another"
Vous devriez retrouver les conteneurs déployés par notre fameuse couche logique qui est le pod.
Vous pouvez aussi supprimer ces pods à l’aide de la commande :
kubectl delete pods <nom_du_pod>
Et un peu plus loin le deployment et le service
Alors maintenant nous allons encore empiler une brique logique sur les pods à savoir le deployement. Un deployment permet de lancer les pods de manière automatisée si on devait le résumer. Le deployment a des capacités étendues.
Ainsi, dans le deployment, nous le verrons plus tard, nous aurons un template de pods. C’est à dire la capcité à lancer et relancer des pods du même type… et même proposer de l’autoscaling de pod en fonction de la sollicitation des ressources ou non.
Alors comment créer un deployment qui va lancer un pod ?
Simplement avec la ligne de commande :
kubectl create deployment monnginx --image nginx
Avec cette ligne nous créons un déploiement de la manière la plus réduite qu’il existe à savoir en spécifiant juste l’image qui servira dans le pod à créer le ou les conteneurs. Dans notre cas, cette image est une nginx.
Je vous invite à lister ce nouvelle object de cette manière :
kubectl get deployment
Vous découvrez donc votre déploiement et si vous lancez un :
kubectl get pods
Vous retrouvez un pod préfixé du nom du déploiement.
Simple non ? Dans la pratique vous le verrez on ne lance presque jamais un pod seul mais vous le ferez par un deployment. Ce dernier apporte aussi de la persistance à votre pod notamment via un autre object créé par le deployment qui se nomme le replicaset. Il a principalement en charge l’existence de vos pods et le respect du bon nombre. Vous pouvez également lister les replicaset
kubectl get replicasets
Et si vous le souhaitez vous pouvez faire le test de supprimer un pod créé par votre deployment.
kubectl delete pods <nom_du_pod>
Que se passe-t-il si vous relisté les pods ensuite ? Kubernetes va recréer le nombre de pods que vous avez demandé de maintenir au deployment.
Alors comment supprimer définitivement les pods ? Simplement en supprimant le pod (il y a d’autres moyens pour d’autres cas de figures).
kubectl delete deployment monginx
Et là kubernetes va supprimer lui même les pods et replicaset.
Voici donc nos premier pas dans un cluster kubernetes réalisés. Si vous souhaitez progresser reportez vous à la playlist de formation kubernetes, c’est gratuit. Vous pouvez aussi retrouver le code pour faire vos tests ici. A bientôt !!
Vous êtes débutant sur ansible ? ou peut être un peu plus avancé mais vous cherchez à vous faire une piqûre de rappel pour remettre les choses en place ? Je vous propose de suivre ma formation ansible en ligne et totalement gratuitevia des vidéos youtube mais également à travers les articles de ce blog devops.
Alors depuis quelques vidéos nous avons posé les bases avec :
l’inventaire
les groupes
les hosts
l’installation
la CLI
Nous allons continuer sur ce chemin pour débuter petit à petit avec cet orchestrateur.
Depuis le temps il est peut être venu le moment de faire quelques actions sur nos serveurs.
A quoi sert le playbook ??
C’est assez simple, dans les définitions et concepts, nous avons vu qu’il y a deux bulles. Avec d’un côté l’inventaire et de l’autre les tasks qui sont des actions individuels que l’on peut rassembler dans des rôles.
Mais comment savoir quelles tâches sont jouées sur quelles machines cibles ?
C’est là qu’intervient le playbook. Il permet de coordonner les deux bulles l’inventaire et les rôles/tasks.
Il s’ait donc ni plus ni moins d’un fichier qui va dire quels groupes ou machines reçoivent quelles actions ? et bien sûr tout cela en format yaml comme la plupart des fichiers dans ansible.
Le playbook minimum resemble à ceci en terme de contenu :
- name: Mon Playbook !!
hosts: all
tasks:
- name: je debug
debug:
msg: "{{ var1 }}"
Découvrons un peu plus ce que l’on a écrit :
le nom du playbook « mon premier playbook »
son périmètre ou ses serveurs cibles, donc notre cas le groupe all (c’est à dire toutes les machines)
un bloc permettant de décrire les tasks
le nom de la première tâche « je debug »
l’appel à un module, en l’occurence « debug »
les paramètres passés à ce module « msg » (pour indiquer un message composé d’une variable au format jinja).
On est pas mal pour débuter avec notre playbook.
A côté il ne faut pas oublier d’avoir un inventaire, comme par exemple :
all:
hosts:
172.17.0.2:
Comprendre la ligne de commande ansible-playbook ?
Comment lancer le playbook ? Comprendre est peut être un bien grand mot mais au moins savoir un peu ce que l’on fait et ce que l’on peut faire avec ansible et ses playbooks.
Dans ce qui est dessous nous allons parler du binaire ansible-playbook.
Voyons déjà une ligne de commande minimaliste pour lancer ce fichier :
ansible-playbook -i inventory.yml playbook.yml
Très simple non ? bon généralement il y aura souvent un peu plus d’options que cela dans votre terminal.
Voici donc quelques options plus ou moins connues (mais même les moins connues sont intéressantes… le diable se cache dans les détails).
* -l : réduire le run à certaines machines ou certains groupes de votre inventaire
* -u : spécifier un user particulier utilisé sur la où les machines distantes
* -b : become est équivalent à taper sudo devant vos commandes pour élever vos privilèges
* -k : pour recevoir la demande de saisie du mot de passe pour la connexion ssh
* -K : le password pour l'élévation de privilège liée au sudo (become)
* -C : check c'est le dry run... très utile quand on ne connait pas la fréquence des runs ansible
* -D : diff c'est très utile pour avoir les différences et les modifications de ce que va faire ansible
* --ask-vault : permet de saisir un password pour déchiffrer les secrets que vous aurez chiffré avec vault
* --syntax-check : vérfier la syntax
* --vault-password-file : passer le vault password par un fichier
* -e : surcharger n'importe quelle variable
* -f : forks, nombre de parallélisation
* -t : filtrer sur les tags (--skip-tags)
* --flush-cache : éviter l'utilisation du cache
* --step : une tâche à la fois (confirmation via prompt)
* --start-at-task : commencer à une tâche spécifiquement
* --list-tags : lister tous les tags rencontrés
* --list-tasks : liste les tâches qui vont être exécutées
Là on y voit plus clair.
Et maintenant commençons à jouer avec les fichiers grâce au module file.
Débuter avec le module FILE
Créer un fichier, un répertoire, leur affecter des droits, des propriétaires… ce sont des commandes courrantes en linux et généralement on apprend cela en débutant : mkdir, touch, chown, chmod, ln…. Et bien pour ansible c’est un peu pareil.
Le module file peut prendre différents paramètres :
* attribute : définitions des paramètres particuliers d'un fichier : immutabilité etc... cf https://fr.wikipedia.org/wiki/Chattr
* force : pour les liens symboliques (créer même si le fichier source existe pas, la destination existe)
* group/owner : le propriétaire et le groupe du fichier ou du répertoire
* mode : les permissions sous les deux formats : "0755" ou "u=rwx,g=rx,o=rx"
* path : la localisation du fichier ou des répertoires
* recurse : création du chemin intermédiaire si n'existe pas (yes/no), attention cela est valable uniquement pour les répertoires
* src : pour les liens (hard ou symbolique)
* state : le type absent / directory / file / hard / link / touch
touch > créé le fichier vide
file > vérifie l'existence et les caractéristiques
Et donc nous voici prêt à écrire notre première task. Par exemple pour créer un répertoire :
Ici ansible va créer le répertoire xavki dans /tmp car le state est de type directory. Et le propriétaire de ce répertoire sera l’utilistaur root.
Avec l’utilisation du mode récursif :
- name: création du répertoire /tmp/xavki
file:
path: /tmp/xavki/1/2/3/4
recurse: yes
state: directory
owner: root
group: root
mode: 0755
Ou encore simplement créer un fichier vide avec touch :
- name: création du répertoire /tmp/xavki
file:
path: /tmp/xavki/1/2/3/4/fichier.txt
state: touch
owner: root
group: root
mode: 0755
Ou encore sous forme de lien :
- name: création du répertoire /tmp/xavki
file:
src: /tmp/xavki/1/2/3/4/
dest: /tmp/symlink
state: link #hard
owner: root
group: root
mode: 0755
Et enfin pour supprimer des fichiers ou des répertoires, il suffit d’utiliser le state absent très courrant dans les modules ansible.
- name: dir sans idempotence
file:
path: /tmp/xavki.txt
state: absent
Voilà j’espère que cette prise en main de ansible et que vous pouvez débuter en toute tranquilité à votre rythme. Je vous rappelle que vous pouvez venir découvrir plus de 900 vidéos sur la chaine xavki avec de nombreuses autres thématiques autour du devops bien sûr. Et retrouvez tous les fichiers de ces présentations sur le dépôt dédié à ansible.
Après avoir découvert la CLI, continuons dans l’univers d’ansible pour apprendre à l’utiliser et bien comprendre ses principes. Eh oui, apprendre ansible c’est bien mais il faut surtout bien assimiler les concepts et bien organiser. Le but de cet article est d’aider les débutants à trouver leur chemin et de partager les pratiques avec les personnes ayant un niveau un peu plus avancé.
Il existe pas mal de bonnes pratiques pour ne pas se louper et toujours garder le cap. Après quelques années d’expériences, je pense que dans le domaine de l’infra as code, il faut toujours avoir des règles, des principes, des nomenclatures pour garder l’objectif de toujours s’y retrouver et éviter les dérives. Et systématiquement, il y a des dérives et il faudra revenir vers vos règles pour ramener les choses dans l’ordre.
Mais bon, quand on ne connait pas ou même lorsque l’on connait juste un peu, ce n’est pas évident de trouver son cap. Et surtout il faut éviter d’inventer des choses qui existent déjà.
Rester dans les fondamentaux a aussi un gros avantage. Si vous intégrez de nouvelles personnes dans vos équipes et qu’elles connaissent déjà les bonnes pratiques, ce sera encore plus facile pour eux d’être intégré technologiquement parlant. Et cela est très important en matière d’IaC car celle-ci décrit votre infrastructure et l’installation d’élements importants.
Bref l’inventaire ou inventory est un élément essentiel dans ansible et dans votre infrastructure. Pour débuter avec ansible, il faut donc bien organiser celui-ci.
Inventory ou fichier d’inventaire, c’est votre infrastructure
L’inventory n’est pas juste ce fichier mais celui-ci est central. Nous verrons dans le point suivant que l’inventaire est aussi composé de ses variables que nous nommons… variables d’inventaire.
L’inventaire comprend la liste de vos machines, il peut être de 3 types de formats : json, yaml, ini. Personnellement, je préfère le format yaml. Pourquoi ? tout simplement car :
tous les autres fichiers ansible sont en format yaml, donc par souci d’homogénéité
également car grâce à son indentation il permet de bien visualiser la hiérarchie des groupes et hosts (même si nous ne sommes pas toujours fan du format yaml).
Une machine est appelée host assez logiquement. Un ou plusieurs hosts peuvent consituer un groupe (group). Les hosts peuvent être décrit par des éléments permettant de les joindre : ip, dns principalement.
Et si vous avez des nomenclatures de serveurs adaptées, vous pourrez même gagner du temps en utilisant des patterns, par exemple : srv-bdd-[1-5].
Et voilà l’essentiel. On peut également passer des variables directement dans ce fichier pour les associer à des groupes ou encore à des hosts. Mais je ne recommande pas de faire cela ou surtout il faut le limiter au maximum pour éviter que votre fichier d’inventaire ansible ne soit imbuvable.
Tutoriels : inventory, sa structure
Les variables d’inventaires ansible
Pour apprendre ansible, nous le reverrons mais il est important d’avoir un peu en tête la hiérarchie des variables ou précédence des variables… 22 types de variables au final dont certaines sont plus prioritaires que d’autres. Rassurez-vous vous n’avez pas besoin de toutes les connaître et surtout il faut éviter autant que possible de toutes les utiliser pour faciliter la maintenance de votre code.
Les formations ansible passent souvent assez rapidement sur les variables d’inventaires. C’est assez logique car sans mise en pratique c’est toujours délicat à aborder. Néanmoins il faut y passer quelques minutes pour bien débuter.
Notre fichier inventory est donc composé de hosts et de groupes. Nous allons donc pouvoir définir des variables de groupes et d’autres spécifiquement pour des hosts. Une fois que l’on a dit cela, il se dégage logiquement une hiérarchie ou précédence, les variables de hosts s’imposent aux variables de groupes. Ainsi, si vous avez un groupe de webserver avec une variable port = 80, si vous avez une exception dans ce groupe vous pourrez surcharger la variable de groupe par la variable du host en question port = 8080, par exemple.
Comment cela s’organise ?
Assez simplement au même niveau que notre inventory (le fichier), on va retrouver deux répertoires :
group_vars
host_vars
Une fois dans ces répertoires nous allons pouvoir créer soit :
des répertoires par groupe avec le nom du groupe ou par nom de host, vous pourrez ainsi créer plusieurs fichiers car vous avez beaucoup de variables et donc un besoin d’organiser un maximum
des fichiers yaml par nom de groupe ou nom de host car vous avez peu de variables.
Prenez bien votre temps pour bien comprendre et retenir ce type d’organisation car vous la retrouverez souvent ou elle pourra repondre à votre besoin. Pour débuter avec l’inventaire ansible c’est déjà un très bon début.
Voici une petite démonstration qui va permettre de rendre parlant tout cela avec une variable que l’on va surcharger à différents endroits.
Nous n’allons pas encore nous jeter dans le grand bain des rôles et playbooks. Pour un débutant, il est important de comprendre le fonctionnement de base d’un outil. En guise de formation et pour faire un premier pas avec la ligne de commande, je vous propose de pratiquer quelques commandes.
Pour savoir si ansible fonctionne de votre serveur/laptop source vers votre machine cible, nous pouvons utiliser le module ping. Attention, il ne s’agit pas là de faire un ping réseau ou icmp mais un ping au sens de ansible. En gros, ansible peut-il faire une connexion ssh vers la machine cible d’où vous le lancer.
Cela induit donc plusieurs paramètres : le serveur et le user. Il est important de ne pas oublier ce dernier car c’est avec lui que sera associé la clef ssh ou le password. L’utilisateur de départ qui initie la connexion est celui qui lance la commande ansible et l’utilisateur final peut être précisé par l’option -u.
Regardons la ligne ci-dessous :
ansible -i "node2," all -u vagrant -m ping
On a :
-i : qui précise le serveur target/cible, cela peut être un nom dns ou une ip. Important,vous devez rajouter une virgule après ce n’est pas une erreur de frappe. Suivi de « all » qui définit le groupe qui par défaut est all, nous reverrons cela plus tard avec l’inventaire.
-u : précise l’utilsiateur de la machine cible. Si vous utilisez une clef ssh c’est dans la conf ssh de ce user que vous devez avoir ajouté votre clef publique (dans le fichier authorized_keys).
-k : si vous n’avez pas de clef publique ou que vous souhaitez utiliser une connexion ssh via un passowrd vous pouvez le faire avec cette option. Ansible proposera alors de le saisir après avoir lancé la commande.
Vous pouvez forcer l’utilisation du password ssh de cette manière :
ansible -i "node2," all -u vagrant -m ping --one-line
Pour utiliser à distance une commande sur le shell distant :
ansible -i "node2," all -u vagrant -m command -a uptime
Et voilà vous avez lancé un uptime à distance et récupéré son résultat.
Poussons plus loin dans l’utilisation de la CLI
Bien sûr on peut même aller plus loin avec la simple ligne de commande ansible. Je vous propose pour vous former quelques exemples qui vous permettront de vous aguerrir par la pratique et sans faire de code pour le moment.
Par exemple, par la ligne de commande nous pouvons définir une variable et afficher son résultat comme si nous étions sur le serveur distant.
ansible -i "node2," all -e "var1=xavki" -m debug -a 'msg={{ var1 }}'
Ainsi nous passons quelques nouvelles options :
-e : pour définir une variable « var1 » avec sa valeur « xavki »
-m : pour préciser le module à utiliser, en l’occurence « debug »
-a : pour définir un paramètre de ce module, nous définissons « msg » avec la valeur de var1. Remarquez le double accolade qui est un format jinja, ansible reposant sur python.
Maintenant lançons un shell un peu plus élaboré avec quelques pipe dans ansible :
ansible -i "node2," all -u vagrant -m shell -a "ps aux | grep vagrant | wc -l" --one-line
On retrouve toujours nos options et nous avons simplement changé de module en utilisant « shell ». Et on profite du oneline pour l’output.
Comment installer à distance un serveur nginx :
ansible -i "node2," all -b -m apt -a 'name=nginx state=latest'
Là encore changement de module pour « apt » et définition de deux paramètres de ce module avec -a.
De même pour arrêter ou redémarer le service systemd :
ansible -i "node2," all -b -m service -a 'name=nginx state=stopped'
Et encore un peu plus…
Et on peut pousser encore plus loin avec d’autres onelines très facile à comprendre et utiliser.
Comment faire l’équivalent d’un scp avec ansible en une ligne ?
ansible -i "node2," all -m copy -a 'src=toto.txt dest=/tmp/titi.txt'
On y définit le fichier source et la destination. Mieux qu’un scp, ansible va faire jouer son idempotence et ne réalisera cette action que si nécessaire. C’est à dire si le fichier n’existe pas ou si il est différent du fichier source.
Pour faire l’inverse et récupérer un fichier d’une machine distante ?
ansible -i "node2," all -m fetch -a 'src=/tmp/titi.txt dest=xavki.txt flat=yes'
Cette fois-ci on utilisera le module « fetch », là encore en une ligne pour débuter tranquillement et apprendre à notre rythme.
Mais savez vous que à chaque run, ansible récupère ce que l’on appelle des facts, ce sont des variables génériques soit nécessaire pour son fonctionnement soit souvent utiles.
Eh bien regardons les facts disponibles pour une machine donnée :
ansible -i "node2," all -m setup -a "filter=ansible_distribution*"
Vous voyez que dans cette exemple, nous filtrons ceux-ci pour ne récupérer que les facts relatifs à la distribution.
Forez vous avec des tutorials ansible sur la chaine Xavki
Vous le savez, la chaine xavki que je tiens, est axée autour du devops. Comment devenir devops ou en acquérir les réflexe ou même la philosophie ? Dans ce domaine, certains outils sont marquants et sortent du lot. L’infrastructure as code est bien sûr un peu essentielle. De nombreux softs existent dans ce domaine et certains ont gagné des parts ces dernières années. Ansible fait partie de ces outils du parfait devops.
Ansible en quelques principes ?
Les principes d’ansible en font de lui un des plus faciles à adopter du marché. Totalement gratuit, il se base sur le langage python qui a fortement progressé ces dernières années agrandissant sensiblement son périmètre au fur et à mesure des années (IA, big data, statistiques, développement, CMS, framework…).
En outre, pour faciliter les choses, il se base sur une méthode de type push. A savoir que ansible ne nécessite pas d’agent. Mieux, une simple connexion ssh et python vous permettront de faire l’essentiel des tâches que vous aurez à réaliser. Un bon point pour les sysadmins qui restent fidèles à leurs scripts bash avec des boucles pour lancer un peu de ssh. Ou encore aux développeurs python qui utilisent paramiko avec une gestion délicate de l’idempotence… Ansible peut satisfaire vraiment beaucoup de monde.
Certains apprécieront moins son langage descriptif à base de yaml. Néanmoins, il permet de glisser sur la tendance et permet d’organiser clairement le code sous réserve de respecter les bonnes indentations. Un bon IDE vous y aidera d’autant plus.
Quelques éléments centraux :
le fichier d’inventaire (inventory file) : facile à deviner, il permet de regrouper la liste des machines, des groupes de machines ou même des patterns de serveurs
les variables d’inventaire : il s’agit des variables spécifiques à des hosts/serveurs ou à des groupes de machines. Elles sont stockées soit dans le fichier d’inventaire mais plutôt dans des répertoires host_vars et group_vars, respectivement pour les serveurs et les groupes de serveurs.
le rôle : regroupe un ensemble de tâches ou d’actions cohérentes pour installer, configurer etc (par exemple installer un serveur web). Un rôle est constitué notamment d’actions que l’on appelle tasks.
les tasks : ce sont des tâches individuelles qui utilise un module suivant des paramètres adaptés à la finalité de l’action
le playbook : fichier permettant de faire la jointure entre l’inventaire et les rôles. Il a une fonction centrale qui permet de dire quel groupe ou quel serveur va avoir tels et tels rôles de joués.
Respecter ces principes est PRIMORDIAL pour la faciliter de l’organisation du code dans son ensemble. S’agissant de bonnes pratiques, n’importe quelle personne ayant suivie une formation ansible pourra s’y retrouver relativement facilement et pouvoir découvrir votre infra en lisant comme dans un livre (on l’oubli trop souvent).
Installer le binaire
Potentiellement il y a 3 choix pour installer ansible :
via le gestionnaire de module python comme pip (pip3)
via le téléchargement de la source
via le gestionnaire de paquets de votre distribution préférée
Personnellement je préfère de loin cette dernière solution. Mais le contexte peut vous en faire décider autrement.
Sur debian on va simplement faire
sudo apt update
sudo apt install ansible
Et voilà le tour est joué. Vous pourrez très bien le faire sur redhat/centos/fedora car le projet ansible est porté par Redhat.
Pour configurer ansible, il est nécessaire de retenir un nom de fichier : ansible.cfg.
Vous pourrez le retrouver dans /etc/ansible/ ou encore dans votre home ou enfin à la racine de votre code (localisation du playbook).
Si vous avez installé ansible, vous êtes prêts à faire feu !!! Sauf si vous n’avez pas créé votre clef ssh. Dans ce cas, je vous propose une petite piqure de rappel ici.
Bref comment débuter et faire vos premières commandes ?
Pour pinguer les machines cibles.
ansible -i "node2," all -u vagrant -m ping
Attention, on parle d’un ping au sens ansible à savoir réussir une connexion ssh globalement.
-i : permet de préciser le fichier d’inventaire mais comme nous ne l’avons pas encore vu et que notre cible est une seule et unique machine on va simplement préciser cette machine entre double quote MAIS n’oubliez pas la virgule et le all (pour indiquer que cette machine fait partie du groupe all.
-u : permet de préciser l’utilisateur distant pour notre ssh ( <user>@<server>)
-m : permet de préciser l’utilisation d’un module, en l’occurence le module ping.
Dans notre cas nous avons une clef ssh mais on peut également faire la même chose par un password ssh :
ansible -i "node2," all -u vagrant -k -m ping
et si vous voulez forcer le fonctionnement par password pour une raison précise :
Installer kubernetes on premise est un petit défi même si certaines solutions sont plus simples que d’autres. J’ai pu tester différentes options comme kubadm, rancher ou en core kubespray mais jamais « the hard way ». Cette dernière méthode est certainement la meilleure pour mieux appréhender la constitution d’un cluster. Certes, cette méthode est formatrice mais peut effrayer notamment faire peur si on veut installer un cluster kubernetes pour de la production. Si vous souhaitez vous former à kubernetes rendez-vous sur cette playlist.
Je vous propose de découvrir une méthode intermédiaire mais d’y ajouter le nécessaire pour rendre votre cluster hautement disponible. Et vous allez voir que contrairement à ce que l’on pense ce n’est pas si compliqué. Je vous invite après cette installation de prendre du recul et d’identifier les points critiques pour être bien à l’aise avec ces éléments. Car le jour où votre cluster kube dysfonctionne en production et on premise, vous pouvez transpirer pas mal pour débuguer car généralement vous n’avez pas mis qu’une seule application dessus.
Mais je vous invite à découvrir les vidéos dans cette playlist youtube :
De quoi avons nous besoin côté machines virtuelles ?
Eh oui on veut tester un environnement de production mais rien ne vous empêches de vous faire la main sur votre laptop ou de simples machines virtuelles. Personnellement, j’utilise souvent virtualbox et je fais le provisioning de manière automatisé avec vagrant. Le Vagrantfile est donc ce qui va le mieux décrire notre infrastructure. J’ai volontairement évité de factoriser les boucles de manière à rendre cela plus lisisble.
Notre infrastructure sera composée :
1 machine de déploiement pour jouer kubespray (ansible) on va pas salir notre laptop non plus et en prod on ne déploie pas de son laptop.
2 haproxy : ils serviront pour loadbalaner l’api kubernetes pour les commandes kubectl vers nos master (port 6443) et également pour loadblancer sur les noeuds worker les flux http/https (80/443)
2 masters : il vaudrait mieux 3 mais notre but est de faire de simples tests tout de même (en ajoute run n’est juste que rajouter une ligne dans le ansible)
1 node : notre objectif n’est pas d’héberger mais de tester un cluster kube et son environnement haute disponibilité.
Ce qui donne :
Vagrant.configure(2) do |config|
common = <<-SHELL
if ! grep -q deploykub /etc/hosts; then sudo echo "192.168.7.120 kdeploykub" >> /etc/hosts ;fi
if ! grep -q node01 /etc/hosts; then sudo echo "192.168.7.121 kmaster01" >> /etc/hosts ;fi
if ! grep -q node02 /etc/hosts; then sudo echo "192.168.7.122 kmaster02" >> /etc/hosts ;fi
if ! grep -q node03 /etc/hosts; then sudo echo "192.168.7.123 knode01" >> /etc/hosts ;fi
if ! grep -q node04 /etc/hosts; then sudo echo "192.168.7.124 haproxy01" >> /etc/hosts ;fi
if ! grep -q node05 /etc/hosts; then sudo echo "192.168.7.125 haproxy02" >> /etc/hosts ;fi
sudo yum -y install vim tree net-tools telnet git python3-pip sshpass
sudo setsebool -P haproxy_connect_any=1
sudo echo "autocmd filetype yaml setlocal ai ts=2 sw=2 et" > /home/vagrant/.vimrc
sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config
sudo systemctl restart sshd
SHELL
config.vm.box = "centos/7"
config.vm.box_url = "centos/7"
config.vm.define "kdeploykub" do |kdeploykub|
kdeploykub.vm.hostname = "kdeploykub"
kdeploykub.vm.network "private_network", ip: "192.168.7.120"
kdeploykub.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "1" ]
v.customize [ "modifyvm", :id, "--memory", "512" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "kdeploykub"]
end
config.vm.provision :shell, :inline => common
end
config.vm.define "kmaster01" do |kmaster01|
kmaster01.vm.hostname = "kmaster01"
kmaster01.vm.network "private_network", ip: "192.168.7.121"
kmaster01.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "2" ]
v.customize [ "modifyvm", :id, "--memory", "2048" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "kmaster01"]
end
config.vm.provision :shell, :inline => common
end
config.vm.define "kmaster02" do |kmaster02|
kmaster02.vm.hostname = "kmaster02"
kmaster02.vm.network "private_network", ip: "192.168.7.122"
kmaster02.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "2" ]
v.customize [ "modifyvm", :id, "--memory", "2048" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "kmaster02"]
end
config.vm.provision :shell, :inline => common
end
config.vm.define "knode01" do |knode01|
knode01.vm.hostname = "knode01"
knode01.vm.network "private_network", ip: "192.168.7.123"
knode01.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "2" ]
v.customize [ "modifyvm", :id, "--memory", "2048" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "knode01"]
end
config.vm.provision :shell, :inline => common
end
config.vm.define "haproxy01" do |haproxy01|
haproxy01.vm.hostname = "haproxy01"
haproxy01.vm.network "private_network", ip: "192.168.7.124"
haproxy01.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "1" ]
v.customize [ "modifyvm", :id, "--memory", "512" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "haproxy01"]
end
config.vm.provision :shell, :inline => common
end
config.vm.define "haproxy02" do |haproxy02|
haproxy02.vm.hostname = "haproxy02"
haproxy02.vm.network "private_network", ip: "192.168.7.125"
haproxy02.vm.provider "virtualbox" do |v|
v.customize [ "modifyvm", :id, "--cpus", "1" ]
v.customize [ "modifyvm", :id, "--memory", "512" ]
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
v.customize ["modifyvm", :id, "--name", "haproxy02"]
end
config.vm.provision :shell, :inline => common
end
end
Commençons par le loadbalancer externe haproxy et sa vip gérée par keepalived
Attention avant de lancer le ansible kubespray, il est nécessaire que votre haproxy et votre VIP soient mis en place.
Donc première chose on installe les deux haproxy et keepalived :
sudo apt install - y haproxy keepalived
et ensuite on édite la configuration de haproxy
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
listen stats
bind *:9000
stats enable
stats uri /stats
stats refresh 2s
stats auth xavki:password
listen kubernetes-apiserver-https
bind *:6443
mode tcp
option log-health-checks
timeout client 3h
timeout server 3h
server master1 192.168.7.121:6443 check check-ssl verify none inter 10000
server master2 192.168.7.122:6443 check check-ssl verify none inter 10000
balance roundrobin
Bien sûr derrière je vous laisse reload le service systemd de haproxy.
Ensuite ajoutons la configuration de keepalived :
vrrp_script reload_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 1
}
vrrp_instance VI_1 {
virtual_router_id 100
state MASTER
priority 100
# interval de check
advert_int 1
# interface de synchro entre les LB
lvs_sync_daemon_interface eth1
interface eth1
# authentification entre les 2 machines LB
authentication {
auth_type PASS
auth_pass secret
}
# vip
virtual_ipaddress {
192.168.7.130/32 brd 192.168.7.255 scope global
}
track_script {
reload_haproxy
}
}
Dans cette configuration on utilise une VIP qui switchera en fonction du service haproxy et du fait qu’il soit fonctionnel au vue de systemd ou non. Cette VIP est la :
192.168.7.130
Par défaut cette VIP sera portée par la machine haproxy1. Si celui-ci tombe cette adresse va être installée sur haproxy2 grâce à keepalived.
Vérifiez que les deux services (haproxy et keepalived) soient bien démarrés sur les 2 machines haproxy. Activez le redémarrage au reboot avec le systemctl enable.
Maintenant configurons kubespray
Kubespray c’est du ansible. donc première chose vous devez l’installer sur la machine de déploiement et installer également pip :
Ensuite on prépare le ansible on va recopier le modéèle d’inventaire :
cp -rfp inventory/sample inventory/mycluster
Puis on modifie cet inventory :
# ## Configure 'ip' variable to bind kubernetes services on a
# ## different ip than the default iface
# ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value.
[all]
kmaster01 ansible_host=192.168.7.121 ip=192.168.7.121 etcd_member_name=etcd1
kmaster02 ansible_host=192.168.7.122 ip=192.168.7.122 etcd_member_name=etcd2
knode01 ansible_host=192.168.7.123 ip=192.168.7.123 etcd_member_name=etcd3
# ## configure a bastion host if your nodes are not directly reachable
# bastion ansible_host=x.x.x.x ansible_user=some_user
[kube-master]
kmaster01
kmaster02
[etcd]
kmaster01
kmaster02
knode01
[kube-node]
knode01
[calico-rr]
[k8s-cluster:children]
kube-master
kube-node
calico-rr
On y a donc déclaré nos nodes master et notre worker.
Particularité ETCD
Particularité pour notre test, nous allons faire ce qu’il ne faut pas faire c’est à dire héberger les services etcd sur les master et surtout sur 1 worker. Pourquoi cela ? Etcd nécessite un nombre impair de noeuds pour son bon fonctionnement. Donc on pourrait en avoir 1 ou 3. Mais si on veut faire des tests de coupures de noeuds il est préférable d’en avoir 3. J’ai donc salement choisi d’en ajouter un sur le worker.
En production il serait recommandé d’externaliser les etcd et ne pas les avoir au sein des machines kubernetes (plus y ajouter des backups etc).
Dernière configuration ansible
Maintenant il nous faut éditer le fichier du group all :
On vient donc de préciser que la VIP est notre point d’entrée du loadbalancer et également que votre cluster répondra sur le nom de domaine elb.kub (vous pourrez modifier votre /etc/hosts).
Maintenant c’est partie on peut jouer notre ansible en tant que user vagrant et on utilisera le password « vagrant »… un standard sur vagrant.
Suivre les technologies du moment, c’est souvent le but recherché par tous les sysadmin et devops. Et cela n’est pas facile avec la démultiplication des technologies de puis l’arrivée du cloud, des conteneurs et de l’infrastructure as code. Une évolution inévitable à l’heure de l’industrialisation de l’informatique, secteur encore jeune qui a besoin d’évoluer.
Kubernetes est pleinement dans cette mouvance. D’ailleurs, le nombre de technologies ou d’outils rattachés à celui est presque affolant. Tous les jours de nouveaux outils, régulièrement de nouveaux concepts émergent également. Bref on en a jamais fini avec kubernetes. D’ailleurs l’une des forces d’un bon devops, c’est de savoir aussi attendre et faire le bon choix parmi cette forêt d’outils qui demain n’existeront plus pour certains.
Apprendre par le bon bout…
K8S pour les intimes commence déjà par docker et les principes de conteneurisation. Je vous invite à découvrir docker via la chaine xavki si vous le souhaitez. Certes kubernetes tente de se détacher de ce « conteneur runtime » mais néanmoins pour apprendre se focaliser sur docker est plutôt une bonne approche.
Mais k8s se comprend avant de s’apprendre avec de nombreux concepts et définitions. Il faut imaginer déjà que k8s est un orchestrateur de conteneurs mais c’est aussi une grosse couche logique sur les conteneurs, leurs réseaux, le stockage et le dns qui va avec.
Dans cette introduction, si vous avez du mal à imaginer kubernetes, je vous propose de vous le présenter avec simplicité.
Kubernetes vient d’ailleurs du grec et signifie timonier. On peut le comprendre car il est à la tête de ce magistral porte conteneurs.
Avec kub vous allez pouvoir bénéficier de nombreuses fonctions avancées et rares, que ne fournissent pas forcément les autres orchestrateurs de conteneurs :
autoguérisson
autoscaling
versionning
rolling update
sécurisation des communication entre les conteneurs par différentes couches
gestion de la persistence de la donnée…
Mais attention ces grands pouvoirs nécessitent une grande responsabilité comme on dit. La responsabilité de celui qui manage le paquebot. Le moindre grain de sable dans cette horlogerie et c’est le trou noir compte tenu du rôle central de l’outil. Courage donc aux sysadmins et aux devops face à ce challenge.
C’est aussi pour cela que beaucoup de choix s’orientent vers des solutions plus ou moins managés :
les solutions cloud : GKE, EKS…
les managers : rancher…
Bref c’est une aventure et on peut même simplifier en indiquant que c’est un vrai outil de virtualisation avec les responsabilités qui vont avec (et les ressources et le temps).
On ne va pas vers kubernetes pour se faire plaisir, il faut y trouver un réel intérêt sur de nombreux facteurs. On ne le fait pas pour faire comme les autres ou parce que c’est la techno du moment.
De nouvelles notions et définitions dans kubernetes
Kubernetes fait partie des technologies embarquant le plus de concepts je trouve. Intellectuellement c’est très intéressant mais c’est aussi une charge.
En voici quelques unes :
Le Pod
Inévitable, il peut regrouper un ou plusieurs conteneurs. C’est la couche logique par excellence qui va permettre une total abstraction avec l’échelon inférieur le conteneur.
Le pod contient les applicatifs, il peut être plus ou moins éphémères suivant sont types. Vous pouvez associer avec lui un applicatif et une tâche ponctuelle nécessaire pour son installation. Il peut être scallé up ou down pour soutenir la charge etc…
Les services
On pourrait les rapprocher à l’exposition des pods mais en même temps il joue une sorte de fonction de dns. L’idée est de sécuriser et permettre l’accès aux pods ayant la même fonction.
Avec eux vous pourrez en quelque sorte exposer vos conteneurs ou à l’opposé les barricader. Si les pods d’un même applicatif sont distribués sur différents noeuds, il aura la charge de fournir comment accéder à ces pods (ip port…). Il permet également de faire du dns au sein du cluster kub.
Le replicaset
Résumons-le simplement à la gestion du nombre de pods.
Le deployment
Un autre échelon très important qui englobe les replicasets et les pods. Il permet notamment de gérer la santé des pods et leur scaling suivant certaines règles. Un deployment va permettre de créer des pods suivant un template de pods. Idéal pour l’instanciation.
Les statefulsets
On peut les résumer à des deployments avec les mêmes fonctions auxquels on ajouter des facilités pour gérer la persistence de la data qui est attachée aux pods. Idéal pour les bases de données essentiellement.
Bon courage à tous mais le plaisir est au rendez-vous si vous ne brûlez pas les étapes.