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âŠ

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, 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, par172.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Ă© (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Ă© .
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-client2. 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 :
- 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 - 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 - Supprimer les anciens crt et key, car sans cela le nouveau certificat ne sera pas Ă©mis :
rm \/etc\/kubernetes\/pki\/apiserver.{key,crt} - Réémettre les certificats pour le serveur API :
kubeadm init phase certs apiserver --config=kubeadm-config.yaml - 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 - 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 - Regénérer la config pour
admin.conf:kubeadm alpha certs renew admin.conf - 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\/16Attention ! Ă 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.confCorrigeons les ConfigMaps dans l'espace de noms - kubectl -n kube-system edit cm kubelet-config-1.16
kube-system:â ici, remplaçonsclusterDNS
par la nouvelle adresse IP du service kube-dns :kubectl -n kube-system get svc kube-dnskubectl -n kube-system edit cm kubeadm-config.â modifionsdata.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. - kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
Il reste à redémarrer tous les pods du cluster : - 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é
- kube-dns-tmp
et avec une nouvelle adresseCréer172.24.0.10. - dans etcdhelper, qui ne modifiera pas le service kube-dns.
ifRemplacer dans tous les kubelets l'adresse - 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. - Supprimer le service
- et changer
et avec une nouvelle adresseserviceSubnetCIDRpour 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/165. 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, ). 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
