Vår erfarenhet av data i etcd Kubernetes kluster direkt (utan K8s API)

I allt högre grad ber klienter oss att ge åtkomst till Kubernetes-klustret för att kunna komma åt tjänster inom klustret: för att kunna ansluta direkt till någon databas eller tjänst, för att ansluta en lokal applikation med applikationer inom klustret...

Vår erfarenhet av data i etcd Kubernetes kluster direkt (utan K8s API)

Det finns till exempel ett behov av att ansluta från din lokala dator till en tjänst memcached.staging.svc.cluster.local. Vi tillhandahåller denna möjlighet med hjälp av ett VPN inom klustret som klienten ansluter till. För att göra detta tillkännager vi subnät av pods, tjänster och pushar kluster-DNS till klienten. Alltså när en klient försöker ansluta till tjänsten memcached.staging.svc.cluster.local, förfrågan går till klustrets DNS och får som svar adressen till denna tjänst från klustertjänstnätverket eller podadressen.

Vi konfigurerar K8s-kluster med kubeadm, där standardtjänstens undernät är 192.168.0.0/16, och nätverket av pods är 10.244.0.0/16. Vanligtvis fungerar allt bra, men det finns ett par punkter:

  • Subnät 192.168.*.* används ofta i klientkontorsnätverk, och ännu oftare i utvecklarhemnätverk. Och då får vi konflikter: hemroutrar fungerar på det här subnätet och VPN skjuter dessa subnät från klustret till klienten.
  • Vi har flera kluster (produktion, scen och/eller flera dev-kluster). Då kommer alla som standard att ha samma subnät för poddar och tjänster, vilket skapar stora svårigheter för att samtidigt arbeta med tjänster i flera kluster.

Vi har för länge sedan anammat praxis att använda olika subnät för tjänster och poddar inom samma projekt - i allmänhet så att alla kluster har olika nätverk. Det finns dock ett stort antal kluster i drift som jag inte skulle vilja rulla över från grunden, eftersom de kör många tjänster, stateful applikationer etc.

Och då frågade vi oss själva: hur ändrar man subnätet i ett befintligt kluster?

Genomsökning av beslut

Den vanligaste metoden är att återskapa alla tjänster med typen ClusterIP. Som ett alternativ, kan ge råd och detta:

Följande process har ett problem: efter att allt har konfigurerats kommer poddarna med den gamla IP:n som en DNS-namnserver i /etc/resolv.conf.
Eftersom jag fortfarande inte hittade lösningen var jag tvungen att återställa hela klustret med kubeadm reset och starta det igen.

Men det här passar inte alla... Här är mer detaljerade introduktioner för vårt fall:

  • Flanell används;
  • Det finns kluster både i molnen och på hårdvara;
  • Jag skulle vilja undvika att omdistribuera alla tjänster i klustret;
  • Det finns ett behov av att generellt göra allt med ett minimum av problem;
  • Kubernetes version är 1.16.6 (dock kommer ytterligare steg att vara liknande för andra versioner);
  • Huvuduppgiften är att säkerställa att i ett kluster distribueras med hjälp av kubeadm med ett tjänsteundernät 192.168.0.0/16, ersätt den med 172.24.0.0/16.

Och det råkade bara vara så att vi länge varit intresserade av att se vad och hur i Kubernetes lagras i etcd, vad som kan göras med det... Så vi tänkte: “Varför inte bara uppdatera data i etcd, ersätta de gamla IP-adresserna (subnätet) med nya? »

Efter att ha letat efter färdiga verktyg för att arbeta med data i etcd hittade vi inget som helt löste problemet. (Förresten, om du känner till några verktyg för att arbeta med data direkt i etcd skulle vi uppskatta länkarna.) En bra utgångspunkt är dock etcdhelper från OpenShift (tack vare dess författare!).

Det här verktyget kan ansluta till etcd med hjälp av certifikat och läsa data därifrån med kommandon ls, get, dump.

Lägg till etcdhelper

Nästa tanke är logisk: "Vad hindrar dig från att lägga till det här verktyget genom att lägga till möjligheten att skriva data till etcd?"

Det blev en modifierad version av etcdhelper med två nya funktioner changeServiceCIDR и changePodCIDR. på henne du kan se koden här.

Vad gör de nya funktionerna? Algoritm changeServiceCIDR:

  • skapa en deserializer;
  • kompilera ett reguljärt uttryck för att ersätta CIDR;
  • vi går igenom alla tjänster med typen ClusterIP i klustret:
    • avkoda värdet från etcd till ett Go-objekt;
    • med ett reguljärt uttryck ersätter vi de två första byten av adressen;
    • tilldela tjänsten en IP-adress från det nya subnätet;
    • skapa en serializer, konvertera Go-objektet till protobuf, skriv ny data till etcd.

Funktion changePodCIDR väsentligen lika changeServiceCIDR - bara istället för att redigera tjänstespecifikationen, gör vi det för noden och ändrar .spec.PodCIDR till ett nytt subnät.

Praxis

Byt tjänst CIDR

Planen för att implementera uppgiften är mycket enkel, men den innebär driftstopp vid tidpunkten för återskapandet av alla poddar i klustret. Efter att ha beskrivit huvudstegen kommer vi också att dela med oss ​​av tankar om hur, i teorin, denna driftstopp kan minimeras.

Förberedande steg:

  • installera den nödvändiga programvaran och montera den korrigerade etcdhelper;
  • backup etcd och /etc/kubernetes.

Kort handlingsplan för att ändra serviceCIDR:

  • ändra apiserver och controller-manager manifest;
  • återutgivning av certifikat;
  • ändra ClusterIP-tjänster i etcd;
  • omstart av alla poddar i klustret.

Följande är en komplett sekvens av åtgärder i detalj.

1. Installera etcd-klient för datadump:

apt install etcd-client

2. Bygg etcdhelper:

  • Installera 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
  • Vi sparar till oss själva etcdhelper.go, nedladdningsberoenden, samla in:
    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. Gör en säkerhetskopia 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. Ändra tjänstens undernät i Kubernetes kontrollplans manifest. I filer /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml ändra parametern --service-cluster-ip-range till ett nytt subnät: 172.24.0.0/16 istället för 192.168.0.0/16.

5. Eftersom vi ändrar tjänstens undernät till vilket kubeadm utfärdar certifikat för apiserver (inklusive), måste de återutges:

  1. Låt oss se vilka domäner och IP-adresser det aktuella certifikatet har utfärdats för:
    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. Låt oss förbereda en minimal konfiguration för 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-адрес мастер узла
  3. Låt oss ta bort den gamla crt och nyckeln, eftersom det nya certifikatet inte kommer att utfärdas utan detta:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Låt oss återutge certifikat för API-servern:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Låt oss kontrollera att certifikatet har utfärdats för det nya undernätet:
    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. När du har utfärdat API-servercertifikatet igen, starta om dess behållare:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Låt oss återskapa konfigurationen för admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Låt oss redigera data 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/16 

    Varning! Just nu slutar domänupplösningen att fungera i klustret, sedan i befintliga pods /etc/resolv.conf den gamla CoreDNS-adressen (kube-dns) registreras, och kube-proxy ändrar iptables-reglerna från det gamla undernätet till det nya. Längre fram i artikeln skrivs det om möjliga alternativ för att minimera driftstopp.

  9. Låt oss fixa ConfigMap i namnområdet kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - byt ut här clusterDNS till den nya IP-adressen för kube-dns-tjänsten: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - vi fixar det data.ClusterConfiguration.networking.serviceSubnet till ett nytt subnät.

  10. Eftersom kube-dns-adressen har ändrats är det nödvändigt att uppdatera kubelet-konfigurationen på alla noder:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Allt som återstår är att starta om alla poddar i klustret:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimera stilleståndstiden

Tankar om hur man minimerar stillestånd:

  1. Efter att ha ändrat kontrollplansmanifesterna, skapa en ny kube-dns-tjänst, till exempel med namnet kube-dns-tmp och ny adress 172.24.0.10.
  2. göra if i etcdhelper, som inte kommer att ändra kube-dns-tjänsten.
  3. Byt ut adressen i alla kubelets ClusterDNS till en ny, medan den gamla tjänsten fortsätter att fungera samtidigt med den nya.
  4. Vänta tills kapslarna med applikationer rullar över antingen av sig själva av naturliga skäl eller vid en överenskommen tid.
  5. Ta bort tjänsten kube-dns-tmp och ändra serviceSubnetCIDR för tjänsten kube-dns.

Den här planen gör att du kan minimera stilleståndstiden till ~en minut - under tiden som tjänsten tas bort kube-dns-tmp och ändra undernätet för tjänsten kube-dns.

Modifiering podNetwork

Samtidigt bestämde vi oss för att titta på hur man modifierar podNetwork med den resulterande etcdhelper. Sekvensen av åtgärder är som följer:

  • fixar konfigurationer i kube-system;
  • fixa kube-controller-manager-manifestet;
  • ändra podCIDR direkt i etcd;
  • starta om alla klusternoder.

Nu mer om dessa åtgärder:

1. Ändra ConfigMaps i namnområdet kube-system:

kubectl -n kube-system edit cm kubeadm-config

- korrigera data.ClusterConfiguration.networking.podSubnet till ett nytt subnät 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

- korrigera data.config.conf.clusterCIDR: 10.55.0.0/16.

2. Ändra controller-manager-manifestet:

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

- korrigera --cluster-cidr=10.55.0.0/16.

3. Titta på de aktuella värdena .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses för alla klusternoder:

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. Ersätt podCIDR genom att göra ändringar direkt 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/16

5. Låt oss kontrollera att podCIDR verkligen har förändrats:

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. Låt oss starta om alla klusternoder en efter en.

7. Om du lämnar minst en nod gammal podCIDR, då kommer inte kube-controller-manager att kunna starta, och poddar i klustret kommer inte att schemaläggas.

Faktum är att byte av podCIDR kan göras ännu enklare (till exempel, ). Men vi ville lära oss hur man arbetar med etcd direkt, eftersom det finns fall när man redigerar Kubernetes-objekt i etcd - bara möjlig variant. (Du kan till exempel inte bara ändra fältet Service utan driftstopp spec.clusterIP.)

Totalt

I artikeln diskuteras möjligheten att arbeta med data i etcd direkt, d.v.s. kringgå Kubernetes API. Ibland låter detta tillvägagångssätt dig göra "knepiga saker". Vi testade operationerna som ges i texten på riktiga K8s-kluster. Men deras status som beredskap för utbredd användning är PoC (proof of concept). Därför, om du vill använda en modifierad version av verktyget etcdhelper på dina kluster, gör det på egen risk.

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar