Aventures avec un cluster Home Kubernetes

Remarque perev. : L'auteur de l'article, Marshall Brekka, occupe le poste de directeur de la conception des systèmes chez Fair.com, qui propose son application pour la location de voitures. Pendant son temps libre, il aime mettre à profit sa vaste expérience pour résoudre des problèmes "domestiques" qui ne surprendront probablement pas les geek (par conséquent, la question "Pourquoi?" - en ce qui concerne les actions décrites ci-dessous - est a priori omise). Ainsi, dans sa publication, Marshall partage les résultats du récent déploiement de Kubernetes sur ... les cartes ARM.



Comme beaucoup d'autres geeks, au fil des ans, j'ai accumulé une variété de cartes de développement comme le Raspberry Pi. Et comme beaucoup de geeks, ils se sont époussetés sur les étagères avec la pensée qu'ils pourraient un jour être utiles. Et maintenant pour moi, ce jour est enfin arrivé!

Pendant les vacances d'hiver, plusieurs semaines en dehors du travail sont apparues, dans lesquelles il y avait suffisamment de temps pour inventorier tout le fer accumulé et décider quoi en faire. Voici ce que j'avais:

  • Boîtier RAID à 5 disques avec connexion USB3;
  • Raspberry Pi modèle B (modèle OG);
  • CubbieBoard 1;
  • Banana Pi M1;
  • Netbook HP (2012?).

Sur les 5 composants de fer répertoriés, j'ai utilisé sauf RAID et un netbook comme NAS temporaire. Cependant, en raison du manque de prise en charge USB3 dans le netbook, RAID n'a pas utilisé le potentiel pleine vitesse.

Objectifs de vie


Étant donné que travailler avec RAID n'était pas optimal lors de l'utilisation d'un netbook, j'ai défini les objectifs suivants pour obtenir la meilleure configuration:

  1. NAS avec USB3 et Gigabit Ethernet;
  2. La meilleure façon de gérer les logiciels sur votre appareil
  3. (bonus) la possibilité de diffuser du contenu multimédia de RAID vers Fire TV.

Étant donné qu'aucun des appareils disponibles ne prend en charge USB3 et Ethernet gigabit, j'ai malheureusement dû faire des achats supplémentaires. Le choix s'est porté sur le ROC-RK3328-CC . Elle possédait toutes les spécifications nécessaires et un support suffisant pour les systèmes d'exploitation.

Ayant résolu mes besoins en matériel (et en attendant l'arrivée de cette solution), je suis passé au deuxième objectif.

Gestion des logiciels sur l'appareil


En partie, mes projets antérieurs liés aux cartes de développement ont échoué en raison d'une attention insuffisante aux problèmes de reproductibilité et de documentation. Lors de la création de la prochaine configuration pour mes besoins actuels, je n'ai pas pris la peine de noter les étapes suivies ou les liens vers les articles de blog que j'ai suivis. Et quand, après des mois ou des années, quelque chose s'est mal passé et que j'ai essayé de résoudre le problème, je ne comprenais pas comment tout était arrangé à l'origine.

Alors je me suis dit que cette fois tout sera différent!



Et il s'est tourné vers le fait que je sais assez bien - à Kubernetes.

Bien que K8s soit une solution trop difficile à un problème assez simple, après presque trois ans de gestion de clusters à l'aide de divers outils (les miens, kops, etc.) dans mon travail principal, je connais très bien ce système. De plus, déployer des K8 en dehors d'un environnement cloud, et même sur des appareils ARM - tout cela semblait une tâche intéressante.

J'ai également pensé que, puisque le matériel disponible ne répond pas aux exigences nécessaires pour le NAS, j'essaierai au moins d'en assembler un cluster et, peut-être, certains logiciels moins gourmands en ressources pourront fonctionner sur des appareils plus anciens.

Kubernetes sur ARM


Au travail, je n'ai pas eu l'occasion d'utiliser l'utilitaire kubeadm pour déployer des clusters, j'ai donc décidé que le moment était venu de l'essayer en action.

Raspbian a été choisi comme système d'exploitation, car il est célèbre pour le meilleur support pour mes cartes.

J'ai trouvé un bon article sur la configuration de Kubernetes sur un Raspberry Pi en utilisant HypriotOS. Comme je n'étais pas sûr de la disponibilité d'HypriotOS pour toutes mes cartes, j'ai adapté ces instructions pour Debian / Raspbian.

Composants requis


Tout d'abord, l'installation des outils suivants était requise:

  • Docker,
  • kubelet
  • kubeadm,
  • kubectl.

Docker doit être installé à l'aide d'un script spécial - script de commodité (comme indiqué dans le cas de l'utilisation de Raspbian).

 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 

Après cela, j'ai installé les composants Kubernetes selon les instructions du blog Hypriot, en les adaptant pour que des versions spécifiques soient utilisées pour toutes les dépendances:

 curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y kubelet=1.13.1-00 kubectl=1.13.1-00 kubeadm=1.13.1-00 

Raspberry pi b


La première difficulté est survenue lors de la tentative d'amorçage d'un cluster sur le Raspberry Pi B:

 $ kubeadm init Illegal instruction 

Il s'est avéré que Kubernetes a supprimé la prise en charge d'ARMv6 . Eh bien, j'ai aussi CubbieBoard et Banana Pi.

Banana pi


Initialement, la même séquence d'actions pour Banana Pi semblait être plus réussie, cependant, la commande kubeadm init essayant d'attendre que le plan de contrôle fonctionne:

 error execution phase wait-control-plane: couldn't initialize a Kubernetes cluster 

En découvrant avec docker ps ce qui se passait avec les conteneurs, j'ai vu que kube-controller-manager et kube-scheduler fonctionnaient depuis au moins 4-5 minutes, mais kube-api-server s'est levé il y a seulement 1-2 minutes:

 $ docker ps CONTAINER ID COMMAND CREATED STATUS de22427ad594 "kube-apiserver --au…" About a minute ago Up About a minute dc2b70dd803e "kube-scheduler --ad…" 5 minutes ago Up 5 minutes 60b6cc418a66 "kube-controller-man…" 5 minutes ago Up 5 minutes 1e1362a9787c "etcd --advertise-cl…" 5 minutes ago Up 5 minutes 

De toute évidence, le api-server était en train de mourir ou le processus de strontium le tuait et le redémarrait.

En vérifiant les journaux, j'ai vu des procédures de démarrage très standard - il y avait un enregistrement du début d'écoute du port sécurisé et une longue pause avant l'apparition de nombreuses erreurs dans les poignées de main TLS:

 20:06:48.604881 naming_controller.go:284] Starting NamingConditionController 20:06:48.605031 establishing_controller.go:73] Starting EstablishingController 20:06:50.791098 log.go:172] http: TLS handshake error from 192.168.1.155:50280: EOF 20:06:51.797710 log.go:172] http: TLS handshake error from 192.168.1.155:50286: EOF 20:06:51.971690 log.go:172] http: TLS handshake error from 192.168.1.155:50288: EOF 20:06:51.990556 log.go:172] http: TLS handshake error from 192.168.1.155:50284: EOF 20:06:52.374947 log.go:172] http: TLS handshake error from 192.168.1.155:50486: EOF 20:06:52.612617 log.go:172] http: TLS handshake error from 192.168.1.155:50298: EOF 20:06:52.748668 log.go:172] http: TLS handshake error from 192.168.1.155:50290: EOF 

Et peu de temps après, le serveur termine son travail. La recherche sur Google a conduit à un tel problème , indiquant une raison possible du lent fonctionnement des algorithmes cryptographiques sur certains appareils ARM.

Je suis allé plus loin et j'ai pensé que peut-être le api-server recevait trop de demandes répétées du scheduler et du controller-manager .

La suppression de ces fichiers du répertoire manifeste indiquera à kubelet d'arrêter l'exécution des pods correspondants:

 mkdir /etc/kubernetes/manifests.bak mv /etc/kubernetes/manifests/kube-scheduler.yaml /etc/kubernetes/manifests.bak/ mv /etc/kubernetes/manifests/kube-controller-mananger.yaml /etc/kubernetes/manifests.bak/ 

L'affichage des derniers journaux du api-server montré que maintenant le processus allait plus loin, cependant, il est toujours mort après environ 2 minutes. Ensuite, je me suis souvenu que le manifeste pouvait contenir un échantillon de vivacité avec des délais d'attente ayant des valeurs trop faibles pour un appareil aussi lent.

Par conséquent, j'ai vérifié /etc/kubernetes/manifests/kube-api-server.yaml - et dedans, bien sûr ...

 livenessProbe: failureThreshold: 8 httpGet: host: 192.168.1.155 path: /healthz port: 6443 scheme: HTTPS initialDelaySeconds: 15 timeoutSeconds: 15 

Pod a été tué après 135 secondes ( initialDelaySeconds + timeoutSeconds * failureThreshold ). Augmentez initialDelaySeconds à 120 ...

Succès! Eh bien, des erreurs de poignée de main se produisent toujours (probablement à partir de kubelet), mais le lancement a toujours eu lieu:

 20:06:54.957236 log.go:172] http: TLS handshake error from 192.168.1.155:50538: EOF 20:06:55.004865 log.go:172] http: TLS handshake error from 192.168.1.155:50384: EOF 20:06:55.118343 log.go:172] http: TLS handshake error from 192.168.1.155:50292: EOF 20:06:55.252586 cache.go:39] Caches are synced for autoregister controller 20:06:55.253907 cache.go:39] Caches are synced for APIServiceRegistrationController controller 20:06:55.545881 controller_utils.go:1034] Caches are synced for crd-autoregister controller ... 20:06:58.921689 storage_rbac.go:187] created clusterrole.rbac.authorization.k8s.io/cluster-admin 20:06:59.049373 storage_rbac.go:187] created clusterrole.rbac.authorization.k8s.io/system:discovery 20:06:59.214321 storage_rbac.go:187] created clusterrole.rbac.authorization.k8s.io/system:basic-user 

Lorsque le api-server s'est levé, j'ai replacé les fichiers YAML pour le contrôleur et le planificateur dans le répertoire manifeste, après quoi ils ont également démarré normalement.

Il est maintenant temps de vous assurer que le téléchargement réussira si vous laissez tous les fichiers dans le répertoire d'origine: est-ce suffisant pour modifier le délai d'initialisation de livenessProbe ?

 20:29:33.306983 reflector.go:134] k8s.io/client-go/informers/factory.go:132: Failed to list *v1.Service: Get https://192.168.1.155:6443/api/v1/services?limit=500&resourceVersion=0: dial tcp 192.168.1.155:6443: i/o timeout 20:29:33.434541 reflector.go:134] k8s.io/client-go/informers/factory.go:132: Failed to list *v1.ReplicationController: Get https://192.168.1.155:6443/api/v1/replicationcontrollers?limit=500&resourceVersion=0: dial tcp 192.168.1.155:6443: i/o timeout 20:29:33.435799 reflector.go:134] k8s.io/client-go/informers/factory.go:132: Failed to list *v1.PersistentVolume: Get https://192.168.1.155:6443/api/v1/persistentvolumes?limit=500&resourceVersion=0: dial tcp 192.168.1.155:6443: i/o timeout 20:29:33.477405 reflector.go:134] k8s.io/client-go/informers/factory.go:132: Failed to list *v1beta1.PodDisruptionBudget: Get https://192.168.1.155:6443/apis/policy/v1beta1/poddisruptionbudgets?limit=500&resourceVersion=0: dial tcp 192.168.1.155:6443: i/o timeout 20:29:33.493660 reflector.go:134] k8s.io/client-go/informers/factory.go:132: Failed to list *v1.PersistentVolumeClaim: Get https://192.168.1.155:6443/api/v1/persistentvolumeclaims?limit=500&resourceVersion=0: dial tcp 192.168.1.155:6443: i/o timeout 20:29:37.974938 controller_utils.go:1027] Waiting for caches to sync for scheduler controller 20:29:38.078558 controller_utils.go:1034] Caches are synced for scheduler controller 20:29:38.078867 leaderelection.go:205] attempting to acquire leader lease kube-system/kube-scheduler 20:29:38.291875 leaderelection.go:214] successfully acquired lease kube-system/kube-scheduler 

Oui, tout fonctionne, bien que ces vieux appareils, apparemment, n'étaient pas destinés à lancer l'avion de contrôle, car les connexions TLS répétées provoquent des freins importants. D'une manière ou d'une autre - une installation fonctionnelle de K8 sur ARM est reçue! Allons plus loin ...

Montage RAID


Étant donné que les cartes SD ne sont pas adaptées à l'enregistrement à long terme, j'ai décidé d'utiliser un stockage plus fiable pour les parties les plus volatiles du système de fichiers - dans ce cas, RAID. 4 sections y ont été mises en évidence:

  • 50 Go;
  • 2 × 20 Go;
  • 3,9 Tb.

Je n'ai pas encore trouvé un objectif spécifique pour les partitions de 20 gigaoctets, mais je voulais laisser des opportunités supplémentaires pour l'avenir.

Dans le /etc/fstab pour la partition de 50 Go, le point de montage a été spécifié comme /mnt/root , et pour 3,9 To - /mnt/raid . Après cela, j'ai monté les répertoires avec etcd et docker sur la partition de 50 Go:

 UUID=655a39e8-9a5d-45f3-ae14-73b4c5ed50c3 /mnt/root ext4 defaults,rw,user,auto,exec 0 0 UUID=0633df91-017c-4b98-9b2e-4a0d27989a5c /mnt/raid ext4 defaults,rw,user,auto 0 0 /mnt/root/var/lib/etcd /var/lib/etcd none defaults,bind 0 0 /mnt/root/var/lib/docker /var/lib/docker none defaults,bind 0 0 

Arrivée ROC-RK3328-CC


Lorsque la nouvelle carte a été livrée, j'ai installé les composants nécessaires pour les K8 dessus (voir le début de l'article) et lancé kubeadm init . Quelques minutes d'attente sont le succès et la sortie de la commande join à exécuter sur d'autres nœuds.

Super! Pas de chichi avec les délais.

Et comme le RAID sera également utilisé sur cette carte, les supports devront être à nouveau configurés. Pour résumer toutes les étapes:

1. Montez les disques dans / etc / fstab


 UUID=655a39e8-9a5d-45f3-ae14-73b4c5ed50c3 /mnt/root ext4 defaults,rw,user,auto,exec 0 0 UUID=0633df91-017c-4b98-9b2e-4a0d27989a5c /mnt/raid ext4 defaults,rw,user,auto 0 0 /mnt/root/var/lib/etcd /var/lib/etcd none defaults,bind 0 0 /mnt/root/var/lib/docker /var/lib/docker none defaults,bind 0 0 

2. Installation des binaires Docker et K8s


 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 

 curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y kubelet=1.13.1-00 kubectl=1.13.1-00 kubeadm=1.13.1-00 

3. Configuration d'un nom d'hôte unique (important car de nombreux nœuds sont ajoutés)


 hostnamectl set-hostname k8s-master-1 

4. Initialisation de Kubernetes


J'omets la phase avec le plan de contrôle, car je veux pouvoir planifier des pods normaux sur ce noeud:

 kubeadm init --skip-phases mark-control-plane 

5. Installation du plugin réseau


Les informations à ce sujet dans l'article Hypriot étaient un peu datées car le plugin réseau Weave est désormais également pris en charge sur ARM :

 export KUBECONFIG=/etc/kubernetes/admin.conf kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" 

6. Ajout d'étiquettes d'hôte


Sur ce nœud, je vais démarrer le serveur NAS, je vais donc le marquer avec des étiquettes pour une éventuelle utilisation future dans le planificateur:

 kubectl label nodes k8s-master-1 marshallbrekka.raid=true kubectl label nodes k8s-master-1 marshallbrekka.network=gigabit 

Connexion d'autres nœuds au cluster


La configuration d'autres appareils (Banana Pi, CubbieBoard) était tout aussi simple. Pour eux, vous devez répéter les 3 premières étapes (changer les paramètres de montage des disques / supports flash, selon leur disponibilité) et exécuter la commande kubeadm join au lieu de kubeadm init .

Recherche de conteneurs Docker pour ARM


La plupart des conteneurs Docker nécessaires sont construits normalement sur un Mac, mais pour ARM, c'est un peu plus compliqué. Ayant trouvé de nombreux articles sur la façon d'utiliser QEMU à ces fins, je suis néanmoins parvenu à la conclusion que la plupart des applications dont j'avais besoin sont déjà assemblées, et beaucoup d'entre elles sont disponibles sur linuxserver .

Prochaines étapes


N'obtenant toujours pas la configuration initiale des appareils sous une forme automatisée / scriptée comme je le souhaiterais, j'ai au moins composé un ensemble de commandes de base (montages, docker et kubeadm ) et les kubeadm documentées dans le référentiel Git. Les autres applications utilisées ont également reçu des configurations YAML pour K8 stockées dans le même référentiel, il est donc très facile d'obtenir la configuration nécessaire à partir de zéro.

À l'avenir, j'aimerais atteindre les objectifs suivants:

  1. Rendre les sites maîtres hautement disponibles
  2. ajouter une surveillance / des notifications pour connaître les défaillances de tous les composants;
  3. Modifier les paramètres DCHP du routeur pour utiliser un serveur DNS du cluster afin de simplifier la découverte des applications (qui veut se souvenir des adresses IP internes?);
  4. exécutez MetalLB pour transférer les services de cluster vers un réseau privé (DNS, etc.).


PS du traducteur


Lisez aussi dans notre blog:

Source: https://habr.com/ru/post/fr435526/


All Articles