Ang aming karanasan sa data sa etcd Kubernetes cluster nang direkta (walang K8s API)

Parami nang parami, ang mga kliyente ay humihiling sa amin na magbigay ng access sa Kubernetes cluster upang ma-access ang mga serbisyo sa loob ng cluster: upang direktang kumonekta sa ilang database o serbisyo, upang ikonekta ang isang lokal na application sa mga application sa loob ng cluster...

Ang aming karanasan sa data sa etcd Kubernetes cluster nang direkta (walang K8s API)

Halimbawa, may pangangailangang kumonekta mula sa iyong lokal na makina patungo sa isang serbisyo memcached.staging.svc.cluster.local. Ibinibigay namin ang kakayahang ito gamit ang isang VPN sa loob ng cluster kung saan kumokonekta ang kliyente. Upang gawin ito, inanunsyo namin ang mga subnet ng mga pod, serbisyo at push cluster DNS sa kliyente. Kaya, kapag sinubukan ng isang kliyente na kumonekta sa serbisyo memcached.staging.svc.cluster.local, ang kahilingan ay mapupunta sa cluster DNS at bilang tugon ay natatanggap ang address ng serbisyong ito mula sa cluster service network o ang pod address.

Kino-configure namin ang mga K8s cluster gamit ang kubeadm, kung saan naroon ang default na subnet ng serbisyo 192.168.0.0/16, at ang network ng mga pod ay 10.244.0.0/16. Karaniwan ang lahat ay gumagana nang maayos, ngunit mayroong ilang mga puntos:

  • Subnet 192.168.*.* kadalasang ginagamit sa mga network ng opisina ng kliyente, at mas madalas sa mga home network ng developer. At pagkatapos ay napupunta tayo sa mga salungatan: gumagana ang mga home router sa subnet na ito at itinutulak ng VPN ang mga subnet na ito mula sa cluster patungo sa kliyente.
  • Mayroon kaming ilang cluster (produksyon, yugto at/o ilang dev cluster). Pagkatapos, bilang default, lahat ng mga ito ay magkakaroon ng parehong mga subnet para sa mga pod at serbisyo, na lumilikha ng malaking kahirapan para sa sabay-sabay na trabaho sa mga serbisyo sa ilang mga kumpol.

Matagal na naming pinagtibay ang kasanayan ng paggamit ng iba't ibang mga subnet para sa mga serbisyo at pod sa loob ng parehong proyekto - sa pangkalahatan, upang ang lahat ng mga cluster ay may iba't ibang network. Gayunpaman, mayroong isang malaking bilang ng mga kumpol na gumagana na hindi ko nais na i-roll over mula sa simula, dahil nagpapatakbo sila ng maraming mga serbisyo, stateful application, atbp.

At pagkatapos ay tinanong namin ang aming sarili: paano baguhin ang subnet sa isang umiiral na kumpol?

Paghahanap ng mga desisyon

Ang pinakakaraniwang kasanayan ay ang muling likhain lahat mga serbisyong may uri ng ClusterIP. Bilang isang opsyon, maaaring magpayo at ito:

May problema ang sumusunod na proseso: pagkatapos ma-configure ang lahat, lalabas ang mga pod ng lumang IP bilang DNS nameserver sa /etc/resolv.conf.
Dahil hindi ko pa rin mahanap ang solusyon, kailangan kong i-reset ang buong kumpol gamit ang pag-reset ng kubeadm at i-init ito muli.

Ngunit hindi ito angkop para sa lahat... Narito ang mga mas detalyadong pagpapakilala para sa aming kaso:

  • Ginagamit ang pranela;
  • Mayroong mga kumpol sa mga ulap at sa hardware;
  • Gusto kong iwasang muling i-deploy ang lahat ng serbisyo sa cluster;
  • Kailangang gawin ang lahat nang may pinakamababang bilang ng mga problema;
  • Ang bersyon ng Kubernetes ay 1.16.6 (gayunpaman, ang mga karagdagang hakbang ay magiging katulad para sa iba pang mga bersyon);
  • Ang pangunahing gawain ay upang matiyak na sa isang kumpol na na-deploy gamit ang kubeadm na may isang subnet ng serbisyo 192.168.0.0/16, palitan ito ng 172.24.0.0/16.

At nagkataon na matagal na kaming interesado na makita kung ano at paano nakaimbak sa Kubernetes sa etcd, kung ano ang maaaring gawin dito... Kaya naisip namin: β€œBakit hindi na lang i-update ang data sa etcd, palitan ang mga lumang IP address (subnet) ng mga bago? "

Ang pagkakaroon ng paghahanap para sa mga handa na tool para sa pagtatrabaho sa data sa etcd, wala kaming nakitang anumang bagay na ganap na nalutas ang problema. (Sa pamamagitan ng paraan, kung alam mo ang tungkol sa anumang mga utility para sa pagtatrabaho sa data nang direkta sa etcd, kami ay nagpapasalamat sa mga link.) Gayunpaman, ang isang magandang panimulang punto ay atbphelper mula sa OpenShift (salamat sa mga may-akda nito!).

Maaaring kumonekta ang utility na ito sa etcd gamit ang mga certificate at magbasa ng data mula doon gamit ang mga command ls, get, dump.

Magdagdag ng etcdhelper

Ang susunod na pag-iisip ay lohikal: "Ano ang pumipigil sa iyo sa pagdaragdag ng utility na ito sa pamamagitan ng pagdaragdag ng kakayahang magsulat ng data sa etcd?"

Ito ay naging isang binagong bersyon ng etcdhelper na may dalawang bagong function changeServiceCIDR ΠΈ changePodCIDR. sa kanya makikita mo ang code dito.

Ano ang ginagawa ng mga bagong tampok? Algorithm changeServiceCIDR:

  • lumikha ng deserializer;
  • mag-compile ng isang regular na expression upang palitan ang CIDR;
  • dumaan kami sa lahat ng mga serbisyo na may uri ng ClusterIP sa cluster:
    • i-decode ang halaga mula sa etcd sa isang Go object;
    • gamit ang isang regular na expression pinapalitan namin ang unang dalawang byte ng address;
    • italaga ang serbisyo ng isang IP address mula sa bagong subnet;
    • lumikha ng isang serializer, i-convert ang Go object sa protobuf, magsulat ng bagong data sa etcd.

Tungkulin changePodCIDR mahalagang magkatulad changeServiceCIDR - tanging sa halip na i-edit ang detalye ng serbisyo, ginagawa namin ito para sa node at pagbabago .spec.PodCIDR sa isang bagong subnet.

Pagsasanay

Baguhin ang serbisyo ng CIDR

Ang plano para sa pagpapatupad ng gawain ay napakasimple, ngunit nagsasangkot ito ng downtime sa oras ng muling paggawa ng lahat ng mga pod sa cluster. Pagkatapos ilarawan ang mga pangunahing hakbang, magbabahagi din kami ng mga saloobin kung paano, sa teorya, ang downtime na ito ay maaaring mabawasan.

Mga hakbang sa paghahanda:

  • pag-install ng kinakailangang software at pag-assemble ng naka-patch na etcdhelper;
  • backup etcd at /etc/kubernetes.

Maikling plano ng aksyon para sa pagpapalit ng serbisyoCIDR:

  • pagbabago ng apiserver at controller-manager manifests;
  • muling pag-isyu ng mga sertipiko;
  • pagpapalit ng mga serbisyo ng ClusterIP sa etcd;
  • i-restart ang lahat ng pod sa cluster.

Ang sumusunod ay isang kumpletong pagkakasunod-sunod ng mga aksyon nang detalyado.

1. I-install ang etcd-client para sa data dump:

apt install etcd-client

2. Bumuo ng etcdhelper:

  • I-install ang 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
  • Nag-iipon tayo para sa ating sarili etcdhelper.go, i-download ang mga dependency, kolektahin ang:
    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. Gumawa ng backup 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. Baguhin ang subnet ng serbisyo sa mga manifest ng Kubernetes control plane. Sa mga file /etc/kubernetes/manifests/kube-apiserver.yaml ΠΈ /etc/kubernetes/manifests/kube-controller-manager.yaml baguhin ang parameter --service-cluster-ip-range sa isang bagong subnet: 172.24.0.0/16 sa halip ng 192.168.0.0/16.

5. Dahil binabago namin ang subnet ng serbisyo kung saan nag-isyu ang kubeadm ng mga sertipiko para sa apiserver (kasama na), kailangan nilang maibigay muli:

  1. Tingnan natin kung aling mga domain at IP address ang ibinigay ng kasalukuyang certificate:
    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. Maghanda tayo ng kaunting config para sa 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. Tanggalin natin ang lumang crt at key, dahil kung wala ito ay hindi maibibigay ang bagong certificate:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Mag-isyu muli tayo ng mga certificate para sa API server:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Suriin natin kung ang sertipiko ay ibinigay para sa bagong 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. Pagkatapos muling ibigay ang certificate ng API server, i-restart ang container nito:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. I-regenerate natin ang config para sa admin.conf:
    kubeadm alpha certs renew admin.conf
  8. I-edit natin ang data sa 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 

    Warning! Sa sandaling ito, humihinto sa paggana ang resolution ng domain sa cluster, dahil sa mga kasalukuyang pod /etc/resolv.conf ang lumang CoreDNS address (kube-dns) ay nakarehistro, at binago ng kube-proxy ang mga panuntunan ng iptables mula sa lumang subnet patungo sa bago. Karagdagan sa artikulo ito ay nakasulat tungkol sa mga posibleng opsyon upang mabawasan ang downtime.

  9. Ayusin natin ang ConfigMap sa namespace kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - Palitan dito clusterDNS sa bagong IP address ng serbisyo ng kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - aayusin natin data.ClusterConfiguration.networking.serviceSubnet sa isang bagong subnet.

  10. Dahil nagbago ang kube-dns address, kailangang i-update ang kubelet config sa lahat ng node:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Ang natitira na lang ay i-restart ang lahat ng mga pod sa cluster:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Bawasan ang downtime

Mga ideya kung paano bawasan ang downtime:

  1. Pagkatapos baguhin ang control plane manifests, lumikha ng bagong serbisyo ng kube-dns, halimbawa, na may pangalan kube-dns-tmp at bagong address 172.24.0.10.
  2. Gumawa if sa etcdhelper, na hindi babaguhin ang serbisyo ng kube-dns.
  3. Palitan ang address sa lahat ng kubelets ClusterDNS sa isang bago, habang ang lumang serbisyo ay patuloy na gagana nang sabay-sabay sa bago.
  4. Maghintay hanggang ang mga pod na may mga application ay gumulong nang mag-isa para sa natural na mga kadahilanan o sa isang napagkasunduang oras.
  5. Tanggalin ang serbisyo kube-dns-tmp at pagbabago serviceSubnetCIDR para sa serbisyo ng kube-dns.

Papayagan ka ng planong ito na bawasan ang downtime hanggang ~isang minuto - para sa tagal ng pag-alis ng serbisyo kube-dns-tmp at pagpapalit ng subnet para sa serbisyo kube-dns.

Modification podNetwork

Kasabay nito, nagpasya kaming tingnan kung paano baguhin ang podNetwork gamit ang resultang etcdhelper. Ang pagkakasunud-sunod ng mga aksyon ay ang mga sumusunod:

  • pag-aayos ng mga config sa kube-system;
  • pag-aayos ng kube-controller-manager manifest;
  • baguhin ang podCIDR nang direkta sa etcd;
  • i-reboot ang lahat ng mga cluster node.

Ngayon higit pa tungkol sa mga pagkilos na ito:

1. Baguhin ang ConfigMap sa namespace kube-system:

kubectl -n kube-system edit cm kubeadm-config

- pagwawasto data.ClusterConfiguration.networking.podSubnet sa isang bagong subnet 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Baguhin ang manifest ng controller-manager:

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

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

3. Tingnan ang kasalukuyang mga halaga .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses para sa lahat ng mga cluster node:

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. Palitan ang podCIDR sa pamamagitan ng direktang paggawa ng mga pagbabago sa 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. Suriin natin kung talagang nagbago ang podCIDR:

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. Isa-isa nating i-reboot ang lahat ng cluster node.

7. Kung mag-iiwan ka ng kahit isang node lumang podCIDR, kung gayon ang kube-controller-manager ay hindi makakapagsimula, at ang mga pod sa cluster ay hindi maiiskedyul.

Sa katunayan, ang pagpapalit ng podCIDR ay maaaring gawin nang mas simple (halimbawa, kaya). Ngunit nais naming matutunan kung paano direktang gumana sa etcd, dahil may mga kaso kapag nag-e-edit ng mga bagay sa Kubernetes sa etcd - solong posibleng variant. (Halimbawa, hindi mo lang mababago ang field ng Serbisyo nang walang downtime spec.clusterIP.)

Kabuuan

Tinatalakay ng artikulo ang posibilidad ng pagtatrabaho sa data sa etcd nang direkta, i.e. pag-bypass sa Kubernetes API. Minsan ang diskarte na ito ay nagpapahintulot sa iyo na gawin ang "mga nakakalito na bagay". Sinubukan namin ang mga operasyong ibinigay sa text sa mga totoong K8s cluster. Gayunpaman, ang kanilang katayuan ng pagiging handa para sa malawakang paggamit ay PoC (patunay ng konsepto). Samakatuwid, kung gusto mong gumamit ng binagong bersyon ng etcdhelper utility sa iyong mga cluster, gawin mo ito sa iyong sariling peligro.

PS

Basahin din sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento