Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Cara menambahkan pemilihan pemimpin berkuasa Kubernetes pada apl Go anda

Cara menambahkan pemilihan pemimpin berkuasa Kubernetes pada apl Go anda

WBOY
WBOYasal
2024-07-20 09:15:39937semak imbas

How to add Kubernetes-powered leader election to your Go apps

Diterbitkan pada asalnya oleh blog

Pustaka standard Kubernetes penuh dengan permata, tersembunyi dalam kebanyakan pelbagai subpakej yang merupakan sebahagian daripada ekosistem. Satu contoh sedemikian yang saya temui baru-baru ini k8s.io/client-go/tools/leaderelection, yang boleh digunakan untuk menambah protokol pemilihan pemimpin pada mana-mana aplikasi yang dijalankan di dalam gugusan Kubernetes. Artikel ini akan membincangkan apakah itu pemilihan pemimpin, cara ia dilaksanakan dalam pakej Kubernetes ini dan memberikan contoh cara kami boleh menggunakan perpustakaan ini dalam aplikasi kami sendiri.

Pemilihan Pemimpin

Pemilihan pemimpin ialah konsep sistem teragih yang merupakan blok binaan teras perisian yang sangat tersedia. Ia membenarkan berbilang proses serentak untuk menyelaras antara satu sama lain dan memilih satu proses "pemimpin", yang kemudiannya bertanggungjawab untuk melakukan tindakan segerak seperti menulis ke stor data.

Ini berguna dalam sistem seperti pangkalan data atau cache yang diedarkan, di mana berbilang proses sedang dijalankan untuk mencipta lebihan terhadap kegagalan perkakasan atau rangkaian, tetapi tidak boleh menulis ke storan secara serentak untuk memastikan ketekalan data. Jika proses pemimpin menjadi tidak bertindak balas pada satu ketika pada masa hadapan, proses yang selebihnya akan memulakan pemilihan pemimpin baharu, akhirnya memilih proses baharu untuk bertindak sebagai pemimpin.

Menggunakan konsep ini, kami boleh mencipta perisian yang sangat tersedia dengan satu ketua dan berbilang replika siap sedia.

Dalam Kubernetes, pakej masa jalan pengawal menggunakan pemilihan ketua untuk menjadikan pengawal sangat tersedia. Dalam penggunaan pengawal, penyelarasan sumber hanya berlaku apabila proses adalah peneraju dan replika lain sedang menunggu dalam keadaan siap sedia. Jika pod pemimpin menjadi tidak bertindak balas, replika yang selebihnya akan memilih ketua baharu untuk melakukan penyelarasan seterusnya dan meneruskan operasi seperti biasa.

Pajakan Kubernetes

Pustaka ini menggunakan Pajakan Kubernetes, atau kunci teragih, yang boleh diperoleh melalui proses. Pajakan ialah sumber Kubernetes asli yang dipegang oleh satu identiti, untuk tempoh tertentu, dengan pilihan pembaharuan. Berikut ialah contoh spesifikasi daripada dokumen:

apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

Pajakan digunakan oleh ekosistem k8s dalam tiga cara:

  1. Denyutan Jantung Nod: Setiap Nod mempunyai sumber Pajakan yang sepadan dan mengemas kini medan Masa pembaharuannya secara berterusan. Jika Masa pembaharuan Pajakan tidak dikemas kini dalam beberapa ketika, Nod akan tercemar kerana tidak tersedia dan tiada lagi Pod akan dijadualkan kepadanya.
  2. Pemilihan Pemimpin: Dalam kes ini, Pajakan digunakan untuk menyelaraskan antara pelbagai proses dengan meminta pemimpin mengemas kini Identiti pemegang Pajakan. Replika siap sedia, dengan identiti berbeza, tersekat menunggu Pajakan tamat tempoh. Jika Pajakan tamat tempoh, dan tidak diperbaharui oleh pemimpin, pemilihan baharu akan berlaku di mana replika yang tinggal cuba untuk mengambil pemilikan Pajakan dengan mengemas kini Identiti pemegangnya dengan mereka sendiri. Memandangkan pelayan API Kubernetes tidak membenarkan kemas kini kepada objek lapuk, hanya satu nod siap sedia akan berjaya mengemas kini Pajakan, pada ketika itu ia akan meneruskan pelaksanaan sebagai peneraju baharu.
  3. Identiti Pelayan API: Bermula pada v1.26, sebagai ciri beta, setiap replika kube-apiserver akan menerbitkan identitinya dengan membuat Pajakan khusus. Memandangkan ini adalah ciri baharu yang agak tipis, tidak banyak lagi yang boleh diperoleh daripada objek Pajakan selain daripada bilangan pelayan API yang sedang dijalankan. Tetapi ini memberi ruang untuk menambah lebih banyak metadata pada Pajakan ini dalam versi k8s akan datang.

Sekarang mari kita terokai kes penggunaan kedua Pajakan ini dengan menulis contoh program untuk menunjukkan cara anda boleh menggunakannya dalam senario pemilihan pemimpin.

Contoh Program

Dalam contoh kod ini, kami menggunakan pakej pemilihan pemimpin untuk mengendalikan pemilihan pemimpin dan manipulasi spesifik Pajakan.

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "k8s.io/client-go/tools/leaderelection"
    rl "k8s.io/client-go/tools/leaderelection/resourcelock"
    ctrl "sigs.k8s.io/controller-runtime"
)

var (
    // lockName and lockNamespace need to be shared across all running instances
    lockName      = "my-lock"
    lockNamespace = "default"

    // identity is unique to the individual process. This will not work for anything,
    // outside of a toy example, since processes running in different containers or
    // computers can share the same pid.
    identity      = fmt.Sprintf("%d", os.Getpid())
)

func main() {
    // Get the active kubernetes context
    cfg, err := ctrl.GetConfig()
    if err != nil {
        panic(err.Error())
    }

    // Create a new lock. This will be used to create a Lease resource in the cluster.
    l, err := rl.NewFromKubeconfig(
        rl.LeasesResourceLock,
        lockNamespace,
        lockName,
        rl.ResourceLockConfig{
            Identity: identity,
        },
        cfg,
        time.Second*10,
    )
    if err != nil {
        panic(err)
    }

    // Create a new leader election configuration with a 15 second lease duration.
    // Visit https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#LeaderElectionConfig
    // for more information on the LeaderElectionConfig struct fields
    el, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
        Lock:          l,
        LeaseDuration: time.Second * 15,
        RenewDeadline: time.Second * 10,
        RetryPeriod:   time.Second * 2,
        Name:          lockName,
        Callbacks: leaderelection.LeaderCallbacks{
            OnStartedLeading: func(ctx context.Context) { println("I am the leader!") },
            OnStoppedLeading: func() { println("I am not the leader anymore!") },
            OnNewLeader:      func(identity string) { fmt.Printf("the leader is %s\n", identity) },
        },
    })
    if err != nil {
        panic(err)
    }

    // Begin the leader election process. This will block.
    el.Run(context.Background())

}

Apa yang menarik tentang pakej pemilihan pemimpin ialah ia menyediakan rangka kerja berasaskan panggilan balik untuk mengendalikan pemilihan pemimpin. Dengan cara ini, anda boleh bertindak atas perubahan keadaan tertentu secara berbutir dan mengeluarkan sumber dengan betul apabila pemimpin baharu dipilih. Dengan menjalankan panggilan balik ini dalam gorouti yang berasingan, pakej tersebut memanfaatkan sokongan serentak Go yang kukuh untuk menggunakan sumber mesin dengan cekap.

Mengujinya

Untuk menguji ini, mari putarkan kelompok ujian menggunakan jenis.

$ kind create cluster

Salin kod sampel ke main.go, buat modul baharu (go mod init leaderelectiontest) dan kemaskannya (go mod tidy) untuk memasang kebergantungannya. Sebaik sahaja anda menjalankan go run main.go, anda sepatutnya melihat output seperti ini:

$ go run main.go
I0716 11:43:50.337947     138 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
I0716 11:43:50.351264     138 leaderelection.go:260] successfully acquired lease default/my-lock
the leader is 138
I am the leader!

The exact leader identity will be different from what's in the example (138), since this is just the PID of the process that was running on my computer at the time of writing.

And here's the Lease that was created in the test cluster:

$ kubectl describe lease/my-lock
Name:         my-lock
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  coordination.k8s.io/v1
Kind:         Lease
Metadata:
  Creation Timestamp:  2024-07-16T15:43:50Z
  Resource Version:    613
  UID:                 1d978362-69c5-43e9-af13-7b319dd452a6
Spec:
  Acquire Time:            2024-07-16T15:43:50.338049Z
  Holder Identity:         138
  Lease Duration Seconds:  15
  Lease Transitions:       0
  Renew Time:              2024-07-16T15:45:31.122956Z
Events:                    <none>

See that the "Holder Identity" is the same as the process's PID, 138.

Now, let's open up another terminal and run the same main.go file in a separate process:

$ go run main.go
I0716 11:48:34.489953     604 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
the leader is 138

This second process will wait forever, until the first one is not responsive. Let's kill the first process and wait around 15 seconds. Now that the first process is not renewing its claim on the Lease, the .spec.renewTime field won't be updated anymore. This will eventually cause the second process to trigger a new leader election, since the Lease's renew time is older than its duration. Because this process is the only one now running, it will elect itself as the new leader.

the leader is 604
I0716 11:48:51.904732     604 leaderelection.go:260] successfully acquired lease default/my-lock
I am the leader!

If there were multiple processes still running after the initial leader exited, the first process to acquire the Lease would be the new leader, and the rest would continue to be on standby.

No single-leader guarantees

This package is not foolproof, in that it "does not guarantee that only one client is acting as a leader (a.k.a. fencing)". For example, if a leader is paused and lets its Lease expire, another standby replica will acquire the Lease. Then, once the original leader resumes execution, it will think that it's still the leader and continue doing work alongside the newly-elected leader. In this way, you can end up with two leaders running simultaneously.

To fix this, a fencing token which references the Lease needs to be included in each request to the server. A fencing token is effectively an integer that increases by 1 every time a Lease changes hands. So a client with an old fencing token will have its requests rejected by the server. In this scenario, if an old leader wakes up from sleep and a new leader has already incremented the fencing token, all of the old leader's requests would be rejected because it is sending an older (smaller) token than what the server has seen from the newer leader.

Implementing fencing in Kubernetes would be difficult without modifying the core API server to account for corresponding fencing tokens for each Lease. However, the risk of having multiple leader controllers is somewhat mitigated by the k8s API server itself. Because updates to stale objects are rejected, only controllers with the most up-to-date version of an object can modify it. So while we could have multiple controller leaders running, a resource's state would never regress to older versions if a controller misses a change made by another leader. Instead, reconciliation time would increase as both leaders need to refresh their own internal states of resources to ensure that they are acting on the most recent versions.

Still, if you're using this package to implement leader election using a different data store, this is an important caveat to be aware of.

Conclusion

Leader election and distributed locking are critical building blocks of distributed systems. When trying to build fault-tolerant and highly-available applications, having tools like these at your disposal is critical. The Kubernetes standard library gives us a battle-tested wrapper around its primitives to allow application developers to easily build leader election into their own applications.

While use of this particular library does limit you to deploying your application on Kubernetes, that seems to be the way the world is going recently. If in fact that is a dealbreaker, you can of course fork the library and modify it to work against any ACID-compliant and highly-available datastore.

Stay tuned for more k8s source deep dives!

Atas ialah kandungan terperinci Cara menambahkan pemilihan pemimpin berkuasa Kubernetes pada apl Go anda. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn