Vår erfaring med data i etcd Kubernetes cluster direkte (uten K8s API)

I økende grad ber klienter oss om å gi tilgang til Kubernetes-klyngen for å kunne få tilgang til tjenester innenfor klyngen: for å kunne koble direkte til en database eller tjeneste, for å koble en lokal applikasjon med applikasjoner i klyngen...

Vår erfaring med data i etcd Kubernetes cluster direkte (uten K8s API)

For eksempel er det behov for å koble fra din lokale maskin til en tjeneste memcached.staging.svc.cluster.local. Vi tilbyr denne muligheten ved å bruke en VPN i klyngen som klienten kobler seg til. For å gjøre dette, kunngjør vi subnett av pods, tjenester og push cluster DNS til klienten. Altså når en klient prøver å koble seg til tjenesten memcached.staging.svc.cluster.local, forespørselen går til klynge-DNS og mottar som svar adressen til denne tjenesten fra klyngetjenestenettverket eller pod-adressen.

Vi konfigurerer K8s-klynger ved å bruke kubeadm, der standardtjenesteundernettet er 192.168.0.0/16, og nettverket av pods er 10.244.0.0/16. Vanligvis fungerer alt bra, men det er et par punkter:

  • Subnett 192.168.*.* ofte brukt i klientkontornettverk, og enda oftere i hjemmenettverk for utviklere. Og så får vi konflikter: hjemmerutere fungerer på dette undernettet og VPN-en skyver disse undernettene fra klyngen til klienten.
  • Vi har flere klynger (produksjon, scene og/eller flere dev klynger). Da vil alle som standard ha samme undernett for poder og tjenester, noe som skaper store vanskeligheter for samtidig arbeid med tjenester i flere klynger.

Vi har for lenge siden tatt i bruk praksisen med å bruke ulike subnett for tjenester og poder innenfor ett prosjekt – generelt, slik at alle klynger har ulike nettverk. Imidlertid er det et stort antall klynger i drift som jeg ikke ønsker å rulle over fra bunnen av, siden de kjører mange tjenester, stateful applikasjoner, etc.

Og så spurte vi oss selv: hvordan endre subnettet i en eksisterende klynge?

Søk etter vedtak

Den vanligste praksisen er å gjenskape alle tjenester med typen ClusterIP. Som et alternativ, kan gi råd og dette:

Følgende prosess har et problem: etter at alt er konfigurert, kommer podene med den gamle IP-en som en DNS-navneserver i /etc/resolv.conf.
Siden jeg fortsatt ikke fant løsningen, måtte jeg tilbakestille hele klyngen med kubeadm reset og starte den på nytt.

Men dette passer ikke for alle... Her er mer detaljerte introduksjoner for vårt tilfelle:

  • Flanell brukes;
  • Det er klynger både i skyene og på maskinvare;
  • Jeg vil gjerne unngå å omdistribuere alle tjenester i klyngen;
  • Det er behov for generelt å gjøre alt med et minimum antall problemer;
  • Kubernetes versjon er 1.16.6 (ytterligere trinn vil imidlertid være lik for andre versjoner);
  • Hovedoppgaven er å sikre at i en klynge distribuert ved hjelp av kubeadm med en tjeneste subnett 192.168.0.0/16, erstatte den med 172.24.0.0/16.

Og det skjedde at vi lenge hadde vært interessert i å se hva og hvordan i Kubernetes er lagret i etcd, hva som kan gjøres med det... Så vi tenkte: "Hvorfor ikke bare oppdatere dataene i etcd, og erstatte de gamle IP-adressene (subnett) med nye? »

Etter å ha søkt etter ferdige verktøy for å jobbe med data i etcd, fant vi ikke noe som helt løste problemet. (Forresten, hvis du vet om noen verktøy for å jobbe med data direkte i etcd, vil vi sette pris på lenkene.) Et godt utgangspunkt er imidlertid etcdhelper fra OpenShift (Takk til forfatterne!).

Dette verktøyet kan koble til etcd ved hjelp av sertifikater og lese data derfra ved hjelp av kommandoer ls, get, dump.

Legg til etcdhelper

Den neste tanken er logisk: "Hva hindrer deg i å legge til dette verktøyet ved å legge til muligheten til å skrive data til etcd?"

Det ble en modifisert versjon av etcdhelper med to nye funksjoner changeServiceCIDR и changePodCIDR. på henne du kan se koden her.

Hva gjør de nye funksjonene? Algoritme changeServiceCIDR:

  • lage en deserializer;
  • kompilere et regulært uttrykk for å erstatte CIDR;
  • vi går gjennom alle tjenester med ClusterIP-typen i klyngen:
    • dekode verdien fra etcd til et Go-objekt;
    • ved å bruke et regulært uttrykk erstatter vi de to første bytene av adressen;
    • tilordne tjenesten en IP-adresse fra det nye subnettet;
    • lag en serializer, konverter Go-objektet til protobuf, skriv nye data til etcd.

Funksjon changePodCIDR i hovedsak lik changeServiceCIDR - bare i stedet for å redigere tjenestespesifikasjonen, gjør vi det for noden og endrer .spec.PodCIDR til et nytt subnett.

Praksis

Endre tjeneste CIDR

Planen for å implementere oppgaven er veldig enkel, men den innebærer nedetid mens alle pods i klyngen gjenskapes. Etter å ha beskrevet hovedtrinnene vil vi også dele tanker om hvordan denne nedetiden i teorien kan minimeres.

Forberedende trinn:

  • installere nødvendig programvare og sette sammen den lappede etcdhelper;
  • backup etcd og /etc/kubernetes.

Kort handlingsplan for endring av serviceCIDR:

  • endre apiserver og kontroller-manager manifester;
  • ny utstedelse av sertifikater;
  • endre ClusterIP-tjenester i etcd;
  • omstart av alle pods i klyngen.

Følgende er en fullstendig sekvens av handlinger i detalj.

1. Installer etcd-klient for datadump:

apt install etcd-client

2. Bygg 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
  • Vi sparer til oss selv etcdhelper.go, nedlastingsavhengigheter, samle inn:
    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. Lag en sikkerhetskopi 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. Endre tjenestedelnettet i Kubernetes kontrollplanmanifester. I filer /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml endre parameteren --service-cluster-ip-range til et nytt subnett: 172.24.0.0/16 i stedet for 192.168.0.0/16.

5. Siden vi endrer tjenesteundernettet som kubeadm utsteder sertifikater til for apiserver (inkludert), må de utstedes på nytt:

  1. La oss se hvilke domener og IP-adresser det gjeldende sertifikatet er utstedt for:
    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. La oss forberede en minimal konfigurasjon for 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. La oss slette den gamle crt og nøkkelen, siden uten dette vil det nye sertifikatet ikke bli utstedt:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. La oss utstede sertifikater på nytt for API-serveren:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. La oss sjekke at sertifikatet ble utstedt for det nye undernettet:
    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. Etter å ha utstedt API-serversertifikatet på nytt, start beholderen på nytt:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. La oss gjenskape konfigurasjonen for admin.conf:
    kubeadm alpha certs renew admin.conf
  8. La oss redigere dataene 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 

    Advarsel! For øyeblikket slutter domeneoppløsningen å fungere i klyngen, siden i eksisterende pods i /etc/resolv.conf den gamle CoreDNS-adressen (kube-dns) er registrert, og kube-proxy endrer iptables-reglene fra det gamle subnettet til det nye. Videre i artikkelen skrives det om mulige alternativer for å minimere nedetid.

  9. La oss fikse ConfigMap i navneområdet kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - bytt ut her clusterDNS til den nye IP-adressen til kube-dns-tjenesten: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - Vi fikser det data.ClusterConfiguration.networking.serviceSubnet til et nytt subnett.

  10. Siden kube-dns-adressen er endret, er det nødvendig å oppdatere kubelet-konfigurasjonen på alle noder:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Alt som gjenstår er å starte alle pods i klyngen på nytt:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimer nedetid

Tanker om hvordan du kan minimere nedetid:

  1. Etter å ha endret kontrollplanmanifestene, opprett en ny kube-dns-tjeneste, for eksempel med navnet kube-dns-tmp og ny adresse 172.24.0.10.
  2. gjøre if i etcdhelper, som ikke vil endre kube-dns-tjenesten.
  3. Bytt ut adressen i alle kubelets ClusterDNS til en ny, mens den gamle tjenesten vil fortsette å fungere samtidig med den nye.
  4. Vent til podene med applikasjoner ruller over enten av seg selv av naturlige årsaker eller til avtalt tid.
  5. Slett tjenesten kube-dns-tmp og endre serviceSubnetCIDR for kube-dns-tjenesten.

Denne planen vil tillate deg å minimere nedetiden til ~ett minutt - for varigheten av tjenestefjerningen kube-dns-tmp og endre subnettet for tjenesten kube-dns.

Modifikasjon podNetwork

Samtidig bestemte vi oss for å se på hvordan vi endrer podNetwork ved å bruke den resulterende etcdhelper. Rekkefølgen av handlinger er som følger:

  • fikse konfigurasjoner kube-system;
  • fikse kube-controller-manager-manifestet;
  • endre podCIDR direkte i etcd;
  • start alle klyngenoder på nytt.

Nå mer om disse handlingene:

1. Endre ConfigMaps i navneområdet kube-system:

kubectl -n kube-system edit cm kubeadm-config

- korrigere data.ClusterConfiguration.networking.podSubnet til et nytt subnett 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Endre controller-manager-manifestet:

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

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

3. Se på gjeldende verdier .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses for alle klyngennoder:

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. Erstatt podCIDR ved å gjøre endringer direkte til 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. La oss sjekke at podCIDR virkelig har endret seg:

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. La oss starte alle klyngenodene på nytt én etter én.

7. Hvis du forlater minst én node gammel podCIDR, da vil ikke kube-controller-manager kunne starte, og pods i klyngen vil ikke bli planlagt.

Faktisk kan endre podCIDR gjøres enda enklere (f.eks. ). Men vi ønsket å lære å jobbe med etcd direkte, fordi det er tilfeller når man redigerer Kubernetes-objekter i etcd - den eneste mulig variant. (Du kan for eksempel ikke bare endre tjenestefeltet uten nedetid spec.clusterIP.)

Total

Artikkelen diskuterer muligheten for å arbeide med data i etcd direkte, dvs. omgå Kubernetes API. Noen ganger lar denne tilnærmingen deg gjøre "vanskelige ting." Vi testet operasjonene gitt i teksten på ekte K8s-klynger. Imidlertid er deres status som beredskap for utbredt bruk PoC (proof of concept). Derfor, hvis du ønsker å bruke en modifisert versjon av etcdhelper-verktøyet på dine klynger, gjør det på egen risiko.

PS

Les også på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar