.png)
Chúng tôi đã chạy Kubernetes cho nghiên cứu học sâu trong hơn hai năm. Trong khi khối lượng công việc lớn nhất của chúng tôi quản lý trực tiếp các VM đám mây trần, Kubernetes cung cấp chu kỳ lặp lại nhanh, khả năng mở rộng hợp lý và không có mẫu chuẩn khiến nó trở nên lý tưởng cho hầu hết các thử nghiệm của chúng tôi. Hiện chúng tôi vận hành một số cụm Kubernetes (một số trên đám mây và một số trên phần cứng vật lý), cụm lớn nhất trong số đó chúng tôi đã đẩy lên hơn 2.500 nút. Cụm này chạy trong Azure trên sự kết hợp của VM D15v2 và NC24.
Trên con đường hướng đến quy mô này, nhiều thành phần hệ thống đã gây ra sự cố, bao gồm etcd, Kube master, Docker image pulls, network, KubeDNS và thậm chí cả bộ đệm ARP của máy chúng tôi. Chúng tôi cảm thấy sẽ hữu ích khi chia sẻ các sự cố cụ thể mà chúng tôi gặp phải và cách chúng tôi giải quyết chúng.
Sau khi vượt qua 500 nút trong cụm của chúng tôi, các nhà nghiên cứu của chúng tôi bắt đầu báo cáo thời gian chờ thường xuyên từ kubectl công cụ dòng lệnh. Chúng tôi đã thử thêm nhiều Kube master (VM chạy kube-apiserver(mở trong cửa sổ mới)). Điều này có vẻ giải quyết được vấn đề tạm thời, nhưng sau khi chúng tôi vượt qua 10 bản sao, chúng tôi biết rằng chúng tôi đang điều trị các triệu chứng chứ không phải nguyên nhân (để so sánh, GKE sử dụng một VM 32 lõi duy nhất cho 500 nút).
Điều này khiến chúng tôi nghi ngờ mạnh mẽ etcd của chúng tôi cụm, là kho lưu trữ trạng thái trung tâm cho các bậc thầy Kube. Tìm kiếm trong Datadog(mở trong cửa sổ mới), chúng tôi thấy độ trễ ghi tăng đột biến lên đến hàng trăm mili giây trên các máy DS15v2 chạy bản sao etcd của chúng tôi, mặc dù mỗi máy đều sử dụng SSD P30 có khả năng xử lý 5.000 IOPS.
Đánh giá hiệu suất với fio, chúng tôi thấy etcd chỉ có thể sử dụng khoảng 10% IOPS khả dụng vì độ trễ ghi là 2ms và etcd thực hiện I/O tuần tự, khiến nó bị giới hạn bởi độ trễ.
Xem thêm: mua tài khoản ChatGPT Plus chính hãng giá rẻ
Sau đó, chúng tôi di chuyển thư mục etcd cho mỗi nút đến đĩa tạm cục bộ, là ổ SSD được kết nối trực tiếp với phiên bản chứ không phải ổ SSD được kết nối mạng. Việc chuyển sang đĩa cục bộ đã đưa độ trễ ghi lên 200us và etcd trở nên khỏe mạnh!
Cụm của chúng tôi chạy tốt cho đến khi chúng tôi vượt qua khoảng 1.000 nút, tại thời điểm đó chúng tôi một lần nữa thấy độ trễ cam kết cao từ etcd. Lần này, chúng tôi nhận thấy kube-apiservers đang đọc hơn 500MB/giây từ etcd. Chúng tôi thiết lập Prometheus(mở trong cửa sổ mới) để theo dõi các apiserver và cũng thiết lập các --audit-log-path cờ --audit-log-maxbackup để cho phép ghi nhật ký nhiều hơn trên apiserver. Điều này làm nổi lên một số truy vấn chậm và các cuộc gọi quá mức đến API LIST cho Sự kiện.
Nguyên nhân gốc rễ: cài đặt mặc định cho Fluentd(mở trong cửa sổ mới)Các quy trình giám sát của Datadog và 's là truy vấn các máy chủ API từ mọi nút trong cụm (ví dụ: vấn đề này(mở trong cửa sổ mới) (hiện đã được sửa). Chúng tôi chỉ cần thay đổi các quy trình này để ít gây hấn hơn với việc thăm dò của chúng và tải trên máy chủ api trở nên ổn định trở lại:
.png)
Một tinh chỉnh hữu ích khác là lưu trữ Kubernetes Events trong một cụm etcd riêng biệt, do đó các đột biến trong quá trình tạo Event sẽ không ảnh hưởng đến hiệu suất của các phiên bản etcd chính. Để thực hiện điều này, chúng ta chỉ cần đặt cờ --etcd-servers-overrides thành thứ gì đó như thế này: --etcd-servers-overrides=/events#https://0.example.com:2381;https://1.example.com:2381;https://2.example.com:2381
Một lỗi khác sau 1.000 nút là đạt đến giới hạn lưu trữ cứng của etcd (mặc định là 2GB), khiến nó ngừng chấp nhận ghi. Điều này gây ra lỗi liên tiếp: tất cả các nút Kube của chúng tôi đều không vượt qua được kiểm tra tình trạng và trình tự động mở rộng của chúng tôi(mở trong cửa sổ mới) quyết định do đó cần phải chấm dứt tất cả các công nhân. Chúng tôi đã tăng kích thước etcd tối đa bằng --quota-backend-bytes cờ và trình tự động điều chỉnh hiện có kiểm tra tính hợp lý để không thực hiện hành động nếu nó chấm dứt hơn 50% cụm.
Bậc thầy Kube
Chúng tôi đặt đồng thời kube-apiserver, kube-controller-manager và kube-scheduler các quy trình trên cùng một máy. Để có tính khả dụng cao, chúng tôi luôn có ít nhất 2 máy chủ chính và đặt cờ --apiserver-count theo số lượng máy chủ API mà chúng tôi đang chạy (nếu không, quá trình giám sát Prometheus có thể bị nhầm lẫn giữa các phiên bản).
Chúng tôi sử dụng Kubernetes chủ yếu như một hệ thống lập lịch hàng loạt và dựa vào trình tự động mở rộng của chúng tôi để tăng và giảm quy mô cụm của chúng tôi một cách động — điều này cho phép chúng tôi giảm đáng kể chi phí cho các nút nhàn rỗi, trong khi vẫn cung cấp độ trễ thấp trong khi lặp lại nhanh chóng. Chính sách mặc định của kube-scheduler là phân bổ tải đều giữa các nút, nhưng chúng tôi muốn ngược lại để các nút không sử dụng có thể bị chấm dứt và cũng để các pod lớn(mở trong cửa sổ mới) có thể được lên lịch nhanh chóng. Vì vậy, chúng tôi đã chuyển sang chính sách sau:
1234567891011121314151{2"kind" : "Policy",3"apiVersion" : "v1",4"predicates" : [5{"name" : "GeneralPredicates"},6{"name" : "MatchInterPodAffinity"},7{"name" : "NoDiskConflict"},8{"name" : "NoVolumeZoneConflict"},9{"name" : "PodToleratesNodeTaints"}10],11"priorities" : [12{"name" : "MostRequestedPriority", "weight" : 1},13{"name" : "InterPodAffinityPriority", "weight" : 2}14]15}Chúng tôi sử dụng KubeDNS rộng rãi để khám phá dịch vụ, nhưng ngay sau khi triển khai chính sách lập lịch mới, nó bắt đầu gặp sự cố về độ tin cậy. Chúng tôi thấy rằng lỗi chỉ xảy ra trên một số pod nhất định của KubeDNS. Với chính sách lập lịch mới, một số máy đã chạy hơn 10 bản sao của KubeDNS, tạo điểm truy cập và chúng tôi đã vượt quá ~200QPS được phép từ mỗi máy ảo Azure để tra cứu tên miền bên ngoài.
Chúng tôi đã khắc phục điều này bằng cách thêm quy tắc chống lại sự tương thích đến các pod KubeDNS của chúng tôi:
12345678910111affinity:2podAntiAffinity:3requiredDuringSchedulingIgnoredDuringExecution:4- weight: 1005labelSelector:6matchExpressions:7- key: k8s-app8operator: In9values:10- kube-dns11topologyKey: kubernetes.io/hostnameKéo hình ảnh Docker
Dota của chúng tôi dự án bắt đầu trên Kubernetes và khi nó mở rộng, chúng tôi nhận thấy rằng các nút Kubernetes mới thường có các pod nằm trong trạng thái Đang chờ xử lý trong một thời gian dài. Hình ảnh trò chơi có kích thước khoảng 17 GB và thường mất 30 phút để kéo một nút cụm mới, vì vậy chúng tôi hiểu tại sao vùng chứa Dota sẽ ở trạng thái Chờ trong một thời gian — nhưng điều này cũng đúng với các vùng chứa khác. Khi đào sâu hơn, chúng tôi thấy rằng kubelet có một --serialize-image-pulls cờ mặc định là true, nghĩa là lệnh kéo hình ảnh Dota đã chặn tất cả các hình ảnh khác. Thay đổi thành false yêu cầu chuyển Docker sang overlay2 thay vì AUFS. Để tăng tốc độ kéo hơn nữa, chúng tôi cũng đã chuyển gốc Docker sang SSD gắn với phiên bản, giống như chúng tôi đã làm với các máy etcd.
Ngay cả sau khi tối ưu hóa tốc độ kéo, chúng tôi vẫn thấy các pod không khởi động được với thông báo lỗi khó hiểu: rpc error: code = 2 desc = net/http: request canceled. Nhật ký kubelet và Docker cũng chứa các thông báo cho biết việc kéo hình ảnh đã bị hủy do thiếu tiến trình. Chúng tôi đã theo dõi gốc đến các hình ảnh lớn mất quá nhiều thời gian để kéo/trích xuất hoặc những lúc chúng tôi có một danh sách dài hình ảnh cần kéo. Để giải quyết vấn đề này, chúng tôi đặt --image-pull-progress-deadline cờ của kubelet thành 30 phút và đặt tùy chọn của daemon Docker max-concurrent-downloads thành 10. (Tùy chọn thứ hai không tăng tốc độ trích xuất các hình ảnh lớn, nhưng cho phép hàng đợi hình ảnh kéo song song.)
Sự cố kéo Docker cuối cùng của chúng tôi là do Google Container Registry. Theo mặc định, kubelet kéo một hình ảnh đặc biệt từ gcr.io (do --pod-infra-container-image cờ kiểm soát) được sử dụng khi bắt đầu bất kỳ container mới nào. Nếu lần kéo đó không thành công vì bất kỳ lý do nào, chẳng hạn như vượt quá hạn ngạch của bạn(mở trong cửa sổ mới), nút đó sẽ không thể khởi chạy bất kỳ vùng chứa nào. Vì các nút của chúng tôi phải trải qua NAT để tiếp cận gcr.io thay vì có IP công khai của riêng chúng, nên rất có thể chúng tôi sẽ đạt đến giới hạn hạn ngạch cho mỗi IP này. Để khắc phục điều này, chúng tôi chỉ cần tải trước hình ảnh Docker đó trong hình ảnh máy cho các công nhân Kubernetes của mình bằng cách sử dụng docker image save -o /opt/preloaded_docker_images.tar và docker image load -i /opt/preloaded_docker_images.tar. Để cải thiện hiệu suất, chúng tôi thực hiện tương tự đối với danh sách trắng các hình ảnh nội bộ OpenAI phổ biến như hình ảnh Dota.
Mạng lưới
Khi các thí nghiệm của chúng tôi phát triển lớn hơn, chúng cũng trở thành các hệ thống phân tán ngày càng phức tạp, phụ thuộc nhiều vào mạng để vận hành. Khi chúng tôi bắt đầu chạy các thí nghiệm phân tán lần đầu, chúng tôi nhận ra ngay rằng mạng của chúng tôi không được cấu hình tốt. Trực tiếp giữa các máy, chúng tôi đạt được thông lượng 10-15Gbit/giây, nhưng các pod Kube của chúng tôi sử dụng Flannel chỉ đạt tối đa khoảng 2Gbit/giây. Điểm chuẩn công khai của Machine Zone hiển thị các con số tương tự, nghĩa là vấn đề không chỉ là do cấu hình kém mà còn là do môi trường của chúng ta. (Ngược lại, Flannel không thêm chi phí này vào máy vật lý của chúng ta.)
Để giải quyết vấn đề này, người dùng có thể thêm hai cài đặt khác nhau để vô hiệu hóa Flannel cho pod của họ: hostNetwork: true và dnsPolicy: ClusterFirstWithHostNet. (Mặc dù đã đọc các cảnh báo trong tài liệu Kubernetes trước khi thực hiện việc này.)
Bộ nhớ đệm ARP
Mặc dù đã điều chỉnh DNS, chúng tôi vẫn thấy các vấn đề không liên tục với việc giải quyết DNS. Một ngày nọ, một kỹ sư báo cáo rằng nc -v máy chủ Redis của họ mất hơn 30 giây để in ra rằng kết nối đã được thiết lập. Chúng tôi đã theo dõi vấn đề này đến ngăn xếp ARP của hạt nhân. Cuộc điều tra ban đầu về máy chủ của pod Redis cho thấy có điều gì đó nghiêm trọng không ổn với mạng: giao tiếp trên bất kỳ cổng nào bị treo trong nhiều giây và không thể giải quyết được tên DNS thông qua dnsmasq cục bộ(mở trong cửa sổ mới) daemon, với dig(mở trong cửa sổ mới) chỉ in một thông báo lỗi bí ẩn: socket.c:1915: internal_send: 127.0.0.1#53: Invalid argument. dmesg(mở trong cửa sổ mới) log cung cấp nhiều thông tin hơn: neighbor table overflow! điều đó có nghĩa là bộ đệm ARP đã hết dung lượng. ARP được sử dụng để ánh xạ địa chỉ mạng như địa chỉ IPv4 thành địa chỉ vật lý như địa chỉ MAC. May mắn thay, điều này dễ khắc phục bằng cách thiết lập một vài tùy chọn trong /etc/sysctl.conf:
1231net.ipv4.neigh.default.gc_thresh1 = 800002net.ipv4.neigh.default.gc_thresh2 = 900003net.ipv4.neigh.default.gc_thresh3 = 100000Cài đặt này thường được điều chỉnh trong các cụm HPC và đặc biệt quan trọng trong các cụm Kubernetes vì mỗi pod có địa chỉ IP riêng, chiếm dung lượng trong bộ đệm ARP.
Các cụm Kubernetes của chúng tôi đã không có sự cố trong khoảng 3 tháng nay và chúng tôi đang có kế hoạch mở rộng thành các cụm lớn hơn vào năm 2018. Chúng tôi vừa nâng cấp lên phiên bản 1.8.4 và rất vui mừng khi thấy rằng phiên bản này hiện chính thức hỗ trợ 5.000. Nếu bạn quan tâm đến việc xây dựng các cụm tính toán quy mô lớn.

Cách đổi Mật khẩu Chat GPT - Hướng dẫn đổi Pass Chat GPT 100% Thành công
Hướng dẫn Cách đăng nhập Chat GPT Nhanh nhất | Có hỗ trợ Miễn phí qua Teamview-Ultraview
Chat GPT Plus là gì? So sánh Chat GPT Plus với Chat GPT Miễn phí
Chat GPT bị giới hạn giải thích vì sao và cách khắc phục
Chat GPT là gì ? Cách đăng Ký Chat GPT Miễn Phí tại Việt Nam