Chuyển đổi Tinder sang Kubernetes

Ghi chú. bản dịch.: Nhân viên của dịch vụ Tinder nổi tiếng thế giới gần đây đã chia sẻ một số chi tiết kỹ thuật về việc di chuyển cơ sở hạ tầng của họ sang Kubernetes. Quá trình này mất gần hai năm và dẫn đến việc ra mắt một nền tảng quy mô rất lớn trên K8, bao gồm 200 dịch vụ được lưu trữ trên 48 nghìn container. Các kỹ sư của Tinder đã gặp phải những khó khăn thú vị gì và đạt được kết quả gì?

Chuyển đổi Tinder sang Kubernetes

Tại sao?

Gần hai năm trước, Tinder quyết định chuyển nền tảng của mình sang Kubernetes. Kubernetes sẽ cho phép nhóm Tinder sắp xếp và chuyển sang sản xuất với nỗ lực tối thiểu thông qua việc triển khai bất biến (triển khai bất biến). Trong trường hợp này, việc tập hợp các ứng dụng, triển khai chúng và bản thân cơ sở hạ tầng sẽ được xác định duy nhất bằng mã.

Chúng tôi cũng đang tìm kiếm giải pháp cho vấn đề về khả năng mở rộng và tính ổn định. Khi việc mở rộng quy mô trở nên quan trọng, chúng tôi thường phải đợi vài phút để các phiên bản EC2 mới hoạt động. Ý tưởng khởi chạy các container và bắt đầu phục vụ lưu lượng truy cập trong vài giây thay vì vài phút đã trở nên rất hấp dẫn đối với chúng tôi.

Quá trình này hóa ra là khó khăn. Trong quá trình di chuyển vào đầu năm 2019, cụm Kubernetes đã đạt đến mức tới hạn và chúng tôi bắt đầu gặp phải nhiều vấn đề khác nhau do lưu lượng truy cập, kích thước cụm và DNS. Trong quá trình thực hiện, chúng tôi đã giải quyết được rất nhiều vấn đề thú vị liên quan đến việc di chuyển 200 dịch vụ và duy trì cụm Kubernetes bao gồm 1000 nút, 15000 nhóm và 48000 vùng chứa đang chạy.

Làm thế nào?

Kể từ tháng 2018 năm XNUMX, chúng tôi đã trải qua nhiều giai đoạn di chuyển khác nhau. Chúng tôi bắt đầu bằng cách đóng gói tất cả các dịch vụ của mình và triển khai chúng trên môi trường đám mây thử nghiệm Kubernetes. Bắt đầu từ tháng XNUMX, chúng tôi bắt đầu di chuyển một cách có phương pháp tất cả các dịch vụ hiện có sang Kubernetes. Đến tháng XNUMX năm sau, chúng tôi đã hoàn tất quá trình di chuyển và hiện nền tảng Tinder chạy độc quyền trên Kubernetes.

Xây dựng hình ảnh cho Kubernetes

Chúng tôi có hơn 30 kho lưu trữ mã nguồn cho các vi dịch vụ chạy trên cụm Kubernetes. Mã trong các kho lưu trữ này được viết bằng các ngôn ngữ khác nhau (ví dụ: Node.js, Java, Scala, Go) với nhiều môi trường thời gian chạy cho cùng một ngôn ngữ.

Hệ thống xây dựng được thiết kế để cung cấp “bối cảnh xây dựng” có thể tùy chỉnh hoàn toàn cho từng vi dịch vụ. Nó thường bao gồm một Dockerfile và một danh sách các lệnh shell. Nội dung của chúng hoàn toàn có thể tùy chỉnh, đồng thời, tất cả các bối cảnh xây dựng này đều được viết theo một định dạng chuẩn. Việc tiêu chuẩn hóa bối cảnh xây dựng cho phép một hệ thống xây dựng duy nhất xử lý tất cả các dịch vụ vi mô.

Chuyển đổi Tinder sang Kubernetes
Hình 1-1. Quy trình xây dựng được tiêu chuẩn hóa thông qua vùng chứa Builder

Để đạt được sự nhất quán tối đa giữa các thời gian chạy (môi trường thời gian chạy) quy trình xây dựng tương tự được sử dụng trong quá trình phát triển và thử nghiệm. Chúng tôi phải đối mặt với một thách thức rất thú vị: chúng tôi phải phát triển một cách để đảm bảo tính nhất quán của môi trường xây dựng trên toàn bộ nền tảng. Để đạt được điều này, tất cả quy trình lắp ráp được thực hiện bên trong một thùng chứa đặc biệt. Xây dựng.

Việc triển khai vùng chứa của anh ấy yêu cầu các kỹ thuật Docker nâng cao. Builder kế thừa ID người dùng cục bộ và các bí mật (chẳng hạn như khóa SSH, thông tin đăng nhập AWS, v.v.) cần thiết để truy cập vào kho lưu trữ Tinder riêng tư. Nó gắn kết các thư mục cục bộ chứa các nguồn để lưu trữ các tạo phẩm xây dựng một cách tự nhiên. Cách tiếp cận này cải thiện hiệu suất vì nó loại bỏ nhu cầu sao chép các tạo phẩm xây dựng giữa bộ chứa Builder và máy chủ. Các tạo phẩm xây dựng được lưu trữ có thể được sử dụng lại mà không cần cấu hình bổ sung.

Đối với một số dịch vụ, chúng tôi phải tạo một vùng chứa khác để ánh xạ môi trường biên dịch sang môi trường thời gian chạy (ví dụ: thư viện bcrypt Node.js tạo các tạo phẩm nhị phân dành riêng cho nền tảng trong quá trình cài đặt). Trong quá trình biên dịch, các yêu cầu có thể khác nhau giữa các dịch vụ và Dockerfile cuối cùng được biên dịch nhanh chóng.

Kiến trúc và di chuyển cụm Kubernetes

Quản lý kích thước cụm

Chúng tôi quyết định sử dụng kube-aws để triển khai cụm tự động trên các phiên bản Amazon EC2. Lúc đầu, mọi thứ đều hoạt động trong một nhóm nút chung. Chúng tôi nhanh chóng nhận ra sự cần thiết phải phân tách khối lượng công việc theo quy mô và loại phiên bản để sử dụng tài nguyên hiệu quả hơn. Logic là việc chạy một số nhóm đa luồng được tải hóa ra lại dễ dự đoán hơn về mặt hiệu suất so với việc chúng cùng tồn tại với một số lượng lớn các nhóm đơn luồng.

Cuối cùng chúng tôi quyết định:

  • m5.4xlarge - để theo dõi (Prometheus);
  • c5.4xlarge - đối với khối lượng công việc Node.js (khối lượng công việc đơn luồng);
  • c5.2xlarge - dành cho Java và Go (khối lượng công việc đa luồng);
  • c5.4xlarge — cho bảng điều khiển (3 nút).

Di cư

Một trong những bước chuẩn bị để di chuyển từ cơ sở hạ tầng cũ sang Kubernetes là chuyển hướng giao tiếp trực tiếp hiện có giữa các dịch vụ sang bộ cân bằng tải mới (Elastic Load Balancers (ELB). Chúng được tạo trên một mạng con cụ thể của đám mây riêng ảo (VPC). Mạng con này đã được kết nối với Kubernetes VPC. Điều này cho phép chúng tôi di chuyển dần dần các mô-đun mà không cần xem xét thứ tự cụ thể của các phần phụ thuộc dịch vụ.

Các điểm cuối này được tạo bằng cách sử dụng tập hợp các bản ghi DNS có trọng số có CNAME trỏ đến từng ELB mới. Để chuyển đổi, chúng tôi đã thêm một mục nhập mới trỏ đến ELB mới của dịch vụ Kubernetes với trọng số là 0. Sau đó, chúng tôi đặt Thời gian tồn tại (TTL) của mục nhập được đặt thành 0. Sau đó, trọng số cũ và mới là được điều chỉnh từ từ và cuối cùng 100% tải được gửi đến máy chủ mới. Sau khi quá trình chuyển đổi hoàn tất, giá trị TTL trở về mức phù hợp hơn.

Các mô-đun Java mà chúng tôi có có thể xử lý được DNS TTL thấp, nhưng các ứng dụng Node thì không. Một trong những kỹ sư đã viết lại một phần mã nhóm kết nối và gói nó trong một trình quản lý cập nhật các nhóm sau mỗi 60 giây. Cách tiếp cận đã chọn hoạt động rất tốt và không có bất kỳ sự suy giảm hiệu suất đáng chú ý nào.

Những bài học

Giới hạn của kết cấu mạng

Sáng sớm ngày 8/2019/XNUMX, nền tảng Tinder bất ngờ gặp sự cố. Để đối phó với sự gia tăng không liên quan về độ trễ của nền tảng vào sáng sớm hôm đó, số lượng nhóm và nút trong cụm đã tăng lên. Điều này khiến bộ đệm ARP trên tất cả các nút của chúng tôi bị cạn kiệt.

Có ba tùy chọn Linux liên quan đến bộ đệm ARP:

Chuyển đổi Tinder sang Kubernetes
(nguồn)

gc_thresh3 - đây là một giới hạn cứng. Sự xuất hiện của các mục "tràn bảng lân cận" trong nhật ký có nghĩa là ngay cả sau khi thu thập rác đồng bộ (GC), vẫn không có đủ dung lượng trong bộ đệm ARP để lưu trữ mục lân cận. Trong trường hợp này, kernel chỉ đơn giản loại bỏ gói tin hoàn toàn.

Chúng tôi sử dụng Flannel như một kết cấu mạng trong Kubernetes. Các gói được truyền qua VXLAN. VXLAN là đường hầm L2 được xây dựng trên mạng L3. Công nghệ này sử dụng đóng gói MAC-in-UDP (Giao thức gói dữ liệu địa chỉ người dùng MAC) và cho phép mở rộng các phân đoạn mạng Lớp 2. Giao thức truyền tải trên mạng trung tâm dữ liệu vật lý là IP cộng với UDP.

Chuyển đổi Tinder sang Kubernetes
Hình 2–1. Sơ đồ flannel (nguồn)

Chuyển đổi Tinder sang Kubernetes
Hình 2–2. Gói VXLAN (nguồn)

Mỗi nút công nhân Kubernetes phân bổ một không gian địa chỉ ảo với mặt nạ /24 từ khối /9 lớn hơn. Đối với mỗi nút, đây là có nghĩa là một mục trong bảng định tuyến, một mục trong bảng ARP (trên giao diện flannel.1) và một mục trong bảng chuyển mạch (FDB). Chúng được thêm vào lần đầu tiên một nút công nhân được khởi động hoặc mỗi lần một nút mới được phát hiện.

Ngoài ra, giao tiếp nút-pod (hoặc pod-pod) cuối cùng sẽ đi qua giao diện eth0 (như thể hiện trong sơ đồ Flannel ở trên). Điều này dẫn đến một mục bổ sung trong bảng ARP cho mỗi máy chủ nguồn và đích tương ứng.

Trong môi trường của chúng ta, kiểu giao tiếp này rất phổ biến. Đối với các đối tượng dịch vụ trong Kubernetes, ELB được tạo và Kubernetes đăng ký từng nút với ELB. ELB không biết gì về nhóm và nút được chọn có thể không phải là đích cuối cùng của gói. Vấn đề là khi một nút nhận được một gói từ ELB, nó sẽ xem xét gói đó có tính đến các quy tắc iptables cho một dịch vụ cụ thể và chọn ngẫu nhiên một nhóm trên một nút khác.

Vào thời điểm xảy ra lỗi, có 605 nút trong cụm. Vì những lý do nêu trên, điều này đã đủ để khắc phục tầm quan trọng gc_thresh3, đó là mặc định. Khi điều này xảy ra, không chỉ các gói bắt đầu bị loại bỏ mà toàn bộ không gian địa chỉ ảo Flannel có mặt nạ /24 cũng biến mất khỏi bảng ARP. Giao tiếp giữa các nút và các truy vấn DNS bị gián đoạn (DNS được lưu trữ trong một cụm; đọc sau trong bài viết này để biết chi tiết).

Để giải quyết vấn đề này, bạn cần tăng giá trị gc_thresh1, gc_thresh2 и gc_thresh3 và khởi động lại Flannel để đăng ký lại các mạng bị thiếu.

Mở rộng DNS bất ngờ

Trong quá trình di chuyển, chúng tôi đã tích cực sử dụng DNS để quản lý lưu lượng và chuyển dần các dịch vụ từ cơ sở hạ tầng cũ sang Kubernetes. Chúng tôi đặt giá trị TTL tương đối thấp cho các RecordSets được liên kết trong Route53. Khi cơ sở hạ tầng cũ đang chạy trên các phiên bản EC2, cấu hình trình phân giải của chúng tôi trỏ tới Amazon DNS. Chúng tôi coi điều này là đương nhiên và tác động của TTL thấp đối với các dịch vụ của chúng tôi cũng như dịch vụ Amazon (chẳng hạn như DynamoDB) hầu như không được chú ý.

Khi di chuyển dịch vụ sang Kubernetes, chúng tôi nhận thấy rằng DNS đang xử lý 250 nghìn yêu cầu mỗi giây. Kết quả là các ứng dụng bắt đầu gặp phải tình trạng hết thời gian chờ liên tục và nghiêm trọng đối với các truy vấn DNS. Điều này xảy ra bất chấp những nỗ lực đáng kinh ngạc nhằm tối ưu hóa và chuyển nhà cung cấp DNS sang CoreDNS (khi tải cao nhất đạt tới 1000 nhóm chạy trên 120 lõi).

Trong khi nghiên cứu các nguyên nhân và giải pháp khả thi khác, chúng tôi đã phát hiện ra Bài viết, mô tả các điều kiện tương tranh ảnh hưởng đến khung lọc gói lưới lọc trong Linux. Thời gian chờ mà chúng tôi quan sát được, cùng với bộ đếm ngày càng tăng chèn_thất bại trong giao diện Flannel phù hợp với kết quả của bài viết.

Sự cố xảy ra ở giai đoạn Dịch địa chỉ mạng nguồn và đích (SNAT và DNAT) và sau đó nhập vào bảng theo dõi. Một trong những cách giải quyết được cộng đồng thảo luận nội bộ và đề xuất là chuyển DNS sang nút công nhân. Trong trường hợp này:

  • SNAT không cần thiết vì lưu lượng vẫn ở bên trong nút. Nó không cần phải được định tuyến qua giao diện eth0.
  • Không cần DNAT vì IP đích là cục bộ của nút chứ không phải nhóm được chọn ngẫu nhiên theo quy tắc iptables.

Chúng tôi quyết định gắn bó với cách tiếp cận này. CoreDNS đã được triển khai dưới dạng DaemonSet trong Kubernetes và chúng tôi đã triển khai máy chủ DNS nút cục bộ trong Resolutionv.conf mỗi nhóm bằng cách đặt cờ --cluster-dns đội kubelet . Giải pháp này tỏ ra có hiệu quả đối với thời gian chờ DNS.

Tuy nhiên, chúng tôi vẫn thấy mất gói và tăng bộ đếm chèn_thất bại trong giao diện Flannel. Điều này tiếp tục xảy ra sau khi giải pháp thay thế được triển khai vì chúng tôi chỉ có thể loại bỏ SNAT và/hoặc DNAT đối với lưu lượng DNS. Điều kiện cuộc đua được bảo toàn cho các loại hình giao thông khác. May mắn thay, hầu hết các gói của chúng tôi là TCP và nếu xảy ra sự cố, chúng sẽ được truyền lại. Chúng tôi vẫn đang cố gắng tìm giải pháp phù hợp cho mọi loại hình giao thông.

Sử dụng Envoy để cân bằng tải tốt hơn

Khi di chuyển các dịch vụ phụ trợ sang Kubernetes, chúng tôi bắt đầu gặp phải tình trạng tải không cân bằng giữa các nhóm. Chúng tôi nhận thấy rằng HTTP Keepalive khiến các kết nối ELB bị treo trên các nhóm sẵn sàng đầu tiên của mỗi lần triển khai được triển khai. Do đó, phần lớn lưu lượng truy cập đã đi qua một tỷ lệ nhỏ các nhóm có sẵn. Giải pháp đầu tiên mà chúng tôi thử nghiệm là đặt MaxSurge ở mức 100% khi triển khai mới trong trường hợp xấu nhất. Hiệu quả hóa ra là không đáng kể và không có gì hứa hẹn khi triển khai ở quy mô lớn hơn.

Một giải pháp khác mà chúng tôi đã sử dụng là tăng yêu cầu tài nguyên một cách giả tạo cho các dịch vụ quan trọng. Trong trường hợp này, các kén được đặt gần đó sẽ có nhiều không gian để cơ động hơn so với các kén nặng khác. Về lâu dài nó cũng sẽ không có tác dụng vì sẽ gây lãng phí tài nguyên. Ngoài ra, các ứng dụng Node của chúng tôi là đơn luồng và do đó, chỉ có thể sử dụng một lõi. Giải pháp thực sự duy nhất là sử dụng tính năng cân bằng tải tốt hơn.

Chúng tôi từ lâu đã muốn đánh giá đầy đủ Đặc phái viên. Tình hình hiện tại cho phép chúng tôi triển khai nó một cách rất hạn chế và nhận được kết quả ngay lập tức. Envoy là proxy lớp XNUMX, mã nguồn mở, hiệu suất cao được thiết kế cho các ứng dụng SOA lớn. Nó có thể triển khai các kỹ thuật cân bằng tải nâng cao, bao gồm thử lại tự động, ngắt mạch và giới hạn tốc độ toàn cầu. (Ghi chú. bản dịch.: Bạn có thể đọc thêm về điều này trong bài viết này về Istio, dựa trên Envoy.)

Chúng tôi đã đưa ra cấu hình sau: có một xe phụ Envoy cho mỗi nhóm và một tuyến đường duy nhất, đồng thời kết nối cụm với vùng chứa cục bộ qua cổng. Để giảm thiểu khả năng xếp tầng và duy trì bán kính tấn công nhỏ, chúng tôi đã sử dụng một nhóm nhóm proxy phía trước Envoy, mỗi nhóm cho mỗi Vùng sẵn sàng (AZ) cho mỗi dịch vụ. Họ dựa vào một công cụ khám phá dịch vụ đơn giản được viết bởi một trong các kỹ sư của chúng tôi, công cụ này chỉ trả về danh sách các nhóm trong mỗi AZ cho một dịch vụ nhất định.

Sau đó, Service Front-Envoys đã sử dụng cơ chế khám phá dịch vụ này với một cụm và tuyến ngược dòng. Chúng tôi đặt thời gian chờ thích hợp, tăng tất cả cài đặt cầu dao và thêm cấu hình thử lại tối thiểu để trợ giúp với các lỗi đơn lẻ và đảm bảo triển khai suôn sẻ. Chúng tôi đã đặt một TCP ELB trước mỗi Đại diện dịch vụ này. Ngay cả khi phần giữ lại từ lớp proxy chính của chúng tôi bị kẹt trên một số nhóm Envoy, chúng vẫn có thể xử lý tải tốt hơn nhiều và được định cấu hình để cân bằng thông qua less_request trong phần phụ trợ.

Để triển khai, chúng tôi đã sử dụng preStop hook trên cả nhóm ứng dụng và nhóm sidecar. Móc đã gây ra lỗi khi kiểm tra trạng thái của điểm cuối quản trị viên nằm trên thùng chứa sidecar và chuyển sang chế độ ngủ trong một thời gian để cho phép các kết nối đang hoạt động chấm dứt.

Một trong những lý do khiến chúng tôi có thể tiến hành nhanh chóng như vậy là do các số liệu chi tiết mà chúng tôi có thể dễ dàng tích hợp vào bản cài đặt Prometheus điển hình. Điều này cho phép chúng tôi biết chính xác điều gì đang xảy ra trong khi chúng tôi điều chỉnh các thông số cấu hình và phân phối lại lưu lượng truy cập.

Kết quả ngay lập tức và rõ ràng. Chúng tôi bắt đầu với các dịch vụ không cân bằng nhất và hiện tại nó hoạt động trước 12 dịch vụ quan trọng nhất trong cụm. Năm nay, chúng tôi đang lên kế hoạch chuyển đổi sang mạng lưới dịch vụ đầy đủ với khả năng phát hiện dịch vụ tiên tiến hơn, ngắt mạch, phát hiện ngoại lệ, giới hạn tốc độ và truy tìm.

Chuyển đổi Tinder sang Kubernetes
Hình 3–1. Sự hội tụ CPU của một dịch vụ trong quá trình chuyển đổi sang Envoy

Chuyển đổi Tinder sang Kubernetes

Chuyển đổi Tinder sang Kubernetes

Kết quả cuối cùng

Thông qua kinh nghiệm này và nghiên cứu bổ sung, chúng tôi đã xây dựng được một nhóm cơ sở hạ tầng vững mạnh với các kỹ năng vững chắc trong việc thiết kế, triển khai và vận hành các cụm Kubernetes lớn. Tất cả các kỹ sư của Tinder hiện đều có kiến ​​thức và kinh nghiệm để đóng gói các container và triển khai ứng dụng lên Kubernetes.

Khi phát sinh nhu cầu về công suất bổ sung trên cơ sở hạ tầng cũ, chúng tôi phải đợi vài phút để các phiên bản EC2 mới khởi chạy. Bây giờ các container bắt đầu chạy và bắt đầu xử lý lưu lượng truy cập trong vòng vài giây thay vì vài phút. Việc lên lịch nhiều vùng chứa trên một phiên bản EC2 cũng giúp cải thiện khả năng tập trung theo chiều ngang. Do đó, chúng tôi dự báo chi phí EC2019 sẽ giảm đáng kể trong năm 2 so với năm ngoái.

Quá trình di chuyển mất gần hai năm nhưng chúng tôi đã hoàn thành vào tháng 2019 năm 200. Hiện tại, nền tảng Tinder chạy độc quyền trên cụm Kubernetes bao gồm 1000 dịch vụ, 15 nút, 000 nhóm và 48 container đang chạy. Cơ sở hạ tầng không còn là lĩnh vực duy nhất của các nhóm vận hành. Tất cả các kỹ sư của chúng tôi đều chia sẻ trách nhiệm này và kiểm soát quá trình xây dựng cũng như triển khai ứng dụng của họ chỉ bằng mã.

Tái bút từ người dịch

Đọc thêm một loạt bài viết trên blog của chúng tôi:

Nguồn: www.habr.com

Thêm một lời nhận xét