Notre expérience de travail avec les données du cluster Kubernetes etcd directement (sans l'API K8s)

De plus en plus, des clients nous contactent pour demander l'accÚs à un cluster Kubernetes afin d'accéder aux services à l'intérieur du cluster : pour pouvoir se connecter directement à une base de données ou à un service, pour relier une application locale aux applications à l'intérieur du cluster


Notre expérience de travail avec les données du cluster Kubernetes etcd directement (sans l'API K8s)

Par exemple, il est nĂ©cessaire de se connecter depuis sa machine locale au service memcached.staging.svc.cluster.local. Nous offrons cette possibilitĂ© grĂące Ă  un VPN Ă  l'intĂ©rieur du cluster, auquel le client se connecte. Pour cela, nous annonçons les sous-rĂ©seaux des pods, des services et nous mettons Ă  jour le DNS du cluster pour le client. Ainsi, lorsque le client essaie de se connecter Ă  un service memcached.staging.svc.cluster.local, la requĂȘte part vers le DNS du cluster et en retour obtient l'adresse de ce service depuis le rĂ©seau de service du cluster ou l'adresse du pod.

Nous configurons les clusters K8s avec kubeadm, oĂč par dĂ©faut, le sous-rĂ©seau des services est 192.168.0.0/16, et le rĂ©seau des pods est 10.244.0.0/16. En gĂ©nĂ©ral, tout fonctionne bien, mais il y a quelques points :

  • Le sous-rĂ©seau 192.168.*.* est souvent utilisĂ© dans les rĂ©seaux d'entreprise des clients, et encore plus souvent — dans les rĂ©seaux domestiques des dĂ©veloppeurs. Et cela provoque des conflits : les routeurs domestiques fonctionnent dans ce sous-rĂ©seau et le VPN pousse ces sous-rĂ©seaux du cluster au client.
  • Nous avons plusieurs clusters (clusters de production, de stage et/ou plusieurs clusters dev). Dans ce cas, tous auront par dĂ©faut les mĂȘmes sous-rĂ©seaux pour les pods et les services, ce qui crĂ©e de grandes complications pour travailler simultanĂ©ment avec des services dans plusieurs clusters.

Nous avons depuis longtemps adoptĂ© la pratique d'utiliser diffĂ©rents sous-rĂ©seaux pour les services et les pods dans le cadre d'un mĂȘme projet - en gros, pour que tous les clusters aient des rĂ©seaux diffĂ©rents. Cependant, il existe un grand nombre de clusters en opĂ©ration que nous prĂ©fĂ©rerions ne pas refondre depuis le dĂ©but, car de nombreux services, applications stateful, etc., y sont dĂ©jĂ  dĂ©ployĂ©s.

Et donc, nous nous sommes posé la question : comment changer le sous-réseau dans un cluster existant ?

Recherche de solutions

La pratique la plus courante est de recréer tout les services de type ClusterIP. En alternative, on peut recommander et ceci :

Le processus suivant a un problÚme : aprÚs avoir tout configuré, les pods apparaissent avec l'ancien IP comme serveur de noms DNS dans /etc/resolv.conf.
Comme je n'ai toujours pas trouvé la solution, j'ai dû réinitialiser l'ensemble du cluster avec kubeadm reset et le réinitialiser à nouveau.

Mais cela ne convient pas à tout le monde
 Voici des informations plus détaillées pour notre cas :

  • Flannel est utilisĂ© ;
  • Il y a des clusters aussi bien dans le cloud que sur le matĂ©riel ;
  • Nous aimerions Ă©viter de redĂ©ployer tous les services dans le cluster;
  • Il est nĂ©cessaire de tout faire avec un minimum de problĂšmes;
  • La version de Kubernetes est 1.16.6 (d'ailleurs, les actions suivantes seront analogues pour d'autres versions);
  • La tĂąche principale consiste Ă  remplacer dans le cluster, dĂ©ployĂ© avec kubeadm, le sous-rĂ©seau de services 192.168.0.0/16, par 172.24.0.0/16.

Et il se trouve que nous avons depuis longtemps Ă©tĂ© curieux de voir ce qui est stockĂ© dans Kubernetes dans etcd, ce qui peut ĂȘtre fait avec cela... Nous avons donc pensĂ© : «Pourquoi ne pas simplement mettre Ă  jour les donnĂ©es dans etcd, en remplaçant les anciennes adresses IP (sous-rĂ©seau) par les nouvelles?»

AprĂšs avoir cherchĂ© des outils prĂȘts Ă  l'emploi pour travailler avec les donnĂ©es dans etcd, nous n'avons trouvĂ© rien qui rĂ©solve complĂštement la tĂąche posĂ©e. (Au fait, si vous connaissez des utilitaires pour manipuler les donnĂ©es directement dans etcd, nous vous serions reconnaissants pour des liens.) Cependant, un bon point de dĂ©part a Ă©tĂ© etcdhelper de OpenShift (merci Ă  ses auteurs!).

Cet utilitaire peut se connecter à etcd à l'aide de certificats et lire les données à partir de là à l'aide de commandes ls, obtenir, dump.

Nous complétons etcdhelper

La pensĂ©e suivante est Ă©vidente : « Qu'est-ce qui nous empĂȘche d'ajouter cette fonctionnalitĂ© Ă  l'utilitaire, en y ajoutant la possibilitĂ© d'Ă©crire des donnĂ©es dans etcd ? »

Elle s'est incarnĂ©e dans une version modifiĂ©e d'etcdhelper avec deux nouvelles fonctionnalitĂ©s changeServiceCIDR et changePodCIDR. Son code peut ĂȘtre consultĂ© ici.

Que font les nouvelles fonctionnalités ? L'algorithme changeServiceCIDR:

  • crĂ©e un dĂ©sĂ©rialiseur;
  • compile une expression rĂ©guliĂšre pour remplacer le CIDR;
  • parcourt tous les services de type ClusterIP dans le cluster :
    • dĂ©codons la valeur d'etcd en objet Go;
    • Ă  l'aide de l'expression rĂ©guliĂšre, remplaçons les deux premiers octets de l'adresse;
    • assignons Ă  chaque service une adresse IP du nouveau sous-rĂ©seau;
    • crĂ©ons un sĂ©rialiseur, transformons l'objet Go en protobuf, Ă©crivons les nouvelles donnĂ©es dans etcd.

Fonction changePodCIDR est essentiellement similaire changeServiceCIDR — seulement au lieu de modifier les spĂ©cifications des services, nous faisons cela pour le nƓud et changeons .spec.PodCIDR en un nouveau sous-rĂ©seau.

Pratique

Changement de serviceCIDR

Le plan pour rĂ©aliser la tĂąche posĂ©e est trĂšs simple, mais implique un temps d'arrĂȘt lors de la recrĂ©ation de tous les pod dans le cluster. AprĂšs la description des Ă©tapes principales, nous partagerons Ă©galement nos rĂ©flexions sur la façon de minimiser thĂ©oriquement ce temps d'arrĂȘt.

Actions préparatoires :

  • installation du logiciel nĂ©cessaire et compilation de etcdhelper patchĂ©;
  • sauvegarde d'etcd et /etc/kubernetes.

Un bref plan d'action pour changer le serviceCIDR :

  • modification des manifests de apiserver et controller-manager ;
  • re-certification;
  • modification des services ClusterIP dans etcd;
  • redĂ©marrage de tous les pod dans le cluster.

Voici la séquence complÚte des actions en détail.

1. Installer le client etcd pour le dump des données :

apt install etcd-client

2. Rassembler etcdhelper :

  • Installer golang :
    GOPATH=\/root\/golang
    mkdir -p $GOPATH\/local
    curl -sSL https:\/\/dl.google.com\/go\/go1.14.1.linux-amd64.tar.gz | tar -xzvC $GOPATH\/local
    echo "export GOPATH="$GOPATH"" >> ~\/bashrc
    echo 'export GOROOT="$GOPATH\/local\/go"' >> ~\/bashrc
    echo 'export PATH="$PATH:$GOPATH\/local\/go\/bin"' >> ~\/bashrc
  • Enregistrer etcdhelper.go, tĂ©lĂ©charger les dĂ©pendances, compiler :
    wget https:\/\/raw.githubusercontent.com\/flant\/examples\/master\/2020\/04-etcdhelper\/etcdhelper.go
    go get go.etcd.io\/etcd\/clientv3 k8s.io\/kubectl\/pkg\/scheme k8s.io\/apimachinery\/pkg\/runtime
    go build -o etcdhelper etcdhelper.go

3. Faire une sauvegarde etcd :

backup_dir=\/root\/backup
mkdir ${backup_dir}
cp -rL \/etc\/kubernetes ${backup_dir}
ETCDCTL_API=3 etcdctl --cacert=\/etc\/kubernetes\/pki\/etcd\/ca.crt --key=\/etc\/kubernetes\/pki\/etcd\/server.key --cert=\/etc\/kubernetes\/pki\/etcd\/server.crt --endpoints https:\/\/192.168.199.100:2379 snapshot save ${backup_dir}\/etcd.snapshot

4. Changer le sous-réseau de service dans les manifests du plan de contrÎle Kubernetes. Dans les fichiers /etc/kubernetes/manifests/kube-apiserver.yaml et /etc/kubernetes/manifests/kube-controller-manager.yaml modifier le paramÚtre --service-cluster-ip-range vers le nouveau sous-réseau : 172.24.0.0/16 au lieu de 192.168.0.0/16.

5. Puisque nous modifions le sous-rĂ©seau de service, pour lequel kubeadm Ă©met des certificats pour le serveur API (y compris), ils doivent ĂȘtre réémis :

  1. Voyons pour quels domaines et adresses IP le certificat actuel a été émis :
    openssl x509 -noout -ext subjectAltName <\/etc\/kubernetes\/pki\/apiserver.crt
    X509v3 Subject Alternative Name:
        DNS:dev-1-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver, IP Address:192.168.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
  2. Préparons une configuration minimale pour kubeadm :
    cat kubeadm-config.yaml
    apiVersion: kubeadm.k8s.io\/v1beta1
    kind: ClusterConfiguration
    networking:
      podSubnet: "10.244.0.0\/16"
      serviceSubnet: "172.24.0.0\/16"
    apiServer:
      certSANs:
      - "192.168.199.100" # Adresse IP du nƓud maütre
  3. Supprimer les anciens crt et key, car sans cela le nouveau certificat ne sera pas émis :
    rm \/etc\/kubernetes\/pki\/apiserver.{key,crt}
  4. Réémettre les certificats pour le serveur API :
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Vérifions que le certificat a été émis pour le nouveau sous-réseau :
    openssl x509 -noout -ext subjectAltName <\/etc\/kubernetes\/pki\/apiserver.crt
    X509v3 Subject Alternative Name:
        DNS:kube-2-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:172.24.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
  6. AprÚs la réémission du certificat du serveur API, redémarrons son conteneur :
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Regénérer la config pour admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Modifier les données dans etcd :
    .\/etcdhelper -cacert \/etc\/kubernetes\/pki\/etcd\/ca.crt -cert \/etc\/kubernetes\/pki\/etcd\/server.crt -key \/etc\/kubernetes\/pki\/etcd\/server.key -endpoint https:\/\/127.0.0.1:2379 change-service-cidr 172.24.0.0\/16 

    Attention ! À ce moment, la rĂ©solution de noms de domaine dans le cluster cesse de fonctionner, car les adresses anciennes de CoreDNS (kube-dns) sont inscrites dans les pods existants, et kube-proxy a modifiĂ© les rĂšgles iptables en passant de l'ancienne sous-rĂ©seau Ă  la nouvelle. L'article suivant aborde les diffĂ©rentes maniĂšres de minimiser les temps d'arrĂȘt. /etc/resolv.conf Corrigeons les ConfigMaps dans l'espace de noms

  9. kubectl -n kube-system edit cm kubelet-config-1.16 kube-system:
    — ici, remplaçons

    clusterDNS par la nouvelle adresse IP du service kube-dns : kubectl -n kube-system get svc kube-dns kubectl -n kube-system edit cm kubeadm-config.

    — modifions

    data.ClusterConfiguration.networking.serviceSubnet Puisque l'adresse de kube-dns a changĂ©, il est nĂ©cessaire de mettre Ă  jour la configuration kubelet sur tous les nƓuds : en un nouveau sous-rĂ©seau.

  10. kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
    Il reste à redémarrer tous les pods du cluster :
  11. kubectl get pods --no-headers=true --all-namespaces |sed -r 's\/(S+)s+(S+).*\/kubectl --namespace 1 delete pod 2\/e'
    Minimisation des temps d'arrĂȘt

IdĂ©es sur la maniĂšre d'attĂ©nuer le temps d'arrĂȘt :

AprÚs avoir modifié les manifests du control plane, créez un nouveau service kube-dns, par exemple, nommé

  1. kube-dns-tmp et avec une nouvelle adresse Créer 172.24.0.10.
  2. dans etcdhelper, qui ne modifiera pas le service kube-dns. if Remplacer dans tous les kubelets l'adresse
  3. ClusterDNS par la nouvelle, tout en laissant l'ancien service fonctionner en mĂȘme temps que le nouveau. Attendez que les pods des applications soient remplacĂ©s, soit par leurs propres moyens, soit Ă  un moment convenu.
  4. Supprimer le service
  5. et changer et avec une nouvelle adresse serviceSubnetCIDR pour le service kube-dns. Ce plan permettra de minimiser le temps d'arrĂȘt Ă  environ une minute — le temps de suppression du service

et de changement de sous-rĂ©seau pour le service et avec une nouvelle adresse Modification de podNetwork . La cause la plus intĂ©ressante et nouvelle pour moi a Ă©tĂ© l'augmentation significative du trafic des requĂȘtes DNS. C'est ce dont je vais parler dans mon post, et ce qu'il convient d'en faire..

Par la mĂȘme occasion, nous avons dĂ©cidĂ© de voir comment modifier podNetwork Ă  l'aide du etcdhelper obtenu. Le dĂ©roulement des actions est le suivant :

corrigeons les configs dans

  • corrigeons le manifest du kube-controller-manager ; kube-system;
  • modifions podCIDR directement dans etcd ;
  • redĂ©marrons tous les nƓuds du cluster.
  • Maintenant, examinons plus en dĂ©tail ces actions :

1. Modifions les ConfigMaps dans l'espace de noms

— corrigeons kube-system:

— modifions

data.ClusterConfiguration.networking.podSubnet par la nouvelle sous-réseau kubectl -n kube-system edit cm kube-proxy 10.55.0.0/16.

data.config.conf.clusterCIDR: 10.55.0.0/16

data.ClusterConfiguration.networking.podSubnet 2. Modifions le manifest du controller-manager :.

vim /etc/kubernetes/manifests/kube-controller-manager.yaml

--cluster-cidr=10.55.0.0/16

data.ClusterConfiguration.networking.podSubnet 3. Vérifions les valeurs actuelles.

.spec.podCIDR .spec.podCIDRs, .InternalIP, .status.addresses, pour tous les nƓuds du cluster : kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'

kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'

[
  {
    "name": "kube-2-master",
    "podCIDR": "10.244.0.0/24",
    "podCIDRs": [
      "10.244.0.0/24"
    ],
    "InternalIP": "192.168.199.2"
  },
  {
    "name": "kube-2-master",
    "podCIDR": "10.244.0.0/24",
    "podCIDRs": [
      "10.244.0.0/24"
    ],
    "InternalIP": "10.0.1.239"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.244.1.0/24",
    "podCIDRs": [
      "10.244.1.0/24"
    ],
    "InternalIP": "192.168.199.222"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.244.1.0/24",
    "podCIDRs": [
      "10.244.1.0/24"
    ],
    "InternalIP": "10.0.4.73"
  }
]

4. Nous allons remplacer le podCIDR en modifiant directement etcd :

./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-pod-cidr 10.55.0.0/16

5. Vérifions que le podCIDR a bien été changé :

kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'

[
  {
    "name": "kube-2-master",
    "podCIDR": "10.55.0.0/24",
    "podCIDRs": [
      "10.55.0.0/24"
    ],
    "InternalIP": "192.168.199.2"
  },
  {
    "name": "kube-2-master",
    "podCIDR": "10.55.0.0/24",
    "podCIDRs": [
      "10.55.0.0/24"
    ],
    "InternalIP": "10.0.1.239"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.55.1.0/24",
    "podCIDRs": [
      "10.55.1.0/24"
    ],
    "InternalIP": "192.168.199.222"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.55.1.0/24",
    "podCIDRs": [
      "10.55.1.0/24"
    ],
    "InternalIP": "10.0.4.73"
  }
]

6. Nous allons redĂ©marrer chaque nƓud du cluster un Ă  un.

7. Si l'un des nƓuds garde l'ancien podCIDR, alors le kube-controller-manager ne pourra pas dĂ©marrer, et les pods dans le cluster ne seront pas planifiĂ©s.

En rĂ©alitĂ©, il est possible de modifier le podCIDR de maniĂšre plus simple (par exemple, ainsi). Mais nous souhaitions vraiment apprendre Ă  travailler directement avec etcd, car il existe des cas oĂč modifier des objets Kubernetes dans etcd est la seule option possible. (Par exemple, il n'est pas simplement possible de modifier le champ spec.clusterIP d'un Service sans temps d'arrĂȘt..)

Conclusion

Cet article aborde la possibilité de travailler avec les données dans etcd directement, c'est-à-dire en contournant l'API Kubernetes. Parfois, cette approche permet de faire des « trucs malins ». Les opérations présentées dans le texte ont été testées sur de réels clusters K8s. Cependant, leur état de préparation à une utilisation générale est PoC (preuve de concept). Par conséquent, si vous souhaitez utiliser une version modifiée de l'outil etcdhelper sur vos clusters, faites-le à vos propres risques.

P.S.

Lisez aussi dans notre blog :

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster