Pengalaman kami bekerja dengan data dalam kluster Kubernetes etcd secara langsung (tanpa K8s API)

Semakin hari, pelanggan meminta kami untuk menyediakan akses kepada kluster Kubernetes untuk dapat mengakses perkhidmatan dalam kluster: supaya mereka boleh menyambung terus ke beberapa pangkalan data atau perkhidmatan, untuk menyambungkan aplikasi tempatan dengan aplikasi dalam kluster...

Pengalaman kami bekerja dengan data dalam kluster Kubernetes etcd secara langsung (tanpa K8s API)

Sebagai contoh, terdapat keperluan untuk menyambung dari mesin tempatan anda ke perkhidmatan memcached.staging.svc.cluster.local. Kami menyediakan keupayaan ini menggunakan VPN dalam kelompok yang disambungkan oleh pelanggan. Untuk melakukan ini, kami mengumumkan subnet pod, perkhidmatan dan DNS kluster tolak kepada pelanggan. Oleh itu, apabila pelanggan cuba menyambung ke perkhidmatan memcached.staging.svc.cluster.local, permintaan pergi ke DNS kluster dan sebagai tindak balas menerima alamat perkhidmatan ini daripada rangkaian perkhidmatan kluster atau alamat pod.

Kami mengkonfigurasi kluster K8s menggunakan kubeadm, di mana subnet perkhidmatan lalai adalah 192.168.0.0/16, dan rangkaian pod ialah 10.244.0.0/16. Biasanya semuanya berfungsi dengan baik, tetapi terdapat beberapa perkara:

  • Subnet 192.168.*.* sering digunakan dalam rangkaian pejabat pelanggan, dan lebih kerap lagi dalam rangkaian rumah pembangun. Dan kemudian kita mendapat konflik: penghala rumah berfungsi pada subnet ini dan VPN menolak subnet ini daripada kluster kepada klien.
  • Kami mempunyai beberapa kluster (pengeluaran, peringkat dan/atau beberapa kluster dev). Kemudian, secara lalai, kesemuanya akan mempunyai subnet yang sama untuk pod dan perkhidmatan, yang menimbulkan kesukaran besar untuk kerja serentak dengan perkhidmatan dalam beberapa kelompok.

Kami telah lama menggunakan amalan menggunakan subnet yang berbeza untuk perkhidmatan dan pod dalam projek yang sama - secara umum, supaya semua kluster mempunyai rangkaian yang berbeza. Walau bagaimanapun, terdapat sebilangan besar kluster dalam operasi yang saya tidak mahu beralih dari awal, kerana ia menjalankan banyak perkhidmatan, aplikasi stateful, dsb.

Dan kemudian kami bertanya kepada diri sendiri: bagaimana untuk menukar subnet dalam kelompok sedia ada?

Mencari keputusan

Amalan yang paling biasa ialah mencipta semula semua perkhidmatan dengan jenis ClusterIP. Sebagai pilihan, boleh menasihati dan ini:

Proses berikut mempunyai masalah: selepas semuanya dikonfigurasikan, pod muncul dengan IP lama sebagai pelayan nama DNS dalam /etc/resolv.conf.
Oleh kerana saya masih tidak menemui penyelesaiannya, saya terpaksa menetapkan semula keseluruhan kluster dengan tetapan semula kubeadm dan menyalakannya semula.

Tetapi ini tidak sesuai untuk semua orang... Berikut adalah pengenalan yang lebih terperinci untuk kes kami:

  • Flanel digunakan;
  • Terdapat kelompok kedua-dua di awan dan pada perkakasan;
  • Saya ingin mengelak daripada menggunakan semula semua perkhidmatan dalam kelompok;
  • Terdapat keperluan untuk melakukan segala-galanya secara amnya dengan bilangan masalah yang minimum;
  • Versi Kubernetes ialah 1.16.6 (namun, langkah selanjutnya akan serupa untuk versi lain);
  • Tugas utama adalah untuk memastikan bahawa dalam kluster digunakan menggunakan kubeadm dengan subnet perkhidmatan 192.168.0.0/16, gantikan dengan 172.24.0.0/16.

Dan kebetulan kami telah lama berminat untuk melihat apa dan bagaimana dalam Kubernetes disimpan dalam etcd, apa yang boleh dilakukan dengannya... Jadi kami fikir: β€œMengapa tidak hanya mengemas kini data dalam etcd, menggantikan alamat IP lama (subnet) dengan yang baru? "

Setelah mencari alat sedia untuk bekerja dengan data dalam etcd, kami tidak menemui apa-apa yang menyelesaikan masalah sepenuhnya. (Dengan cara ini, jika anda mengetahui tentang sebarang utiliti untuk bekerja dengan data secara langsung dalam etcd, kami akan menghargai pautan tersebut.) Walau bagaimanapun, titik permulaan yang baik ialah etcdhelper daripada OpenShift (terima kasih kepada pengarangnya!).

Utiliti ini boleh menyambung ke etcd menggunakan sijil dan membaca data dari sana menggunakan arahan ls, get, dump.

Tambah etcdhelper

Pemikiran seterusnya adalah logik: "Apakah yang menghalang anda daripada menambah utiliti ini dengan menambahkan keupayaan untuk menulis data ke etcd?"

Ia menjadi versi diubah suai bagi etcdhelper dengan dua fungsi baharu changeServiceCIDR ΠΈ changePodCIDR. pada dia anda boleh melihat kod tersebut di sini.

Apakah yang dilakukan oleh ciri baharu? Algoritma changeServiceCIDR:

  • buat deserializer;
  • menyusun ungkapan biasa untuk menggantikan CIDR;
  • kami melalui semua perkhidmatan dengan jenis ClusterIP dalam kluster:
    • menyahkod nilai daripada etcd ke dalam objek Go;
    • menggunakan ungkapan biasa kami menggantikan dua bait pertama alamat;
    • berikan perkhidmatan alamat IP daripada subnet baharu;
    • buat serializer, tukar objek Go ke protobuf, tulis data baru ke etcd.

Fungsi changePodCIDR pada dasarnya serupa changeServiceCIDR - hanya daripada mengedit spesifikasi perkhidmatan, kami melakukannya untuk nod dan perubahan .spec.PodCIDR ke subnet baharu.

Amalan

Tukar perkhidmatan CIDR

Pelan untuk melaksanakan tugas adalah sangat mudah, tetapi ia melibatkan masa henti pada masa penciptaan semula semua pod dalam kelompok. Selepas menerangkan langkah-langkah utama, kami juga akan berkongsi pemikiran tentang bagaimana, secara teori, masa henti ini boleh diminimumkan.

Langkah persediaan:

  • memasang perisian yang diperlukan dan memasang etcdhelper yang ditampal;
  • sandaran etcd dan /etc/kubernetes.

Pelan tindakan ringkas untuk menukar perkhidmatanCIDR:

  • menukar apiserver dan pengawal-pengurus manifes;
  • pengeluaran semula sijil;
  • menukar perkhidmatan KlusterIP dalam etcd;
  • mulakan semula semua pod dalam kelompok.

Berikut adalah urutan tindakan yang lengkap secara terperinci.

1. Pasang etcd-client untuk pembuangan data:

apt install etcd-client

2. Bina etcdhelper:

  • Pasang 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
  • Kita simpan untuk diri sendiri etcdhelper.go, muat turun kebergantungan, kumpulkan:
    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. Buat sandaran dll:

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. Tukar subnet perkhidmatan dalam satah kawalan Kubernetes nyata. Dalam fail /etc/kubernetes/manifests/kube-apiserver.yaml ΠΈ /etc/kubernetes/manifests/kube-controller-manager.yaml menukar parameter --service-cluster-ip-range ke subnet baharu: 172.24.0.0/16 bukannya 192.168.0.0/16.

5. Memandangkan kami menukar subnet perkhidmatan yang kubeadm mengeluarkan sijil untuk apiserver (termasuk), ia perlu dikeluarkan semula:

  1. Mari lihat domain dan alamat IP yang mana sijil semasa telah dikeluarkan untuk:
    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. Mari sediakan konfigurasi minimum untuk 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. Mari padamkan crt dan kunci lama, kerana tanpa ini sijil baharu tidak akan dikeluarkan:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Mari keluarkan semula sijil untuk pelayan API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Mari semak sama ada sijil telah dikeluarkan untuk subnet baharu:
    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. Selepas mengeluarkan semula sijil pelayan API, mulakan semula bekasnya:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Mari kita jana semula konfigurasi untuk admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Mari edit data dalam 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 

    Amaran! Pada masa ini, resolusi domain berhenti berfungsi dalam kelompok, kerana dalam pod sedia ada /etc/resolv.conf alamat CoreDNS lama (kube-dns) didaftarkan, dan kube-proxy menukar peraturan iptables daripada subnet lama kepada yang baharu. Selanjutnya dalam artikel itu ditulis tentang pilihan yang mungkin untuk meminimumkan masa henti.

  9. Mari kita betulkan ConfigMap dalam ruang nama kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - ganti di sini clusterDNS ke alamat IP baharu perkhidmatan kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - kami akan membetulkannya data.ClusterConfiguration.networking.serviceSubnet ke subnet baharu.

  10. Memandangkan alamat kube-dns telah berubah, adalah perlu untuk mengemas kini konfigurasi kubelet pada semua nod:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Apa yang tinggal ialah untuk memulakan semula semua pod dalam kelompok:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimumkan masa henti

Pemikiran tentang cara meminimumkan masa henti:

  1. Selepas menukar satah kawalan manifes, buat perkhidmatan kube-dns baharu, sebagai contoh, dengan nama kube-dns-tmp dan alamat baru 172.24.0.10.
  2. Buat if dalam etcdhelper, yang tidak akan mengubah suai perkhidmatan kube-dns.
  3. Gantikan alamat dalam semua kubelet ClusterDNS kepada yang baharu, manakala perkhidmatan lama akan terus berfungsi serentak dengan yang baharu.
  4. Tunggu sehingga pod dengan aplikasi berguling sama ada dengan sendirinya atas sebab semula jadi atau pada masa yang dipersetujui.
  5. Padam perkhidmatan kube-dns-tmp dan berubah serviceSubnetCIDR untuk perkhidmatan kube-dns.

Pelan ini akan membolehkan anda meminimumkan masa henti sehingga ~ seminit - untuk tempoh penyingkiran perkhidmatan kube-dns-tmp dan menukar subnet untuk perkhidmatan tersebut kube-dns.

Pengubahsuaian podNetwork

Pada masa yang sama, kami memutuskan untuk melihat cara mengubah suai podNetwork menggunakan etcdhelper yang terhasil. Urutan tindakan adalah seperti berikut:

  • membetulkan konfigurasi dalam kube-system;
  • membetulkan manifes pengurus-pengawal-kube;
  • tukar podCIDR terus dalam etcd;
  • but semula semua nod kluster.

Sekarang lebih lanjut mengenai tindakan ini:

1. Ubah suai ConfigMap dalam ruang nama kube-system:

kubectl -n kube-system edit cm kubeadm-config

- membetulkan data.ClusterConfiguration.networking.podSubnet ke subnet baharu 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Ubah suai manifes pengurus-pengawal:

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

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

3. Lihat nilai semasa .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses untuk semua nod kluster:

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. Gantikan podCIDR dengan membuat perubahan terus kepada 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. Mari kita semak bahawa podCIDR telah benar-benar berubah:

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. Mari but semula semua nod kelompok satu demi satu.

7. Jika anda meninggalkan sekurang-kurangnya satu nod podCIDR lama, maka kube-controller-manager tidak akan dapat dimulakan, dan pod dalam kelompok tidak akan dijadualkan.

Malah, menukar podCIDR boleh dilakukan dengan lebih mudah (contohnya, jadi). Tetapi kami ingin belajar cara bekerja dengan etcd secara langsung, kerana terdapat kes apabila mengedit objek Kubernetes dalam etcd - tunggal varian yang mungkin. (Sebagai contoh, anda tidak boleh menukar medan Perkhidmatan tanpa masa henti spec.clusterIP.)

Jumlah

Artikel membincangkan kemungkinan bekerja dengan data dalam etcd secara langsung, i.e. memintas API Kubernetes. Kadang-kadang pendekatan ini membolehkan anda melakukan "perkara rumit." Kami menguji operasi yang diberikan dalam teks pada kluster K8 sebenar. Walau bagaimanapun, status kesediaan mereka untuk kegunaan meluas adalah PoC (bukti konsep). Oleh itu, jika anda ingin menggunakan versi diubah suai utiliti etcdhelper pada kluster anda, lakukan dengan risiko anda sendiri.

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komen