🚀 Giới Thiệu Chuỗi Bài Viết: Khai Phóng Siêu Năng Quan Sát Ứng Dụng ML trên Kubernetes! Chào mừng bạn đến với chuỗi bài viết "Bản Kế Hoạch Quan Sát (Observability Blueprint): Instrument, Deploy, Observe – OpenTelemetry Hành Động Trên Kubernetes"! Nghe tên đã thấy "ngầu" rồi phải không? Ở đây, chúng ta sẽ cùng nhau biến một ứng dụng Machine Learning (ML) bé nhỏ trở thành một "siêu anh hùng" thực thụ, có khả năng tự kể chuyện về mọi thứ đang diễn ra bên trong nó. Làm sao ư? Bằng cách trang bị cho nó những "siêu năng lực" về quan sát (observability) qua metrics, logs và traces, sử dụng toàn bộ công cụ mã nguồn mở xịn sò như OpenTelemetry, Prometheus, Jaeger, Loki và Grafana. Sẵn sàng chưa? Trong series này, chúng ta sẽ cùng nhau: * 🤖 Xây dựng một ứng dụng ML siêu đơn giản bằng FastAPI. * 📦 "Trang bị cảm biến" (instrument) cho nó bằng OpenTelemetry – đây là bước biến nó thành "siêu nhân" đó! * 🐳 Đóng gói ứng dụng vào Docker – để nó có thể đi bất cứ đâu. * ☸️ Triển khai ứng dụng lên Kubernetes – sân chơi của những ứng dụng "khủng" hơn. * 🔭 Thu thập và hình ảnh hóa dữ liệu quan sát như một chuyên gia – để chúng ta biết được "siêu nhân" của mình đang làm gì! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qe21puhqkmfu1mbr8nzn.png' alt='Mô hình tổng quan Observability với OpenTelemetry và Kubernetes'> 🎯 Mục tiêu cuối cùng của chúng ta là gì? Là biến một ứng dụng ML (kiểu "sản phẩm thật") trở nên "trong suốt" từ mọi góc độ, và tuyệt vời hơn nữa là tất cả đều bằng công cụ mã nguồn mở! Nghe đã thấy "phê" rồi đúng không? --- ### Phần 1: Xây Dựng Ứng Dụng ML Đơn Giản Với FastAPI Bắt đầu thôi! Ở phần đầu tiên này, chúng ta sẽ xây dựng nền tảng: một ứng dụng ML nho nhỏ để dự đoán giá nhà. Nghe có vẻ phức tạp nhưng đảm bảo sẽ đơn giản hết sức! 🧰 **Những thứ cần có trước khi "triển":** Trước khi chúng ta bắt đầu cuộc phiêu lưu này, hãy chắc chắn bạn đã chuẩn bị những thứ sau nhé: * Python 3.8+ (hoặc phiên bản mới hơn) * `pip` (công cụ quản lý thư viện của Python, chắc là bạn đã có rồi!) * Một chút xíu kiến thức cơ bản về Python và API kiểu REST (đừng lo, chúng ta sẽ giữ mọi thứ thật thân thiện và dễ hiểu!) <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/your_toolkit.png' alt='Hình ảnh một bộ công cụ lập trình'> 🔧 **Bước 0: Thiết Lập Môi Trường Ảo – Người Bạn Thân Của Lập Trình Viên "Sạch Sẽ"** Bạn có muốn project của mình bị lẫn lộn với các thư viện linh tinh khác trên máy không? Chắc chắn là không rồi! Đó là lý do tại sao chúng ta cần môi trường ảo (virtual environment). Nó giống như việc tạo ra một "hộp cát" riêng biệt cho mỗi dự án Python vậy, đảm bảo các thư viện không bị "đụng độ" nhau. Cực kỳ gọn gàng và chuyên nghiệp! Hãy chạy các lệnh sau trong terminal của bạn: ```bash python3 -m venv venv source venv/bin/activate # Trên Windows thì dùng: venv\Scripts\activate pip3 install --upgrade pip ``` Tiếp theo, chúng ta cần một danh sách các "nguyên liệu" (thư viện) mà ứng dụng của chúng ta sẽ cần. Hãy tạo một file tên là `requirements.txt` và điền vào đó các dòng sau: ``` fastapi==0.110.0 pydantic==1.10.14 uvicorn==0.29.0 scikit-learn==1.4.2 numpy==1.26.4 ``` Sau đó, chỉ cần một lệnh thần thánh để "nấu" tất cả các nguyên liệu này vào môi trường ảo của bạn: ```bash pip3 install -r requirements.txt ``` Thế là xong phần chuẩn bị rồi! Giờ thì bắt đầu vào việc chính nào! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/virtual_env.png' alt='Môi trường ảo Python'> 🧠 **Bước 1: Machine Learning Là Gì? Và Tại Sao Lại Là Hồi Quy Tuyến Tính (Linear Regression)?** Machine Learning (ML) – nghe có vẻ "hàn lâm" đúng không? Thực ra, nó chỉ là "nghệ thuật" (và cả khoa học nữa) dạy cho máy tính cách học hỏi các mẫu (patterns) từ dữ liệu, thay vì chúng ta phải viết ra từng dòng quy tắc một. Giống như bạn dạy một đứa trẻ nhận biết con mèo vậy, thay vì phải mô tả "nó có 4 chân, lông, kêu meo meo...", bạn chỉ cần cho nó xem hàng ngàn bức ảnh mèo là nó tự động nhận ra! Trong phần này, chúng ta sẽ dùng "Hồi quy tuyến tính" (Linear Regression) – một mô hình ML cơ bản nhất, kiểu như "Hello World" trong thế giới ML vậy. Mô hình này giả định rằng có một mối quan hệ "đường thẳng" giữa dữ liệu đầu vào và đầu ra. Đơn giản mà nói, chúng ta sẽ dùng nó để dự đoán giá nhà: nhà càng to thì giá càng cao. Nghe có vẻ hiển nhiên đúng không? Nhưng đây lại là bước khởi đầu cực kỳ quan trọng đó! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9im0yte98sz7v9qht7oi.png' alt='Minh họa Hồi quy Tuyến tính'> Giờ thì cùng xem đoạn code huấn luyện mô hình của chúng ta nhé. Đừng lo, nó khá đơn giản thôi: ```python # train_model.py import numpy as np # Thư viện tính toán số học mạnh mẽ import pickle # Dùng để lưu trữ (serialize) đối tượng Python from sklearn.linear_model import LinearRegression # Mô hình hồi quy tuyến tính từ thư viện scikit-learn # Dữ liệu mẫu: kích thước nhà (tính bằng feet vuông) và giá tương ứng (đô la) # Chúng ta chỉ có vài điểm dữ liệu để minh họa thôi nhé! # X: Kích thước nhà (đầu vào) # y: Giá nhà (đầu ra) X = np.array([[500], [1000], [1500], [2000], [2500]]) y = np.array([100000, 150000, 200000, 250000, 300000]) # Khởi tạo mô hình Hồi quy tuyến tính model = LinearRegression() # Huấn luyện mô hình của chúng ta với dữ liệu đã cho # Mô hình sẽ "học" cách tìm ra mối quan hệ đường thẳng giữa X và y model.fit(X, y) # Sau khi huấn luyện, chúng ta sẽ lưu mô hình lại vào một file # File .pkl (pickle) này chứa "bộ não" của mô hình, để chúng ta có thể dùng nó sau này mà không cần huấn luyện lại with open("house_price_model.pkl", "wb") as f: pickle.dump(model, f) print("Model đã được huấn luyện và lưu với tên 'house_price_model.pkl'") ``` Khi bạn chạy đoạn code này, nó sẽ tạo ra một file `.pkl`. Đây là một phiên bản "đóng gói" của mô hình chúng ta vừa huấn luyện, và chúng ta sẽ tải nó vào ứng dụng API của mình sau này. Thật tiện lợi phải không? ⚡ **Bước 2: Phục Vụ Mô Hình Qua FastAPI – Biến Ứng Dụng Thành API "Ngon Bổ Rẻ"** Giờ chúng ta đã có mô hình, làm sao để "cho cả thế giới" dùng nó đây? Đó là lúc FastAPI ra tay! 📦 **Tại sao lại là FastAPI?** FastAPI là một framework web cực kỳ hiện đại của Python, được sinh ra để phục vụ các API. Nó nổi bật nhờ: * **Tốc độ:** Nhanh như điện xẹt (nhờ Starlette và Pydantic)! * **Trải nghiệm phát triển đỉnh cao:** Viết code mà cứ như đang chơi vậy. * **Tự động tạo tài liệu API:** Bạn không cần phải tốn công viết docs nữa, FastAPI lo tất! Nó giống như Flask, nhưng có thêm tính năng gợi ý kiểu dữ liệu (type hints) và tài liệu Swagger (OpenAPI) có sẵn. Một sự kết hợp hoàn hảo! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/fastapi_logo.png' alt='Logo FastAPI'> Cùng bắt tay vào viết ứng dụng API của chúng ta nào. Hãy tạo một file tên là `app.py`: ```python # app.py from fastapi import FastAPI, Request # Import FastAPI để xây dựng API và Request để truy cập thông tin yêu cầu from pydantic import BaseModel # Dùng để định nghĩa cấu trúc dữ liệu cho request và response import pickle # Để tải mô hình đã lưu import numpy as np # Để xử lý dữ liệu đầu vào cho mô hình # -------------------------- # Thiết Lập Ứng Dụng FastAPI # -------------------------- app = FastAPI() # Khởi tạo ứng dụng FastAPI của chúng ta # Tải mô hình đã được huấn luyện # Mở file 'house_price_model.pkl' và tải mô hình vào biến 'model' with open("house_price_model.pkl", "rb") as f: model = pickle.load(f) # -------------------------- # Endpoint Kiểm Tra Tình Trạng (Healthcheck) # -------------------------- # Khi ai đó truy cập vào địa chỉ gốc của API (ví dụ: http://localhost:8000/) @app.get("/") def read_root(request: Request): # Chúng ta sẽ trả về một thông báo để biết API đang hoạt động return {"message": "House Price Prediction API của chúng ta đang hoạt động rồi!"} # -------------------------- # Định Nghĩa Cấu Trúc Dữ Liệu Cho Request (Schema) # -------------------------- # Đây là cách chúng ta quy định dữ liệu mà client sẽ gửi lên khi gọi API dự đoán # Ví dụ: {"features": [1500]} class HouseFeatures(BaseModel): features: list[float] # Chúng ta mong đợi một danh sách các số thực (ví dụ: kích thước nhà) # -------------------------- # Endpoint Dự Đoán Giá Nhà # -------------------------- # Khi ai đó gửi yêu cầu POST đến /predict/ @app.post("/predict/") def predict(data: HouseFeatures, request: Request): # Lấy dữ liệu kích thước nhà từ request # np.array(data.features).reshape(1, -1) giúp chuyển đổi danh sách thành định dạng mà mô hình mong muốn prediction = model.predict(np.array(data.features).reshape(1, -1)) # In ra dự đoán (cho mục đích debug, sau này sẽ thay bằng logger xịn hơn) # logger.info(f"Prediction made: {prediction[0]}") # Phần này có lẽ nên để sau khi tích hợp logging # Trả về kết quả dự đoán cho client return {"predicted_price": prediction[0]} ``` ▶️ **Chạy thử nào!** Mở terminal và chạy lệnh sau (nhớ là đang trong môi trường ảo nhé!): ```bash uvicorn app:app --reload ``` Lệnh này sẽ khởi động server của chúng ta. `app:app` có nghĩa là tìm biến `app` trong file `app.py`. `--reload` giúp server tự động khởi động lại mỗi khi bạn thay đổi code – tiện lợi cực kỳ! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/terminal_run.png' alt='Chạy uvicorn trong terminal'> 🧪 **Kiểm tra ngay thôi!** Mở một terminal khác (hoặc dùng Postman/Insomnia) để gửi request đến API của chúng ta: * **Kiểm tra trạng thái API (Healthcheck):** ```bash curl -i 'http://127.0.0.1:8000/' ``` Bạn sẽ thấy một tin nhắn báo hiệu API đang hoạt động! * **Dự đoán giá nhà:** Gửi một yêu cầu POST đến `/predict/` với dữ liệu kích thước nhà: ```bash curl -i -X POST 'http://127.0.0.1:8000/predict/' \ -H "Content-Type: application/json" \ -d '{"features": [1500]}' ``` 💡 **Và đây là kết quả thần kỳ:** ```json { "predicted_price": 200000.0 } ``` Chúc mừng! Bạn vừa tự tay xây dựng và triển khai thành công mô hình ML đầu tiên của mình dưới dạng một API rồi đó! Một bước tiến lớn đúng không nào? 🎉 <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/api_success.png' alt='API dự đoán giá nhà hoạt động thành công'> ✅ **Tiếp theo là gì?** Trong Phần 2, chúng ta sẽ "đóng gói" ứng dụng này vào Docker để nó sẵn sàng cho "chuyến du hành" ra thế giới bên ngoài. Và đặc biệt hơn, chúng ta sẽ bắt đầu suy nghĩ về việc "trang bị cảm biến" cho ứng dụng (instrumenting) – vì sao ư? Vì một API mà không có các chỉ số quan sát để khoe thì còn gì là thú vị nữa chứ! Hẹn gặp lại bạn ở phần tiếp theo!
Chào bạn! Bạn có còn nhớ cái thời mà chúng ta thường mò mẫm theo từng bước hướng dẫn chi tiết không? Hay lắm, hôm nay chúng ta sẽ cùng "hồi sinh" lại tinh thần đó nhé! Bạn sẽ được hướng dẫn tỉ mỉ cách cấu hình tính sẵn sàng cao (High Availability – HA) cho OpenTelemetry Collector. Mục tiêu ư? Đơn giản là để bạn không bao giờ mất một mẩu dữ liệu đo lường (telemetry) nào, kể cả khi hệ thống gặp sự cố nút (node failures), đang nâng cấp cuốn chiếu (rolling upgrades) hay bỗng dưng lưu lượng tăng vọt (traffic spikes). Bài viết này sẽ đi sâu vào cả ví dụ trên Docker lẫn Kubernetes, kèm theo các demo cấu hình thực tế luôn, tha hồ mà "nhúng tay" vào code!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXf662Py8qqh7bAFVVDw65mJrq9Bac4lSLQ_uI8ZWE68xhy5_a473HtVInTitqdm2b4RuwGzncwNQnyVTHQQotGWMgfYKqMUquwGu7IrlFrwM-OO3qYFmapJaPwz6ILNfXKL7vXkgQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Hướng dẫn cấu hình HA cho OpenTelemetry Collector'>Nhưng trước hết, để cùng tần số với nhau, chúng ta hãy đặt ra một vài nền tảng cơ bản đã nhé! Vậy, 'tính sẵn sàng cao' (HA) với OpenTelemetry Collector được định nghĩa thế nào đây? Đơn giản là bạn muốn đảm bảo rằng việc thu thập và xử lý dữ liệu đo lường vẫn hoạt động trơn tru, ngay cả khi một vài 'anh bạn' Collector riêng lẻ gặp sự cố. Nghe có vẻ phức tạp, nhưng về cơ bản, nó xoay quanh ba điểm cốt yếu sau: * Tránh mất dữ liệu khi đang gửi đến một backend giám sát bị 'chết giấc' đột ngột. * Đảm bảo dữ liệu đo lường được thu thập liên tục trong suốt quá trình nâng cấp hệ thống hoặc khi có sự cố hạ tầng. * Cho phép mở rộng ngang (horizontal scalability) để cân bằng tải cho các dấu vết (traces), nhật ký (logs) và số liệu (metrics). Để đạt được tính sẵn sàng cao, chúng ta nên áp dụng mô hình triển khai Agent-Gateway. Nghe có vẻ 'ngầu' đúng không? Nhưng tóm lại, nó hoạt động thế này: * **Agent Collectors:** Những 'chú lính chì' này chạy trên mỗi máy chủ, container, hoặc nút (node) của bạn. Chúng giống như những người thu gom rác chuyên nghiệp, thu thập dữ liệu ngay tại nguồn. * **Gateway Collectors:** Đây là những 'trung tâm điều khiển' tập trung, có khả năng mở rộng. Chúng nhận dữ liệu đo lường từ các Agent Collectors và thực hiện các tác vụ xử lý phức tạp hơn. Mỗi 'tầng' này đều có thể được mở rộng độc lập và theo chiều ngang, giống như bạn có thể thêm bao nhiêu 'người thu gom rác' hoặc 'trung tâm xử lý' tùy thích vậy.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXf-s6Pyq4XLsbHr4xnJ9_lIYX22sn2FFfgPn6N-nt1vn6WpX-krQhmlESURE2Q4_GX9B92-j85IQyF-NDJa0ymu2bzVd3SkZskX8gepv3gkWvljQvk0PzUPS2Epou8Ey4B_rbViQA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Mô hình triển khai Agent-Gateway của OpenTelemetry Collector'>À mà này, có một điểm cực kỳ thú vị bạn cần nhớ đó là: thực chất, **Agent Collector và Gateway Collector về cơ bản là cùng một file nhị phân (binary)!** Chúng giống hệt nhau, không khác gì nhau. Điểm khác biệt DUY NHẤT nằm ở **VỊ TRÍ CHÚNG CHẠY** mà thôi. Bạn cứ hình dung thế này nhé: * **Agent Collector:** 'Anh bạn' này chạy sát sườn với workload của bạn. Trong môi trường Kubernetes, nó có thể là một sidecar hay một deployment cho từng namespace; còn với Docker, nó sẽ là một service chạy ngay cạnh ứng dụng của bạn trong file `docker-compose.yaml`. Thường thì đội phát triển (dev team) sẽ chịu trách nhiệm quản lý các instance Collector này. * **Gateway Collector:** Đây là một 'phiên bản' Collector hoạt động độc lập, tập trung – bạn có thể nghĩ đến một Collector chạy riêng trong một namespace cụ thể hoặc thậm chí là cả một cụm Kubernetes riêng biệt. 'Anh bạn' này thường thuộc quyền sở hữu của đội vận hành nền tảng (platform team). Đây là bước cuối cùng trong đường ống xử lý dữ liệu đo lường, cho phép đội platform áp dụng các chính sách như lọc nhật ký, lấy mẫu dấu vết (sampling traces) hay loại bỏ các số liệu không cần thiết, trước khi gửi chúng đến hệ thống backend giám sát. Nếu muốn tìm hiểu sâu hơn, bạn có thể xem giải thích siêu hay ho trên StackOverflow này nhé: <a href="https://stackoverflow.com/questions/73802116/gateway-vs-agent-opentelemetry-collector-deployment-on-kubernetes">StackOverflow</a>. Đúng vậy, StackOverflow vẫn còn 'hot' lắm nha. Không phải mọi thứ đều do AI giải thích đâu! 😂Để đạt được tính sẵn sàng cao một cách toàn diện, tôi sẽ hướng dẫn bạn cách cấu hình các yếu tố sau: * **Nhiều Instance Collector:** Mỗi instance đều có khả năng xử lý toàn bộ khối lượng công việc, kèm theo bộ nhớ dự phòng để đệm dữ liệu tạm thời. * **Bộ Cân Bằng Tải (Load Balancer):** Nó sẽ phân phối dữ liệu đo lường đến và đảm bảo định tuyến nhất quán. Bộ cân bằng tải còn hỗ trợ tự động chuyển đổi dự phòng (failover) nếu một collector bị 'ngủm củ tỏi'. * **Bộ nhớ Chia Sẻ (Shared Storage):** Bộ nhớ bền vững để lưu trữ trạng thái và quản lý cấu hình của collector. Giờ thì, đã đến lúc chúng ta cùng 'xắn tay áo' và bắt đầu thực hành một chút code nào! <h3>Cấu hình Agent-Gateway Tính sẵn sàng cao (HA) với OpenTelemetry Collector</h3> Đầu tiên, hãy để tôi giải thích khái niệm này bằng cách sử dụng Docker và hình dung nó qua Bindplane. Kiến trúc này hoàn toàn có thể áp dụng và sử dụng cho bất kỳ loại thiết lập máy ảo Linux hoặc Windows nào. Về Kubernetes, chúng ta sẽ khám phá chi tiết hơn ở phần dưới nhé.Bạn có ba lựa chọn chính để triển khai: Một là sử dụng bộ cân bằng tải 'ngoại' như Nginx hoặc Traefik. Hai là dùng <code>loadbalancing exporter</code> tích hợp sẵn trong chính Collector. Cuối cùng, nếu bạn là 'fan cứng' của môi trường container, hãy tận dụng tính năng cân bằng tải 'chuẩn bản địa' trong Kubernetes. <h3>Nginx Load Balancer</h3> Lựa chọn Nginx thường là giải pháp đơn giản và 'ăn liền' nhất. Tôi sẽ thiết lập kiến trúc với các thành phần sau: * Ba Gateway Collectors chạy song song * Một bộ cân bằng tải Nginx * Một Agent Collector được cấu hình để tạo ra dữ liệu đo lường (mô phỏng ứng dụng)<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeezt57L7eebMutLMwC9s5aGItPDE2nl0LZncw3M4XqPJZPuWz6ZgQG3pC9VRgmhTReTzkbdg9fVX34KaAGAqbKfoMkb09sPcx0-0hBlhw76GKwOB8fOFQMEqPDFC1CN1MbR00-Ng?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Kiến trúc HA OpenTelemetry Collector với Nginx'>Cấu trúc này là mức tối thiểu 'cơ bản nhất' mà bạn sẽ cần. Lưu ý rằng bạn sẽ phải sử dụng ba dịch vụ riêng biệt cho các gateway collector. Lý do ư? Đơn giản là vì mỗi collector cần có đường dẫn <code>file_storage</code> riêng để lưu trữ dữ liệu trong hàng đợi bền vững (persistent queue). Trong Docker, điều này có nghĩa là bạn phải đảm bảo mỗi container có một volume duy nhất. Để tôi giải thích cách nó hoạt động nhé. Hãy copy nội dung dưới đây vào file <code>docker-compose.yaml</code> của bạn:<pre><code>version: '3.8' volumes: gw1-storage: gw2-storage: gw3-storage: telgen-storage: external-gw-storage: services: gw1: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: gw1 hostname: gw1 command: ["--config=/etc/otel/config/config.yaml"] volumes: - ./config:/etc/otel/config - gw1-storage:/etc/otel/storage environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "<secret>" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/gw1-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml gw2: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: gw2 hostname: gw2 command: ["--config=/etc/otel/config/config.yaml"] volumes: - ./config:/etc/otel/config - gw2-storage:/etc/otel/storage environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "<secret>" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/gw2-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml gw3: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: gw3 hostname: gw3 command: ["--config=/etc/otel/config/config.yaml"] volumes: - ./config:/etc/otel/config - gw3-storage:/etc/otel/storage environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "<secret>" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/gw3-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml otlp-lb: image: nginx:1.25-alpine volumes: - ./nginx-otlp.conf:/etc/nginx/nginx.conf:ro ports: - "4317:4317" - "4318:4318" depends_on: [gw1, gw2, gw3] telgen: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: telgen hostname: telgen command: ["--config=/etc/otel/config/config.yaml"] volumes: - ./config:/etc/otel/config - telgen-storage:/etc/otel/storage environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "<secret>" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/telgen-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml external-gw: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: external-gw hostname: external-gw command: ["--config=/etc/otel/config/external-gw-config.yaml"] volumes: - ./config:/etc/otel/config - external-gw-storage:/etc/otel/storage environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "<secret>" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/external-gw-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/external-gw-config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml </code></pre>Bây giờ, hãy mở giao diện Bindplane của bạn và nhấp vào nút 'Install Agent'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXfn_YKusSoaliryk41rAU1fVA_JrjeWe7MFy7FwQ6EyG7N85Rqz9zHVB2Ug2KNQoo51nzUj1qQ61n-xWTABRnm24WbhxCfkFF0fSAeT5TbCRBBrkU8BhjdASCtG2k_Zu3uoUVHYZg?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Nút Install Agent trong Bindplane'>Chọn nền tảng là Linux (vì tôi đang demo với Docker mà), rồi nhấn 'Next'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXctzthy2_5kGf-A_w4JOpLjmLQ6XptbtW_TM3aBS7nLwtSt6h-8lsoovOPQRo8Bm7sXwRWI7cYDBEvm0IuGEjll7MOKeGe4Npj2IVH9Ku6TscaPZUP8eGop6NcXrUsA800mQLO_qw?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Chọn nền tảng Linux trong Bindplane'>Màn hình này sẽ hiển thị các biến môi trường mà bạn cần thay thế trong file <code>docker-compose.yaml</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcZm5gnqAVnJUmqFrHoWyNmFnPh51GThiaYwLtyawqdumxhJMi5fz91JcJbQUYUERZnIJeiHFBFyitXqP0RqE5M6bYd-a8beF6ktnScI-Dp9-OiVVbj5VOHE0d6OyjIVsI8nrUZMw?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Biến môi trường cần thiết trong Bindplane'>Hãy thay thế <code>OPAMP_SECRET_KEY</code> bằng khóa bí mật của riêng bạn từ Bindplane nhé. Nếu bạn đang dùng phiên bản Bindplane tự host, thì cũng nhớ thay <code>OPAMP_ENDPOINT</code> cho đúng địa chỉ của mình luôn. Bạn cứ lấy giá trị nằm sau <code>-e</code> (cho endpoint) và <code>-s</code> (cho secret) là được. Sau đó, hãy tạo một file <code>nginx-otlp.conf</code> cho bộ cân bằng tải nhé.<pre><code>worker_processes auto; events { worker_connections 1024; } stream { upstream otlp_grpc { server gw1:4317 max_fails=3 fail_timeout=15s; server gw2:4317 max_fails=3 fail_timeout=15s; server gw3:4317 max_fails=3 fail_timeout=15s; } server { listen 4317; proxy_pass otlp_grpc; proxy_connect_timeout 1s; proxy_timeout 30s; } } http { upstream otlp_http { server gw1:4318 max_fails=3 fail_timeout=15s; server gw2:4318 max_fails=3 fail_timeout=15s; server gw3:4318 max_fails=3 fail_timeout=15s; } server { listen 4318; location / { proxy_pass http://otlp_http; proxy_next_upstream error timeout http_502 http_503 http_504; } } } </code></pre>Hãy tạo một thư mục <code>./config</code> trong cùng thư mục gốc với file <code>docker-compose.yaml</code> của bạn, và bên trong đó, tạo thêm 3 file sau: * <code>config.yaml</code> * <code>telgen-config.yaml</code> * <code>logging.yaml</code> Dán cấu hình cơ bản này vào <code>config.yaml</code> và <code>telgen-config.yaml</code> để BDOT Collector có một cấu hình khởi tạo. Sau đó, tôi sẽ tiếp tục cấu hình nó bằng Bindplane. <pre><code>receivers: nop: processors: batch: exporters: nop: service: pipelines: metrics: receivers: [nop] processors: [batch] exporters: [nop] telemetry: metrics: level: none </code></pre> Và đây là cấu hình cơ bản cho <code>logging.yaml</code>: <pre><code>output: stdout level: info </code></pre> Giờ thì, khởi động các dịch vụ Docker Compose thôi! <code>docker compose up -d</code> Sau khi mọi thứ đã 'lên sóng', hãy nhảy vào Bindplane và tạo ba cấu hình cho: * <code>telgen</code> * <code>otlp-lb-gw</code> * <code>external-gw</code><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXesZDlHq7y4MGpIUpMtch8b0GXf8Pm57aFw_v2eMLebZJpPcTHav13oM7pcQBogYN15lNF5cYVWtdSCLZ3TzVAGcJALCIDojVCb6UNhgEufFZUZw036QL6jVJF9AjI9_DQVNDUlyg?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Tạo các cấu hình trong Bindplane'>Cấu hình <code>telgen</code> sẽ có một nguồn 'Telemetry Generator'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXetxAFjijBCHrqudqo6Nx5ptFQs9K6wUgCShmP4u6PC_pS8JoNDVN41o0VdLI27EzA50DPQhFNL0K5v5wmKdNbKanHG8x5-hMwTImgcZui3euEss-UXfWJPtHdLuRBmSmDzgRM6_w?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Telemetry Generator source cho telgen'>Và một đích đến OTLP.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXfTUPjp5dK7prmmnhAP1UzjWcTrXbIeET2W7fknXcKGhPfcTrUKrJJM74bEfuGrAXIWsQAYxF6f_zX5COWorrAROU30fxl0de3URQfvS_d83CwBoKWVa05LSmq6jx3s7hWlhD-qbw?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP destination cho telgen'>Đích đến OTLP được cấu hình để gửi dữ liệu đo lường đến hostname <code>otlp-lb</code>, đây chính là hostname của bộ cân bằng tải Nginx mà tôi đang chạy trong Docker Compose. Tiếp theo, cấu hình <code>otlp-lb-gw</code> có một nguồn OTLP lắng nghe trên <code>0.0.0.0</code> và các cổng <code>4317</code> và <code>4318</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeJ-mtywLKRCOh45XFSbxLcDMSift4tKlICOYdqNNLs52hQllKcQ6EjUn6CQeeLGAkH-CdH3BcXaqyVye6R5eGmU1s_SFEwmW5AAkExU2_7yHDbqrIBVMTwy3xwx-i55Cf0FIBa_g?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP source cho otlp-lb-gw'>Đích đến cũng là OTLP, nhưng thay vào đó lại gửi tới hostname <code>external-gw</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXek5G8ftb-qCfzHBaxRjLAEwAwoROqQkSPjlaqTIrpdVkUuDmmJi16Ln45Q5p7Pk-H8Dwhqn1JIkeyXaNjKvYlYxGUb9k8ghV2OaI-MY1yFtoD1K3SBu_uMGKvPm3avOIKXAu_9TQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP destination gửi đến external-gw'>Cuối cùng, cấu hình <code>external-gw</code> cũng sử dụng một nguồn OTLP y hệt.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcs84JO2gQO32TEBNpY2bgtjLz3cFvVk5Gb4bnCNichKm60_Ahl9pW2L06bpRfRZrTNb-gsinFovNdU5r2d0HfLthvwZ_4iqGbgLbsJ05qMVUCQw64dMQVeKQlz5f6t6PWnrvcMdA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP source cho external-gw'>Và một đích đến 'Dev Null'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXdjW_PsGUOZ4sbgV3wpjGTDpQmrCSWZv-EcrkMB8hijFBPIIGEBDX4FTNogQBp8WnYgc1tVNiUp5Ydq-94eDM_afidRMab2arkEnpgpzZzvhd_Bx9g_7g92wn49aNBgbjxGluL_ZQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Dev Null destination cho external-gw'>Với thiết lập này, bạn có thể thoải mái 'thả' bất kỳ đích đến nào bạn muốn vào danh sách các đích đến cho cấu hình <code>external-gw</code>. Cứ thỏa sức sáng tạo nhé! 😂 Nếu bạn mở node processor cho đích đến Dev Null, bạn sẽ thấy nhật ký đang 'ào ạt' chảy qua bộ cân bằng tải đó.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcH-2WUot1eiW-vQNfFo2954OlF2Snf_t-oIgXekc7XP19Xz8fAAMj2pgn2dIOFyocxWdxPu9SXlpuwOU3EwNEAmgeZGd7SDCDy7tJYoS6lmZFzkTW08Hhdxajf9abLxJTerj5gdQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Nhật ký chảy qua bộ cân bằng tải'>Còn trong cấu hình <code>otlp-lb-gw</code>, nếu bạn mở một node processor, bạn sẽ thấy tải được phân phối đều 'chằn chặn' cho cả ba collector.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeCNzO1TP_0_PaPjVKj8oZleRuC9WBpWjzfbIWjLEatrHWFicF9y17vEQH9OSL3qghk0FOtGy0NqHaT4_igIhC8DCZGYV0euocJ-cg16osje5Ukm1jf3mtioQ75_bI3FVFBBYw3pQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Tải được phân phối đều trên các collector'>Đó là cách bạn cân bằng tải dữ liệu đo lường trên nhiều collector bằng Nginx. Nếu bạn thích 'áp dụng' các cấu hình này qua Bindplane CLI, bạn có thể 'sắm' các file trên GitHub tại đây nhé: <a href="https://github.com/observIQ/high-availability-agent-gateway-opentelemetry-collector/tree/main/docker-nginx">GitHub</a>. <h3>Load Balancing Exporter</h3> Lựa chọn thứ hai là sử dụng <code>loadbalancing exporter</code> chuyên dụng có sẵn trong Collector. Với exporter này, bạn có thể chỉ định nhiều collector 'đích' (downstream collectors) mà sẽ nhận lưu lượng dữ liệu đo lường một cách đồng đều.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXfh5o14JJXuMeqNRD_kcN4PTqNJE7ghhgprm_xO_kUOz2wP5tOJqIg1E0bZJ7x3C6vUiLcHfUMqumrFxtyYZ8JLLe74f89zEhE9NnUKZxmB2AY71bhaIF6XZyFFK-dXqAhEseGlnA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Load Balancing Exporter trong OpenTelemetry Collector'>Một lưu ý nhỏ về <code>loadbalancing exporter</code> trước khi chúng ta đi sâu hơn nhé. Bạn không phải lúc nào cũng cần đến nó đâu. Nhiệm vụ chính của nó là đảm bảo các đoạn span (spans) từ cùng một dấu vết (trace) luôn đi cùng nhau và được định tuyến đến cùng một backend collector. Điều này cực kỳ hữu ích cho việc theo dõi phân tán (distributed tracing) khi có lấy mẫu (sampling). Nhưng nếu bạn chỉ gửi nhật ký và số liệu, hoặc thậm chí là dấu vết mà không cần các quy tắc lấy mẫu phức tạp, thì bạn hoàn toàn có thể bỏ qua nó và tiếp tục dùng Nginx. Tôi sẽ thiết lập kiến trúc y hệt như trên, nhưng thay vì Nginx load balancer, chúng ta sẽ dùng thêm một collector nữa: * Ba Gateway Collectors chạy song song * Một Gateway Collector sử dụng <code>loadbalancing exporter</code> * Một Agent Collector được cấu hình để tạo dữ liệu đo lường (mô phỏng ứng dụng) Thiết lập này hoạt động y hệt như một bộ cân bằng tải Nginx. Tuy nhiên, nó lại yêu cầu ít bước và ít công sức cấu hình hơn. Không cần phải cấu hình và chạy Nginx, không cần quản lý các file Nginx cụ thể; thay vào đó, bạn chỉ cần chạy thêm một instance collector và sử dụng file <code>config.yaml</code> quen thuộc của Collector là xong. Cách thay thế cho trường hợp sử dụng trên như sau: trong file <code>docker-compose.yaml</code>, bạn hãy thay thế dịch vụ Nginx <code>otlp-lb</code> bằng một collector khác có tên là <code>lb</code>.<pre><code>services: # ... lb: image: ghcr.io/observiq/bindplane-agent:1.79.2 container_name: lb hostname: lb command: ["--config=/etc/otel/config/lb-config.yaml"] volumes: - ./config:/etc/otel/config - lb-storage:/etc/otel/storage ports: - "4317:4317" - "4318:4318" environment: OPAMP_ENDPOINT: "wss://app.bindplane.com/v1/opamp" OPAMP_SECRET_KEY: "01JFJGVKWHQ1SPQVDGZEHVA995" OPAMP_LABELS: ephemeral=true MANAGER_YAML_PATH: /etc/otel/config/lb-manager.yaml CONFIG_YAML_PATH: /etc/otel/config/lb-config.yaml LOGGING_YAML_PATH: /etc/otel/config/logging.yaml depends_on: [gw1, gw2, gw3] # ... </code></pre>Tạo một file <code>lb-config.yaml</code> cơ bản cho instance collector này trong thư mục <code>./config</code>. Bindplane sẽ tự động cập nhật file này từ xa ngay khi bạn thêm đích đến cho <code>loadbalancing exporter</code>. <pre><code>receivers: nop: processors: batch: exporters: nop: service: pipelines: metrics: receivers: [nop] processors: [batch] exporters: [nop] telemetry: metrics: level: none </code></pre> Bây giờ thì, hãy 'khởi động lại' Docker Compose nhé! <code>docker compose down</code> <code>docker compose up -d</code> Lệnh này sẽ khởi chạy collector <code>lb</code> mới. Trong Bindplane, bạn hãy tạo một cấu hình mới tên là <code>lb</code> và thêm một nguồn OTLP lắng nghe trên <code>0.0.0.0</code> cùng các cổng <code>4317</code> và <code>4318</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXdBQz-SxaC45RNgV1n30jBgicpnF6cO4pQGtgzlFI3TCSkeyn_ZRt5qacCfYU022VHRAbTSHZcRhKFbwhafo2kzpwyd1GrDG36iKwwwoPqM7FcFVPxiBtNwlIgFgWbqhXVat5tt?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP source cho lb collector'>Bây giờ, hãy tạo một đích đến tùy chỉnh và dán cấu hình <code>loadbalancing exporter</code> vào trường nhập liệu. <pre><code>loadbalancing: protocol: otlp: tls: insecure: true timeout: 30s retry_on_failure: enabled: true initial_interval: 5s max_elapsed_time: 300s max_interval: 30s sending_queue: enabled: true num_consumers: 10 queue_size: 5000 resolver: static: hostnames: - gw1:4317 - gw2:4317 - gw3:4317 </code></pre> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeP2t78EAJW1Zh2MXOSfIlAV9vnEJ9ZBQpWxM2WUQ1H8ByS2FfiAzdRPETKNlr19MuwAmAYTHZY8SgQ5UFMqThlzgNj5g3XIDUq9quMuJ0gyG2oWZqXK7-jEyiPEg6yGGiXU896?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình loadbalancing exporter trong Bindplane'>Lưu ý rằng các hostname này tương ứng với hostname của các gateway collector đã cấu hình trong Docker Compose. Hãy lưu cấu hình này và triển khai nó cho collector <code>lb</code> mới. Mở cấu hình <code>gw</code> trong Bindplane và chọn một node processor, bạn sẽ thấy dữ liệu đo lường đang 'chảy' qua cả 3 instance gateway collector.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcOvZlU840MzOAcbg0GchCV0fy0CD976O_QiOQhqv1xYmq-4QlrTcCPnZDV17qTej88c3OSjGQ4hKvM__RtihOeTvYMNGy8xYVnFt0cDFq6bdYPHXhHKMJKzcLrURcL4jXu9-Ry-A?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Telemetry flowing through gateway collectors'>Bạn sẽ thấy sự phân chia thậm chí còn rõ ràng hơn khi xem thông lượng dữ liệu đo lường trên tất cả các collector trong phần 'Agents'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcoeOjEISGNLoX_mfhZUqMhow_igfUG0_MsdPMrJGvflzCy3RJ-BUMe--3OG6pc_qDHkXK6noM_eKURWZIscZF4K1DVpdyr6Ge4Rd3QJMEnIQJZE3O8nIX0O6WdKeChh2-X9G862Q?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Thông lượng telemetry trong Agents view'>Collector <code>lb</code> và <code>external-gw</code> đang báo cáo thông lượng như nhau, với ba gateway collector cân bằng tải lưu lượng một cách đồng đều. <code>loadbalancing exporter</code> đang hoạt động như một sự thay thế 'ngon lành' cho Nginx. Tôi gọi đó là một 'chiến thắng' vang dội! Ít công sức cấu hình hơn, ít thành phần 'nhảy nhót' hơn, và không cần phải học cấu hình Nginx cụ thể. Thay vào đó, bạn chỉ cần tập trung vào mỗi collector thôi. Để nhanh chóng khởi chạy ví dụ này, bạn có thể áp dụng các cấu hình này qua Bindplane CLI, tải các file trên GitHub tại đây: <a href="https://github.com/observIQ/high-availability-agent-gateway-opentelemetry-collector/tree/main/docker-loadbalancing-exporter">GitHub</a>. Vì giờ bạn đã nắm khá vững cách cấu hình hạ tầng OpenTelemetry Collector cho tính sẵn sàng cao, hãy cùng đi sâu hơn vào chi tiết về khả năng phục hồi nhé! <h3>Xây Dựng Khả Năng Phục Hồi Cho Collector Của Bạn</h3> Khi nói đến khả năng phục hồi (resilience), các tính năng như logic thử lại (retry logic), hàng đợi bền vững (persistent queues) và gom nhóm (batching) nên được xử lý ở các Agent Collectors. Đây là những instance nằm gần nhất với workload của bạn; chúng dễ bị mất dữ liệu nhất nếu có gì đó 'trục trặc'. Nhiệm vụ của Agent là thu thập, đệm và chuyển tiếp dữ liệu đo lường một cách đáng tin cậy, ngay cả khi backend bị 'chập chờn' hoặc chậm chạp. Đây là cách bạn cấu hình OpenTelemetry Collector để có khả năng phục hồi, tránh mất dữ liệu đo lường khi gặp sự cố mạng hoặc mất kết nối với backend dữ liệu đo lường: * **Gom nhóm (Batching):** Gom các tín hiệu lại trước khi xuất, giúp cải thiện hiệu quả. * **Thử lại (Retry):** Đảm bảo các tác vụ xuất dữ liệu thất bại sẽ được thử lại. Đối với các workload quan trọng, bạn nên tăng <code>max_elapsed_time</code> để chịu được thời gian gián đoạn dài hơn—nhưng hãy nhớ rằng điều này sẽ làm tăng kích thước bộ đệm trên đĩa. * **Hàng đợi bền vững (Persistent Queue):** Lưu trữ các tác vụ thử lại trên đĩa, bảo vệ dữ liệu khỏi bị mất nếu Collector bị 'sập nguồn'. Bạn có thể cấu hình: * <code>Number of consumers</code> – số lượng worker thử lại chạy song song. * <code>Queue size</code> – số lượng gói (batches) được lưu trữ. * <code>Persistence</code> – cho phép đệm dữ liệu trên đĩa để tăng độ tin cậy. <h3>Thử lại (Retry) & Hàng đợi bền vững (Persistent Queue)</h3> May mắn thay, Bindplane đã xử lý cả cơ chế thử lại và hàng đợi bền vững 'ngoài hộp' (out of the box) cho các OTLP exporter rồi. Hãy cùng xem cấu hình <code>telgen</code>. Đây là collector mà chúng ta đang chạy ở chế độ agent, mô phỏng một lượng lớn lưu lượng dữ liệu đo lường. Trong file <code>telgen-config.yaml</code>, bạn sẽ thấy OTLP exporter được cấu hình với cả hàng đợi bền vững và tính năng thử lại.<pre><code>exporters: otlp/lb: compression: gzip endpoint: gw:4317 retry_on_failure: enabled: true initial_interval: 5s max_elapsed_time: 300s max_interval: 30s sending_queue: enabled: true num_consumers: 10 queue_size: 5000 storage: file_storage/lb timeout: 30s tls: insecure: true </code></pre> Điều này là do các cài đặt nâng cao cho mọi OTLP exporter trong Bindplane đã có sẵn cấu hình mặc định này.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXc72tjin5I68b0vGQXZbmGRQDYmA8IQ8qbwyObWuhlSpZgTuaVwkI1RyJIETVERnW6bEl-zmUMKhxR4oHI7ix3TXBONpS2lkWU33T3fxCVosTWeQGD8Fi4hysux2UhCdcsfsq_qSw?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cài đặt nâng cao của OTLP exporter trong Bindplane'>Thư mục hàng đợi bền vững ở đây chính là thư mục <code>storage</code> mà chúng ta đã cấu hình bằng cách tạo một volume trong Docker. Sau đó, Bindplane sẽ tự động cấu hình một extension lưu trữ trong config và kích hoạt nó như thế này: <pre><code># docker-compose.yaml ... volumes: gw1-storage: gw2-storage: gw3-storage: telgen-storage: lb-storage: external-gw-storage: ... </code></pre> <pre><code># telgen-config.yaml ... extensions: file_storage/lb: compaction: directory: ${OIQ_OTEL_COLLECTOR_HOME}/storage on_rebound: true directory: ${OIQ_OTEL_COLLECTOR_HOME}/storage service: extensions: - file_storage/lb ... </code></pre> Giờ đây, đường ống dữ liệu đo lường của bạn đã trở nên kiên cường (resilient) và sẵn sàng HA, với khả năng duy trì dữ liệu để sống sót qua các lần khởi động lại, bộ đệm hàng đợi bền vững để xử lý các sự cố tạm thời, và khả năng phục hồi chuyển đổi dự phòng để ngăn ngừa mất dữ liệu. <h3>Gom nhóm (Batching)</h3> Gom nhóm lại là một câu chuyện khác, bởi vì bạn cần thêm một processor vào node processor để kích hoạt nó trước khi kết nối với đích đến. Các collector ở chế độ agent nên gom nhóm dữ liệu đo lường trước khi gửi đến gateway collector. OTLP receiver ở phía gateway sẽ nhận các gói đã gom nhóm và chuyển tiếp chúng đến backend dữ liệu đo lường mà bạn chọn. Trong cấu hình <code>telgen</code>, hãy nhấp vào một node processor và thêm một batch processor.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXf6emSTjc9thPdpN3y5yIdXT5M7YeRx53riuSzRYdQE7G0u5f42uY0FiVV8A9Yasl08L5z2JRXTGsPo9qdqCAoG0hgasHO82xhYyYQsh5nJWwz0PLeuiEmuKWkaF_jmWBkqIX5eaA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Thêm batch processor trong telgen configuration'>Cấu hình này sẽ gửi một gói tín hiệu dữ liệu đo lường mỗi 200ms bất kể kích thước. Hoặc, nó sẽ gửi một gói có kích thước 8192 bất kể thời gian chờ. Áp dụng processor này trong Bindplane sẽ tạo ra một cấu hình trông như thế này: <pre><code># telgen-config.yaml ... processors: batch/lb: null batch/lb-0__processor0: send_batch_max_size: 0 send_batch_size: 8192 timeout: 200ms ... </code></pre> <h3>Cân bằng tải tự nhiên trong Kubernetes với HorizontalPodAutoscaler</h3> Cuối cùng, sau tất cả những phân tích, giải thích và sơ đồ, đã đến lúc tôi cho bạn thấy trông nó sẽ thế nào khi áp dụng vào thực tế với một ví dụ Kubernetes đơn giản. Việc sử dụng Kubernetes là kiến trúc được đội Bindplane và cộng đồng OpenTelemetry khuyến nghị. Kubernetes sẽ giúp bạn tối đa hóa lợi ích khi dùng Bindplane đó! Tôi sẽ thiết lập kiến trúc với các thành phần sau: * Một Collector ở chế độ Agent chạy trên mỗi node của cụm K8s, được cấu hình để tạo dữ liệu đo lường (mô phỏng ứng dụng). * Một Deployment của Gateway Collector sử dụng HorizontalPodAutoscaler để mở rộng từ 2 đến 10 pod. * Một dịch vụ ClusterIP. * Được cấu hình với bộ nhớ bền vững (persistent storage), hàng đợi gửi (sending queue) và cơ chế thử lại (retry). * Một Gateway Collector bên ngoài chạy trên một cụm khác, hoạt động như một backend dữ liệu đo lường giả lập. May mắn là việc lấy tất cả các file YAML manifest của Kubernetes cho các collector đều chỉ cần 'chỉ và nhấp' từ giao diện người dùng của Bindplane. Tuy nhiên, bạn cần xây dựng các cấu hình trước, sau đó mới áp dụng các collector vào cụm K8s của mình. Để đơn giản hóa, tôi sẽ minh họa cách khởi tạo hai cụm K8s bằng <code>kind</code>, và sử dụng chúng trong demo này. <code>kind create cluster --name kind-2</code> <code>kind create cluster --name kind-1</code> <code># đảm bảo bạn đã đặt ngữ cảnh sang cụm kind-1 trước</code> <code>kubectl config use-context kind-kind-1</code> Tiếp theo, hãy nhảy vào Bindplane và tạo ba cấu hình cho: * <code>telgen-kind-1</code> * <code>gw-kind-1</code> * <code>external-gw-kind-2</code><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeanLg1JOJMNBBXsBuyn_ooaBuXQH8Id6TdZXS9bL1QhPT_GKEiygbuFt4zpDQ4JZT0-LFHSK-ZlyiT75k9DMBB8AVgNyIQbhho3HbgWdF209x8p7a1sBYsRfCtzzkshgvkXbvhPw?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Tạo cấu hình cho các cụm Kubernetes'>Cấu hình <code>telgen-kind-1</code> có một nguồn Custom với <code>telemetrygeneratorreceiver</code>.<pre><code>telemetrygeneratorreceiver: generators: - additional_config: body: 127.0.0.1 - - [30/Jun/2025:12:00:00 +0000] \"GET /index.html HTTP/1.1\" 200 512 severity: 9 type: logs payloads_per_second: 1 </code></pre> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXc-LZuY_TOqimPQLPzUOy0-fYVT1Vfv5QJscX4Zfm4ktmr7ViwKVjcd7hnwSdzm-16aSSwnbBsnzJAmQeSvGC-BhsCcKn-gKrOK1Ps3FqNvF2dTck3hMa0JgwXomNQP0ipuhA63?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Custom source với telemetrygeneratorreceiver'>Và một đích đến là Bindplane Gateway. Lưu ý: Điều này tương tự như bất kỳ đích đến OTLP nào khác.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXdTPqRpRijEMhq7MKSHrqYFM9_FSF1nryvbukTwlhE_B0FrchnHOInswZBodEEmOabn7fNr8SPmRS0wGKTKqK-X4sVlmDLT7gRL7_hji074_lvOT0V9gUCoYVM22HZam_JI7yNT?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Bindplane Gateway destination'>Đích đến Bindplane Gateway được cấu hình để gửi dữ liệu đo lường đến hostname <code>bindplane-gateway-agent.bindplane-agent.svc.cluster.local</code>, đây là hostname cho dịch vụ Bindplane Gateway Collector trong Kubernetes mà bạn sẽ khởi động ngay sau đây. Bước cuối cùng cho cấu hình này là nhấp vào một node processor và thêm một batch processor.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXf6emSTjc9thPdpN3y5yIdXT5M7YeRx53riuSzRYdQE7G0u5f42uY0FiVV8A9Yasl08L5z2JRXTGsPo9qdqCAoG0hgasHO82xhYyYQsh5nJWwz0PLeuiEmuKWkaF_jmWBkqIX5eaA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Thêm batch processor cho telgen-kind-1'>Tiếp theo, cấu hình <code>gw-kind-1</code> có một nguồn Bindplane Gateway lắng nghe trên <code>0.0.0.0</code> và các cổng <code>4317</code> và <code>4318</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcJnp5O0ub82RNBJhpE5hTTgWNJayK3siFEWq3Bu9I0_HX6dqO3UMD9W0RyKLW7FpsEi_zZK89Uxv8Wz1crjlH1jhheDs0sqUgNN-RapvUI-T9TW5_-KFylSgNHF8EDR95z6YJ18Q?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Bindplane Gateway source cho gw-kind-1'>Đích đến là OTLP, và sẽ gửi dữ liệu đo lường đến địa chỉ IP (<code>172.18.0.2</code>) của gateway bên ngoài đang chạy trên cụm K8s thứ hai. Lưu ý: IP này có thể khác tùy thuộc vào cụm của bạn. Nếu bạn đang sử dụng <code>kind</code> như tôi trong demo này, IP sẽ là <code>172.18.0.2</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXfJZMuWYLxfPxQxczC21OtV1Jk6ohX3_WqngBXsIQKrk6bPKh8QWYOCcaPnDlL7xqMpzL080Zepb84dfKYYFRytKvjp7O5bVz5beQ2iI9E5WP_3l1eSq079sxyprpa86IVekE_Rzg?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP destination gửi đến external gateway'>Cuối cùng, cấu hình <code>external-gw-kind-2</code> cũng lại sử dụng một nguồn OTLP.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXcs84JO2gQO32TEBNpY2bgtjLz3cFvVk5Gb4bnCNichKm60_Ahl9pW2L06bpRfRZrTNb-gsinFovNdU5r2d0HfLthvwZ_4iqGbgLbsJ05qMVUCQw64dMQVeKQlz5f6t6PWnrvcMdA?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình OTLP source cho external-gw-kind-2'>Và, một đích đến 'Dev Null'.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXdjW_PsGUOZ4sbgV3wpjGTDpQmrCSWZv-EcrkMB8hijFBPIIGEBDX4FTNogQBp8WnYgc1tVNiUp5Ydq-94eDM_afidRMab2arkEnpgpzZzvhd_Bx9g_7g92wn49aNBgbjxGluL_ZQ?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Cấu hình Dev Null destination cho external-gw-kind-2'>Bạn cứ thoải mái sử dụng Bindplane CLI và các tài nguyên này để áp dụng tất cả các cấu hình cùng một lúc mà không cần phải làm thủ công trên giao diện người dùng nhé: <a href="https://github.com/observIQ/high-availability-agent-gateway-opentelemetry-collector/tree/main/k8s-loadbalancing">GitHub</a>. Với các cấu hình đã được tạo, bạn có thể dễ dàng cài đặt các collector bằng cách lấy các file manifest từ tài khoản Bindplane của mình. Hãy điều hướng đến giao diện 'Install Agents' trong Bindplane và chọn môi trường Kubernetes. Sau đó, chọn nền tảng 'Node' và cấu hình <code>telgen-kind-1</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXdZ1JbFmWY1gKSoxp_G3WIycXz8MxcLOwpevEKwjYakmdPzxr-p8UkJHMUn-_0JZJcm-rGTc5ALCWAiK38JD6-_WiTs7Oewx8to7wvQA90e8xIOKDXkiK7k_EImf7QHn7KPMaho?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Install Agent trong Bindplane cho Kubernetes Node'>Nhấp 'Next' sẽ hiển thị một file manifest để bạn áp dụng vào cụm.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXe1XVpWtfttEEOrPQyRzFVx8a13ZYNATHJ-Skd9o6_-hNIfegMvLtrNPK72N7XS8ykdwh3Hjh0jR7f07Cm9YWVIBBzQOCVzcyr09i8bEap6jp1oEpY2u9-Qty5TYck59XB4r-BM6w?key=SNFg6wIEkaldv4Ub0dUGcA' alt='File manifest DaemonSet cho telgen-kind-1'>Lưu file này dưới tên <code>node-agent-kind-1.yaml</code>. Dưới đây là một đoạn ví dụ về nó. Hoặc, bạn có thể xem toàn bộ file trên GitHub tại đây: <a href="https://github.com/observIQ/high-availability-agent-gateway-opentelemetry-collector/blob/main/k8s-loadbalancing/node-agent-kind-1.yaml">GitHub</a>. Tóm lại, manifest này triển khai BDOT Collector dưới dạng DaemonSet trên mỗi node, sử dụng OpAMP để nhận cấu hình từ Bindplane. Nó bao gồm: * RBAC để đọc các đối tượng Kubernetes (pods, nodes, deployments, v.v.). * Các Service để mở các cổng OTLP (4317 gRPC, 4318 HTTP). * Một init container để khởi tạo cấu hình ban đầu cho collector, cấu hình này sẽ được thay thế bằng cấu hình <code>telgen-kind-1</code> ngay khi khởi động. * Persistent hostPath storage (lưu trữ trên đĩa host) cho các tác vụ thử lại và bộ đệm đĩa. * Prometheus annotations để thu thập metrics. File của bạn sẽ chứa các giá trị <code>OPAMP_ENDPOINT</code>, <code>OPAMP_SECRET_KEY</code>, và <code>OPAMP_LABELS</code> chính xác. Hãy tiếp tục và áp dụng manifest này vào cụm K8s đầu tiên. <code>kubectl config use-context kind-kind-1</code> <code>kubectl apply -f node-agent-kind-1.yaml</code> Bây giờ, hãy cài đặt một collector khác trong cụm K8s, nhưng lần này hãy chọn 'Gateway' và cấu hình <code>gw-kind-1</code>.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://lh7-rt.googleusercontent.com/docsz/AD_4nXeEGBgMI2M2sfGyYVFQDF2qY1AsxogeiJOkCPy_LEvqbWMX-U9Nyemtf8Gg4rRDzqgo2_auBlrvYy2MViE-tojeAV2_okhIFhBszQeRmXj7zbz49U6GdcDlu8NbN3PfQtRj_DQVNDUlyg?key=SNFg6wIEkaldv4Ub0dUGcA' alt='Install Agent trong Bindplane cho Kubernetes Gateway'>Bạn sẽ nhận được một file manifest khác để áp dụng, nhưng lần này là một deployment. Lưu nó dưới tên <code>gateway-collector-kind-1.yaml</code>. Đây là nội dung của nó trên GitHub: <a href="https://github.com/observIQ/high-availability-agent-gateway-opentelemetry-collector/blob/main/k8s-loadbalancing/gateway-collector-kind-1.yaml">GitHub</a>. Đây là toàn bộ manifest dưới dạng deployment với Horizontal Pod Autoscaler. <pre><code>--- apiVersion: v1 kind: Namespace metadata: labels: app.kubernetes.io/name: bindplane-agent name: bindplane-agent --- apiVersion: v1 kind: ServiceAccount metadata: labels: app.kubernetes.io/name: bindplane-agent name: bindplane-agent namespace: bindplane-agent --- apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway name: bindplane-gateway-agent namespace: bindplane-agent spec: ports: - appProtocol: grpc name: otlp-grpc port: 4317 protocol: TCP targetPort: 4317 - appProtocol: http name: otlp-http port: 4318 protocol: TCP targetPort: 4318 - appProtocol: tcp name: splunk-tcp port: 9997 protocol: TCP targetPort: 9997 - appProtocol: tcp name: splunk-hec port: 8088 protocol: TCP targetPort: 8088 selector: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway sessionAffinity: None type: ClusterIP --- apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway name: bindplane-gateway-agent-headless namespace: bindplane-agent spec: clusterIP: None ports: - appProtocol: grpc name: otlp-grpc port: 4317 protocol: TCP targetPort: 4317 - appProtocol: http name: otlp-http port: 4318 protocol: TCP targetPort: 4318 selector: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway sessionAffinity: None type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: bindplane-gateway-agent labels: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway namespace: bindplane-agent spec: selector: matchLabels: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway template: metadata: labels: app.kubernetes.io/name: bindplane-agent app.kubernetes.io/component: gateway annotations: prometheus.io/scrape: "true" prometheus.io/path: /metrics prometheus.io/port: "8888" prometheus.io/scheme: http prometheus.io/job-name: bindplane-gateway-agent spec: serviceAccount: bindplane-agent affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: topologyKey: kubernetes.io/hostname labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: [bindplane-agent] - key: app.kubernetes.io/component operator: In values: [gateway] securityContext: runAsNonRoot: true runAsUser: 1000000000 runAsGroup: 1000000000 fsGroup: 1000000000 seccompProfile: type: RuntimeDefault initContainers: - name: setup-volumes image: ghcr.io/observiq/bindplane-agent:1.80.1 securityContext: runAsNonRoot: true runAsUser: 1000000000 runAsGroup: 1000000000 readOnlyRootFilesystem: true allowPrivilegeEscalation: false seccompProfile: type: RuntimeDefault capabilities: drop: - ALL command: - 'sh' - '-c' -
Bắt đầu hành trình Observability với OpenTelemetry! Phần 1 của chuỗi bài viết sẽ hướng dẫn bạn xây dựng ứng dụng Machine Learning đơn giản bằng FastAPI và chuẩn bị cho việc giám sát toàn diện trên Kubernetes.