Flere og flere kunder henvender sig til os med anmodning om at få adgang til Kubernetes-klyngen for at kunne interagere med tjenester inden i klyngen: for at kunne oprette forbindelse direkte til en database eller tjeneste, til at forbinde en lokal applikation med applikationer inden i klyngen…

For eksempel opstår behovet for at tilslutte sig fra sin lokale maskine til tjenesten memcached.staging.svc.cluster.local. Vi tilbyder en sådan mulighed ved hjælp af VPN inde i klyngen, som kunden tilslutter sig. Til dette annoncerer vi undernetværkene for pod'er, tjenester og pushover klynge-DNS til kunden. Så når kunden prøver at oprette forbindelse til tjenesten memcached.staging.svc.cluster.local, sendes anmodningen til klyngens DNS, og i svaret får man adressen på den pågældende tjeneste fra klynge-service-netværket eller adressen på pod'en.
Vi konfigurerer K8s-klynger ved hjælp af kubeadm, hvor service-under-netværket som standard er 192.168.0.0/16, og pod-netværket er 10.244.0.0/16. Normalt virker alt godt, men der er et par ting:
- Under-netværket
192.168.*.*bruges ofte i kundernes kontornetværk, og endnu oftere i udviklernes hjemmenetværk. Og så opstår der konflikter: hjemme-routerne arbejder i dette under-netværk, og VPN pusher disse under-netværk fra klyngen til kunden. - Vi har flere klynger (produktion, stage og/eller flere dev-klynger). Så i dem vil der som standard være ens under-netværk for pod'er og tjenester, hvilket skaber store vanskeligheder ved samtidig arbejde med tjenester i flere klynger.
Vi har længe vedtaget praksis med at bruge forskellige under-netværk for tjenester og pod'er inden for et projekt — generelt for at sikre, at alle klynger har forskellige netværk. Der er dog et stort antal klynger i drift, som vi ikke ønsker at genopbygge fra bunden, da mange tjenester, stateful-ansøgninger osv. kører i dem.
Og så stillede vi spørgsmålet: hvordan ændrer man under-netværket i en eksisterende klynge?
Søgen efter løsninger
Den mest almindelige praksis er at genskabe alle tjenester med typen ClusterIP. Som et alternativ, også sådan:
Den følgende proces har et problem: efter at alt er konfigureret, kommer pod'erne op med den gamle IP som en DNS-navneserver i /etc/resolv.conf.
Da jeg stadig ikke fandt løsningen, måtte jeg nulstille hele klyngen med kubeadm reset og init den igen.
Men det passer ikke alle… Her er mere detaljerede indgange for vores tilfælde:
- Flannel anvendes;
- Der er klyngedannelser både i skyen og på hardware;
- Vi ønsker at undgå at redeploye alle tjenester i klyngen;
- Der er behov for at gøre alt med minimal mængde problemer;
- Kubernetes-versionen er 1.16.6 (senere handlinger vil dog være ens for andre versioner);
- Den primære opgave er at erstatte den service-subnetværk i en klynge, der er oprettet med kubeadm,
192.168.0.0/16, med172.24.0.0/16.
Og det så tilfældigvis ud til, at vi længe har været interesserede i at se, hvad der opbevares i Kubernetes i etcd, og hvad der i det hele taget kan gøres med det... Så vi tænkte: «Hvorfor ikke blot opdatere dataene i etcd ved at erstatte de gamle IP-adresser (subnetværk) med nye??»
Efter at have søgt efter færdige værktøjer til at arbejde med data i etcd, fandt vi ikke noget, der fuldt ud løste den stillede opgave. (Forresten, hvis du kender nogen værktøjer til at arbejde med data direkte i etcd - ville vi være taknemmelige for links.) Men et godt udgangspunkt blev (tak til dets forfattere!).
Dette værktøj kan tilslutte sig etcd ved hjælp af certifikater og læse data derfra ved hjælp af kommandoer ls, get, dump.
Vi tilføjer etcdhelper
Næste tanke er åbenlys: «Hvad hindrer os i at skrive denne værktøj med mulighed for at skrive data i etcd?»
Det blev til en modificeret version af etcdhelper med to nye funktioner changeServiceCIDR og changePodCIDR. Du kan se på dens kode .
Hvad gør de nye funktioner? Algoritmen changeServiceCIDR:
- opretter en deserializer;
- kompilerer et regulært udtryk til at erstatte CIDR;
- gennemløber alle tjenester med typen ClusterIP i klyngen:
- afkoder værdien fra etcd til et Go-objekt;
- ved hjælp af det regulære udtryk erstatter vi de første to bytes af adressen;
- tilknytter tjenesten en IP-adresse fra det nye subnetværk;
- opretter en serializer, konverterer Go-objektet til protobuf og skriver de nye data i etcd.
Funktion changePodCIDR er i princippet identisk, changeServiceCIDR — blot i stedet for at redigere specifikationen for tjenesterne gør vi det for noden og ændrer .spec.PodCIDR til det nye subnetværk.
Praktik
Ændring af serviceCIDR
Planen for implementering af den stillede opgave er meget enkel, men indebærer nedetid i øjeblikket hvor alle pod'er i klyngen skal genskabes. Efter at have beskrevet de grundlæggende trin vil vi også dele vores overvejelser om, hvordan denne nedetid teoretisk set kan minimeres.
Forberedende handlinger:
- installation af nødvendig software og bygning af det patchede etcdhelper;
- backup af etcd og
/etc/kubernetes.
Kort handlingsplan for ændring af serviceCIDR:
- ændring af apiserver- og controller-manager-manifester;
- genudstedelse af certifikater;
- ændring af ClusterIP-tjenester i etcd;
- genstart af alle pods i klyngen.
Her præsenteres den fulde handlingsforløb i detaljer.
1. Installerer etcd-client til data dump:
apt install etcd-client2. Samler etcdhelper:
- Installerer 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 - Gemmer vi
etcdhelper.go, downloader afhængigheder, bygger: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. Laver backup af 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. Ændrer service subnet i Kubernetes control plane-manifesterne. I filerne /etc/kubernetes/manifests/kube-apiserver.yaml og /etc/kubernetes/manifests/kube-controller-manager.yaml ændrer vi parameteren --service-cluster-ip-range til det nye subnet: 172.24.0.0/16 i stedet for 192.168.0.0/16.
5. Da vi ændrer service subnet, som kubeadm udsteder certifikater for apiserver (bl.a.), skal de genudstedes:
- Lad os se, hvilke domæner og IP-adresser det nuværende certifikat er udstedt til:
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 - Lad os forberede en minimal konfiguration til 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" # IP-adresse for master node - Fjerner de gamle crt og key, da det nye certifikat ikke kan udstedes uden dette:
rm /etc/kubernetes/pki/apiserver.{key,crt} - Genudsteder certifikaterne til API-serveren:
kubeadm init phase certs apiserver --config=kubeadm-config.yaml - Lad os kontrollere, at certifikatet er udstedt for det nye subnet:
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 - Efter genudstedelse af certifikatet for API-serveren genstarter vi dens container:
docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart - Genskaber konfigurationen til
admin.conf:kubeadm alpha certs renew admin.conf - Redigerer vi oplysningerne i 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\/16Opmærksomhed! På dette tidspunkt stopper opdateringen af domæner i klyngen, da de allerede eksisterende pods har
/etc/resolv.confden gamle adresse CoreDNS (kube-dns) indtastet, og kube-proxy ændrede iptables-reglerne fra den gamle subnetmaske til den nye. Nedenfor i artiklen beskrives mulige strategier for at minimere nedetid. - Lad os rette ConfigMap'erne i navnerummet
kube-system:kubectl -n kube-system edit cm kubelet-config-1.16— her ændrer vi
clusterDNStil den nye IP-adresse for tjenesten kube-dns:kubectl -n kube-system get svc kube-dns.kubectl -n kube-system edit cm kubeadm-config— vi retter
data.ClusterConfiguration.networking.serviceSubnettil det nye subnetværk. - Da adressen for kube-dns er ændret, skal kubelet-konfigurationen opdateres på alle noder:
kubeadm upgrade node phase kubelet-config && systemctl restart kubelet - Det er nu nødvendigt at genstarte alle pods i klyngen:
kubectl get pods --no-headers=true --all-namespaces |sed -r 's\/(S+)s+(S+).*\/kubectl --namespace 1 delete pod 2\/e'
Minimere nedetid
Overvej, hvordan man kan minimere nedetid:
- Efter ændringerne af kontrolplane-manifestene skal der oprettes en ny kube-dns-tjeneste, for eksempel ved navn
kube-dns-tmpmed en ny adresse172.24.0.10. - Gøre
ifi etcdhelper, som ikke vil modificere kube-dns-tjenesten. - Erstatte adressen
ClusterDNSi alle kubelet'er med den nye, mens den gamle tjeneste fortsætter med at fungere samtidig med den nye. - Vent på, at pods med applikationer migrerer, enten naturligt over tid eller på et aftalt tidspunkt.
- Fjern tjenesten
kube-dns-tmpog ændreserviceSubnetCIDRfor kube-dns-tjenesten.
Denne plan vil muliggøre at minimere nedetiden til ~et minut — for tiden det tager at fjerne tjenesten kube-dns-tmp og ændre subnetmasken for kube-dns.
Modifikation af podNetwork
Samtidig besluttede vi at se, hvordan man kan modificere podNetwork ved hjælp af den opnåede etcdhelper. Følgende rækkefølge skal følges:
- retter konfigurationerne i
kube-system; - retter kube-controller-manager manifestet;
- ændrer podCIDR direkte i etcd;
- genstarter alle noder i klyngen.
Nu lidt mere om disse handlinger:
1. Modificer ConfigMap'erne i navnerummet kube-system:
kubectl -n kube-system edit cm kubeadm-config — retter data.ClusterConfiguration.networking.podSubnet til det nye subnet 10.55.0.0/16.
kubectl -n kube-system edit cm kube-proxy — retter data.config.conf.clusterCIDR: 10.55.0.0\/16.
2. Modificer controller-manager manifestet:
vim \/etc\/kubernetes\/manifests\/kube-controller-manager.yaml — retter --cluster-cidr=10.55.0.0\/16.
3. Tjek de nuværende værdier .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses for alle noder i klyngen:
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. Vi ændrer podCIDR ved at foretage ændringer direkte i 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. Vi verificerer, at podCIDR rent faktisk er ændret:
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. Genstart alle noder i klyngen en ad gangen.
7. Hvis der forbliver mindst én node med den gamle podCIDR, kan kube-controller-manager ikke starte, og pods i klyngen vil ikke blive planlagt.Faktisk kan ændringen af podCIDR også ske enklere (for eksempel,
). Men vi ønskede at lære at arbejde direkte med etcd, fordi der findes tilfælde, hvor redigering af Kubernetes-objekter i etcd er den eneste mulige løsning. (For eksempel kan man ikke bare ændre feltet spec.clusterIP uden downtime..)
Resultat
Artiklen beskriver muligheden for at arbejde med data i etcd direkte, det vil sige uden om Kubernetes API. Nogle gange gør denne tilgang det muligt at lave "smarte ting". De operationer, der er beskrevet i teksten, har vi testet på virkelige K8s-klynger. Men deres beredskab til bred anvendelse er PoC (proof of concept). Så hvis du ønsker at bruge den modificerede version af værktøjet etcdhelper på dine klynger, gør det på eget ansvar.
P.S.
Læs også på vores blog:
- «»;
- «»;
- «»;
- «».
Kilde: habr.com
