[Chuyên sâu] Uber Engineering Tech Stack, Phần I: Nền Tảng

2742

Uber Engineering

Với nhiệm vụ truân chuyển hành khách nhanh và tiện lợi nhất, Uber cần xây dựng và làm việc với nhiều dữ liệu phức tạp. Bới thế, một nền tảng cho phép tài xế tìm “mối làm ăn” và đi lại cho hợp lý đã ra đời.

Screenshots của ứng dụng Uber, giao diện tài xế ở New York, Trung Quốc, và Ấn Độ tại thời điểm đầu năm 2016.

Để UI trở nên thật đơn giản, đội ngũ Uber phải xây dựng một hệ thống khá phức tạp, vừa vận hành liên tục, có thể xử lý các tương tác rắc rối, và đáp ứng được lượng traffic khổng lồ. Họ đã phải phân nhỏ cấu trúc nguyên khối ban đầu thành nhiều phần khác nhau để đáp ứng cho nhu cầu mở rộng sau này.

Thách thức với Uber Engineering: không người dùng miễn phí, tăng trưởng chóng mặt

Cũng như bao công ty phần mềm thành công khác, Uber đối mặt với rất nhiều khó khăn khi mở rộng quy mô ra toàn cầu. Những khó khăn đó bao gồm: thứ nhất, Uber hiện chỉ mới 6 năm tuổi, nên chưa thể giải quyết các vấn đề đó ngay được, và thứ hai là mô hình của Uber dựa trên thế giới thực và thời điểm thực.

Uber_MarketMap_April92015_
Tháng 4 năm 2015, mật độ xuất hiện của Uber trên thế giới, lên đến 300 thành phố

 

Không giống như những dịch vụ freemium, Uber là trung gian giao dịch của nhiều người dùng như: giữa tài xế và hành khách, hay giữa thực khách và người vận chuyển . Những người này phụ thuộc vào công nghệ của Uber để kiếm tiền, hay đi đến bất kỳ đâu mà họ muốn. Bởi thế, Uber phải ưu tiên tính khả dụng và tốc độ mở rộng. Khi Uber mở rộng địa bàn hoạt động, thì việc dịch vụ phải phát triển theo là đương nhiên. Sự linh hoạt trong đội ngũ kỹ thuật của Uber khuyến khích cạnh tranh, ý tưởng tốt nhất sẽ chiến thắng. Những ý tưởng này không cần thiết phải mới lạ. Nếu sở hữu công cụ đủ mạnh, Uber sẽ dùng nó cho đến khi yêu cầu vượt quá giới hạn của công cụ. Khi cần nhiều hơn nữa, Uber sẽ xây dựng những giải pháp in-house. Đội ngũ Uber Engineering đã phải làm việc rất cật lực và sáng tạo trong những năm vừa qua. Trong suốt năm 2016, Uber đã ấp ủ được những kế hoạch vĩ mô hơn. Trong lúc bạn đang đọc những dòng này, có lẽ mọi thứ đã và đang thay đổi. Tuy vậy, bài viết sẽ cố hết sức mô tả lại những công nghệ mà Uber đang sử dụng hiện nay. Uber hy vọng, thông qua bài viết, các bạn có thể hiểu được mục tiêu cao nhất của việc tận dụng công cụ và công nghệ trong kinh doanh.

Uber’s Tech Stack

Hãy mườn tượng Uber như một cây cổ thụ. Nhìn vào công nghệ làm nên Uber, bạn sẽ thấy một stack chung (thân cây) với với những trọng tâm khác nhau cho từng team hoặc phòng ban (nhánh cây). Giống nhau về cơ bản, nhưng lại đâm nhánh tua tủa với những công cụ và dịch vụ khác nhau.

botswana_baobab

Hãy bắt đầu từ dưới lên

Từ dưới là từ đâu? Platform!

Bài viết đầu tiên này tập trung vào nền tảng, phần cốt yếu cho cả một tổ chức Uber Engineering. Platform team sẽ xây dựng và duy trì nền tảng này, từ đó những kỹ sư khác có thể cho ra đời những chương trình, tính năng và ứng dụng mà bạn đang cầm trên tay.

Lưu trữ và cơ sở hạ tầng

Uber sử dụng mô hình đám mây lai (hybrid cloud model), kết hợp giữa cloud provider và nhiều active data center khác nhau. Nếu data center này có sự cố. Uber sẽ chỉ định thành phố kế tiếp có data center gần nhất, chuyển giao xử lý và dữ liệu qua đó. Như vậy, họ sẽ không cần đến center “dự phòng”. Để hiện thực hóa mô hình này, họ đã dùng đến một loạt internal tools và Terraform.

Nhu cầu lưu trữ luôn thay đổi để phù hợp với tăng trưởng. Mới đầu, single Postgres instance đáp ứng khá tối, nhưng khi Uber mở rộng quá nhanh, họ cần phải tăng khả năng lưu trữ và giảm thời gian phản hồi.

mezzanine-overhaul
Cuối hè 2014, Project Mezzanine tái cấu trúc hệ thống để phù hợp với cấu trúc cao cấp hơn.

 

Uber hiện đang sử dụng Schemaless, Riak, and Cassandra. Schemaless dùng để lưu trữ dữ liệu dài hạn; Riak và Cassandra để đáp ứng yêu cầu số lượng cao, độ trễ thấp. Để lưu trữ phân phối và phân tích dữ liệu phức tạp, Uber dùng kho Hadoop.

Với caching và queuing, Uber dùng Redis. Twemproxy với thuật toán hashing ổn định, cung cấp khả năng mở rộng cho caching layer mà không phải đánh đổi cache hit rate. Celery workers xử lý async workflow operations (thao tác workflow không đồng bộ) bằng các Redis instances ở trên.

Logging

Dịch vụ của Uber tương tác với nhau và với thiết bị di động, và sự tương tác này rất cần thiết cho các mục đích internal như debugging, cũng như các vấn đề về kinh doanh như tính phí tự động. Với logging, họ dùng Kafka clusters, và dữ liệu được lưu trữ vào Hadoop và/hoặc một dịch vự web lưu trữ trước khi hết hạn trong Kafka. Dữ liệu còn được đưa vào (theo thời gian thực) nhiều dịch vụ và index vào stack ELK để phục vụ tìm kiếm và hiển thị (ELK là viết tắt của Elasticsearch, Logstash, và Kibana).

App Provisioning

Uber dùng Docker containers trên Mesos để chạy các microservices, cùng Aurora cho các service ngốn thời gian và cron jobs. Team Application Platform còn cho ra đời một thư viện mẫu giúp build service vào các ‘Docker images ship được’.

Định tuyến và Khám phá dịch vụ

Service-oriented architecture (SOA) tập trung vào khám phá và điều hướng dịch vụ, là nhân tố tạo nên thành công cho Uber. Các dịch vụ phải có khả năng giao tiếp với nhau trong mạng lưới phức tạp như thế này. Và để giải quyết, Uber tìm đến tổ hợp HAProxyHyperbahn. Hyperbahn là một phần của tập hợp phần mềm nguồn mở do chính Uber phát triển, cả Ringpop, TChannel, và Hyperbahn đều có sứ mệnh mở rộng việc tự động hóa, trí thông minh, và hiệu năng cho mạng lưới dịch vụ.

Legacy service dùng local HAProxy instance để định tuyến JSON qua HTTP request đến các service khác, với front-end web server NGINX sẽ proxy đến các servers ở back-end. Cách thức truyền tải dữ liệu thấu đáo này giúp việc troubleshoot trở nên dễ dàng hơn.

Tuy nhiên, Uber lại đang ưu tiên độ ổn định về lâu dài hơn là khả năng debug. Các giao thức thay thế cho HTTP (như SPDY, HTTP/2, và TChannel) cùng với ngôn ngữ định dạng giao diện như Thrift Protobuf sẽ giúp tăng tốc độ và độ ổn định cho hệ thống. Ringpoq (hashing layer) ổn định, giúp phối hợp và tự hồi phục ở cấp độ ứng dụng. Hyperbahn cho phép các dịch vụ tìm kiếm và giao tiếp với nhau, đơn giản và chắn chắn hơn, ngay cả khi các dịch vụ này liên tục được Mesos lên lịch trình.

Thay vì đối chiếu với quá khứ để xác định thay đổi, Uber chuyển sang pub-sub pattern (công bố cập nhật đến subscribers). HTTP/2 và SPDY có thể tận dụng push model này dễ dàng hơn. Khi chuyến sang push, nhiều tính năng poll-based trong ứng dụng Uber sẽ sẽ được tăng tốc mạnh mẽ.

Phát triển và Triển khai

Phabricator hỗ trợ rất nhiều thao tác internal, từ code review đến documentation hay tự động hóa process. Uber tìm kiếm thông qua code trên OpenGrok. với các projects nguồn mở, họ phát triển công khai trên GitHub, để theo dõi sự cố và tạo điều kiện cho code reviews.

Uber Engineering luôn cố gắng xây dựng môi trường kỹ thuật càng kích thích production càng tốt. Bởi lẽ đó, Uber chủ yếu phát triển trên vitual macines chạy trên cloud provider hoặc một laptop nào đó của developer. Uber tự xây dựng internal deployment system của riêng mình để tự quản lý builds, cùng Jenkins liên tục đảm nhiệm integration. Uber còn kết hợp Packer, Vagrant, Boto, và Unison để cho ra các tools nhằm xây dựng, quản lý, và phát triển trên vitual machines. Đồng thời, Clusto được dùng để quản lý inventory. Puppet sẽ quản lý system configuration.

Uber làm việc không ngừng nghỉ để xậy dựng và duy trì các kênh giao tiếp ổn định, không chỉ hướng đến dịch vụ mà còn để tạo điều hiện tốt nhất cho đội ngũ kỹ sư. Với information discovery, Uber xây dựng uBlame với mục đích theo dõi team nào đảm nhiệm dịch vụ nào, và Whober để tìm tên, nhận dạng, thông tin liên hệ, và cấu trúc tổ chức. Họ còn sử dụng một trang tài liệu nội bộ dùng Sphinx, giúp autobuild tài liệu từ repositories. Một enterprise alerting service sẵn sàng đánh động các kỹ sư thường trực để đảm bảo server luôn hoạt động. Đa phần developers chạy OSX trên laptop của mình, và đa số production instances của Uber chạy Linux với Debian Jessie.

Ngôn ngữ

Ở cấp độ thấp hơn, kỹ sư Uber thường thao tác chủ yếu bằng Python, Node.js, Go, và Java. Uber bắt đầu bằng hai ngôn ngữ chính: Node.js cho Marketplace team, và Python cho mọi người còn lại. Trong đó, Node.js hiện vẫn là ngôn ngữ của đa số dịch vụ tại Uber.

Uber còn tận dụng Go và Java vì lý do hiệu suất. Java tận dụng lợi thế hệ sinh thái nguồn mở và có thể kết hợp với các công nghệ bên ngoài, như Hadoop và những công cụ phân tích khác. Còn với Go, Uber có được sự hiệu quả, đơn giản, và tốc độ khá nhanh.

Uber loại bỏ và thay thế code Python khi họ phá vỡ codebase gốc thành nhiều phần nhỏ hơn. Mô hình lập trình không đồng bộ giúp Uber thu được lưu lượng khả quan hơn. Họ còn sử dụng Tornado và Python; nhưng native support với truy cập đồng thời của Go lại lý tưởng cho đa số dịch vụ (tập trung vào hiệu suất) mới xuất hiện.

Uber sẽ viết thêm các công cụ trong C và C++ khi cần thiết (như với code hiệu suất cao ở cấp độ hệ thống); đồng thời, sử dụng phần mềm viết bằng những ngôn ngữ này— chẳng hạn như HAProxy—Nhưng trong thực tế, Uber cũng rất ít khi phải động vào C hay C++.

Và tất nhiên, những kỹ sư đứng đầu không chỉ làm việc với những ngôn ngữ như Java, Go, Python hay Node.

Testing

Để đảm bảo các dịch vụ có thể sử lý lượng lớn yêu cầu từ môi trường production, Uber đã phát triển hai internal tools: Hailstorm và uDestroy. Hailstorm điều khiển integaration test và kích thích peak load trong các thời điểm off-peak, còn uDestroy cố ý bẻ nhỏ để xử lý unexpected failures tốt hơn.

Nhân viên Uber sử dụng phiên bản ứng dụng beta để liên lục kiểm tra những thay đổi mới nhất, cùng với app feedback reporter xác định bugs, trước khi đến tay người dùng. Mỗi khi có người chụp screenshot trong ứng dụng Uber, tính năng này sẽ thúc giục người đó nộp bug-fix task trong Phabricator.

Độ an toàn/ổn định

Kỹ sư viết dịch vụ backend nào sẽ phải chụi trách nhiệm cho chính phần đó. Nếu họ viết code bị break trong production, họ sẽ được thông báo ngay. Uber sử dụng Nagios để thực thi và quản lý các thông báo này.

Hướng đến tính khả dụng và con số 1 tỷ lượt khách mỗi ngày, đội ngũ site reliability engineers chỉ sẽ chú trọng vào các dịch vụ họ cần để thành công.

Buổi tech talk tháng hai năm 2016 sơ lược lịch sử của đội ngũ Uber Site Reliability Engineering.

Khả năng quan sát

“Quan sát” ở đây là chỉ sự đảm bảo cho cả Uber, cũng như các phần khác của Uber, được khỏe mạnh. Một tập hợp các hệ thống, do văn phòng Uber New York phát triển, hoạt động như tai, mắt, và hệ miễn dịch của toàn bộ đội ngũ Uber Engineering khắp toàn thế giới.

Telemetry

Uber phát triển M3 bằng Go để thu thập và lưu trữ số liệu từ mọi phần của Uber Engineering (mọi server, host service, và code)

Sau khi thu thập dữ liệu, Uber sẽ tìm trends, xây dựng dashboards và graphs bằng cách điều chỉnh Grafana để khái quát thông tin rõ ràng hơn nữa. Mỗi kỹ sư theo dõi dashboard thường nhìn dữ liệu tập trung vào từng vùng hoặc khu vực cụ thể, xoay quanh một loạt thí nghiệm, hoặc liên quan đến một sản phẩm cụ thể.

Anomaly Detection

Argos, in-house anomaly detection tool (công cụ phát hiện bất thường nội bộ) giúp kiểm tra các thông số được nhập và so sánh chúng với các mô dình dự đoán dựa trên data trong lịch sử để xác định xem, liệu dữ liệu đang có có nằm trong giới hạn đã dự đoán trước hay không.

hourly_thresholds
Uber so sánh ngưỡng dynamic (đỏ, vàng), được tạo từng giờ từ outlier detection algorithm (thuật toán xác định bỏ xót), với in-streaming data. Uber còn thêm dữ liệu chính xác từng phút để tham khảo (xanh), tất nhiên dữ liệu này trước đó vẫn chưa biết được. Hai ngưỡng giới hạn sẽ theo sát mô hình của số liệu thực.
Acting on Metrics

Uber có công cụ μMonitor, cho phép kỹ sư xem các thông tin và giới hạn này (thông số tĩnh và thông số thông minh của Argos), từ đó có cách đối phó phù hợp. Nếu một dòng data đi trật khỏi giới hạn, như lượt khách giảm xuống ngưỡng cho phép ở một số thành phố chẳng hạn, thì thông tin này sẽ được chuyển về Common Action Gateway (hệ thống phản hồi tự động). Thay vì phải đánh động kỹ sư mỗi khi có vấn đề xảy ra, hệ thống sẽ thay mặt xử lý và giảm thiểu thời gian ảnh hưởng của vấn đề nếu có thể. Nếu deploy có vấn đề xảy ra, hệ thống cũng sẽ tự động rollback.

Đa số công cụ theo dõi của Uber chỉ dùng được nội bộ do tính phù hợp cụ thể đến mỗi hệ thống hạ tầng của họ, hy vọng, công cụ sẽ được chung hóa và open source trong thời gian gần nhất.

Sử dụng data một cách sáng tạo

Storm and Spark kết hợp nhiều data streams thành các thông số kinh doanh hữu ích. Đội ngũ tái tạo thông tin của Uber sẽ tạo các framework và ứng dụng “tái sử dụng được” để xử lý dữ liệu hình ảnh.

xp_platform_example
Khả năng hình ảnh hóa table và confidence interval (khoảng tin cậy) tạo sứ thú vị sống động cho Morpheus, nền tảng A/B testing của Uber.

Mapping and experimentation teams phụ thuộc vào việc hình ảnh hóa dữ liệu, để có thể chuyển dũ liệu thành thông tin rõ ràng, khai thác được. City operations teams có thể thấy dòng tài xế ở thành phố mà họ đảm nhiệm trong thời gian thực, dưới dạng xe trên bản đồ, thay vì những số liệu nhàm chán trong SQL queries.

Uber sử dụng JavaScript (ES5ES6) và React để xây dựng sản phẩm dữ liệu, công cụ chính của Uber. Họ còn dùng tất cả chuẩn đồ họa web cho các thành phần hình ảnh:  SVG, Canvas 2D, and WebGL. Trong đó, nhiều thư viện mà Uber phát triển là nguồn mở, như react-map-gl, công cụ chuyên dùng để hình ảnh hóa mapping.

reactmap_gl_ex
Hình ảnh cho thấy sức mạnh của react-map-gl

Uber cũng xây dựng nhiều frameworks chuyên dụng cho việc hình ảnh hóa. Những công nghệ khác như R and Shiny và Python, có thể truy cập các framework này để xây dựng thành phần biểu đồ. Uber còn muốn ‘hình ảnh biểu thị các lượng thông tin mật độ cao’ có thể hoạt động mượt mà trên browser. Để đạt được cả hai mục đích, Uber dùng đến open-source WebGL-based visualization tools.

Uber heatmap thể hiện mật độ phương tiện chuyên chở hành khách. Sau đó, các ô percentiles đứng đầu sẽ bị ẩn đi để hiển thị các ô bên dưới.

Mapping

Maps teams của Uber ưu tiên datasets và thuật toán; làm việc xoay quanh data, hiển thị và định tuyến của bản đồ; cùng hệ thống thu thập, đề nghị địa chỉ/địa điểm. Dịch vụ bản đồ chạy trên một stack Java riêng biệt.

Dịch vụ có khối lượng lớn nhất trong khu vực này là Gurafu, cung cấp một loạt tiện ích làm việc với road map data, cải thiện hiệu quả và độ chính xác bằng cách cung cấp nhiều tùy chọn định tuyến phức tạp hơn nữa. Gurafu theo sau µETA (µETA thêm một lớp business logic lên trên các ETAs – phân nhóm thử nghiệm). Cả Gurafu và µETA đều là dịch vụ web được xây dựng trên framework DropWizard.

Khách hàng và hoạt động kinh doanh của Uber đa phần phụ thuộc vào ETAs độ chính xác cao, vậy nên các kỹ sư chuyên về Map Services càng có áp lực phải cải tiến những hệ thống này chính xác hơn nữa. Uber sẽ thực hiện phân tích ETA error để xác định và xử lý ngọn nguồn sự cố. Bên cạnh sự chính xác, quy mô của vấn đề cũng khá thú vị: cứ mỗi giây, nhiều hệ thống trong tổ chức thực hiện vô số quyết định thông qua thông tin do ETA cung cấp. Những request này phải có độ trễ dao động ở mức 5 mili giây, bằng không độ hiệu quả của thuật toán sẽ gặp nhiều trở ngại. Uber còn phải quan tâm đến cách phân bổ bộ nhớ, phép tính song song hóa; và gửi request đến các tài nguyên chạy chậm như system disk hoặc data center network.

Map Services còn hỗ trợ mạnh mẽ mọi công nghệ backend sau các hộp tìm kiếm trong ứng dụng cho tài xế. Gồm các công nghệ: search engine tự động hoành thành, engine dự đoán, và geocoding service đảo ngược. Search engine tự động hoành thành cho phép tình kiếm vị trí tốc độ nhanh, ưu tiên địa phương, dựa trên lịch sử người dùng và các tín hiệu khác. Lượng địa điểm dự đoán chiếm khoảng 50% địa điểm được nhập vào. Còn geocoder đảo ngược xác định địa điểm của người dùng dựa trên GPS.

Techtalk via Uber

CHIA SẺ