Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Tại Mail.ru Group, chúng tôi có Tarantool - đây là một máy chủ ứng dụng ở Lua, cũng đóng vai trò như một cơ sở dữ liệu (hoặc ngược lại?). Nó nhanh và mát, nhưng khả năng của một máy chủ vẫn không bị giới hạn. Chia tỷ lệ dọc cũng không phải là thuốc chữa bách bệnh nên Tarantool có các công cụ để chia tỷ lệ theo chiều ngang - mô-đun vshard [1]. Nó cho phép bạn phân chia dữ liệu trên một số máy chủ, nhưng bạn phải mày mò dữ liệu để thiết lập và đính kèm logic nghiệp vụ.

Tin tốt: chúng tôi đã thu thập được một số bức ảnh lớn (ví dụ: [2], [3]) và tạo ra một khuôn khổ khác sẽ đơn giản hóa đáng kể giải pháp cho vấn đề này.

Hộp mực Tarantool là một khuôn khổ mới để phát triển các hệ thống phân tán phức tạp. Nó cho phép bạn tập trung vào việc viết logic nghiệp vụ thay vì giải quyết các vấn đề về cơ sở hạ tầng. Bên dưới phần cắt, tôi sẽ cho bạn biết cách hoạt động của khung này và cách viết các dịch vụ phân tán bằng cách sử dụng nó.

Và chính xác thì vấn đề là gì?

Chúng tôi có một con tarantula, chúng tôi có vshard - bạn còn muốn gì hơn nữa?

Thứ nhất, đó là vấn đề thuận tiện. Cấu hình vshard được cấu hình thông qua các bảng Lua. Để hệ thống phân tán gồm nhiều quy trình Tarantool hoạt động chính xác, cấu hình phải giống nhau ở mọi nơi. Không ai muốn làm điều này bằng tay. Do đó, tất cả các loại tập lệnh, Ansible và hệ thống triển khai đều được sử dụng.

Bản thân Cartridge quản lý cấu hình vshard, nó thực hiện việc này dựa trên cấu hình phân tán riêng. Về cơ bản nó là một tệp YAML đơn giản, một bản sao của tệp này được lưu trữ trong mỗi phiên bản Tarantool. Đơn giản hóa là chính khung này sẽ giám sát cấu hình của nó và đảm bảo rằng nó giống nhau ở mọi nơi.

Thứ hai, đó lại là vấn đề thuận tiện. Cấu hình vshard không liên quan gì đến việc phát triển logic nghiệp vụ và chỉ khiến lập trình viên mất tập trung vào công việc của mình. Khi thảo luận về kiến ​​trúc của một dự án, chúng ta thường nói về các thành phần riêng lẻ và sự tương tác của chúng. Còn quá sớm để nghĩ đến việc triển khai một cụm cho 3 trung tâm dữ liệu.

Chúng tôi đã giải quyết đi giải quyết lại những vấn đề này và tại một thời điểm nào đó, chúng tôi đã cố gắng phát triển một phương pháp đơn giản hóa cách làm việc với ứng dụng trong toàn bộ vòng đời của nó: tạo, phát triển, thử nghiệm, CI/CD, bảo trì.

Cartridge giới thiệu khái niệm về vai trò cho từng quy trình Tarantool. Vai trò là một khái niệm cho phép nhà phát triển tập trung vào việc viết mã. Tất cả các vai trò có sẵn trong dự án đều có thể chạy trên một phiên bản Tarantool và điều này là đủ cho các thử nghiệm.

Các tính năng chính của Tarantool Cartridge:

  • điều phối cụm tự động;
  • mở rộng chức năng của ứng dụng bằng các vai trò mới;
  • mẫu ứng dụng để phát triển và triển khai;
  • tích hợp sharding tự động;
  • tích hợp với khung thử nghiệm Luest;
  • quản lý cụm bằng WebUI và API;
  • công cụ đóng gói và triển khai.

Chào thế giới!

Tôi nóng lòng muốn giới thiệu chính khung đó, vì vậy chúng ta sẽ để lại câu chuyện về kiến ​​trúc ở phần sau và bắt đầu với thứ gì đó đơn giản. Nếu chúng tôi cho rằng bản thân Tarantool đã được cài đặt, thì tất cả những gì còn lại là phải làm

$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATH

Hai lệnh này sẽ cài đặt các tiện ích dòng lệnh và cho phép bạn tạo ứng dụng đầu tiên của mình từ mẫu:

$ cartridge create --name myapp

Và đây là những gì chúng tôi nhận được:

myapp/
├── .git/
├── .gitignore
├── app/roles/custom.lua
├── deps.sh
├── init.lua
├── myapp-scm-1.rockspec
├── test
│   ├── helper
│   │   ├── integration.lua
│   │   └── unit.lua
│   ├── helper.lua
│   ├── integration/api_test.lua
│   └── unit/sample_test.lua
└── tmp/

Đây là kho lưu trữ git với dòng chữ “Xin chào, Thế giới!” được tạo sẵn! ứng dụng. Hãy thử chạy nó ngay lập tức, sau khi đã cài đặt các phần phụ thuộc trước đó (bao gồm cả chính khung):

$ tarantoolctl rocks make
$ ./init.lua --http-port 8080

Vì vậy, chúng tôi có một nút đang chạy cho ứng dụng phân đoạn trong tương lai. Một giáo dân tò mò có thể ngay lập tức mở giao diện web, định cấu hình một cụm nút bằng chuột và tận hưởng kết quả, nhưng còn quá sớm để vui mừng. Cho đến nay, ứng dụng không thể làm được điều gì hữu ích, vì vậy tôi sẽ kể cho bạn về việc triển khai sau, nhưng bây giờ là lúc viết mã.

Phát triển ứng dụng

Hãy tưởng tượng, chúng ta đang thiết kế một dự án phải nhận dữ liệu, lưu dữ liệu và tạo báo cáo mỗi ngày một lần.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Chúng tôi bắt đầu vẽ sơ đồ và đặt ba thành phần trên đó: cổng, bộ lưu trữ và bộ lập lịch. Chúng tôi đang nghiên cứu sâu hơn về kiến ​​trúc. Vì chúng tôi sử dụng vshard làm bộ lưu trữ nên chúng tôi thêm vshard-router và vshard-storage vào sơ đồ. Cả cổng và bộ lập lịch đều không truy cập trực tiếp vào bộ lưu trữ; đó là mục đích của bộ định tuyến, đó là mục đích mà nó được tạo ra.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Sơ đồ này vẫn không thể hiện chính xác những gì chúng ta sẽ xây dựng trong dự án vì các thành phần trông có vẻ trừu tượng. Chúng ta vẫn cần xem điều này sẽ được chiếu lên Tarantool thực như thế nào - hãy nhóm các thành phần của chúng ta theo quy trình.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Có rất ít ý nghĩa trong việc giữ vshard-router và cổng trên các phiên bản riêng biệt. Tại sao chúng ta cần lướt mạng một lần nữa nếu đây đã là trách nhiệm của bộ định tuyến? Chúng phải được chạy trong cùng một quy trình. Nghĩa là, cả cổng và vshard.router.cfg đều được khởi tạo trong một quy trình và cho phép chúng tương tác cục bộ.

Ở giai đoạn thiết kế, thật thuận tiện khi làm việc với ba thành phần, nhưng tôi, với tư cách là một nhà phát triển, khi viết mã, không muốn nghĩ đến việc tung ra ba phiên bản Tarnatool. Tôi cần chạy thử nghiệm và kiểm tra xem tôi đã viết cổng chính xác chưa. Hoặc có thể tôi muốn trình diễn một tính năng nào đó cho đồng nghiệp của mình. Tại sao tôi phải gặp rắc rối khi triển khai ba bản sao? Đây là cách mà khái niệm về vai trò ra đời. Vai trò là một mô-đun luash thông thường có vòng đời được quản lý bởi Cartridge. Trong ví dụ này có bốn trong số chúng - cổng, bộ định tuyến, bộ lưu trữ, bộ lập lịch. Có thể có nhiều hơn trong một dự án khác. Tất cả các vai trò có thể được chạy trong một quy trình và điều này là đủ.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Và khi nói đến việc triển khai để dàn dựng hoặc sản xuất, thì chúng tôi sẽ chỉ định cho mỗi quy trình Tarantool một nhóm vai trò riêng tùy thuộc vào khả năng của phần cứng:

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Quản lý cấu trúc liên kết

Thông tin về vai trò nào đang chạy phải được lưu trữ ở đâu đó. Và “ở đâu đó” này là cấu hình phân tán mà tôi đã đề cập ở trên. Điều quan trọng nhất về nó là cấu trúc liên kết cụm. Dưới đây là 3 nhóm sao chép của 5 tiến trình Tarantool:

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Chúng tôi không muốn mất dữ liệu nên chúng tôi xử lý thông tin về các quy trình đang chạy một cách cẩn thận. Hộp mực theo dõi cấu hình bằng cách sử dụng cam kết hai pha. Khi chúng tôi muốn cập nhật cấu hình, trước tiên chúng tôi sẽ kiểm tra xem tất cả các phiên bản đều có sẵn và sẵn sàng chấp nhận cấu hình mới hay không. Sau đó, giai đoạn thứ hai áp dụng config. Vì vậy, ngay cả khi một bản sao tạm thời không có sẵn thì sẽ không có điều gì xấu xảy ra. Đơn giản là cấu hình sẽ không được áp dụng và bạn sẽ thấy lỗi trước.

Cũng trong phần cấu trúc liên kết, một tham số quan trọng như người đứng đầu mỗi nhóm sao chép được chỉ định. Thông thường đây là bản sao đang được ghi lại. Phần còn lại thường chỉ đọc, mặc dù có thể có ngoại lệ. Đôi khi các nhà phát triển dũng cảm không sợ xung đột và có thể ghi dữ liệu song song vào nhiều bản sao, nhưng có một số thao tác dù thế nào đi nữa cũng không nên thực hiện hai lần. Đối với điều này có một dấu hiệu của một nhà lãnh đạo.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Cuộc sống của các vai diễn

Để tồn tại một vai trò trừu tượng trong kiến ​​trúc như vậy, khung công tác phải quản lý chúng bằng cách nào đó. Đương nhiên, việc kiểm soát diễn ra mà không cần khởi động lại quy trình Tarantool. Có 4 lệnh gọi lại để quản lý vai trò. Bản thân Cartridge sẽ gọi chúng tùy thuộc vào nội dung được ghi trong cấu hình phân tán của nó, từ đó áp dụng cấu hình cho các vai trò cụ thể.

function init()
function validate_config()
function apply_config()
function stop()

Mỗi vai trò có một chức năng init. Nó được gọi một lần khi vai trò được bật hoặc khi Tarantool được khởi động lại. Chẳng hạn, ở đó rất thuận tiện để khởi tạo box.space.create hoặc bộ lập lịch có thể khởi chạy một số sợi nền sẽ thực hiện công việc trong những khoảng thời gian nhất định.

Một chức năng init có thể không đủ Hộp mực cho phép các vai trò tận dụng cấu hình phân tán mà nó sử dụng để lưu trữ cấu trúc liên kết. Chúng ta có thể khai báo một phần mới trong cùng cấu hình và lưu trữ một phần cấu hình doanh nghiệp trong đó. Trong ví dụ của tôi, đây có thể là lược đồ dữ liệu hoặc cài đặt lịch trình cho vai trò người lập lịch trình.

Cuộc gọi cụm validate_config и apply_config mỗi khi cấu hình phân tán thay đổi. Khi một cấu hình được áp dụng theo cam kết hai pha, cụm sẽ kiểm tra xem mỗi vai trò đã sẵn sàng chấp nhận cấu hình mới này chưa và nếu cần, sẽ báo cáo lỗi cho người dùng. Khi mọi người đồng ý rằng cấu hình là bình thường thì apply_config.

Ngoài ra vai trò có một phương pháp stop, cần thiết để dọn dẹp đầu ra của vai trò. Nếu chúng tôi nói rằng bộ lập lịch không còn cần thiết trên máy chủ này nữa, nó có thể dừng các sợi mà nó đã bắt đầu init.

Các vai trò có thể tương tác với nhau. Chúng ta đã quen với việc viết lệnh gọi hàm trong Lua, nhưng có thể xảy ra trường hợp một quy trình nhất định không có vai trò mà chúng ta cần. Để tạo điều kiện thuận lợi cho các cuộc gọi qua mạng, chúng tôi sử dụng mô-đun phụ trợ rpc (cuộc gọi thủ tục từ xa), được xây dựng trên cơ sở netbox tiêu chuẩn được tích hợp trong Tarantool. Điều này có thể hữu ích, ví dụ: nếu cổng của bạn muốn trực tiếp yêu cầu người lập lịch thực hiện công việc ngay bây giờ, thay vì đợi một ngày.

Một điểm quan trọng khác là đảm bảo khả năng chịu lỗi. Hộp mực sử dụng giao thức SWIM để theo dõi sức khỏe [4]. Nói tóm lại, các tiến trình trao đổi “tin đồn” với nhau qua UDP—mỗi tiến trình thông báo cho các tiến trình lân cận những tin tức mới nhất và chúng phản hồi. Nếu đột nhiên câu trả lời không đến, Tarantool bắt đầu nghi ngờ có điều gì đó không ổn, một lúc sau nó kể lại cái chết và bắt đầu kể cho mọi người xung quanh tin tức này.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Dựa trên giao thức này, Cartridge tổ chức xử lý lỗi tự động. Mỗi quy trình sẽ giám sát môi trường của nó và nếu quy trình dẫn đầu đột nhiên ngừng phản hồi, bản sao có thể đảm nhận vai trò của nó và Cartridge sẽ định cấu hình các vai trò đang chạy tương ứng.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Bạn cần phải cẩn thận ở đây, vì việc chuyển đổi qua lại thường xuyên có thể dẫn đến xung đột dữ liệu trong quá trình sao chép. Tất nhiên, bạn không nên kích hoạt tính năng chuyển đổi dự phòng tự động một cách ngẫu nhiên. Chúng ta phải hiểu rõ ràng điều gì đang xảy ra và đảm bảo rằng bản sao sẽ không bị phá vỡ sau khi người lãnh đạo được phục hồi và vương miện được trả lại cho anh ta.

Từ tất cả những điều này, bạn có thể có cảm giác rằng các vai trò tương tự như microservice. Theo một nghĩa nào đó, chúng chỉ như vậy, chỉ dưới dạng các mô-đun bên trong các quy trình Tarantool. Nhưng cũng có một số khác biệt cơ bản. Đầu tiên, tất cả các vai trò của dự án phải nằm trong cùng một cơ sở mã. Và tất cả các quy trình Tarantool phải được khởi chạy từ cùng một cơ sở mã, để không có những điều bất ngờ như vậy khi chúng tôi cố gắng khởi tạo bộ lập lịch nhưng đơn giản là nó không tồn tại. Ngoài ra, bạn không nên cho phép có sự khác biệt về phiên bản mã, vì hoạt động của hệ thống trong tình huống như vậy rất khó dự đoán và gỡ lỗi.

Không giống như Docker, chúng ta không thể chỉ nhận một vai trò "hình ảnh", đưa nó sang máy khác và chạy ở đó. Vai trò của chúng tôi không bị cô lập như các vùng chứa Docker. Ngoài ra, chúng tôi không thể chạy hai vai trò giống hệt nhau trên một phiên bản. Một vai trò có thể tồn tại hoặc không; theo một nghĩa nào đó, đó là một vai trò đơn lẻ. Và thứ ba, các vai trò phải giống nhau trong toàn bộ nhóm sao chép, vì nếu không thì sẽ vô lý - dữ liệu giống nhau nhưng cấu hình thì khác.

Công cụ triển khai

Tôi đã hứa sẽ chỉ ra cách Cartridge giúp triển khai các ứng dụng. Để giúp cuộc sống của những người khác trở nên dễ dàng hơn, khung này đóng gói các gói RPM:

$ cartridge pack rpm myapp -- упакует для нас ./myapp-0.1.0-1.rpm
$ sudo yum install ./myapp-0.1.0-1.rpm

Gói đã cài đặt chứa hầu hết mọi thứ bạn cần: cả ứng dụng và các phần phụ thuộc đã cài đặt. Tarantool cũng sẽ xuất hiện trên máy chủ dưới dạng phần phụ thuộc của gói RPM và dịch vụ của chúng tôi đã sẵn sàng ra mắt. Việc này được thực hiện thông qua systemd, nhưng trước tiên bạn cần viết một chút cấu hình. Ở mức tối thiểu, hãy chỉ định URI của từng quy trình. Ví dụ ba là đủ.

$ sudo tee /etc/tarantool/conf.d/demo.yml <<CONFIG
myapp.router: {"advertise_uri": "localhost:3301", "http_port": 8080}
myapp.storage_A: {"advertise_uri": "localhost:3302", "http_enabled": False}
myapp.storage_B: {"advertise_uri": "localhost:3303", "http_enabled": False}
CONFIG

Có một sắc thái thú vị ở đây. Thay vì chỉ chỉ định cổng giao thức nhị phân, chúng tôi chỉ định toàn bộ địa chỉ công khai của quy trình bao gồm cả tên máy chủ. Điều này là cần thiết để các nút cụm biết cách kết nối với nhau. Bạn nên sử dụng 0.0.0.0 làm địa chỉ Advertising_uri là một ý tưởng tồi; địa chỉ này phải là địa chỉ IP bên ngoài chứ không phải liên kết ổ cắm. Nếu không có nó, sẽ không có gì hoạt động, vì vậy Cartridge đơn giản sẽ không cho phép bạn khởi chạy một nút có sai ad_uri.

Bây giờ cấu hình đã sẵn sàng, bạn có thể bắt đầu quá trình. Vì một đơn vị systemd thông thường không cho phép nhiều quá trình khởi động, nên cái gọi là các ứng dụng trên Hộp mực sẽ được cài đặt. các đơn vị khởi tạo hoạt động như thế này:

$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_B

Trong cấu hình, chúng tôi đã chỉ định cổng HTTP mà Cartridge phục vụ giao diện web - 8080. Hãy truy cập vào đó và xem:

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Chúng tôi thấy rằng mặc dù các tiến trình đang chạy nhưng chúng vẫn chưa được cấu hình. Hộp mực vẫn chưa biết ai nên sao chép với ai và không thể tự mình đưa ra quyết định nên phải chờ hành động của chúng ta. Nhưng chúng ta không có nhiều sự lựa chọn: vòng đời của một cụm mới bắt đầu bằng cấu hình của nút đầu tiên. Sau đó, chúng tôi sẽ thêm những người khác vào cụm, gán vai trò cho họ và tại thời điểm này, quá trình triển khai có thể được coi là đã hoàn tất thành công.

Hãy cùng rót một ly đồ uống yêu thích của bạn và thư giãn sau một tuần làm việc dài. Ứng dụng này có thể được sử dụng.

Hộp mực Tarantool: phân chia phần phụ trợ Lua thành ba dòng

Kết quả

Kết quả là gì? Hãy dùng thử, sử dụng, để lại phản hồi, tạo vé trên Github.

tài liệu tham khảo

[1] Tarantool » 2.2 » Tài liệu tham khảo » Tham khảo đá » Mô-đun vshard

[2] Cách chúng tôi triển khai hoạt động kinh doanh đầu tư cốt lõi của Alfa-Bank dựa trên Tarantool

[3] Kiến trúc thanh toán thế hệ mới: chuyển đổi khi chuyển sang Tarantool

[4] SWIM - giao thức xây dựng cụm

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/hộp mực

Nguồn: www.habr.com

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