Pengalaman kami bekerja dengan data di etcd Kubernetes-cluster secara langsung (tanpa API K8s)

Semakin sering, pelanggan meminta kami untuk memberikan akses ke kluster Kubernet untuk kemungkinan mengakses layanan di dalam kluster: agar dapat terhubung langsung ke beberapa basis data atau layanan, untuk menghubungkan aplikasi lokal dengan aplikasi di dalam kluster ...



Misalnya, ada kebutuhan untuk menyambungkan dari mesin lokal Anda ke layanan memcached.staging.svc.cluster.local. Kami memberikan kesempatan seperti itu menggunakan VPN di dalam kluster tempat klien terhubung. Untuk melakukan ini, kami mengumumkan subnet pods, layanan, dan push DNS cluster ke klien. Dengan demikian, ketika klien mencoba untuk terhubung ke layanan memcached.staging.svc.cluster.local, permintaan pergi ke DNS cluster dan sebagai tanggapannya menerima alamat layanan ini dari jaringan layanan cluster atau alamat pod.

Kami mengkonfigurasi cluster K8 menggunakan kubeadm, di mana subnet layanan secara default 192.168.0.0/16dan jaringan pod 10.244.0.0/16. Biasanya semuanya bekerja 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 dev). Kemudian di semua dari mereka secara default akan ada subnet yang sama untuk pod dan layanan, yang menciptakan kesulitan besar untuk bekerja dengan layanan di beberapa cluster secara bersamaan.

Untuk beberapa waktu sekarang kami telah mengadopsi praktik menggunakan subnet yang berbeda untuk layanan dan polong dalam proyek yang sama - secara umum, sehingga semua cluster dengan jaringan yang berbeda. Namun, ada sejumlah besar cluster yang beroperasi yang saya tidak ingin gulung dari awal, karena mereka menjalankan banyak layanan, aplikasi stateful, dll.

Dan kemudian kami bertanya pada diri sendiri: bagaimana saya akan mengubah subnet di cluster yang ada?

Mencari keputusan


Praktik paling umum adalah membuat kembali semua layanan dengan tipe ClusterIP. Atau, mereka juga dapat menyarankan ini:

Proses berikut memiliki masalah: setelah semuanya dikonfigurasi, pod muncul dengan IP lama sebagai server nama DNS di /etc/resolv.conf.
Karena saya masih belum menemukan solusinya, saya harus mengatur ulang seluruh cluster dengan reset kubeadm dan init lagi.

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

  • Digunakan oleh Flannel;
  • Ada kelompok di awan dan di atas besi;
  • Saya ingin menghindari penyebaran berulang semua layanan di cluster;
  • Ada kebutuhan untuk melakukan segalanya dengan masalah minimal;
  • Versi Kubernetes adalah 1.16.6 (namun, tindakan lebih lanjut akan serupa untuk versi lain);
  • Tugas utama adalah untuk 192.168.0.0/16menggantinya dengan cluster yang digunakan menggunakan kubeadm dengan subnet layanan 172.24.0.0/16.

Dan kebetulan bahwa untuk waktu yang lama itu menarik bagi kami untuk melihat apa dan bagaimana di Kubernet disimpan di etcd, apa yang dapat dilakukan dengan itu sama sekali ... Jadi kami berpikir: โ€œ Mengapa tidak memperbarui data di etcd dengan mengganti alamat IP lama (subnet) ke yang baru ? "

Mencari alat yang sudah jadi untuk bekerja dengan data di etcd, kami tidak menemukan apa pun yang sepenuhnya menyelesaikan tugas. (Omong-omong, jika Anda tahu tentang utilitas untuk bekerja dengan data secara langsung di etcd, kami akan berterima kasih atas tautannya .) Namun, OpenShift etcdhelper menjadi titik awal yang baik (terima kasih kepada penulisnya!) .

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

Tambahkan etcdhelper


Pikiran berikut ini logis: "Apa yang mencegah untuk menambah utilitas ini, menambahkan kemampuan untuk menulis data ke etcd?"

Ini telah diterjemahkan ke dalam versi modifikasi etcdhelper dengan dua fitur baru changeServiceCIDRdan changePodCIDR. Anda dapat melihat kodenya di sini .

Apa yang dilakukan fitur baru? Algoritma changeServiceCIDR:

  • buat deserializer;
  • kompilasi ekspresi reguler untuk menggantikan CIDR;
  • kami melewati semua layanan dengan tipe ClusterIP di cluster:

    • mendekode nilai dari etcd ke objek Go;
    • menggunakan ekspresi reguler, ganti dua byte pertama dari alamat;
    • menetapkan layanan alamat IP dari subnet baru;
    • membuat serializer, mengonversi objek Go ke protobuf, menulis data baru ke etcd.

Fungsinya changePodCIDRpada dasarnya sama changeServiceCIDR- hanya alih-alih mengedit spesifikasi layanan, kami melakukannya untuk node dan mengubahnya .spec.PodCIDRke subnet baru.

Praktek


Ubah serviceCIDR


Rencana untuk implementasi tugas sangat sederhana, tetapi melibatkan downtime pada saat penciptaan kembali semua pod di cluster. Setelah menjelaskan langkah-langkah utama, kami juga akan membagikan pemikiran kami tentang bagaimana, secara teori, yang sederhana ini dapat diminimalisir.

Tindakan persiapan:

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

Rencana tindakan singkat untuk mengubah serviceCIDR:

  • Memodifikasi manifestasi apiserver dan pengontrol-manajer
  • penerbitan kembali sertifikat;
  • Layanan ClusterIP berubah di etcd;
  • mulai ulang semua pod dalam sebuah cluster.

Berikut ini adalah urutan lengkap tindakan secara terperinci.

1. Instal etcd-client untuk dump data:

apt install etcd-client

2. Kami mengumpulkan bantuan dll:

  • Kami menempatkan 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 menyelamatkan diri etcdhelper.go, memuat dependensi, mengumpulkan:

    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 dlld:

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 kontrol Kubernetes. Dalam file /etc/kubernetes/manifests/kube-apiserver.yamldan /etc/kubernetes/manifests/kube-controller-manager.yamlubah parameter --service-cluster-ip-rangeke subnet baru: 172.24.0.0/16sebagai gantinya 192.168.0.0/16.

5. Karena kami mengubah subnet layanan yang kubeadm mengeluarkan sertifikat untuk apiserver (termasuk), mereka harus diterbitkan kembali:

  1. Mari kita lihat domain dan alamat IP mana sertifikat saat ini dikeluarkan:

    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. Siapkan 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 kita hapus crt dan kunci lama, karena tanpa ini sertifikat baru tidak akan dikeluarkan:

    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Pasang kembali sertifikat untuk server API:

    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. , :

    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. API- :

    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. admin.conf:

    kubeadm alpha certs renew admin.conf
  8. 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 

    ! , pod' /etc/resolv.conf CoreDNS (kube-dns), kube-proxy iptables . .
  9. ConfigMap' kube-system:

    kubectl -n kube-system edit cm kubelet-config-1.16

    โ€” clusterDNS IP- kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    โ€” data.ClusterConfiguration.networking.serviceSubnet .
  10. kube-dns, kubelet :

    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Tetap memulai ulang semua pod di kluster:

    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(\S+)\s+(\S+).*/kubectl --namespace \1 delete pod \2/e'

Meminimalkan Downtime


Pikiran tentang cara meminimalkan downtime:

  1. Setelah mengubah manifestasi bidang kontrol, buat layanan kube-dns baru, misalnya, dengan nama kube-dns-tmpdan alamat baru 172.24.0.10.
  2. Buat ifdi etcdhelper, yang tidak akan mengubah layanan kube-dns.
  3. Ganti alamat di semua kubelet ClusterDNSdengan yang baru, sementara layanan lama akan terus bekerja secara bersamaan dengan yang baru.
  4. Tunggu polong dengan aplikasi bergulir sendiri karena alasan alami, atau pada waktu yang disepakati.
  5. Hapus layanan kube-dns-tmpdan ubah serviceSubnetCIDRuntuk layanan kube-dns.

Paket ini akan meminimalkan waktu henti hingga ~ satu menit - untuk saat layanan dihapus kube-dns-tmpdan subnet untuk layanan diganti kube-dns.

Modifikasi podNetwork


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

  • kami memperbaiki konfigurasi di kube-system;
  • kami memperbaiki manifes dari kube-controller-manager;
  • kami mengubah 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

- Memperbaiki data.ClusterConfiguration.networking.podSubnetsubnet baru 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

- kami benar data.config.conf.clusterCIDR: 10.55.0.0/16.

2. Ubah manifes controller-manager:

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

- kami benar --cluster-cidr=10.55.0.0/16.

3. Lihatlah nilai-nilai saat ini .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addressesuntuk semua node di 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 membuat perubahan langsung ke 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. 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. Pada gilirannya, kami akan reboot semua node cluster.

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

Bahkan, mengubah podCIDR dapat dibuat lebih mudah (misalnya, seperti ini ). Tapi kami ingin belajar bagaimana bekerja dengan etcd secara langsung, karena ada kasus ketika mengedit objek Kubernetes di etcd adalah satu - satunya pilihan yang mungkin. (Misalnya, Anda tidak bisa hanya mengubah bidang Layanan tanpa downtime spec.clusterIP.)

Total


Artikel ini mempertimbangkan kemungkinan bekerja dengan data di etcd secara langsung, mis. melewati API Kubernetes. Terkadang pendekatan ini memungkinkan Anda untuk melakukan "hal-hal yang rumit." Operasi yang dijelaskan dalam teks diuji pada kelompok K8 nyata. Namun, status kesiapan mereka untuk digunakan secara luas adalah PoC (proof of concept) . Karena itu, jika Anda ingin menggunakan versi modifikasi dari utilitas etcdhelper pada cluster Anda, lakukan dengan risiko dan risiko Anda sendiri.

PS


Baca juga di blog kami:


All Articles