CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.
Bài blog CFSDN này phân tích sơ lược về cơ chế trục xuất Kubelet được tác giả sưu tầm và biên soạn. Nếu các bạn quan tâm tới bài viết này thì nhớ like nhé.
。
Để bảo vệ các nút, Kubelet cho phép chức năng loại bỏ các Pod trên các nút khi tài nguyên nút không đủ. Gần đây mình có nghiên cứu về cơ chế trục xuất của Kubelet và thấy có rất nhiều điều đáng học hỏi nên mình sẽ tổng hợp lại và chia sẻ với các bạn.
Cấu hình Kubelet
Chức năng trục xuất của Kubelet cần được bật trong cấu hình và ngưỡng trục xuất đã được định cấu hình. Các tham số liên quan đến việc trục xuất trong cấu hình Kubelet như sau:
- loại KubeletConfiguration struct {
- ...
- // Bản đồ của tên tín hiệu ĐẾN số lượng xác định ngưỡng trục xuất cứng. Vì ví dụ: {"bộ nhớ.có sẵn": "300 dặm"}.
- Bản đồ EvictionHard[string]string
- // Bản đồ của tên tín hiệu ĐẾN số lượng xác định ngưỡng trục xuất mềm. Vì ví dụ: {"bộ nhớ.có sẵn": "300 dặm"}.
- Bản đồ EvictionSoft[string]string
- // Bản đồ của tên tín hiệu ĐẾN số lượng xác định thời gian ân hạn vì mỗi tín hiệu trục xuất nhẹ nhàng. Vì ví dụ: {"bộ nhớ.có sẵn": "30 tuổi"}.
- Bản đồ EvictionSoftGracePeriod[string]string
- // Khoảng thời gian vì mà kubelet có ĐẾN chờ trước khi chuyển đổi ngoài của tình trạng áp lực trục xuất.
- EvictionPressureTransitionPeriod metav1.Duration
- // Thời gian gia hạn tối đa được phép (TRONG giây) ĐẾN sử dụng khi vỏ kết thúc TRONG phản ứng ĐẾN ngưỡng trục xuất mềm đang được đáp ứng.
- EvictionMaxPodGracePeriod int32
- // Bản đồ của tên tín hiệu ĐẾN số lượng xác định mức thu hồi tối thiểu, mô tả mức tối thiểu
- // số lượng của một tài nguyên nhất định mà kubelet sẽ lấy lại khi thực hiện việc trục xuất pod trong khi
- // tài nguyên đó là chịu áp lực. Vì ví dụ: {"imagefs.có sẵn": "2Gi"}
- Bản đồ EvictionMinimumReclaim[string]string
- ...
- }
Trong số đó, EvictionHard có nghĩa là trục xuất cứng. Sau khi đạt đến ngưỡng, nó sẽ bị trục xuất trực tiếp; EvictionSoft có nghĩa là trục xuất mềm, nghĩa là bạn chỉ có thể đặt khoảng thời gian trục xuất mềm. Khoảng thời gian được đặt bằng EvictionSoftGracePeriod; EvictionMinimumReclaim có nghĩa là đặt ngưỡng khả dụng tối thiểu , chẳng hạn như hình ảnh.
Các tín hiệu trục xuất có thể được thiết lập là:
- Memory.available: node.status.capacity[memory] - node.stats.memory.workingSet, bộ nhớ khả dụng của nút
- nodefs.available: node.stats.fs.available, dung lượng khả dụng của hệ thống file được Kubelet sử dụng
- nodefs.inodesFree: node.stats.fs.inodesFree, số lượng inode có thể sử dụng trong hệ thống tệp được Kubelet sử dụng
- imagefs.available: node.stats.runtime.imagefs.available, dung lượng khả dụng của hệ thống tệp được sử dụng để lưu trữ hình ảnh và các lớp có thể ghi của vùng chứa khi vùng chứa đang chạy
- imagefs.inodesFree: node.stats.runtime.imagefs.inodesFree, dung lượng inode có thể sử dụng của hệ thống tệp được sử dụng để lưu trữ hình ảnh và các lớp có thể ghi của vùng chứa khi vùng chứa đang chạy
- allocatableMemory.available: bộ nhớ còn trống để phân bổ Pod
- pid.available: node.stats.rlimit.maxpid - node.stats.rlimit.curproc, PID còn lại để phân bổ Pod
Cách thức hoạt động của Trình quản lý Trục xuất
Công việc chính của Eviction Manager là ở chức năng đồng bộ hóa. Có hai nơi để kích hoạt tác vụ đồng bộ hóa. Một là tác vụ giám sát, được kích hoạt 10 giây một lần; vị trí còn lại là tác vụ thông báo được bắt đầu dựa trên tín hiệu trục xuất được người dùng định cấu hình để giám sát các sự kiện kernel.

thông báo
Trình thông báo được khởi động bởi Trình thông báo ngưỡng trong trình quản lý trục xuất. Mỗi tín hiệu trục xuất do người dùng định cấu hình tương ứng với một Trình thông báo ngưỡng và Trình thông báo ngưỡng và trình thông báo sẽ giao tiếp qua kênh Khi trình thông báo gửi tin nhắn đến kênh, Trình thông báo ngưỡng tương ứng sẽ kích hoạt một kênh. đồng bộ hóa logic.
Trình thông báo sử dụng các nhóm của kernel Các ngưỡng bộ nhớ cho phép các tiến trình ở chế độ người dùng thiết lập thông qua sự kiện. Khi bộ nhớ.usage_in_bytes đạt đến một ngưỡng nhất định, kernel sẽ gửi thông báo đến ứng dụng. Phương pháp cụ thể là ghi " " vào cgroup.event_control.
Mã khởi tạo của trình thông báo như sau (một số mã không liên quan đã bị xóa để thuận tiện cho việc đọc). Nó chủ yếu tìm bộ mô tả tệp watchfd của bộ nhớ.usage_in_bytes và bộ mô tả tệp controlfd của cgroup.event_control để hoàn tất việc đăng ký ngưỡng bộ nhớ cgroup. .
- func NewCgroupNotifier(đường dẫn, chuỗi thuộc tính, ngưỡng int64) (CgroupNotifier, lỗi) {
- var watchfd, eventfd, epfd, controlfd số nguyên
-
- watchfd, err = unix.Mở(fmt. Sprintf("%s/%s", đường dẫn, thuộc tính), unix.O_RDONLY|unix.O_CLOEXEC, 0)
- hoãn Unix.Đóng(xemfd)
-
- controlfd, err = unix.Mở(fmt. Sprintf("%s/cgroup.event_control", đường dẫn), unix.O_WRONLY|unix.O_CLOEXEC, 0)
- hoãn Unix.Đóng(kiểm soát)
-
- eventfd, lỗi = unix.Eventfd(0, unix.EFD_CLOEXEC)
- hoãn func() {
- // Đóng eventfd nếu chúng ta gặp lỗi sau đó TRONG khởi tạo
- nếu err != nil {
- hệ điều hành unix.Đóng(sự kiện)
- }
- }()
-
- epfd, lỗi = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
- hoãn func() {
- // Đóng epfd nếu chúng ta gặp lỗi sau này TRONG khởi tạo
- nếu err != nil {
- hệ điều hành unix.Đóng(epfd)
- }
- }()
-
- cấu hình := fmt.Sprintf("%d %d %d", eventfd, watchfd, ngưỡng)
- _, err = unix.Write(controlfd, []byte(config))
-
- trở lại &linuxCgroupThông báo
- eventfd: eventfd,
- epfd: epfd,
- dừng: make(chan struct{}),
- }, không
- }
Trình thông báo cũng sẽ lắng nghe sự kiện trên thông qua epoll khi nó bắt đầu. Khi nó lắng nghe sự kiện được gửi bởi kernel, điều đó có nghĩa là bộ nhớ được sử dụng đã vượt quá ngưỡng và tín hiệu được gửi đến kênh.
- func (n *linuxCgroupNotifier) Bắt đầu(eventCh chan<- struct{}) {
- lỗi := unix.EpollCtl(n.epfd, unix.EPOLL_CTL_ADD, n.eventfd, &unix.EpollEvent{
- Fd: int32(n.eventfd),
- Sự kiện: unix.EPOLLIN,
- })
-
- vì {
- lựa chọn {
- trường hợp <-n.dừng lại:
- trở lại
- mặc định:
- }
- sự kiện, lỗi := chờ (n.epfd, n.eventfd, notifierRefreshInterval)
- nếu err != nil {
- khôn ngoan.InfoS("Trình quản lý xóa: lỗi khi chờ sự kiện memcg", "lầm", lỗi)
- trở lại
- } khác nếu !event {
- // Hết thời gian TRÊN chờ đã. Cái này là dự kiến nếu ngưỡng là không đã vượt qua
- Tiếp tục
- }
- // Tiêu thụ sự kiện từ sự kiệnfd
- buf := make([]byte, eventSize)
- _, lỗi = unix.Đọc(n.eventfd, buf)
- nếu err != nil {
- khôn ngoan.InfoS("Trình quản lý xóa: lỗi khi đọc sự kiện memcg", "lầm", lỗi)
- trở lại
- }
- eventCh <- cấu trúc{}{}
- }
- }
Mỗi lần logic đồng bộ hóa được thực thi, nó sẽ xác định xem trình thông báo đã được cập nhật trong vòng 10 giây hay chưa và khởi động lại trình thông báo. Ngưỡng bộ nhớ của nhóm được tính bằng tổng bộ nhớ trừ đi ngưỡng trục xuất do người dùng đặt.
đồng bộ hóa
Đồng bộ hóa logic chính của Trình quản lý trục xuất có nhiều chi tiết, vì vậy mã nguồn sẽ không được đăng ở đây. Những nội dung chính cần sắp xếp như sau:
- Xây dựng hàm xếp hạng cho từng tín hiệu;
- Cập nhật ngưỡng và khởi động lại trình thông báo;
- Nhận mức sử dụng tài nguyên của nút hiện tại (thông tin nhóm) và tất cả các nhóm đang hoạt động;
- Đối với mỗi tín hiệu, hãy xác định xem mức sử dụng tài nguyên của nút hiện tại có đạt đến ngưỡng trục xuất hay không. Nếu không, hãy thoát khỏi chu kỳ hiện tại;
- Ưu tiên tất cả các tín hiệu Ưu tiên là: các tín hiệu liên quan đến bộ nhớ được loại bỏ trước;
- Gửi một sự kiện trục xuất tới máy chủ apiserver;
- Ưu tiên tất cả các nhóm đang hoạt động;
- Loại bỏ các nhóm theo thứ tự được sắp xếp.
Tính toán lệnh trục xuất
Thứ tự các nhóm bị loại bỏ chủ yếu phụ thuộc vào ba yếu tố:
- Liệu mức sử dụng tài nguyên của nhóm có vượt quá yêu cầu của nhóm hay không;
- Giá trị ưu tiên của nhóm;
- mức sử dụng bộ nhớ của nhóm;
Thứ tự phán đoán của 3 yếu tố cũng dựa trên thứ tự đã đăng ký trong orderBy. Việc sắp xếp đa cấp của hàm orderBy ở đây cũng là một cách triển khai đáng học hỏi (sao chép bài tập về nhà) trong Kubernetes. Bạn đọc quan tâm có thể tự mình kiểm tra mã nguồn.
- // rankMemoryPressure sắp xếp các pod đầu vào vì trục xuất TRONG phản ứng ĐẾN áp lực bộ nhớ.
- // Nó xếp hạng qua liệu hoặc không việc sử dụng pod vượt quá nhu cầu của nó, sau đó qua sự ưu tiên, Và
- // Cuối cùng qua sử dụng bộ nhớ trên yêu cầu.
- hàm rankMemoryPressure(pods []*v1.Pod, số liệu thống kê statsFunc) {
- orderedBy(exceedMemoryRequests(thống kê), ưu tiên, bộ nhớ(thống kê)).Sort(pods)
- }
Đuổi Pod
Tiếp theo là việc thực hiện đuổi Pods. Việc trục xuất Pod của Trình quản lý trục xuất là một hành động tiêu diệt rõ ràng và gọn gàng. Việc triển khai cụ thể sẽ không được phân tích ở đây. Điều đáng chú ý là sẽ có phán quyết trước khi trục xuất. Nếu IsCriticalPod trả về đúng, nó sẽ không bị trục xuất.
- func (m *managerImpl) evictPod(pod *v1.Pod, gracePeriodOverride int64, evictMsg string, chú thích map[string]string) bool {
- // Nếu pod là được đánh dấu BẰNG phê bình Và tĩnh, Và ủng hộ vì chú thích pod quan trọng là được kích hoạt,
- // LÀM không loại bỏ những quả như vậy. Tĩnh vỏ quả là không được nhận lại sau đó trục xuất.
- // https://github.com/kubernetes/kubernetes/issues/40573 có thêm thông tin chi tiết.
- nếu kubelettypes.IsCriticalPod(pod) {
- khôn ngoan.ErrorS(nil, "Trình quản lý trục xuất: không thể trục xuất một nhóm quan trọng", "vỏ", khôn ngoan.KObj(pod))
- trở lại SAI
- }
- // ghi lại rằng chúng ta đang loại bỏ pod
- m.recorder.AnnotatedEventf(pod, chú thích, v1.EventTypeWarning, Lý do, evictMsg)
- // cái này là một cuộc gọi chặn Và nên chỉ một trở lại khi cái vỏ Và các thùng chứa của nó đã bị phá hủy.
- khôn ngoan.V(3).InfoS("Đuổi pod", "vỏ", thông minh.KObj(pod), "podUID"nhóm.UID "tin nhắn", loại bỏMsg)
- lỗi := m.killPodFunc(pod, ĐÚNG VẬY, &gracePeriodOverride, chức năng(trạng thái *v1.PodStatus) {
- trạng thái.Giai đoạn = v1.PodFailed
- trạng thái.Lý do = Lý do
- trạng thái.Tin nhắn = evictMsg
- })
- nếu err != nil {
- khôn ngoan.ErrorS(err, "Người quản lý trục xuất: nhóm không thể trục xuất", "vỏ", khôn ngoan.KObj(pod))
- } khác {
- khôn ngoan.InfoS("Người quản lý trục xuất: nhóm đã được trục xuất thành công", "vỏ", khôn ngoan.KObj(pod))
- }
- trở lại ĐÚNG VẬY
- }
再看看 IsCriticalPod 的代码:
- func IsCriticalPod(pod *v1.Pod) bool {
- if IsStaticPod(pod) {
- trở lại ĐÚNG VẬY
- }
- if IsMirrorPod(pod) {
- trở lại ĐÚNG VẬY
- }
- if pod.Spec.Priority != nil && IsCriticalPodBasedOnPriority(*pod.Spec.Priority) {
- trở lại ĐÚNG VẬY
- }
- trở lại SAI
- }
-
- // IsMirrorPod trả lại ĐÚNG VẬY if the passed Pod là a Mirror Pod.
- func IsMirrorPod(pod *v1.Pod) bool {
- _, ok := pod.Annotations[ConfigMirrorAnnotationKey]
- trở lại ok
- }
-
- // IsStaticPod trả lại ĐÚNG VẬY if the pod là Một tĩnh pod.
- func IsStaticPod(pod *v1.Pod) bool {
- source, err := GetPodSource(pod)
- trở lại err == nil && source != ApiserverSource
- }
-
- func IsCriticalPodBasedOnPriority(priority int32) bool {
- trở lại priority >= scheduling.SystemCriticalPriority
- }
从代码看,如果 Pod 是 Static、Mirror、Critical Pod 都不驱逐。其中 Static 和 Mirror 都是从 Pod 的 annotation 中判断;而 Critical 则是通过 Pod 的 Priority 值判断的,如果 Priority 为 system-cluster-critical/system-node-critical 都属于 Critical Pod.
不过这里值得注意的是,官方文档里提及 Critical Pod 是说,如果非 Static Pod 被标记为 Critical,并不完全保证不会被驱逐:https://kubernetes.io/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods 。因此,很有可能是社区并没有想清楚这种情况是否要驱逐,并不排除后面会改变这段逻辑,不过也有可能是文档没有及时更新??.
Tóm tắt
本文主要分析了 Kubelet 的 Eviction Manager,包括其对 Linux CGroup 事件的监听、判断 Pod 驱逐的优先级等。了解了这些之后,我们就可以根据自身应用的重要性来设置优先级,甚至设置成 Critical Pod.
原文链接:https://mp.weixin.qq.com/s/xEE-GoYg0b6aeMHHar7qWw 。
最后此篇关于浅析 Kubelet 驱逐机制的文章就讲到这里了,如果你想了解更多关于浅析 Kubelet 驱逐机制的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Tôi là một lập trình viên xuất sắc, rất giỏi!