Nasze doświadczenie z danymi w klastrze etcd Kubernetes bezpośrednio (bez API K8s)

Coraz częściej klienci proszą nas o udostępnienie klastra Kubernetes, aby móc uzyskać dostęp do usług w ramach klastra: aby móc bezpośrednio połączyć się z jakąś bazą danych lub usługą, aby połączyć aplikację lokalną z aplikacjami w ramach klastra...

Nasze doświadczenie z danymi w klastrze etcd Kubernetes bezpośrednio (bez API K8s)

Na przykład istnieje potrzeba połączenia się z komputera lokalnego z usługą memcached.staging.svc.cluster.local. Zapewniamy tę możliwość za pomocą VPN w klastrze, z którym łączy się klient. W tym celu ogłaszamy podsieci podów, usług i przekazujemy klientowi klaster DNS. Tak więc, gdy klient próbuje połączyć się z usługą memcached.staging.svc.cluster.local, żądanie trafia do DNS klastra i w odpowiedzi otrzymuje adres tej usługi z sieci usług klastrowych lub adres poda.

Klastry K8s konfigurujemy za pomocą kubeadm, gdzie znajduje się domyślna podsieć usługi 192.168.0.0/16, a sieć kapsuł jest 10.244.0.0/16. Zwykle wszystko działa dobrze, ale jest kilka punktów:

  • Podsieć 192.168.*.* często stosowane w sieciach biurowych klientów, a jeszcze częściej w sieciach domowych deweloperów. A potem dochodzą konflikty: routery domowe pracują w tej podsieci, a VPN wypycha te podsieci z klastra do klienta.
  • Mamy kilka klastrów (produkcyjny, etapowy i/lub kilka klastrów deweloperskich). Wtedy domyślnie wszystkie będą miały te same podsieci dla podów i usług, co stwarza duże trudności przy jednoczesnej pracy z usługami w kilku klastrach.

Już dawno przyjęliśmy praktykę wykorzystywania różnych podsieci dla usług i podów w ramach tego samego projektu – ogólnie rzecz biorąc, tak aby wszystkie klastry miały różne sieci. Istnieje jednak duża liczba działających klastrów, których nie chciałbym przenosić od zera, ponieważ obsługują wiele usług, aplikacji stanowych itp.

A potem zadaliśmy sobie pytanie: jak zmienić podsieć w istniejącym klastrze?

Wyszukiwanie decyzji

Najczęstszą praktyką jest odtwarzanie wszystko usługi z typem ClusterIP. Jako opcja, mogę doradzić i to:

W następującym procesie występuje problem: po skonfigurowaniu wszystkiego pody wyświetlają stary adres IP jako serwer nazw DNS w pliku /etc/resolv.conf.
Ponieważ nadal nie znalazłem rozwiązania, musiałem zresetować cały klaster za pomocą polecenia kubeadm reset i zainicjować go ponownie.

Ale to nie jest odpowiednie dla wszystkich... Oto bardziej szczegółowe wprowadzenie do naszego przypadku:

  • Używana jest flanela;
  • Klastry istnieją zarówno w chmurach, jak i na sprzęcie;
  • Chciałbym uniknąć ponownego wdrażania wszystkich usług w klastrze;
  • Ogólnie rzecz biorąc, należy zrobić wszystko przy minimalnej liczbie problemów;
  • Wersja Kubernetesa to 1.16.6 (jednak dalsze kroki będą podobne dla pozostałych wersji);
  • Głównym zadaniem jest zapewnienie, aby w klastrze wdrożonym przy użyciu kubeadm znajdowała się podsieć serwisowa 192.168.0.0/16, zamień go na 172.24.0.0/16.

I tak się złożyło, że od dawna byliśmy ciekawi, co i jak w Kubernetesie jest przechowywane w etcd, co można z tym zrobić… Pomyśleliśmy więc: „Dlaczego po prostu nie zaktualizować danych w etcd, zastępując stare adresy IP (podsieć) nowymi? "

Szukając gotowych narzędzi do pracy z danymi w etcd, nie znaleźliśmy niczego, co całkowicie rozwiązałoby problem. (Nawiasem mówiąc, jeśli znasz jakieś narzędzia do pracy z danymi bezpośrednio w etcd, będziemy wdzięczni za linki.) Jednak dobrym punktem wyjścia jest itp.pomocnik z OpenShift (dzięki jego autorom!).

To narzędzie może łączyć się z etcd przy użyciu certyfikatów i odczytywać stamtąd dane za pomocą poleceń ls, get, dump.

Dodaj etcdhelper

Następna myśl jest logiczna: „Co powstrzymuje Cię przed dodaniem tego narzędzia poprzez dodanie możliwości zapisu danych do etcd?”

Stało się zmodyfikowaną wersją etcdhelper z dwiema nowymi funkcjami changeServiceCIDR и changePodCIDR. na jej możesz zobaczyć kod tutaj.

Co dają nowe funkcje? Algorytm changeServiceCIDR:

  • utwórz deserializator;
  • skompiluj wyrażenie regularne, aby zastąpić CIDR;
  • przechodzimy przez wszystkie usługi o typie ClusterIP w klastrze:
    • dekodować wartość z etcd do obiektu Go;
    • za pomocą wyrażenia regularnego zastępujemy pierwsze dwa bajty adresu;
    • przypisać usłudze adres IP z nowej podsieci;
    • utwórz serializator, przekonwertuj obiekt Go na protobuf, zapisz nowe dane do itp.

Funkcja changePodCIDR zasadniczo podobne changeServiceCIDR - tylko zamiast edytować specyfikację usługi, robimy to dla węzła i zmieniamy .spec.PodCIDR do nowej podsieci.

Praktyka

Zmień usługę CIDR

Plan realizacji zadania jest bardzo prosty, jednak wiąże się z przestojem na czas ponownego utworzenia wszystkich podów w klastrze. Po opisaniu głównych kroków podzielimy się również przemyśleniami na temat tego, jak teoretycznie można zminimalizować ten przestój.

Kroki przygotowawcze:

  • instalowanie niezbędnego oprogramowania i instalowanie załatanego pliku etcdhelper;
  • kopia zapasowa itp. i /etc/kubernetes.

Krótki plan działania dotyczący zmiany usługiCIDR:

  • zmiana manifestów apiservera i menedżera kontrolerów;
  • ponowne wydanie certyfikatów;
  • zmiana usług ClusterIP w itp.;
  • uruchom ponownie wszystkie zasobniki w klastrze.

Poniżej znajduje się pełna sekwencja działań w szczegółach.

1. Zainstaluj klienta etcd do zrzutu danych:

apt install etcd-client

2. Zbuduj narzędzie etcdhelper:

  • Zainstaluj Golanga:
    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
  • Oszczędzamy dla siebie etcdhelper.go, pobierz zależności, zbierz:
    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. Zrób kopię zapasową itp.:

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. Zmień podsieć usługi w manifestach płaszczyzny kontrolnej Kubernetes. W plikach /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml zmienić parametr --service-cluster-ip-range do nowej podsieci: 172.24.0.0/16 zamiast 192.168.0.0/16.

5. Ponieważ zmieniamy podsieć usługi, do której kubeadm wystawia certyfikaty dla apiserver (w tym), należy je ponownie wystawić:

  1. Zobaczmy dla jakich domen i adresów IP został wystawiony aktualny certyfikat:
    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. Przygotujmy minimalną konfigurację 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. Usuńmy stary crt i klucz, ponieważ bez tego nowy certyfikat nie zostanie wydany:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Wystawmy ponownie certyfikaty dla serwera API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Sprawdźmy, czy certyfikat został wydany dla nowej podsieci:
    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. Po ponownym wydaniu certyfikatu serwera API zrestartuj jego kontener:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Wygenerujmy ponownie konfigurację dla admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Edytujmy dane w 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 

    Ostrzeżenie! W tym momencie rozpoznawanie domeny przestaje działać w klastrze, ponieważ w istniejących podach /etc/resolv.conf zarejestrowany jest stary adres CoreDNS (kube-dns), a kube-proxy zmienia reguły iptables ze starej podsieci na nową. W dalszej części artykułu napisano o możliwych opcjach minimalizacji przestojów.

  9. Naprawmy ConfigMap w przestrzeni nazw kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - zamień tutaj clusterDNS na nowy adres IP usługi kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - naprawimy to data.ClusterConfiguration.networking.serviceSubnet do nowej podsieci.

  10. Ponieważ adres kube-dns uległ zmianie, konieczna jest aktualizacja konfiguracji kubelet na wszystkich węzłach:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Pozostaje tylko zrestartować wszystkie pody w klastrze:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimalizuj przestoje

Myśli o tym, jak zminimalizować przestoje:

  1. Po zmianie manifestów płaszczyzny sterującej utwórz nową usługę kube-dns, na przykład o nazwie kube-dns-tmp i nowy adres 172.24.0.10.
  2. Zrobić if w etcdhelper, który nie zmodyfikuje usługi kube-dns.
  3. Zamień adres we wszystkich kubeletach ClusterDNS na nową, podczas gdy stara usługa będzie nadal działać jednocześnie z nową.
  4. Poczekaj, aż kapsuły z aplikacjami przewrócą się albo same, z przyczyn naturalnych, albo w ustalonym terminie.
  5. Usuń usługę kube-dns-tmp i zmienić serviceSubnetCIDR dla usługi kube-dns.

Plan ten pozwoli Ci zminimalizować przestoje do ~minuty - na czas usunięcia usługi kube-dns-tmp i zmianę podsieci dla usługi kube-dns.

Modyfikacja podNetwork

Jednocześnie postanowiliśmy sprawdzić, jak zmodyfikować podNetwork za pomocą wynikowego pliku etcdhelper. Sekwencja działań jest następująca:

  • naprawianie konfiguracji w kube-system;
  • naprawianie manifestu kube-controller-manager;
  • zmień podCIDR bezpośrednio w etcd;
  • zrestartuj wszystkie węzły klastra.

Teraz więcej o tych akcjach:

1. Zmodyfikuj ConfigMap w przestrzeni nazw kube-system:

kubectl -n kube-system edit cm kubeadm-config

- poprawianie data.ClusterConfiguration.networking.podSubnet do nowej podsieci 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Zmodyfikuj manifest menedżera-kontrolera:

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

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

3. Spójrz na bieżące wartości .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses dla wszystkich węzłów klastra:

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. Zastąp podCIDR, wprowadzając zmiany bezpośrednio w 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. Sprawdźmy, czy podCIDR rzeczywiście się zmienił:

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. Zrestartujmy jeden po drugim wszystkie węzły klastra.

7. Jeśli opuścisz przynajmniej jeden węzeł stary podCIDR, wówczas kube-controller-manager nie będzie mógł zostać uruchomiony, a pody w klastrze nie zostaną zaplanowane.

Tak naprawdę zmianę podCIDR można przeprowadzić jeszcze prościej (np. tak). Chcieliśmy jednak nauczyć się pracować bezpośrednio z etcd, ponieważ zdarzają się przypadki edycji obiektów Kubernetesa w etcd - pojedynczy możliwy wariant. (Na przykład nie można po prostu zmienić pola Usługa bez przestojów spec.clusterIP.)

Łączny

W artykule omówiono możliwość bezpośredniej pracy z danymi w etcd, tj. z pominięciem API Kubernetes. Czasami takie podejście pozwala na wykonanie „trudnych rzeczy”. Operacje podane w tekście przetestowaliśmy na prawdziwych klastrach K8s. Jednak ich stan gotowości do powszechnego stosowania jest PoC (weryfikacja koncepcji). Dlatego też, jeśli chcesz używać zmodyfikowanej wersji narzędzia etcdhelper w swoich klastrach, robisz to na własne ryzyko.

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz