Data PlatformStacks Integration

Stacks Integration

Góc nhìn thực tế khi ghép nhiều open-source data platform stacks thành một platform thống nhất: identity, authorization, runtime consistency và governance automation.

Cá nhân tớ thấy trên mạng hiện đã có khá nhiều bài viết mô tả cách xây dựng một Data Platform hoặc LakeHouse, từ storage, catalog, processing engine, query engine cho tới orchestration và dashboarding. Những nội dung này rất hữu ích để tham khảo khi bắt đầu.

Vì vậy trong page này, tớ sẽ không đi sâu lặp lại các phần đó, mà tập trung vào góc nhìn cá nhân khi làm việc với các open-source stack trong thực tế, đặc biệt là các vấn đề xuất hiện khi nhiều thành phần cần vận hành cùng nhau. Phần architecture và deployment, nếu cần, tớ sẽ bổ sung lại chi tiết hơn ở các page khác.

Sau khi các thành phần đã được dựng và bắt đầu chạy ổn định, một lớp bài toán khác sẽ bắt đầu xuất hiện: làm sao để nhiều hệ thống độc lập thực sự vận hành cùng nhau như một platform thống nhất.

Khi đó, những câu hỏi như sau sẽ bắt đầu xuất hiện:

  • Identity nên đi xuyên suốt giữa các layer như thế nào?
  • Access control nên được enforce ở đâu?
  • Làm sao để notebook, scheduler và production jobs chia sẻ cùng runtime assumptions?
  • Làm sao để metadata, ownership và governance vẫn đáng tin cậy theo thời gian?

Đây cũng là nơi nhiều “working architectures” bắt đầu bộc lộ vấn đề. Page này tập trung vào phần thứ ba đó: Integration.

Phần lớn nội dung bên dưới đến từ những gì tớ từng trực tiếp gặp khi làm việc với các open-source data platform ở nhiều môi trường khác nhau. Nó sẽ ít mang tính lý thuyết hơn, và nghiêng nhiều hơn về những pain point thực tế, trade-off phải chấp nhận, cũng như những gì thật sự cần để nhiều công cụ riêng lẻ vận hành như một platform hoàn chỉnh.

Architecture at a Glance

Trước khi đi sâu hơn, tớ nghĩ nên thống nhất một góc nhìn architecture ở mức high-level. Sơ đồ dưới đây là logical view cơ bản theo hướng Lakehouse; thực tế triển khai sẽ phức tạp hơn nhiều.

Tương ứng với từng lớp phía trên, đây là một vài open-source components thường gặp:

  • Access / Productivity: Jupyter Notebook, JupyterHub, Superset,..
  • Orchestration: Apache Airflow, Mage, Prefect,...
  • Compute: Apache Spark, Apache Flink, Trino,...
  • Storage: S3, ADLS, HDFS,...
  • Open Table Format: Iceberg, Delta,...
  • Catalog: Hive Metastore, Nessie, Unity Catalog,...
  • Metadata / Lineage: OpenMetadata, DataHub,...
  • Access Control / Audit: Apache Ranger, Open Policy Agent

Đây cũng là lý do một số commercial platform như Databricks có lợi thế nhất định: nhiều bài toán integration giữa các layer đã được xử lý sẵn ở cấp độ sản phẩm.

Authentication

Với nhiều open-source components ghép lại với nhau, câu hỏi đầu tiên luôn là: user login bằng gì, và mọi thành phần có cùng tin vào một identity source hay không.

Với một tools đơn lẻ, authentication thường khá đơn giản: local user, LDAP, OAuth2 hoặc SSO. Nhưng khi nhiều thành phần, bài toán thay đổi hoàn toàn. Người dùng không chỉ login vào một UI mà cần di chuyển xuyên suốt giữa notebook, scheduler, query engine, catalog và storage. Khi đó, các vấn đề thực tế bắt đầu xuất hiện:

  • Login phân tán: người dùng phải sử dụng nhiều account hoặc nhiều credential cho các công cụ khác nhau.
  • Quản lý user phân mảnh: mỗi thành phần có cơ chế quản trị user riêng; việc thêm, sửa hoặc xoá user phải thao tác ở nhiều nơi.
  • Identity không nhất quán: cùng một người dùng nhưng xuất hiện dưới nhiều username hoặc principal khác nhau giữa các hệ thống.
  • Identity propagation khó khăn: làm sao để truyền end-user identity từ tool này sang tool khác, từ layer phía trên xuống layer phía dưới, nhưng vẫn đảm bảo an toàn và tránh impersonation hoặc privilege injection.
  • Audit thiếu tin cậy: khó xác định ai là người thực sự thực hiện hành động cuối cùng.

Nhiều open-source tools thực ra hỗ trợ authentication khá tốt khi đứng riêng lẻ. Tuy nhiên mỗi tool thường có user store, cơ chế quản trị và identity model riêng.

  • Apache Airflow, Apache Superset, JupyterHub thường lưu user trong internal database hoặc sync từ external identity provider, nhưng vẫn cần quản trị mapping và permission theo cách riêng của từng hệ thống.
  • Trino thường không quản lý user theo kiểu local application database, mà dựa vào external authentication như LDAP, OAuth2, certificates, JWT và username pattern mapping qua connector / access control rules.

Khi chỉ vận hành từng tool riêng lẻ, đây không phải vấn đề lớn. Nhưng khi ghép nhiều thành phần thành một data platform thống nhất, việc onboarding user, đồng bộ role và duy trì cùng một identity xuyên suốt giữa các layer bắt đầu trở nên phức tạp hơn rất nhiều.

Spark Authentication

Apache Spark có hỗ trợ một số security primitives như Kerberos integration, internal RPC authentication, TLS và ACL controls. Tuy nhiên trong nhiều môi trường data platform hiện đại, các cơ chế này chưa đủ để giải quyết bài toán end-user authentication theo cách nhất quán.

Vấn đề nằm ở chỗ Spark thường không tự quản lý end-user identity từ đầu đến cuối, mà nhận identity từ môi trường chạy xung quanh. Ví dụ Spark driver có thể chạy dưới OS user hiện tại, dùng Kerberos ticket đã được cấp sẵn, service account của cluster hoặc credential được inject từ bên ngoài. Spark chủ yếu sử dụng identity đã có, thay vì tự xác thực người dùng như một identity provider độc lập.

Trong mô hình interactive phổ biến như notebook hoặc unmanaged client, người dùng thường tự gọi SparkSession.builder() để tạo session. Khi đó phía client có thể thay đổi nhiều tham số quan trọng như catalog endpoint, warehouse path, Hadoop credentials, IAM profile, token path hoặc Spark configs liên quan tới security. Nếu không có lớp kiểm soát bên ngoài, trust boundary sẽ nằm khá nhiều ở phía client runtime.

Trong các open-source stacks, nếu dùng JupyterHub hoặc shared notebook environments, vẫn có một số cách triển khai thực tế để giảm bớt rủi ro về identity và session control.

  • Pre-provisioned Spark Session: Một cách tớ từng triển khai là không để user tự gọi SparkSession.builder() từ đầu, mà dùng init script và IPython kernel / cell hook để khởi tạo sẵn Spark session ngay khi notebook server hoặc kernel start lên. Khi vào notebook, user chỉ cần sử dụng trực tiếp biến spark đã được provision sẵn.

    Session này được cấu hình trước về catalog endpoint, warehouse path, default namespace, credential source và identity context của user hiện tại.

    Phần identity có thể được resolve theo hai hướng:

    • OS user mapping từ JupyterHub: single-user notebook server được spawn dưới đúng OS user tương ứng, từ đó dùng identity này để map xuống Spark principal hoặc runtime context.
    • JupyterHub token flow: lấy token của user trong notebook runtime, thực hiện OAuth2 / token introspection với JupyterHub để resolve lại end-user identity (thông qua custom extension của Spark hoặc wrapper logic trước khi khởi tạo Spark Session).

    Sau khi identity được verify, platform sẽ inject identity và các config liên quan ngay trong quá trình khởi tạo session (thông qua init script, kernel startup hook hoặc wrapper quanh SparkSession builder). Điều này đảm bảo mọi Spark session đều được tạo ra với đúng identity context, thay vì để user tự cấu hình.

    Cách tiếp cận này giúp chuẩn hoá toàn bộ quá trình bootstrap Spark session, giảm khả năng user override các tham số nhạy cảm và đảm bảo identity được gắn nhất quán từ đầu vào runtime.

  • Controlled Session Bootstrap Layer: Một hướng khác là cung cấp internal Python library hoặc custom notebook extension. Thay vì cho user tạo session thủ công, platform cung cấp interface chung để request session. Lớp này có thể control map user sang runtime principal phù hợp, inject config chuẩn rồi mới trả về Spark session đã được kiểm soát.

Tuy nhiên cần nói rõ: đây vẫn chủ yếu là cơ chế giảm rủi ro và chuẩn hoá usage model. Nếu người dùng còn toàn quyền với Python runtime, shell access hoặc khả năng tự thay đổi client config, thì trust boundary cuối cùng vẫn nằm ở phía client. Vì vậy mô hình này giúp tốt hơn nhiều, nhưng chưa phải lời giải triệt để cho multi-tenant authentication ở mức platform.

Đây cũng là lý do một số commercial platform như Databricks có lợi thế nhất định: họ không chỉ cung cấp Spark engine, mà chủ động kiểm soát quá trình bootstrap runtime ngay từ khi cluster được bring up. Với Databricks, khi cluster được provision, Spark driver và execution session thường đã được khởi tạo sẵn như một phần của managed runtime. Điều này khác với mô hình unmanaged client, nơi user tự gọi SparkSession.builder() và trực tiếp quyết định cách session được tạo ra.

Code của user trong notebook thường không trực tiếp sở hữu quá trình tạo Spark session mới, mà chủ yếu tương tác với Spark driver JVM hoặc session đã tồn tại thông qua execution layer của platform, chẳng hạn Spark Connect hoặc custom notebook REPL / command bridge nội bộ.

Mô hình này cho phép platform kiểm soát tốt hơn việc gắn user identity, cấp credential tạm thời, audit command execution, enforce governance policy và chuẩn hoá session lifecycle trước khi workload thực sự chạy trên cluster runtime.

Identity Propagation: Superset to Trino

Một ví dụ khá điển hình trong data platform là luồng truy cập từ BI layer xuống query engine, chẳng hạn từ Superset xuống Trino.

Người dùng có thể login thành công vào Superset bằng SSO, OAuth2 hoặc LDAP. Tuy nhiên điều đó chưa đồng nghĩa với việc end-user identity được giữ nguyên khi query thực sự chạy xuống Trino. Trong nhiều triển khai mặc định, Superset kết nối tới Trino thông qua một shared connection hoặc technical service account được cấu hình sẵn trong SQLAlchemy connection string. Khi đó mọi truy vấn từ nhiều người dùng khác nhau đều đi xuống Trino dưới cùng một principal.

Về mặt trải nghiệm, người dùng vẫn thấy mình đã login vào Superset. Nhưng ở tầng query engine, identity gốc gần như biến mất. Điều này tạo ra nhiều vấn đề thực tế:

  • Trino không biết người dùng cuối thực sự là ai để enforce schema-level, catalog-level hoặc row-level policy chính xác
  • Audit logs chỉ ghi nhận service account thay vì end-user thực sự đã chạy query
  • Rate limit, resource group, quota hoặc workload governance theo từng user trở nên khó áp dụng
  • Least privilege gần như chỉ tồn tại ở UI layer, không còn xuyên suốt xuống engine layer

Về bản chất, login ở BI layer nhưng không propagate identity xuống compute/query layer thì authentication mới chỉ giải quyết được phần giao diện, chưa giải quyết được quyền truy cập thực sự.

Hiện tại trong ecosystem open-source, bài toán propagate identity từ Superset xuống Trino vẫn chưa có một giải pháp chính thức và production-ready. Một số hướng đang được thảo luận (ví dụ pull request - tính đến 29/04/2026 vẫn đang open), nhưng chưa phải là cơ chế ổn định để sử dụng rộng rãi.

Trong thực tế, một workaround có thể được dùng là:

  • Cho phép Trino expose HTTP endpoint (insecure)
  • Không yêu cầu password hoặc secret ở connection
  • Cho phép Superset truyền username xuống như một phần của request

Superset truyền username xuống Trino ở chế độ impersonation

Khi đó Trino có thể nhận username từ Superset và coi đó là end-user identity để áp policy hoặc ghi log.

Tuy nhiên cách làm này có vấn đề rất lớn:

  • Bất kỳ ai có quyền chỉnh sửa connection trong Superset đều có thể giả mạo user khác
  • Không có cơ chế xác thực (password/token/signature) để verify identity
  • Trust boundary bị đẩy hoàn toàn về phía Superset
  • Có thể dẫn tới privilege escalation nếu policy phía dưới dựa vào username này

Nói cách khác, cách này chỉ phù hợp trong môi trường hoàn toàn trusted, nhưng không phù hợp với multi-tenant hoặc production systems yêu cầu security cao. Hơn nữa, khi không có cơ chế xác thực đáng tin cậy, việc mapping user giữa Superset và Trino không còn đơn thuần là truyền username, mà phụ thuộc vào việc hai hệ thống có đang sử dụng cùng một nguồn identity hay không.

Nếu không có một identity source chung (SSO / IdP) và cơ chế đồng bộ user giữa các công cụ, thì cùng một người dùng có thể tồn tại dưới nhiều username hoặc principal khác nhau. Khi đó việc propagate identity gần như không còn ý nghĩa, vì phía dưới cũng không thể xác định đó có thực sự là cùng một user hay không.

Đây là một ví dụ rõ ràng cho thấy: nếu không có identity propagation đáng tin cậy, thì việc login ở BI layer gần như không mang lại nhiều giá trị ở tầng query engine.

Unified Identity Source

Từ các vấn đề trên, một yêu cầu gần như bắt buộc trong data platform là phải có một centralized identity source (SSO / IdP) đóng vai trò source of truth cho việc xác thực user. Tuy nhiên, chỉ sử dụng SSO là chưa đủ.

SSO chủ yếu giải quyết bài toán authentication, nhưng data platform vẫn cần một user management layer phía trên để xử lý các vấn đề đặc thù:

  • Chuẩn hoá identity model: mapping user về một principal format thống nhất xuyên suốt các hệ thống, thay vì mỗi tool sử dụng một username hoặc identity riêng.
  • Quản lý context ở cấp platform: kiểm soát role, group, ownership và quyền truy cập tool ở một nơi duy nhất, thay vì cấu hình rời rạc trên từng hệ thống.
  • Đồng bộ user lifecycle: đảm bảo onboarding, deprovision và thay đổi role được propagate nhất quán giữa các công cụ.
  • Kiểm soát identity trước khi propagate: mọi identity cần được chuẩn hoá và verify trước khi đi xuống query engine, catalog hoặc storage layer.

Nếu không có lớp này, mỗi tool vẫn sẽ tự quản lý user theo cách riêng, và identity từ SSO khi đi xuống các layer phía dưới vẫn có thể bị phân mảnh hoặc không còn nhất quán.

Trong thực tế, lớp user management này có thể được triển khai theo nhiều cách:

  • Đồng bộ trực tiếp từ IdP (LDAP / AD / OIDC claims) vào các tool
  • Xây internal metadata / identity service để quản lý user và quyền ở cấp platform

Điểm quan trọng không phải là công nghệ cụ thể, mà là việc platform có một nơi duy nhất chịu trách nhiệm chuẩn hoá và quản lý identity trước khi nó được sử dụng bởi các thành phần khác.

Service Identity (Machine-to-Machine Authentication)

Trong các platform nội bộ, bài toán này thường đơn giản hơn về mặt vận hành, do toàn bộ hệ thống được platform team kiểm soát (deployment, CI/CD, policy). Trong một số trường hợp, việc sử dụng credential cấu hình sẵn vẫn có thể chấp nhận được nếu được giới hạn phạm vi và không lộ ra user layer.

Tuy nhiên, vẫn cần tránh hardcode credential một cách tuỳ tiện và giữ được khả năng kiểm soát. Một số cách triển khai thường dùng:

  • Sử dụng service account của Kubernetes để cho phép các workload như Apache Airflow submit Spark application (qua Spark Operator), thay vì nhúng credential trực tiếp trong code.
  • Cấu hình một identity riêng cho service ở các hệ thống như Trino (ví dụ basic auth user hoặc JWT), và coi đây như service account cho workload hệ thống.

Các cách này giúp tách biệt giữa end-user identity và system identity, đồng thời giảm rủi ro so với việc hardcode credential trực tiếp.

Dù vậy, đây vẫn là workaround phù hợp với môi trường nội bộ. Với các hệ thống yêu cầu bảo mật cao hơn hoặc multi-tenant, bài toán service identity trở nên phức tạp hơn đáng kể. Về mặt nguyên tắc, hướng tiếp cận thường sẽ là sử dụng short-lived credential, token-based authentication hoặc centralized identity management cho service, thay vì credential tĩnh.

Tuy nhiên trong thực tế, việc triển khai những cơ chế này một cách nhất quán giữa nhiều thành phần (scheduler, compute, storage, query engine) không hề đơn giản, và phụ thuộc khá nhiều vào hạ tầng cũng như mức độ kiểm soát của platform.

Đây cũng là một phần mà tớ chưa có một cách tiếp cận hoàn chỉnh, dù hiểu rõ mục tiêu cần đạt được và cũng là nơi các managed platform có lợi thế rõ rệt. Ví dụ với Databricks, thay vì tự xây dựng cơ chế service identity từ đầu, platform tận dụng trực tiếp IAM của cloud provider:

  • Trên Azure: sử dụng service principal hoặc managed identity
  • Trên AWS: sử dụng IAM role và instance profile
  • Trên GCP: sử dụng service account

Các cơ chế này đã cung cấp sẵn identity, credential lifecycle (rotation, expiration), cũng như integration với storage và các dịch vụ khác trong cùng hệ sinh thái. Nhờ đó, việc gán identity cho workload (job, cluster, query) trở nên nhất quán hơn, và không cần tự triển khai toàn bộ identity flow giữa các thành phần như trong open-source stack.

Ngược lại, trong môi trường open-source, khi không có một IAM layer thống nhất phía dưới, platform team thường phải tự xây hoặc ghép nhiều cơ chế khác nhau để đạt được hành vi tương tự, khiến bài toán service identity trở nên khó hơn đáng kể.

Authorization

Khi đã chấp nhận các trade-off ở phía authentication, hoặc triển khai được một phương án phù hợp, bước tiếp theo cần quan tâm là authorization.

Trong data platform, authorization không chỉ dừng lại ở việc xác định ai có thể truy cập vào công cụ nào hoặc dataset nào, mà còn bao gồm cách dữ liệu được kiểm soát và sử dụng ở mức chi tiết hơn:

  • Tagging và classification: phân loại dữ liệu (PII, sensitive, internal, public,…) để làm cơ sở cho policy
  • Masking và filtering: ẩn hoặc biến đổi dữ liệu theo user hoặc context (column masking, row-level filtering, dynamic views,…)
  • Policy definition và enforcement: xác định rule truy cập dựa trên user, role, group hoặc attribute, và đảm bảo các rule này được áp dụng nhất quán

Nói cách khác, authorization trong data platform không chỉ là “có được truy cập hay không”, mà là “truy cập được tới đâu và nhìn thấy dữ liệu ở mức nào”.

Ở cấp độ từng công cụ riêng lẻ, hầu hết các hệ thống đều đã có cơ chế authorization riêng:

  • Apache Airflow, Apache Superset: thường sử dụng RBAC dựa trên database nội bộ, quản lý role và permission ở mức application
  • JupyterHub: có cơ chế phân quyền cơ bản (user, group, admin), nhưng tương đối hạn chế nếu cần kiểm soát chi tiết ở mức data access
  • Trino: hỗ trợ nhiều mô hình khác nhau như file-based access control, hoặc tích hợp với các hệ thống bên ngoài như Open Policy Agent (OPA) hoặc Apache Ranger

Khi vận hành độc lập, các cơ chế này thường đủ dùng. Tuy nhiên khi ghép lại thành một data platform, mỗi tool lại có một model authorization riêng, dẫn tới việc policy bị phân tán và khó duy trì nhất quán.

Điều này quay lại vấn đề ở phần authentication: nếu identity không được chuẩn hoá và propagate đúng cách, thì ngay cả khi từng tool có cơ chế authorization riêng, việc enforce policy xuyên suốt giữa các layer vẫn rất khó đạt được.

Ranger integration for Trino

Với Trino, từ các phiên bản gần đây (khoảng 475+), hệ thống đã hỗ trợ tích hợp native với Apache Ranger thông qua plugin chính thức. Việc triển khai tương đối đơn giản so với các giải pháp tự xây trước đây: cấu hình plugin, kết nối tới Ranger admin và định nghĩa policy trực tiếp trên Ranger UI.

Ở mức độ authorization, Ranger cung cấp một mô hình khá đầy đủ: phân quyền theo catalog, schema, table, column, kết hợp với row-level filtering và masking. Điều này giúp tập trung hoá policy và tách logic authorization ra khỏi từng query engine riêng lẻ.

Tuy nhiên, trong thực tế vận hành vẫn có một số điểm cần cân nhắc:

  • User / group sync: Trino và Ranger không chia sẻ cùng một user store mặc định. Nếu identity không được đồng bộ từ một nguồn chung (IdP), việc mapping user giữa hai hệ thống có thể không nhất quán.
  • Tag / metadata sync: Nếu sử dụng tag-based policy, cần có cơ chế đồng bộ metadata hoặc classification từ catalog (ví dụ Hive Metastore, Iceberg,…) sang Ranger để policy hoạt động đúng.
  • UX khi define policy: Trong nhiều trường hợp, việc cấu hình user hoặc resource trong Ranger vẫn mang tính free-text (username, schema, table pattern), thiếu sự liên kết trực tiếp với entity thực tế trong Trino. Điều này dễ dẫn tới sai sót và gây khó khăn khi quản lý policy ở quy mô lớn.

Nhìn chung, Ranger integration với Trino đã giúp giải quyết khá tốt bài toán centralized authorization ở query layer. Tuy nhiên để đạt được trải nghiệm vận hành tốt, vẫn cần đầu tư thêm vào việc đồng bộ identity và metadata giữa các hệ thống.

Custom Extension for JupyterHub

Mặc dù cơ chế authorization native của JupyterHub tương đối cơ bản, hệ thống này vẫn cung cấp một số extension points đủ linh hoạt để xây thêm lớp kiểm soát ở cấp platform.

JupyterHub hỗ trợ khái niệm services và có thể đóng vai trò như một OAuth2 provider. Điều này cho phép các thành phần bên ngoài hoặc các extension nội bộ thực hiện authentication và lấy thông tin identity trực tiếp từ JupyterHub.

Dựa trên đó, một số hướng triển khai thực tế có thể bao gồm:

  • Mở rộng Spawner: kế thừa và custom hoá Spawner để kiểm soát cách notebook server được khởi tạo, bao gồm việc inject environment variables, credential hoặc secret tương ứng với từng user, group hoặc profile.
  • Profile-based isolation: sử dụng profile (resource / environment preset) và tuỳ chỉnh lại UI chọn profile để giới hạn user chỉ nhìn thấy và sử dụng những profile được phép.
  • Custom admin / management layer: xây thêm interface cho admin để quản lý mapping giữa user, profile, resource, thay vì phụ thuộc hoàn toàn vào cấu hình tĩnh.
  • Inject runtime context: kết hợp với cơ chế init script hoặc kernel hook để inject identity, config và credential vào runtime ngay khi notebook start.

Các hướng này cho phép platform kiểm soát tốt hơn việc user có thể chạy gì, trên environment nào và với identity nào, thay vì để toàn bộ quyền kiểm soát nằm ở phía notebook runtime.

Tuy nhiên, tương tự các cơ chế phía trên, đây vẫn là lớp kiểm soát ở access layer. Nếu người dùng có quyền truy cập sâu hơn (shell access, custom container, bypass spawn flow), thì trust boundary cuối cùng vẫn cần được đảm bảo ở các layer phía dưới như compute hoặc storage.

Ranger integration for Spark

Ngoài việc kiểm soát ở notebook layer, có thể kết hợp với cơ chế pre-init Spark session ở trên để đẩy enforcement xuống sâu hơn ở compute layer thông qua integration với Apache Ranger.

Spark không có native integration với Ranger giống như Trino, tuy nhiên có thể sử dụng extension (ví dụ extension từ Apache Kyuubi) để tích hợp với Ranger vào Spark runtime:

Mặc dù tài liệu chưa thực sự rõ ràng, trong thực tế đã có thể triển khai và áp dụng policy của Ranger lên nhiều dạng workload khác nhau:

  • Spark SQL
  • DataFrame API
  • RDD-based operations

Đặc biệt, khi sử dụng cùng catalog (Hive Metastore / Iceberg catalog), policy từ Ranger có thể được enforce xuyên suốt tới metadata và data layer:

  • Kiểm soát truy cập ở mức catalog / schema / table / column
  • Áp dụng masking hoặc filtering thông qua Spark SQL
  • Tác động trực tiếp tới các table Iceberg thay vì chỉ dừng ở query layer

Điều này giúp đảm bảo authorization không chỉ tồn tại ở phía Spark API, mà còn gắn chặt với metadata và table format phía dưới.

Tuy nhiên, các vấn đề gặp phải vẫn tương tự như với Trino:

  • Cần đảm bảo authentication và identity propagation được thiết lập đúng từ đầu, nếu không policy sẽ không áp dụng chính xác
  • User / group mapping giữa Spark runtime và Ranger cần được đồng bộ
  • Metadata hoặc tag-based policy cần có cơ chế sync từ catalog

Nhìn chung, việc tích hợp Ranger với Spark giúp đưa policy enforcement xuống gần data layer hơn, nhưng vẫn phụ thuộc mạnh vào việc platform có kiểm soát được identity flow xuyên suốt hay không.

Authorization in LakeHouse environment

Từ góc nhìn cá nhân, authorization trong Lakehouse không nên được triển khai tại một điểm duy nhất, mà cần được enforce ở nhiều layer khác nhau, bao gồm:

  • Catalog layer: quản lý metadata, schema, table, column và policy ở mức logical
  • Query / compute engine: enforce access trong quá trình query execution
  • Storage layer: kiểm soát truy cập trực tiếp tới data (object / file level)

Quan trọng hơn, các layer này không thể hoạt động độc lập, mà cần được đồng nhất về identity và policy xuyên suốt.

Trong open-source ecosystem hiện tại, từng layer đều có thể giải quyết một phần bài toán:

  • Catalog: một số hệ thống mới như Gravitino, Polaris bắt đầu hỗ trợ policy ở metadata layer
  • Engine: có thể integrate với Apache Ranger hoặc OPA để enforce ở query layer
  • Storage: thường phải dựa vào cơ chế cấp credential (IAM, token, key) và kiểm soát permission ở mức object storage

Tuy nhiên, các cơ chế này phần lớn vẫn rời rạc và thiếu một control plane thống nhất. Việc kết hợp chúng thường mang tính “vá víu”: giảm bớt rủi ro, nhưng khó đảm bảo policy được enforce một cách nhất quán trong mọi trường hợp.

Ví dụ, nếu Spark có thể truy cập trực tiếp tới storage mà không đi qua catalog hoặc query engine, toàn bộ policy ở các layer phía trên có thể bị bypass. Khi đó, authorization ở các layer này gần như mất hiệu lực.

Điều này cho thấy bài toán authorization trong Lakehouse không chỉ là “có policy”, mà là đảm bảo policy được enforce tại những điểm mà người dùng không thể bypass. Đây cũng là nơi các managed platform có lợi thế: họ kiểm soát được toàn bộ execution path, từ identity tới data access, thay vì phải ghép nhiều cơ chế rời rạc lại với nhau.

Runtime Consistency

Sau khi giải quyết bài toán identity và authorization, một vấn đề khác thường xuất hiện trong data platform là sự không nhất quán về runtime giữa các môi trường khác nhau.

Trong thực tế, cùng một đoạn code có thể chạy trong notebook, scheduler hoặc production job, nhưng mỗi môi trường lại có:

  • Version thư viện khác nhau (Spark, Python, connector,…)
  • Dependency khác nhau (jar, wheel, native library,…)
  • Config khác nhau (catalog, credential, endpoint,…)

Điều này dẫn tới các vấn đề rất quen thuộc:

  • Code chạy được trong notebook nhưng fail khi chạy qua Airflow
  • Query chạy được trên Spark nhưng không chạy được trên Trino
  • Data read/write hành vi khác nhau do version hoặc config mismatch

Khi platform scale lên, việc đảm bảo một runtime nhất quán giữa các environment trở nên quan trọng không kém gì authentication hay authorization.

Shared Artifacts & Transparency Configuration

Một cách tiếp cận thực tế để giảm sự khác biệt về runtime giữa các môi trường là sử dụng shared storage như một “source of truth” cho code và dependency.

Ví dụ, có thể mount object storage (S3, ADLS,…) vào các môi trường khác nhau (notebook, scheduler, compute cluster) dưới dạng local filesystem. Khi đó:

  • Code (ETL script, notebook, job definition)
  • JAR (connector, Iceberg, custom library,…)
  • Python package (wheel, zip, dependency bundle,…)

có thể được load từ cùng một đường dẫn logic, thay vì mỗi môi trường tự quản lý riêng.

Cách tiếp cận này giúp:

  • Giảm sự khác biệt giữa notebook, Airflow job và production runtime
  • Đảm bảo các job sử dụng cùng version của dependency
  • Đơn giản hoá việc distribute artifact giữa các môi trường

Ngoài việc đồng nhất code và dependency, platform cũng cần một giải pháp nữa để giảm sự khác biệt giữa các môi trường là chuẩn hoá cách cấu hình và vận hành job thông qua platform layer.

Ví dụ, có thể xây dựng custom plugin cho Apache Airflow để cung cấp một abstraction layer phía trên workflow definition. Thay vì để user trực tiếp cấu hình DAG, connection hoặc parameter một cách thủ công, plugin này có thể:

  • Cung cấp UI hoặc template để tạo job theo chuẩn chung của platform
  • Áp dụng sẵn các config mặc định (catalog, credential source, cluster config,…)
  • Validate input và giới hạn các option được phép sử dụng
  • Tự động inject dependency, parameter hoặc runtime context cần thiết

Cách tiếp cận này giúp:

  • Giảm sai sót do cấu hình thủ công (human error)
  • Đảm bảo các job tuân theo cùng một execution model
  • Tăng tính nhất quán giữa notebook, scheduler và production job

Tương tự, ở phía interactive environment, các custom extension trên JupyterHub (đã đề cập ở phần trước) cũng có thể đóng vai trò như một abstraction layer cho runtime. Thay vì để user tự cấu hình Spark Session, dependency hoặc credential trong từng notebook, platform có thể:

  • Pre-provision sẵn runtime (Spark session, config, credential)
  • Giới hạn các option cấu hình thông qua profile hoặc extension
  • Inject identity và dependency ngay từ khi kernel start

Điều này giúp user không cần cấu hình thủ công trên từng session, đồng thời đảm bảo các notebook tuân theo cùng một runtime model đã được kiểm soát.

Về bản chất, cả Airflow plugin và JupyterHub extension đều thực hiện cùng một việc: chuyển phần cấu hình phức tạp từ phía user xuống platform layer, từ đó giảm variability giữa các environment và tăng tính nhất quán khi thực thi workload. Thay vì để mỗi user “tự assemble” runtime và config, platform cung cấp một interface chuẩn hoá để định nghĩa workload, từ đó kiểm soát tốt hơn toàn bộ lifecycle của job.

Containerized Runtime

Việc mount storage, transparent configuration với users không giải quyết hoàn toàn vấn đề runtime consistency:

  • Vẫn cần quản lý version và compatibility của dependency
  • Không kiểm soát được environment (Python version, Spark runtime, OS,…)
  • Có thể phát sinh race condition hoặc inconsistency nếu artifact bị thay đổi mà không có versioning rõ ràng

Thay vì cố gắng đồng bộ code, dependency và environment giữa các môi trường khác nhau, platform có thể tận dụng container như một đơn vị triển khai chuẩn:

  • Mỗi workload được thực thi trong một container image đã được build sẵn
  • Image này bao gồm toàn bộ runtime environment (OS, Python version, system library, Spark distribution, connector,…)
  • Notebook, scheduler và production job đều sử dụng cùng một hoặc một tập image đã được chuẩn hoá

Cách tiếp cận này giúp:

  • Đảm bảo environment consistency ngay từ đầu, thay vì xử lý sai lệch sau khi xảy ra
  • Giảm phụ thuộc vào việc cấu hình runtime thủ công ở từng môi trường
  • Cho phép version hoá và rollback toàn bộ runtime thông qua image

Trong mô hình này, các cơ chế như shared storage (phân phối code và dependency) hoặc platform abstraction (Airflow plugin, JupyterHub extension) đóng vai trò bổ trợ:

  • Shared artifact giúp đồng bộ “cái gì được chạy”
  • Platform layer giúp kiểm soát “cách chạy”
  • Container đảm bảo “chạy trong môi trường nào”

Tuy nhiên, containerization cũng không thay thế hoàn toàn các layer phía trên:

  • Vẫn cần kiểm soát configuration và identity injection trong runtime
  • Vẫn cần chuẩn hoá cách workload được định nghĩa và thực thi
  • Vẫn cần quản lý lifecycle và version của image

Do đó, container nên được xem là nền tảng cho runtime consistency, trên đó các lớp abstraction và control của platform được xây dựng để đảm bảo hệ thống vận hành nhất quán ở quy mô lớn.

Governance Automation & Observability

Sau khi đã thiết lập identity, authorization và runtime consistency, bước tiếp theo là đảm bảo các cơ chế này tiếp tục hoạt động một cách nhất quán khi platform phát triển theo thời gian.

Trong thực tế, việc phụ thuộc vào cấu hình thủ công hoặc quy trình vận hành riêng lẻ dễ dẫn tới sai sót, đặc biệt khi số lượng user, workload và data ngày càng tăng. Những sai lệch nhỏ trong cấu hình hoặc thiếu đồng bộ giữa các hệ thống có thể tích luỹ và trở thành vấn đề lớn.

Do đó, platform cần các lớp automation để giảm thiểu human error và đảm bảo governance luôn được enforce một cách ổn định, đồng thời có thể quan sát và kiểm tra được.

Một số hướng triển khai phổ biến bao gồm:

  • Storage-level protection (SSE / encryption): sử dụng các cơ chế như SSE-S3, SSE-KMS hoặc tương đương để đảm bảo dữ liệu luôn được mã hoá ở mức storage. Việc kiểm soát access tới key (KMS) gián tiếp trở thành một phần của authorization.
  • Automatic lineage collection: trong các hệ thống hiện đại có thể được tự động hoá thông qua integration trực tiếp ở query engine và compute layer, thay vì phụ thuộc vào việc user khai báo thủ công. Cả Trino và Spark đều hỗ trợ tích hợp với các chuẩn như OpenLineage thông qua plugin hoặc extension, cho phép tự động capture thông tin về query execution, input/output dataset và dependency giữa các bảng. Trong môi trường Lakehouse, đặc biệt với các open table format như Iceberg, lineage trở nên nhất quán hơn nhờ metadata được chuẩn hoá ở mức table và catalog. Thông tin lineage sau đó có thể được đẩy về các hệ thống metadata như OpenMetadata hoặc DataHub để theo dõi data flow và dependency toàn platform mà không cần user maintain thủ công.
  • Attribute-based access control (ABAC) & data-level enforcement: tận dụng ABAC kết hợp với tagging để định nghĩa policy linh hoạt hơn, đồng thời áp dụng masking và row-level filtering (ví dụ qua Apache Ranger) nhằm kiểm soát dữ liệu ở mức chi tiết theo user hoặc context.
  • Centralized audit logging: sử dụng các hệ thống như Apache Ranger hoặc audit log từ engine để ghi nhận đầy đủ hành vi truy cập dữ liệu (ai truy cập gì, ở đâu, khi nào). Đây là cơ sở cho compliance, debugging và incident investigation.

Các cơ chế này thường hoạt động ở nhiều layer khác nhau (engine, storage, catalog), nhưng điểm quan trọng là chúng cần được thiết kế theo hướng tự động và nhất quán, thay vì phụ thuộc vào hành vi của người dùng.

Về bản chất, governance không chỉ là việc định nghĩa policy, mà là khả năng:

  • Quan sát được hệ thống đang hoạt động như thế nào
  • Phát hiện sai lệch hoặc misuse
  • Và đảm bảo các rule vẫn được enforce khi system thay đổi

Nếu không có các lớp automation này, những gì được thiết kế ở phần authentication, authorization và runtime rất dễ bị “drift” theo thời gian khi số lượng user, workload và data tăng lên. Đây cũng là một trong những khác biệt lớn giữa một hệ thống “có thể chạy được” và một platform có thể vận hành ổn định ở quy mô lớn.

My Summary

Trong ecosystem open-source, hầu hết các thành phần đều đã giải quyết tốt một phần bài toán riêng lẻ (authentication, authorization, processing, storage,…). Tuy nhiên, khi kết hợp lại thành một platform hoàn chỉnh, vẫn tồn tại nhiều khoảng trống: identity không nhất quán, policy không xuyên suốt, runtime không đồng bộ và governance khó duy trì theo thời gian.

Các giải pháp ở từng layer chủ yếu mang tính local optimization: giúp giảm rủi ro, chuẩn hoá usage hoặc cải thiện trải nghiệm cục bộ. Điều này giúp hệ thống “vận hành được tốt hơn”, nhưng chưa thực sự giải quyết triệt để bài toán integration ở cấp độ platform.

Vì vậy, ở góc nhìn platform, hướng đi hợp lý là xây dần một lớp control plane đứng phía trên toàn bộ stack hiện tại, từng bước wrapping các thành phần phía dưới thay vì thay mới toàn bộ ngay từ đầu.

Thứ tự triển khai thực tế có thể đi theo các phase sau:

  • Phase 1 - Centralize identity & user management: xây một user management layer tập trung, dùng chung một IdP/SSO làm source of truth, đồng bộ user/group/role và chuẩn hoá principal format giữa các công cụ.
  • Phase 2 - Catalog & governance control plane: xây lớp UI hợp nhất (wrap OpenMetadata + Ranger) để người dùng thực hiện discovery, tagging/classification và phân quyền tại một điểm quản trị thống nhất. Quan trọng là UI này nên tối ưu trải nghiệm select structured (user/group dropdown, catalog/schema/table picker, tag selector,...) thay vì freetext pattern như Ranger, giảm sai sót và dễ scale hơn. Đặc biệt, lớp này cần khắc phục những hạn chế khi phụ thuộc vào user-sync hay tag-sync của Ranger (yếu, không real-time), bằng cách quản lý riêng việc đồng bộ user/group từ IdP vào UI, và sync metadata/tag từ catalog (OpenMetadata, Iceberg metadata) theo cách không phụ thuộc vào Ranger, đảm bảo dữ liệu luôn fresh và consistent.
  • Phase 3 - Unified productivity & execution layer: mở rộng control plane ra các bề mặt làm việc chính như query UI (nối Trino), notebook UI (wrap Jupyter) và orchestration UI (wrap Airflow) để chuẩn hoá trải nghiệm runtime và workflow end-to-end.

Khi đi theo lộ trình này, platform sẽ dần chuyển từ mô hình “nhiều công cụ ghép lại” sang mô hình “một sản phẩm data platform thống nhất”, và về dài hạn có thể tiến gần hơn tới trải nghiệm kiểu Databricks.

Đây cũng là điểm khác biệt cốt lõi giữa việc “triển khai một tập hợp công cụ” và “xây dựng một data platform”: không chỉ chọn đúng thành phần, mà phải chủ động sở hữu cách chúng được tích hợp và vận hành như một hệ thống thống nhất.

On this page