Tapasztalatunk az etcd Kubernetes klaszter adataival kapcsolatosan közvetlenül (anélkül, hogy a K8s API-t használnánk)

Egyre gyakrabban keresnek meg bennünket ügyfelek, hogy biztosítsunk hozzáférést a Kubernetes klaszterhez, hogy közvetlenül csatlakozhassanak egy adatbázishoz vagy szolgáltatáshoz, hogy a helyi alkalmazás kommunikálhasson az klaszterben található alkalmazásokkal...

Tapasztalatunk az etcd Kubernetes klaszter adataival kapcsolatosan közvetlenül (anélkül, hogy a K8s API-t használnánk)

Például felmerülhet az igény, hogy a saját helyi gépünkről csatlakozzunk a szolgáltatáshoz memcached.staging.svc.cluster.local. Ezt a lehetőséget VPN-en keresztül biztosítjuk a klaszteren belül, amelyhez az ügyfél csatlakozik. Ennek érdekében bejelentjük a podok, szolgáltatások alhálózatait és át push-oljuk a klaszter DNS-t az ügyfélnek. Így, amikor az ügyfél megpróbál csatlakozni a szolgáltatáshoz, memcached.staging.svc.cluster.locala kérés a klaszter DNS-hez megy, és válaszként megkapja az adott szolgáltatás címét a klaszter szolgáltatási hálózatából vagy a pod címét.

A K8s klasztereket kubeadm segítségével állítjuk be, ahol alapértelmezetten a szolgáltatási alhálózat — 192.168.0.0/16, a podok hálózata pedig — 10.244.0.0/16. Általában jól működik, de van néhány dolog:

  • Az alhálózat 192.168.*.* gyakran használva van az ügyfelek irodai hálózataiban, és még gyakrabban a fejlesztők otthoni hálózataiban. Így konflikusok alakulnak ki: az otthoni routerek ebben az alhálózatban működnek, és a VPN push-olja ezeket az alhálózatokat az ügyfélnek a klaszterből.
  • Több klaszterünk van (production, staging és/vagy több dev klaszter). Így mindegyikben alapértelmezés szerint azonos alhálózatok lesznek a podok és szolgáltatások számára, ami kellemetlen bonyodalmakat okoz a szolgáltatások egyidejű használatával több klaszterben.

Már elég régóta alkalmazzuk a különböző alhálózatok használatának gyakorlatát a szolgáltatások és podok esetében egy projekten belül — lényegében hogy minden klaszter eltérő hálózatokkal bírjon. azonban van sok olyan klaszter, amellyel dolgozunk, és ezeket nem szeretnénk nulláról átállítani, mivel sok szolgáltatás, állapotfenntartó alkalmazás stb. fut.

Így azt kérdeztük, hogyan lehetne megváltoztatni az alhálózatot a meglévő klaszterben?

Megoldások keresése

A legelterjedtebb gyakorlat az, hogy újra létrehozzuk mindent a ClusterIP típusú szolgáltatásokat. Mint lehetőség, ajánlhatják és ezt is:

A következő folyamatnak van egy problémája: miután mindent beállítottunk, a podok a régi IP-címmel érkeznek DNS névszerverként a /etc/resolv.conf fájlba.
Mivel még mindig nem találtam meg a megoldást, le kellett nulláznom az egész klasztert a kubeadm reset használatával, és újra el kellett indítanom.

De nem mindenki számára megfelel ez... Íme részletesebb bevezetés a mi esetünkre:

  • A Flannel használatban van;
  • Vannak klaszterek a felhőkben és a hardvereken is;
  • Szeretnénk elkerülni a klaszter összes szolgáltatásának újra telepítését;
  • Az igény az, hogy mindent a lehető legkevesebb probléma mellett végezzünk el;
  • A Kubernetes verziója — 1.16.6 (továbbá a további lépések hasonlóak lesznek más verziók esetén is);
  • A fő feladat az, hogy a kubeadm által telepített klaszterben, amely rendelkezik egy szolgáltatás alhálózattal, 192.168.0.0/16cseréljük le azt 172.24.0.0/16.

S ahogy ez összejött, régóta érdekelt minket, hogy a Kubernetesben hogyan tárolódnak az adatok az etcd-ben, és mit lehet ezzel kezdeni... Arra gondoltunk: „Miért ne frissíthetnénk az etcd adatait úgy, hogy a régi IP-címeket (alhálózatot) újakkal helyettesítjük??»

Kész eszközöket keresve az etcd adataival való munkához, nem találtunk semmit, ami teljes mértékben megoldotta volna a feladatot. (Mellesleg, ha ismersz bármilyen eszközt az etcd adatok közvetlen kezelésére — örülnénk a linkeknek.) A jó kiindulópont azonban a etcdhelper OpenShift-től (köszönet a szerzőinek!).

Ez az eszköz képes csatlakozni az etcd-hez tanúsítványok segítségével, és parancsokon keresztül olvasni az adatokat. ls, get, dump.

Hozzáadjuk az etcdhelper-t

A következő gondolat természetes: „Mi akadályoz meg abban, hogy ezt az eszközt kiegészítsük az etcd adatok írásának lehetőségével?”

Ez megvalósult egy módosított etcdhelper verzióban, két új funkcióval: changeServiceCIDR és changePodCIDR. A kódját itt megnézheted. itt.

Mire valók az új funkciók? Az algoritmus: changeServiceCIDR:

  • deszerializálót hozunk létre;
  • összeállítunk egy reguláris kifejezést a CIDR cseréjére;
  • végigmegyünk az összes ClusterIP típusú szolgáltatáson a klaszterben:
    • dekódoljuk az etcd-ből származó értéket Go-objektummá;
    • a reguláris kifejezés segítségével lecseréljük a cím első két bájtját;
    • az új alhálózatból IP-címet rendelünk a szolgáltatáshoz;
    • szerializálót hozunk létre, átalakítjuk a Go-objektumot protobufba, és az új adatokat beírjuk az etcd-be.

Funkció changePodCIDR lényegében hasonló changeServiceCIDR — csak itt a szolgáltatások specifikációjának szerkesztése helyett ezt a csomóponton végezzük, és módosítjuk .spec.PodCIDR az új alhálózatra.

Gyakorlat

A serviceCIDR váltás

A feladat megvalósításának terve nagyon egyszerű, de leállítást igényel az összes pod újra létrehozása alatt a klaszterben. Az alaplépések felsorolása után megosztjuk a gondolatainkat arról, hogyan lehet elméletben minimalizálni ezt az állást.

Előkészületek:

  • a szükséges szoftverek telepítése és a javított etcdhelper összeállítása;
  • etcd biztonsági mentés és /etc/kubernetes.

Rövid akcióterv a serviceCIDR váltására:

  • az apiserver és a controller-manager manifestjeinek módosítása;
  • a tanúsítványok újra kiadása;
  • ClusterIP szolgáltatások módosítása az etcd-ben;
  • minden pod újraindítása a klaszterben.

Következik a részletes lépések teljes sora.

1. Telepítjük az etcd-clientet az adatok mentéséhez:

apt install etcd-client

2. Összegyűjtjük az etcdhelper-t:

  • Telepítjük a golang-ot:
    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
  • Mentjük az etcdhelper.go, letöltjük a függőségeket, összegyűjtjük:
    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. Készítünk egy biztonsági másolatot az etcd-ről:

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. Módosítjuk a szolgáltatási alhálózatot a Kubernetes control plane manifestjeiben. A fájlokban /etc/kubernetes/manifests/kube-apiserver.yaml és /etc/kubernetes/manifests/kube-controller-manager.yaml módosítjuk a paramétert --service-cluster-ip-range az új alhálózatra: 172.24.0.0/16 $CI_COMMIT_TAG 192.168.0.0/16.

5. Mivel megváltoztatjuk a szolgáltatási alhálózatot, amelyre a kubeadm tanúsítványokat bocsát ki az apiserver számára (többek között), újra kell őket adni:

  1. Nézzük meg, hogy jelenleg mely domainek és IP-címek szolgálnak ki a tanúsítvány:
    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 cím:192.168.0.1, IP cím:10.0.0.163, IP cím:192.168.199.100
  2. Készítünk egy minimális konfigurációt a kubeadm-hez:
    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" # a mester csomópont IP-címe
  3. Töröljük a régi crt és key fájlokat, mivel ezek nélkül az új tanúsítvány nem lesz kiadható:
    rm \/etc\/kubernetes\/pki\/apiserver.{key,crt}
  4. Újra kiadjuk a tanúsítványokat az API szerver számára:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Ellenőrizzük, hogy a tanúsítvány kiadásra került az új alhálózatra:
    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 cím:172.24.0.1, IP cím:10.0.0.163, IP cím:192.168.199.100
  6. A API szerver tanúsítványának újra kiadása után indítsuk újra a konténerét:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Generáljuk újra a konfigurációt a admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Szerkesszük az adatokat az etcd-ben:
    .\/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 

    Figyelem! Ebben a pillanatban a klaszterben megszűnik a domainnevek feloldása, mivel a már meglévő pod-okban /etc/resolv.conf a régi CoreDNS (kube-dns) cím szerepel, míg a kube-proxy megváltoztatta az iptables szabályait a régi alhálózatról az újra. A cikk további részében a leállás minimalizálásának lehetséges módjairól lesz szó.

  9. Javítjuk a ConfigMap-eket a névtérben kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    — itt a clusterDNS szót az új kube-dns szolgáltatás IP-címére cseréljük: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    — javítjuk a data.ClusterConfiguration.networking.serviceSubnet az új alhálózatra.

  10. Mivel megváltozott a kube-dns címe, frissítenie kell a kubelet konfigurációját az összes csomóponton:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Már csak a klaszter összes pod-ját kell újraindítani:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's\/(S+)s+(S+).*\/kubectl --namespace 1 delete pod 2\/e'

Leállás minimalizálása

Gondolkodj el, hogyan lehet minimalizálni a leállást:

  1. A control plane manifestjeinek módosítása után hozz létre egy új kube-dns szolgáltatást, például a következő néven: kube-dns-tmp és új címmel 172.24.0.10.
  2. Hozz létre if az etcdhelper-ben, ami nem módosítja a kube-dns szolgáltatást.
  3. Cseréld le az összes kubelet-nél a ClusterDNS címet az újra, ezzel párhuzamosan a régi szolgáltatás továbbra is működni fog.
  4. Várd meg, amíg az alkalmazások pod-jai maguktól frissülnek, vagy az előre megbeszélt időben.
  5. Töröld a szolgáltatást kube-dns-tmp és módosítsd a serviceSubnetCIDR értékét a kube-dns szolgáltatásra.

Ez a terv lehetővé teszi a leállás minimalizálását körülbelül 1 percre — a szolgáltatás törlésének idejére kube-dns-tmp és a szolgáltatás alhálózatának cseréjére kube-dns.

podNetwork módosítása

Eközben megnéztük, hogyan lehet módosítani a podNetwork-öt a létrehozott etcdhelper segítségével. A lépések a következők:

  • javítsuk a konfigurációkat a kube-system;
  • javítsuk a kube-controller-manager manifestét;
  • közvetlenül módosítsuk a podCIDR-t az etcd-ben;
  • újra kell indítani az összes klaszter csomópontot.

Most részletesebben ezekről a lépésekről:

1. Módosítsuk a ConfigMap-eket a névtérben kube-system:

kubectl -n kube-system edit cm kubeadm-config

— javítsuk data.ClusterConfiguration.networking.podSubnet az új alhálózatra 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

— javítsuk data.config.conf.clusterCIDR: 10.55.0.0\/16.

2. Módosítsuk a controller-manager manifestjét:

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

— javítsuk --cluster-cidr=10.55.0.0\/16.

3. Nézd meg a jelenlegi értékeket .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses az összes klaszter csomópontban:

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. Cseréljük ki a podCIDR-t, közvetlenül módosítva az etcd-t:

./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. Ellenőrizzük, hogy a podCIDR valóban megváltozott:

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. Sorban indítsuk újra az összes klasztercsomópontot.

7. Ha bármelyik csomóponton a régi podCIDR marad akkor a kube-controller-manager nem tud elindulni, és a podok a klaszterben nem lesznek ütemezve.Valójában a podCIDR megváltoztatása egyszerűbben is elvégezhető (például,

). De szeretnénk megtanulni, hogyan lehet közvetlenül dolgozni az etcd-vel, mert vannak esetek, amikor a Kubernetes objektumok módosítása az etcd-ben az egyetlen ígylehetséges megoldás. (Például, nem lehet csak úgy cserélni a Service "spec.clusterIP" mezőjét a cikk bemutatja az etcd adatok közvetlen kezelésének lehetőségét, vagyis a Kubernetes API megkerülését. Néha ez a megközelítés lehetővé teszi, hogy 'furfangos dolgokat' csináljunk. A szövegben bemutatott műveleteket valódi K8s klasztereken teszteltük. Azonban a széleskörű felhasználásra való alkalmasságuk státusza - PoC (proof of concept) . Tehát ha módosított verziót szeretne használni az etcdhelper eszközből a klaszterein, azt saját felelősségére tegye..)

Result

etcd 3.4.3: a tároló megbízhatóságának és biztonságának vizsgálata Calico a Kubernetes hálózatban: bevezetés és néhány tapasztalat6 érdekes rendszerszintű hiba a Kubernetes üzemeltetése során [és megoldásuk]

P.S.

Olvasson további információkat a blogunkban:

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster