Après avoir vue la partie du rôle Ansible chargée de déployer l’ensemble des prérequis à l’installation de kubernetes sur les serveurs, il est temps de poursuivre sur la préparation de la configuration à proprement parler des composants K8S.
Cet article est directement lié au précédent. Il est conseillé de prendre connaissance de ce qui a été dit précédemment avant de démarrer la lecture de ce qui va suivre.
Pour rappel:
Ce rôle va se poursuivre avec le reste de fichiers et templates rattachés à l’arborescence Ansible présentée dans l’article précédent.
Nous nous étions arrêtés sur le déploiement de containerd à travers le fichier 04-containerd.yml.
Le fichier 05-configure-k8s.yml est en charge de préparer les éléments de configuration qui vont être exploités par Kubernetes.
Comme pour les fichiers yaml présentés dansl'article précédent, il va d'abord être affiché dans sa totalité (voir ci-dessous), puis sa logique et l'objectif des actions Ansible qui le compose vont être décrit par la suite.
---
#Configuration pour Kubeadm
- name: "Set kubeadm configuration"
become: yes
template:
src: "kubeadm.conf.j2"
dest: "/etc/kubernetes/kubeadm.conf"
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.kubeadm-config
#Création des repertoires de destination pour les configurations
- name: Creates configuration directory for admin user
become: yes
file:
path: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
- name: Creates network configuration directory for admin user
become: yes
file:
path: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/network"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
- name: Creates cloud configuration directory for admin user
become: yes
file:
path: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
- name: Creates cloud cpi configuration directory for admin user
become: yes
file:
path: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/cpi"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.cpi
- name: Creates cloud csi configuration directory for admin user
become: yes
file:
path: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/csi"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
- name: Creates kube directory for adminuser
become: yes
file:
path: "/home/{{ var_admin_user }}/.kube"
state: directory
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
#Configuration cilium
- name: "Set cilium configuration"
become: yes
template:
src: "cilium-values.yaml.j2"
dest: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/network/cilium-values.yaml"
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.cilium
#Récupération de la CLI de cilium
- name: Download and extract cilium cli
become: yes
ansible.builtin.unarchive:
src: "{{ var_cilium_cli }}"
dest: /usr/local/bin
remote_src: yes
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.cilium"
#Configuration du Cloud Provider Interface (vSphere)
- name: "Set vSphere CPI configuration"
become: yes
template:
src: "vsphere-cloud-controller-manager.yaml.j2"
dest: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/cpi/vsphere-cloud-controller-manager.yaml"
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.cpi
#Configuration du Container Storage Interface (vSphere)
- name: "Set namespace csi config"
become: yes
template:
src: "csi-namespace.yaml.j2"
dest: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/csi/namespace.yaml"
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.csi-namespace-config
- name: "Set csi config"
become: yes
template:
src: "csi-vsphere.conf.j2"
dest: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/csi/csi-vsphere.conf"
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.csi-config
- name: "Set csi yaml"
become: yes
template:
src: "vsphere-csi-driver.yaml.j2"
dest: "/home/{{ var_admin_user }}/{{ var_vip_kub_endpoint }}/cloud/csi/vsphere-csi-driver.yaml"
owner: "{{ var_admin_user }}"
mode: 0770
when: "'TAG-COOLCORP-ROLE-MASTER' in tags"
tags: role_k8s_deploy.configure-k8s.csi-config
Pour rappel, toutes les valeurs des variables appelées sont déclarées dans le fichier main.yml du dossier defaults de l'arborescence Ansible.
Cette même arborescence contient le dossier templates dans lequel on retrouve les fichiers *.j2 servant de base pour être recopié sur les serveurs.
Lorsqu'ils sont recopiés à destination, les variables déclarées au sein des templates vont être remplacées par leur valeur issue du fichier main.yml du dossier defaults.
Le traitement démarre par la création du fichier kubadm.conf qui va être déployé sur chaque control plane, dans /etc/kubernetes
Le fichier est basé sur le template kubeadm.conf.j2, présent dans le dossier templates de l’arborescence du rôle Ansible.
---
apiVersion: kubeadm.k8s.io/{{ var_api_version }}
kind: ClusterConfiguration
kubernetesVersion: {{ var_kub_version }}
networking:
podSubnet: "{{ var_pod_subnet }}"
serviceSubnet: "{{ var_service_subnet }}"
controlPlaneEndpoint: "{{ var_vip_kub_endpoint }}:6443"
clusterName: "{{ var_kub_name }}"
controllerManager:
extraArgs:
cloud-provider: external
apiServer:
certSANs:
- {{ var_vip_kub_vip }}
- {{ var_vip_kub_endpoint }}
Kubeadm est l’installateur par défaut de Kubernetes, il suit les versions du projet et permet d’initialiser le cluster, d’y ajouter les nodes et de le mettre à jour.
D’autres moyens sont disponibles pour déployer K8S, mais kubeadm reste largement employé. Par contre, il est important de noter qu’un cluster installé par kubeadm, doit se maintenir avec kubeadm. Il est fortement déconseillé d’utiliser par la suite d’autres méthodes de gestion et d’installation.
Il est possible de lancer kubeadm directement, mais dans le cas présenté ici, nous allons lui passer le fichier de configuration générée par Ansible lorsqu’on sera à l’étape de l’initialisation du cluster.
Ainsi il est possible de figer les paramètres à l’avance. La plupart de ces paramètres sont définis en tant que variables, et donc disponibles dans le fichier main.yaml du répertoire defaults du rôle Ansible.
var_api_version fait référence à la version de l’API kubernetes que kubeadm utilise pour définir et gérer la configuration du cluster. Étant donné que cette dernière évolue avec le temps, il est intéressant de pouvoir la changer facilement. Pour l'instant il s'agit de la v1beta3
var_kub_version est la version de kubernetes que l’on souhaite déployer, dans cet exemple, il s’agit de la 1.29.4.
var_pod_subnet représente le sous réseau dédié au pod. Il s’agit d’un réseau interne au cluster. Nous aurons l’occasion d’y revenir, mais un pod est l’élément de base pour K8S. Il n’existe pas d’objet de plus bas niveau. Le conteneur en lui-même n’est pas représenté dans l’API Kubernetes, c’est le pod qui est utilisé. Un pod par contre peut contenir un ou plusieurs conteneurs.
Choisissez toujours pour ce réseau, un masque autorisant suffisamment d’IP, car le nombre de pod a tendance à augmenter très vite. Dans mon cas, j’utilise le subnet 10.11.0.0/16.
Très important également, n’utilisez pas un réseau connu de votre infrastructure et/ou routé de votre côté, sinon vous risquez de rencontrer des problèmes.
var_service_subnet est également un réseau, mais cette fois-ci rattaché aux objets de type service. Nous aurons également l’occasion d’en reparler, mais un service permet d’exposer vos pods. Sans l’usage de services, vos pods, et le ou les conteneurs qui s’y trouvent, sont isolés et ne peuvent pas communiquer au sein du cluster et encore moins à l’extérieur de ce dernier.
Comme pour le réseau dédié au pod, il faut retenir un masque disposant de beaucoup d’IP et d’une plage non utilisée sur votre infrastructure. Dans le cluster déployé ici, on retiendra 10.12.0.0/16.
Cliquez sur l'image pour l'agrandir.
var_vip_kub_endpoint est le nom dns du cluster sur lequel sera exposé l’API K8S.
Pour rappel, l’API est portée par les control plane. Dans l’architecture cible, on dispose de trois serveurs de ce type pour assurer une haute disponibilité du service.
On passe par un load balancer en amont chargé de rediriger les requêtes vers l’un ou l’autre des control plane. Le nom DNS devra donc résoudre l’IP du loadbalancer. Le nom retenu est kub.coolcorp.priv.
var_kub_name est le nom court du cluster, soit simplement kub dans mon cas.
Cliquez sur l'image pour l'agrandir.
var_vip_kub_vip est l’IP du loadbalancer (192.168.10.91) et est utilisé un peu plus bas, dans la partie certSANs du template, en même temps que var_vip_kub_endpoint déjà présenté.
Dans kubernetes, tous les flux sont chiffrés. Pour cela kubernetes fourni sa propre autorité de certification racine en charge de créer tous les certificats internes au système. Il est donc important que le certificat qui va être rattaché à l’API dispose de l’IP et du nom par lesquels elle va être appelée.
La suite des actions décrite dans le fichier ansible 05-configure-k8s.yml permet de définir des répertoires destinés à héberger les différents fichiers de configurations que l’on va générer.
Personnellement j’utilise mon home, dans lequel je créer des sous-dossiers par périmètre.
Je ne vais pas rentrer dans le détail des répertoires, pour aller directement à la création du fichier de configuration de cilium.
Cilium a été retenu dans l’architecture comme solution réseau. Pour rappel, Kubernetes, dans son approche modulaire, ne fournit pas de driver pour gérer le network. Il en existe de nombreux, présentant des caractéristiques différentes, comme le protocole de routage retenu, les capacités de filtrage intégrées… L’important est de sélectionner un provider compatible avec CNI (n’hésitez pas à revoir la partie architecture).
Cilium est de plus en plus déployé, de par son intégration de eBPF, (extended Berkeley Packet Filter) une technologie avancée pour l’analyse et la manipulation de paquets réseau. Nous rentrerons dans le détail le moment venu.
Un fichier cilium-values.yaml.j2 présent dans le dossier templates de l’arborescence du rôle Ansible permet de définir les paramètres de bases.
kubeProxyReplacement: strict
k8sServiceHost: {{ var_vip_kub_vip }}
k8sServicePort: 6443
ipam:
mode: "cluster-pool"
operator:
clusterPoolIPv4PodCIDRList:
- "{{ var_cilium_subnet }}"
hubble:
relay:
enabled: true
ui:
enabled: true
frontend:
server:
ipv6:
enabled: false
tls:
auto:
enabled: true
method: helm
certValidityDuration: 1095
var_cilium_subnet
: Comme pour les pods et les services, il va être nécessaire de définir un nouveau subnet. Cilium de par l’usage de la technologie VXLAN ("Virtual Extensible LAN") et du tunneling associé va nécessiter un réseau supplémentaire. Retenez également un subnet non utilisé de votre côté (dans l’exemple 10.13.0.0/16).Le reste de la conf permet de désactiver l’IPv6, d’activer TLS et également d’activer la fonction hubble relay chargée de collecter et d’agréger les données de flux réseau, ainsi que la fonction hubble ui qui est une GUI pour visualiser et analyser le trafic réseau au sein du cluster.
À noter qu’on déploie également le CLI cilium, car celle-ci peut être pratique pour lancer des diagnostics ou piloter les composants cilium.
C’est ensuite la partie CPI pour Cloud Provider Interface qui est configurée via le template vsphere-cloud-controller-manager.yaml.j2.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: cloud-controller-manager
labels:
vsphere-cpi-infra: service-account
component: cloud-controller-manager
namespace: kube-system
---
apiVersion: v1
kind: Secret
metadata:
name: vsphere-cloud-secret
labels:
vsphere-cpi-infra: secret
component: cloud-controller-manager
namespace: kube-system
# NOTE: this is just an example configuration, update with real values based on your environment
stringData:
{{ var_vcenter }}.username: "{{ var_vcenter_user }}"
{{ var_vcenter }}.password: "{{ var_vcenter_password }}"
---
Contenu directement récupéré sur https://raw.githubusercontent.com/kubernetes/cloud-provider-vsphere/master/releases/v1.29/vsphere-cloud-controller-manager.yaml
Ce fichier reprend en grande partie le contenu fourni par vmware que l’on peut trouver ici.
(attention, le fichier peut évoluer avec le temps et les versions de Kubernetes, comme au moment de cet article on cherche à déployer la version 1.29 de K8S, l’URL fait référence à la 1.29).
Seul le début du fichier est customisé avec des variables pour constituer le template. Il reprend les éléments de connexion au vCenter dont le compte de service dédié.
On n’y retrouve donc un password issue de la variable var_vcenter_password.
Comme expliqué dans l’article précédent, ce n’est pas une bonne pratique d’avoir cette valeur en clair dans le fichier des variables main.yml du dossier defaults.
On termine cette partie par la configuration du driver CSI qui va nous permettre d’utiliser par la suite des volumes persistent contenu dans des datastores vmware.
C’est d’abord le fichier décrivant un namespace dédié qui est créé grâce au template csi-namespace.yaml.j2 dont le contenu est le suivant.
apiVersion: v1
kind: Namespace
metadata:
name: vmware-system-csi
Nous aurons l’occasion de reparler des namespaces, mais c’est un objet Kubernetes permettant de définir une zone logique au sein de son cluster auquel on pourra spécifier des droits d’accès spécifiques et des quotas de ressources (CPU, mémoire…). On pourra également utiliser indirectement les namespaces pour segmenter des flux réseau au sein du cluster (grâce à des network policy).
C’est ensuite le template csi-vsphere.conf.j2 qui va servir de référence.
[Global]
cluster-id = "{{ var_kub_name }}"
[VirtualCenter "{{ var_vcenter }}"]
insecure-flag = "true"
user = "{{ var_vcenter_user }}"
password = "{{ var_vcenter_password }}"
port = "443"
datacenters = "{{ var_vcenter_datacenter }}"
On reprend ici des variables déjà connues, comme les accès au vCenter et le nom du cluster K8S.
Le dernier composant à créer est basé sur le template sphere-csi-driver.yaml.j2 Celui-ci est directement copié de ce que fourni vmware à cette URL.
Cette fois-ci la version spécifiée dans l’URL est directement la version du driver (dans l’exemple la 3.0.2).
---
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
name: csi.vsphere.vmware.com
spec:
attachRequired: true
podInfoOnMount: false
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: vsphere-csi-controller
namespace: vmware-system-csi
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: vsphere-csi-controller-role
rules:
Contenu directement récupéré sur https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v3.0.2/manifests/vanilla/vsphere-csi-driver.yaml.
Comme pour le CPI, le CSI évolue dans le temps et doit être en adéquation avec la version de k8s sur laquelle il va être déployé. Il est important de bien suivre les matrices de compatibilité.
Avant de terminer j’en profite pour faire un petit aparté sur le compte d’accès utilisé par les différents add on vmware (CPI/CSI). Ce compte doit disposer de droits biens spécifiques qui sont décrits dans la doc d’installation. On peut même utiliser deux comptes différents. Dans mon cas, comme pour terraform et Ansible c’est un compte AD que j’utilise.
On peut se simplifier la vie en donnant des droits administrateurs, mais ça reste une mauvaise pratique. Dans tous les cas, privilégiez au moins un compte spécifique.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Ces accès sont nécessaires, car lorsqu’on va indiquer à K8S de déployer un volume persistant basé sur le CSI vSphere, K8S va passer par ce compte pour créer le volume sur le datastore vSphere et mapper ce dernier a la VMs qui va héberger à l’instant T le pod qui va exploiter ce volume.
Cliquez sur l'image pour l'agrandir.
Arrivés à cette étape du rôle Ansible dédié au déploiement du cluster, les VMs disposent de tous les fichiers de configuration de bases associés aux composants nécessaires à l’initialisation du cluster.
Ma démarche n’a pas vocation de totalement automatiser l’installation du cluster avec Ansible, comme pourrait le faire Kubespray.
L’objectif est d’avoir une plateforme opérationnelle pour initialiser un cluster avec toutes les dépendances nécessaires et la configuration prête à être passée en argument de l’installation des différents composants.
Il est bien entendu possible d’aller beaucoup plus loin dans l’automatisation, mais dans un cadre pédagogique et pour mon propre besoin, je préfère me limiter à l’industrialisation de la configuration et conserver une initialisation manuelle du cluster par la suite.
Néanmoins, je garde encore quelque fichier Ansible sous le coude, pour préparer les composants annexes au cluster. Ces composants sont présentés dans la suite du cookbook.