La nostra experiència amb dades al clúster etcd Kubernetes directament (sense l'API K8s)

Cada cop més, els clients ens demanen que proporcionem accés al clúster de Kubernetes per poder accedir als serveis dins del clúster: poder connectar-nos directament a alguna base de dades o servei, connectar una aplicació local amb aplicacions dins del clúster...

La nostra experiència amb dades al clúster etcd Kubernetes directament (sense l'API K8s)

Per exemple, cal connectar-se des de la vostra màquina local a un servei memcached.staging.svc.cluster.local. Oferim aquesta capacitat mitjançant una VPN dins del clúster al qual es connecta el client. Per fer-ho, anunciem subxarxes de pods, serveis i push cluster DNS al client. Així, quan un client intenta connectar-se al servei memcached.staging.svc.cluster.local, la sol·licitud va al DNS del clúster i, com a resposta, rep l'adreça d'aquest servei de la xarxa de serveis del clúster o l'adreça del pod.

Configurem els clústers K8s mitjançant kubeadm, on es troba la subxarxa de servei predeterminada 192.168.0.0/16, i la xarxa de beines és 10.244.0.0/16. Normalment tot funciona bé, però hi ha un parell de punts:

  • Subxarxa 192.168.*.* s'utilitza sovint a les xarxes d'oficines del client, i encara més sovint a les xarxes domèstiques de desenvolupadors. I aleshores tenim conflictes: els encaminadors domèstics funcionen en aquesta subxarxa i la VPN empeny aquestes subxarxes del clúster al client.
  • Tenim diversos clústers (producció, fase i/o diversos clústers de desenvolupament). Aleshores, per defecte, totes elles tindran les mateixes subxarxes per a pods i serveis, la qual cosa crea grans dificultats per treballar simultàniament amb serveis en diversos clústers.

Fa temps que hem adoptat la pràctica d'utilitzar diferents subxarxes per a serveis i pods dins del mateix projecte, en general, de manera que tots els clústers tinguin xarxes diferents. Tanmateix, hi ha un gran nombre de clústers en funcionament que no m'agradaria revertir des de zero, ja que executen molts serveis, aplicacions amb estat, etc.

I llavors ens vam preguntar: com canviar la subxarxa en un clúster existent?

Recerca de decisions

La pràctica més habitual és recrear tots serveis amb tipus ClusterIP. Com a opció, pot aconsellar i això:

El procés següent té un problema: després de tot configurat, els pods apareixen amb l'antiga IP com a servidor de noms DNS a /etc/resolv.conf.
Com que encara no vaig trobar la solució, vaig haver de restablir tot el clúster amb kubeadm reset i tornar-lo a iniciar.

Però això no és apte per a tothom... Aquí hi ha introduccions més detallades per al nostre cas:

  • S'utilitza franel·la;
  • Hi ha clústers tant als núvols com al maquinari;
  • M'agradaria evitar tornar a desplegar tots els serveis del clúster;
  • En general, cal fer-ho tot amb un nombre mínim de problemes;
  • La versió de Kubernetes és 1.16.6 (no obstant això, els passos posteriors seran similars per a altres versions);
  • La tasca principal és assegurar-se que en un clúster desplegat amb kubeadm amb una subxarxa de servei 192.168.0.0/16, substituïu-lo per 172.24.0.0/16.

I va passar que fa temps que estàvem interessats a veure què i com s'emmagatzema Kubernetes a etcd, què es pot fer amb ell... Així que vam pensar: “Per què no actualitzar les dades a etcd, substituint les antigues adreces IP (subxarxa) per unes de noves? "

Després d'haver buscat eines preparades per treballar amb dades a etcd, no hem trobat res que resolgués completament el problema. (Per cert, si coneixeu alguna utilitat per treballar amb dades directament a etcd, us agrairem els enllaços.) No obstant això, un bon punt de partida és etcdhelper des d'OpenShift (gràcies als seus autors!).

Aquesta utilitat es pot connectar a etcd mitjançant certificats i llegir dades des d'allà mitjançant ordres ls, get, dump.

Afegeix etcdhelper

El següent pensament és lògic: "Què us impedeix afegir aquesta utilitat afegint la capacitat d'escriure dades a etcd?"

Es va convertir en una versió modificada de etcdhelper amb dues funcions noves changeServiceCIDR и changePodCIDR. En ella pots veure el codi aquí.

Què fan les noves funcions? Algorisme changeServiceCIDR:

  • crear un deserialitzador;
  • compilar una expressió regular per substituir CIDR;
  • passem per tots els serveis amb el tipus ClusterIP al clúster:
    • descodificar el valor de etcd en un objecte Go;
    • utilitzant una expressió regular substituïm els dos primers bytes de l'adreça;
    • assigneu al servei una adreça IP de la nova subxarxa;
    • crear un serialitzador, convertir l'objecte Go en protobuf, escriure dades noves a etcd.

Funció changePodCIDR essencialment semblants changeServiceCIDR - només que en lloc d'editar l'especificació del servei, ho fem pel node i canviem .spec.PodCIDR a una nova subxarxa.

Pràctica

Canvi de servei CIDR

El pla per implementar la tasca és molt senzill, però implica temps d'inactivitat en el moment de la recreació de tots els pods del clúster. Després de descriure els passos principals, també compartirem idees sobre com, en teoria, es pot minimitzar aquest temps d'inactivitat.

Passos preparatoris:

  • instal·lar el programari necessari i muntar el etcdhelper pegat;
  • còpia de seguretat, etcd i /etc/kubernetes.

Breu pla d'acció per canviar el servei CIDR:

  • canviar els manifests apiserver i controller-manager;
  • reemissió de certificats;
  • canviar els serveis ClusterIP a etcd;
  • reinicieu tots els pods del clúster.

La següent és una seqüència completa d'accions en detall.

1. Instal·leu etcd-client per a l'abocament de dades:

apt install etcd-client

2. Construeix etcdhelper:

  • Instal·leu 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
  • Estalviem per a nosaltres mateixos etcdhelper.go, descarregar dependències, recopilar:
    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. Feu una còpia de seguretat 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. Canvieu la subxarxa del servei als manifests del pla de control de Kubernetes. En fitxers /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml canviar el paràmetre --service-cluster-ip-range a una nova subxarxa: 172.24.0.0/16 en comptes de 192.168.0.0/16.

5. Com que estem canviant la subxarxa del servei a la qual kubeadm emet certificats per a apiserver (inclosos), cal que es tornin a emetre:

  1. Vegem per a quins dominis i adreces IP s'ha emès el certificat actual:
    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. Preparem una configuració mínima per a 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. Suprimim el crt i la clau antics, ja que sense això no s'emetrà el nou certificat:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Tornem a emetre certificats per al servidor API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Comprovem que s'ha emès el certificat per a la nova subxarxa:
    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. Després de tornar a emetre el certificat del servidor API, reinicieu el seu contenidor:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Regenerem la configuració de admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Editem les dades a 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 

    Atenció! En aquest moment, la resolució de domini deixa de funcionar al clúster, ja que en pods existents /etc/resolv.conf l'antiga adreça CoreDNS (kube-dns) està registrada i kube-proxy canvia les regles d'iptables de la subxarxa antiga a la nova. Més enllà a l'article s'escriu sobre possibles opcions per minimitzar el temps d'inactivitat.

  9. Arreglem ConfigMap a l'espai de noms kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - substitueix aquí clusterDNS a la nova adreça IP del servei kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - Ho arreglarem data.ClusterConfiguration.networking.serviceSubnet a una nova subxarxa.

  10. Com que l'adreça de kube-dns ha canviat, cal actualitzar la configuració de kubelet a tots els nodes:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Només queda reiniciar tots els pods del clúster:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimitzar el temps d'inactivitat

Reflexions sobre com minimitzar el temps d'inactivitat:

  1. Després de canviar els manifests del pla de control, creeu un nou servei kube-dns, per exemple, amb el nom kube-dns-tmp i nova adreça 172.24.0.10.
  2. fer if a etcdhelper, que no modificarà el servei kube-dns.
  3. Substituïu l'adreça a tots els kubelets ClusterDNS a un de nou, mentre que l'antic servei continuarà funcionant simultàniament amb el nou.
  4. Espereu fins que les beines amb aplicacions passin per si soles per raons naturals o en un moment acordat.
  5. Suprimeix el servei kube-dns-tmp i canvi serviceSubnetCIDR per al servei kube-dns.

Aquest pla us permetrà minimitzar el temps d'inactivitat a ~ un minut, mentre duri l'eliminació del servei kube-dns-tmp i canviar la subxarxa del servei kube-dns.

Modificació podNetwork

Al mateix temps, vam decidir veure com modificar podNetwork mitjançant l'etcdhelper resultant. La seqüència d'accions és la següent:

  • arreglant les configuracions kube-system;
  • arreglar el manifest kube-controller-manager;
  • canviar podCIDR directament a etcd;
  • reinicieu tots els nodes del clúster.

Ara més sobre aquestes accions:

1. Modifiqueu ConfigMap a l'espai de noms kube-system:

kubectl -n kube-system edit cm kubeadm-config

- correcció data.ClusterConfiguration.networking.podSubnet a una nova subxarxa 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

- correcció data.config.conf.clusterCIDR: 10.55.0.0/16.

2. Modifiqueu el manifest del gestor del controlador:

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

- correcció --cluster-cidr=10.55.0.0/16.

3. Observa els valors actuals .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses per a tots els nodes del clúster:

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. Substituïu podCIDR fent canvis directament a 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. Comprovem que podCIDR ha canviat realment:

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. Reiniciem tots els nodes del clúster un per un.

7. Si deixeu almenys un node antic podCIDR, aleshores el kube-controller-manager no es podrà iniciar i els pods del clúster no es programaran.

De fet, canviar podCIDR es pot fer encara més senzill (per exemple, tan). Però volíem aprendre a treballar amb etcd directament, perquè hi ha casos en què s'editen objectes de Kubernetes a etcd: l’únic variant possible. (Per exemple, no podeu canviar el camp Servei sense temps d'inactivitat spec.clusterIP.)

Total

L'article parla de la possibilitat de treballar directament amb dades en etcd, és a dir. evitant l'API de Kubernetes. De vegades, aquest enfocament us permet fer "coses complicades". Hem provat les operacions que es donen al text en clústers K8s reals. No obstant això, el seu estat de preparació per a un ús generalitzat és PoC (prova de concepte). Per tant, si voleu utilitzar una versió modificada de la utilitat etcdhelper als vostres clústers, feu-ho sota el vostre propi risc.

PS

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari