A nostra sperienza cù dati in eccd Kubernetes cluster direttamente (senza API K8s)

Sempre più, i clienti ci dumandanu di furnisce l'accessu à u cluster Kubernetes per pudè accede à i servizii in u cluster: per pudè cunnette direttamente à una basa di dati o serviziu, per cunnette una applicazione locale cù applicazioni in u cluster ...

A nostra sperienza cù dati in eccd Kubernetes cluster direttamente (senza API K8s)

Per esempiu, ci hè bisognu di cunnette da a vostra macchina locale à un serviziu memcached.staging.svc.cluster.local. Offremu sta capacità utilizendu una VPN in u cluster à quale u cliente si cunnetta. Per fà questu, annunziemu subnets di pods, servizii è push cluster DNS à u cliente. Cusì, quandu un cliente prova à cunnette à u serviziu memcached.staging.svc.cluster.local, a dumanda va à u cluster DNS è in risposta riceve l'indirizzu di stu serviziu da a reta di serviziu di u cluster o l'indirizzu pod.

Cunfiguremu clusters K8s usendu kubeadm, induve a subnet di serviziu predeterminata hè 192.168.0.0/16, è a reta di pods hè 10.244.0.0/16. Di solitu tuttu funziona bè, ma ci sò parechji punti:

  • Subnet 192.168.*.* spessu usatu in e rete di l'uffiziu di u cliente, è ancu più spessu in e rete di casa di sviluppatore. E poi finiscemu cun cunflitti: i routers di casa travaglianu in questa subnet è a VPN spinge sti subnets da u cluster à u cliente.
  • Avemu parechji clusters (produzzione, stage è / o parechji dev clusters). Allora, per difettu, tutti averebbenu i stessi subnets per pods è servizii, chì creanu grandi difficultà per u travagliu simultaneo cù servizii in parechji clusters.

Avemu longu aduttatu a pratica di utilizà diverse subnets per servizii è pods in u stessu prughjettu - in generale, per chì tutti i clusters anu rete diverse. Tuttavia, ci sò un gran numaru di clusters in funziunamentu chì ùn vogliu micca vultà da zero, postu chì eseguinu assai servizii, applicazioni stateful, etc.

E poi avemu dumandatu: cumu cambià a subnet in un cluster esistente?

Ricerca di decisioni

A pratica più cumuna hè di ricreà tutte e servizii cù u tipu ClusterIP. Comu opzione, pò cunsiglià è questu:

U prucessu seguente hà un prublema: dopu à tuttu cunfiguratu, i pods venenu cù l'antica IP cum'è un servitore di nome DNS in /etc/resolv.conf.
Siccomu ùn aghju micca trovu a suluzione, aghju avutu à resettate tuttu u cluster cù kubeadm reset è init di novu.

Ma questu ùn hè micca adattatu per tutti... Eccu introduzioni più dettagliate per u nostru casu:

  • Flannel hè utilizatu;
  • Ci sò clusters in i nuvuli è in hardware;
  • Mi piacerebbe evità di ridistribuisce tutti i servizii in u cluster;
  • Ci hè bisognu di fà in generale tuttu cù un numeru minimu di prublemi;
  • A versione di Kubernetes hè 1.16.6 (in ogni casu, altri passi seranu simili per altre versioni);
  • U compitu principale hè di assicurà chì in un cluster implementatu cù kubeadm cù una subnet di serviziu 192.168.0.0/16, rimpiazzà cù 172.24.0.0/16.

È hè accadutu chì eramu longu interessatu à vede ciò chì è cumu in Kubernetes hè almacenatu in etcd, ciò chì si pò fà cun ellu ... Allora avemu pensatu: "Perchè micca solu aghjurnà e dati in etcd, rimpiazzà i vechji indirizzi IP (subnet) cù novi?

Dopu avè cercatu arnesi pronti per travaglià cù dati in etcd, ùn avemu micca truvatu nunda chì risolve u prublema cumplettamente. (Per via, se sapete di qualsiasi utilità per travaglià cù dati direttamente in etcd, apprezzemu i ligami.) Tuttavia, un bonu puntu di partenza hè etcdhelper da OpenShift (grazie à i so autori!).

Questa utilità pò cunnette à etcd cù certificati è leghje e dati da quì cù cumandamenti ls, get, dump.

Aghjunghjite etcdhelper

U prossimu pensamentu hè logicu: "Chì vi impedisce di aghjunghje sta utilità aghjunghjendu a capacità di scrive dati à etcd?"

Hè diventata una versione mudificata di etcdhelper cù duie funzioni novi changeServiceCIDR и changePodCIDR. nantu à ella pudete vede u codice ccà.

Chì facenu i novi funziunalità? Algoritmu changeServiceCIDR:

  • creà un deserializatore;
  • compilà una espressione regulare per rimpiazzà CIDR;
  • andemu per tutti i servizii cù u tipu ClusterIP in u cluster:
    • decode u valore da etcd in un oggettu Go;
    • usendu una espressione regulare rimpiazzà i primi dui bytes di l'indirizzu;
    • assignà u serviziu un indirizzu IP da a nova subnet;
    • criemu un serializzatore, cunvertisce l'ughjettu Go in protobuf, scrivite novi dati à etcd.

funziunava changePodCIDR essenzialmente simili changeServiceCIDR - solu invece di edità a specificazione di u serviziu, facemu per u node è cambià .spec.PodCIDR à una nova subnet.

Prutizzioni

Cambia u serviziu CIDR

U pianu per l'implementazione di u compitu hè assai simplice, ma implica downtime à u mumentu di a ricreazione di tutti i pods in u cluster. Dopu avè descrittu i passi principali, avemu ancu sparte pinsamenti nantu à cumu, in teoria, stu downtime pò esse minimizatu.

Passi preparatori:

  • installà u software necessariu è assemblà u etcdhelper patchatu;
  • backup etcd è /etc/kubernetes.

Breve pianu d'azzione per cambià u serviziu CIDR:

  • cambià i manifesti apiserver è controller-manager;
  • riemissione di certificati;
  • cambià i servizii ClusterIP in etcd;
  • riavvia tutti i pods in u cluster.

A seguita hè una sequenza cumpleta di l'azzioni in dettagliu.

1. Installa etcd-client per u dump di dati:

apt install etcd-client

2. Custruite etcdhelper:

  • Installa 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
  • Salvemu per noi stessi etcdhelper.go, scaricate dipendenze, raccoglie:
    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. Fate una copia di salvezza 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. Cambia a subnet di serviziu in i manifesti di u pianu di cuntrollu di Kubernetes. In i schedari /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml cambià u paràmetru --service-cluster-ip-range à una nova subnet: 172.24.0.0/16 invece di 192.168.0.0/16.

5. Siccomu avemu cambiatu a subnet di serviziu à quale kubeadm emette certificati per apiserver (cumpresu), anu da esse riedutu:

  1. Videmu per quali domini è indirizzi IP u certificatu attuale hè statu emessu:
    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. Preparamu una cunfigurazione minima per 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. Sguassemu u vechju crt è chjave, postu chì senza questu u novu certificatu ùn serà micca emessu:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Riessumu i certificati per u servitore API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Cuntrollamu chì u certificatu hè statu emessu per a nova 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
  6. Dopu avè riessuatu u certificatu di u servitore API, riavvia u so containeru:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Rigenerate a cunfigurazione per admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Editemu i dati in 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 

    Attenzione! À questu mumentu, a risoluzione di u duminiu ferma di travaglià in u cluster, postu chì in pods esistenti /etc/resolv.conf u vechju indirizzu CoreDNS (kube-dns) hè registratu, è kube-proxy cambia e regule iptables da a vechja subnet à a nova. In più in l'articulu hè scrittu annantu à l'opzioni pussibuli per minimizzà i tempi di inattività.

  9. Fixemu ConfigMap in u spaziu di nomi kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - rimpiazzà quì clusterDNS à u novu indirizzu IP di u serviziu kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - l'avemu da riparà data.ClusterConfiguration.networking.serviceSubnet à una nova subnet.

  10. Siccomu l'indirizzu kube-dns hè cambiatu, hè necessariu aghjurnà a cunfigurazione di kubelet in tutti i nodi:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Tuttu ciò chì resta hè di riavvià tutti i pods in u cluster:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimizà i tempi di inattività

Pensieri nantu à cumu minimizzà i tempi di inattività:

  1. Dopu avè cambiatu i manifesti di u pianu di cuntrollu, creanu un novu serviziu kube-dns, per esempiu, cù u nome kube-dns-tmp è novu indirizzu 172.24.0.10.
  2. Fate if in etcdhelper, chì ùn mudificà micca u serviziu kube-dns.
  3. Sustituisce l'indirizzu in tutti i kubelets ClusterDNS à un novu, mentri u vechju serviziu cuntinuà à travaglià simultaneamente cù u novu.
  4. Aspettate finu à chì i baccelli cù l'applicazioni passanu per elli stessi per ragioni naturali o à un tempu accunsentutu.
  5. Sguassà u serviziu kube-dns-tmp è cambia serviceSubnetCIDR per u serviziu kube-dns.

Stu pianu vi permetterà di minimizzà u downtime à ~ un minutu - per a durata di a rimozione di u serviziu kube-dns-tmp è cambià a subnet per u serviziu kube-dns.

Mudificazione podNetwork

À u listessu tempu, avemu decisu di guardà cumu mudificà podNetwork utilizendu u etcdhelper risultatu. A sequenza di l'azzioni hè a siguenti:

  • riparà e cunfigurazioni in kube-system;
  • riparà u manifestu kube-controller-manager;
  • cambia podCIDR direttamente in etcd;
  • riavvia tutti i nodi di cluster.

Avà più nantu à queste azzioni:

1. Mudificà ConfigMap in u namespace kube-system:

kubectl -n kube-system edit cm kubeadm-config

- currezzione data.ClusterConfiguration.networking.podSubnet à una nova subnet 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Mudificà u manifestu controller-manager:

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

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

3. Fighjate à i valori attuali .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses per tutti i nodi di cluster:

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. Sustituisci podCIDR fendu cambiamenti direttamente à 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. Cuntrollamu chì podCIDR hè veramente cambiatu:

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. Reboot all cluster nodes unu per unu.

7. Sè vo lascià almenu un node vechju podCIDR, allura kube-controller-manager ùn serà micca pussibule di inizià, è i pods in u cluster ùn saranu micca pianificati.

In fattu, cambià podCIDR pò esse fattu ancu più simplice (per esempiu, tantu). Ma vulemu amparà à travaglià direttamente cù etcd, perchè ci sò casi quandu editate l'uggetti Kubernetes in etcd - u solu varianti pussibuli. (Per esempiu, ùn pudete micca solu cambià u campu di serviziu senza downtime spec.clusterIP.)

U risultatu

L'articulu discute a pussibilità di travaglià cù dati in etcd direttamente, i.e. ignora l'API Kubernetes. Calchì volta stu approcciu vi permette di fà "cose ​​difficili". Avemu pruvatu l'operazioni datu in u testu nantu à i clusters K8s reali. Tuttavia, u so statutu di prontezza per l'usu generalizatu hè PoC (prova di cuncettu). Dunque, sè vo vulete usà una versione mudificata di l'utilità etcdhelper nantu à i vostri clusters, fate cusì à u vostru propiu risicu.

PS

Leghjite puru nant'à u nostru blog:

Source: www.habr.com

Add a comment