Pengalaman kami dengan data di klaster etcd Kubernetes secara langsung (tanpa K8s API)

Semakin banyak klien yang meminta kami untuk memberikan akses ke cluster Kubernetes agar dapat mengakses layanan di dalam cluster: sehingga mereka dapat langsung terhubung ke beberapa database atau layanan, untuk menghubungkan aplikasi lokal dengan aplikasi di dalam cluster...

Pengalaman kami dengan data di klaster etcd Kubernetes secara langsung (tanpa K8s API)

Misalnya, ada kebutuhan untuk terhubung dari mesin lokal Anda ke suatu layanan memcached.staging.svc.cluster.local. Kami menyediakan kemampuan ini menggunakan VPN di dalam cluster tempat klien terhubung. Untuk melakukan ini, kami mengumumkan subnet pod, layanan, dan mendorong DNS cluster ke klien. Jadi, ketika klien mencoba menyambung ke layanan memcached.staging.svc.cluster.local, permintaan masuk ke DNS cluster dan sebagai tanggapan menerima alamat layanan ini dari jaringan layanan cluster atau alamat pod.

Kami mengonfigurasi klaster K8 menggunakan kubeadm, yang merupakan subnet layanan default 192.168.0.0/16, dan jaringan podnya adalah 10.244.0.0/16. Biasanya semuanya berfungsi dengan baik, tetapi ada beberapa poin:

  • Subnet 192.168.*.* sering digunakan di jaringan kantor klien, dan bahkan lebih sering di jaringan rumah pengembang. Dan kemudian kita mendapatkan konflik: router rumah bekerja pada subnet ini dan VPN mendorong subnet ini dari cluster ke klien.
  • Kami memiliki beberapa cluster (produksi, panggung dan/atau beberapa cluster pengembangan). Kemudian, secara default, semuanya akan memiliki subnet yang sama untuk pod dan layanan, sehingga menimbulkan kesulitan besar untuk bekerja secara bersamaan dengan layanan di beberapa cluster.

Kami telah lama mengadopsi praktik penggunaan subnet berbeda untuk layanan dan pod dalam proyek yang sama - secara umum, sehingga semua cluster memiliki jaringan yang berbeda. Namun, ada sejumlah besar cluster yang sedang beroperasi yang saya tidak ingin mengulanginya dari awal, karena cluster tersebut menjalankan banyak layanan, aplikasi stateful, dll.

Lalu kami bertanya pada diri sendiri: bagaimana cara mengubah subnet di cluster yang sudah ada?

Pencarian keputusan

Praktik yang paling umum adalah menciptakan kembali semua layanan dengan tipe ClusterIP. Sebagai pilihan, dapat memberi saran dan ini:

Proses berikut ini mengalami masalah: setelah semuanya dikonfigurasi, pod akan menampilkan IP lama sebagai server nama DNS di /etc/resolv.conf.
Karena saya masih tidak menemukan solusinya, saya harus mereset seluruh cluster dengan kubeadm reset dan memulainya lagi.

Tapi ini tidak cocok untuk semua orang... Berikut adalah perkenalan yang lebih rinci untuk kasus kami:

  • Flanel digunakan;
  • Ada cluster di cloud dan di perangkat keras;
  • Saya ingin menghindari penerapan ulang semua layanan di cluster;
  • Ada kebutuhan untuk melakukan segala sesuatu secara umum dengan jumlah masalah yang minimal;
  • Versi Kubernetes adalah 1.16.6 (namun, langkah selanjutnya akan serupa untuk versi lainnya);
  • Tugas utamanya adalah memastikan bahwa dalam sebuah cluster disebarkan menggunakan kubeadm dengan subnet layanan 192.168.0.0/16, ganti dengan 172.24.0.0/16.

Dan kebetulan kami sudah lama tertarik untuk melihat apa dan bagaimana Kubernetes disimpan di dll, apa yang bisa dilakukan dengannya... Jadi kami berpikir: β€œMengapa tidak memperbarui data di dll saja, mengganti alamat IP (subnet) lama dengan yang baru? "

Setelah mencari alat siap pakai untuk bekerja dengan data di dll, kami tidak menemukan apa pun yang sepenuhnya menyelesaikan masalah. (Omong-omong, jika Anda mengetahui utilitas apa pun untuk bekerja dengan data secara langsung di dll, kami akan sangat menghargai tautannya.) Namun, ini adalah titik awal yang baik dlldhelper dari OpenShift (terima kasih kepada penulisnya!).

Utilitas ini dapat terhubung ke etcd menggunakan sertifikat dan membaca data dari sana menggunakan perintah ls, get, dump.

Tambahkan dlldhelper

Pemikiran berikutnya adalah logis: β€œApa yang menghentikan Anda menambahkan utilitas ini dengan menambahkan kemampuan untuk menulis data ke dll?”

Ini menjadi versi modifikasi dari etcdhelper dengan dua fungsi baru changeServiceCIDR ΠΈ changePodCIDR. Pada dia Anda dapat melihat kodenya di sini.

Apa fungsi fitur-fitur baru ini? Algoritma changeServiceCIDR:

  • membuat deserialisasi;
  • kompilasi ekspresi reguler untuk menggantikan CIDR;
  • kita melalui semua layanan dengan tipe ClusterIP di cluster:
    • memecahkan kode nilai dari etcd menjadi objek Go;
    • menggunakan ekspresi reguler kami mengganti dua byte pertama alamat;
    • menetapkan layanan alamat IP dari subnet baru;
    • kita membuat serializer, mengubah objek Go menjadi protobuf, menulis data baru ke dll.

Fungsi changePodCIDR pada dasarnya mirip changeServiceCIDR - hanya saja alih-alih mengedit spesifikasi layanan, kami melakukannya untuk node dan mengubahnya .spec.PodCIDR ke subnet baru.

Praktek

Ubah CIDR layanan

Rencana untuk mengimplementasikan tugas ini sangat sederhana, namun melibatkan waktu henti pada saat pembuatan ulang semua pod di cluster. Setelah menguraikan langkah-langkah utama, kami juga akan berbagi pemikiran tentang bagaimana, secara teori, downtime ini dapat diminimalkan.

Langkah-langkah persiapan:

  • menginstal perangkat lunak yang diperlukan dan merakit etcdhelper yang ditambal;
  • cadangan dlld dan /etc/kubernetes.

Rencana tindakan singkat untuk mengubah layananCIDR:

  • mengubah manifes apiserver dan controller-manager;
  • penerbitan kembali sertifikat;
  • mengubah layanan ClusterIP di dll;
  • restart semua pod di cluster.

Berikut ini adalah rangkaian tindakan lengkap secara detail.

1. Instal klien-etcd untuk dump data:

apt install etcd-client

2. Bangun dlldhelper:

  • Instal 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
  • Kami menabung untuk diri kami sendiri etcdhelper.go, unduh dependensi, 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 cadangan 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. Ubah subnet layanan di manifes bidang kendali Kubernetes. Dalam file /etc/kubernetes/manifests/kube-apiserver.yaml ΠΈ /etc/kubernetes/manifests/kube-controller-manager.yaml mengubah parameternya --service-cluster-ip-range ke subnet baru: 172.24.0.0/16 daripada 192.168.0.0/16.

5. Karena kami mengubah subnet layanan tempat kubeadm menerbitkan sertifikat untuk apiserver (termasuk), sertifikat tersebut perlu diterbitkan ulang:

  1. Mari kita lihat untuk domain dan alamat IP mana sertifikat saat ini diterbitkan:
    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 siapkan konfigurasi minimal 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 kita hapus crt dan kunci lama, karena tanpa ini sertifikat baru tidak akan diterbitkan:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Mari menerbitkan ulang sertifikat untuk server API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Mari kita periksa apakah sertifikat diterbitkan untuk subnet baru:
    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. Setelah menerbitkan ulang sertifikat server API, mulai ulang penampungnya:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Mari kita buat ulang konfigurasinya admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Mari kita edit data di dll:
    ./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 

    Peringatan! Saat ini, resolusi domain berhenti bekerja di cluster, sejak di pod yang ada /etc/resolv.conf alamat CoreDNS lama (kube-dns) didaftarkan, dan kube-proxy mengubah aturan iptables dari subnet lama ke subnet baru. Lebih lanjut dalam artikel tersebut ditulis tentang opsi yang memungkinkan untuk meminimalkan waktu henti.

  9. Mari kita perbaiki ConfigMap di namespace kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - ganti di sini clusterDNS ke alamat IP baru dari layanan kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - kami akan memperbaikinya data.ClusterConfiguration.networking.serviceSubnet ke subnet baru.

  10. Karena alamat kube-dns telah berubah, konfigurasi kubelet di semua node perlu diperbarui:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Yang tersisa hanyalah memulai ulang semua pod di cluster:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Minimalkan waktu henti

Pemikiran tentang cara meminimalkan waktu henti:

  1. Setelah mengubah manifes bidang kontrol, buatlah layanan kube-dns baru, misalnya dengan nama kube-dns-tmp dan alamat baru 172.24.0.10.
  2. Buat if di etcdhelper, yang tidak akan mengubah layanan kube-dns.
  3. Ganti alamat di semua kubelet ClusterDNS ke yang baru, sedangkan layanan lama akan terus bekerja bersamaan dengan yang baru.
  4. Tunggu hingga pod dengan aplikasi bergulir dengan sendirinya karena alasan alami atau pada waktu yang disepakati.
  5. Hapus layanan kube-dns-tmp dan berubah serviceSubnetCIDR untuk layanan kube-dns.

Paket ini akan memungkinkan Anda meminimalkan waktu henti hingga ~ satu menit - selama durasi penghapusan layanan kube-dns-tmp dan mengubah subnet untuk layanan tersebut kube-dns.

Modifikasi podNetwork

Pada saat yang sama, kami memutuskan untuk melihat cara memodifikasi podNetwork menggunakan etcdhelper yang dihasilkan. Urutan tindakannya adalah sebagai berikut:

  • memperbaiki konfigurasi di kube-system;
  • memperbaiki manifes kube-controller-manager;
  • ubah podCIDR langsung di etcd;
  • reboot semua node cluster.

Sekarang lebih lanjut tentang tindakan ini:

1. Ubah ConfigMap di namespace kube-system:

kubectl -n kube-system edit cm kubeadm-config

- mengoreksi data.ClusterConfiguration.networking.podSubnet ke subnet baru 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

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

2. Ubah manifes pengontrol-manajer:

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

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

3. Lihatlah nilai-nilai saat ini .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses untuk semua node cluster:

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. Ganti podCIDR dengan melakukan perubahan langsung pada 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 periksa apakah podCIDR 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 kita reboot semua node cluster satu per satu.

7. Jika Anda meninggalkan setidaknya satu node podCIDR lama, maka kube-controller-manager tidak akan dapat dijalankan, dan pod-pod di cluster tidak akan dijadwalkan.

Faktanya, mengubah podCIDR dapat dilakukan dengan lebih sederhana (misalnya, jadi). Namun kami ingin mempelajari cara bekerja dengan etcd secara langsung, karena ada beberapa kasus ketika mengedit objek Kubernetes di etcd - satu-satunya varian yang mungkin. (Misalnya, Anda tidak bisa begitu saja mengubah bidang Layanan tanpa waktu henti spec.clusterIP.)

Total

Artikel ini membahas kemungkinan bekerja dengan data di dll secara langsung, mis. melewati API Kubernetes. Terkadang pendekatan ini memungkinkan Anda melakukan β€œhal-hal rumit”. Kami menguji operasi yang diberikan dalam teks pada cluster K8 yang sebenarnya. Namun, status kesiapannya untuk digunakan secara luas adalah PoC (bukti konsep). Oleh karena itu, jika Anda ingin menggunakan versi modifikasi dari utilitas etcdhelper pada klaster Anda, risikonya Anda tanggung sendiri.

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar