Khám phá cách xây dựng một trợ lý AI thông minh, giúp bạn đặt câu hỏi và nhận câu trả lời tức thì từ bất kỳ tài liệu PDF nào, sử dụng GPT-3.5, Redis Vector Search và Docker. Hướng dẫn chi tiết từng bước, kiến thức lập trình dễ hiểu và mẹo bảo mật.
Bạn gặp khó khăn khi phát triển LLM cục bộ? Khám phá ollama-dev-env, một dự án Docker giúp đơn giản hóa việc triển khai LLM với GPU NVIDIA và các mô hình mã nguồn mở như DeepSeek Coder. Tăng tốc làm việc với AI ngay tại máy của bạn!
Chào mừng các bạn đến với thế giới "ngốn" tài nguyên của các mô hình ngôn ngữ lớn (LLM)! Bạn biết đấy, mấy em LLM này "ăn" CPU/GPU khỏe re, và để "thuần hóa" chúng chạy mượt mà, hiệu quả thì chúng ta cần đến một "vũ khí bí mật" mang tên vLLM. Nó giống như một động cơ siêu tốc chuyên để "tăng ga" cho các mô hình AI vậy đó!Trong bài viết này, chúng ta sẽ cùng nhau đi một vòng, từ A đến Z, để "đóng gói" mô hình Pixtral yêu quý của chúng ta vào một "chiếc hộp thần kỳ" (Docker container), sau đó dùng vLLM để "khởi động" em nó, và cuối cùng là "mở cửa" cho cả thế giới bên ngoài có thể "giao tiếp" với Pixtral thông qua một API. Nghe có vẻ phức tạp à? Yên tâm đi, mình sẽ biến nó thành câu chuyện dễ hiểu nhất cho bạn!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/LLM_Docker_vLLM.png' alt='Mô hình LLM, Docker và vLLM'>📦 Chuẩn bị Đồ nghề (Prerequisites)Trước khi bắt tay vào cuộc phiêu lưu, chúng ta cần đảm bảo có đủ "đồ chơi" nhé:Docker: Cứ cài bản mới nhất cho "ngon lành cành đào" nha! Đây là công cụ giúp chúng ta tạo ra các "hộp" biệt lập cho ứng dụng.NVIDIA GPU và CUDA Drivers: Nếu bạn muốn Pixtral chạy nhanh như tên lửa (và tận dụng sức mạnh của card đồ họa), thì không thể thiếu "bộ đôi" này rồi.Model Pixtral: Bạn có thể tải thẳng từ Hugging Face về hoặc đã có sẵn trên máy rồi thì càng tốt.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Prerequisites.png' alt='Các yêu cầu chuẩn bị'>🛠 Bước 1: Tạo "Bản thiết kế" DockerfileTưởng tượng Dockerfile giống như một công thức nấu ăn, hay một bản thiết kế chi tiết để tạo ra "ngôi nhà" cho Pixtral vậy. Chúng ta sẽ "xây" một cái file tên là Dockerfile với nội dung sau:FROM nvidia/cuda:12.2.2-runtime-ubuntu22.04# Cài đặt mấy "công cụ" cần thiết cho hệ thốngRUN apt-get update && apt-get install -y git wget curl python3 python3-pip && rm -rf /var/lib/apt/lists/*# Cài đặt "động cơ siêu tốc" vLLMRUN pip3 install --upgrade pipRUN pip3 install vllm# Đặt "chỗ làm việc" mặc định trong hộpWORKDIR /app# "Mở cửa" cổng 8000 để sau này giao tiếpEXPOSE 8000# Lệnh mặc định khi hộp được khởi động (có thể thay đổi sau)CMD ["bash"]FROM nvidia/cuda:12.2.2-runtime-ubuntu22.04: "Này Docker, hãy bắt đầu từ một nền tảng có sẵn CUDA của NVIDIA nhé, để em Pixtral chạy GPU được!"RUN apt-get update && apt-get install -y ...: "Giờ thì cài đặt mấy thứ lặt vặt như git, python, pip để Pixtral có đủ đồ chơi mà chạy!"RUN pip3 install vllm: "À, quan trọng nhất, phải cài đặt ngay vLLM chứ, 'động cơ' của chúng ta đó!"WORKDIR /app: "Tất cả mọi thứ sau này sẽ diễn ra trong thư mục /app nhé!"EXPOSE 8000: "Tớ muốn cái hộp này sẽ 'lắng nghe' ở cổng 8000. Ai muốn nói chuyện thì gõ cửa cổng này nha!"CMD ["bash"]: "Khi khởi động, cứ mặc định mở terminal bash cho dễ 'tương tác'!"<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Dockerfile_concept.png' alt='Dockerfile như bản thiết kế ngôi nhà'>⚙️ Bước 2: "Đúc" Chiếc Hộp Mẫu (Build Docker Image)Sau khi có "bản thiết kế" Dockerfile rồi, giờ là lúc biến nó thành một "chiếc hộp mẫu" sẵn sàng để nhân bản. Chúng ta sẽ dùng lệnh:docker build -t pixtral-vllm .docker build: "Này Docker, xây hộ tớ một cái!"-t pixtral-vllm: "Tên nó là pixtral-vllm nhé, cho dễ gọi!".: "À, file Dockerfile nằm ngay trong thư mục này đó!"Quá trình này có thể mất chút thời gian tùy thuộc vào tốc độ mạng và cấu hình máy bạn, vì nó sẽ tải về và cài đặt mọi thứ đấy.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Building_Docker_Image.png' alt='Quá trình xây dựng Docker image'>📂 Bước 3: "Khởi động" Chiếc Hộp và "Mời" Pixtral vào (Run the Container)Tuyệt vời! Chúng ta đã có "chiếc hộp mẫu" rồi. Giờ là lúc "khai sinh" một bản sao đang chạy từ nó và bảo vLLM "phục vụ" Pixtral!docker run --gpus all -it -p 8000:8000 pixtral-vllm \ python3 -m vllm.entrypoints.openai.api_server \ --model <huggingface_repo_or_local_path_to_pixtral>docker run: "Cho chạy ngay một cái hộp!"--gpus all: "Nhớ là phải dùng tất cả sức mạnh GPU có sẵn nhé, không là Pixtral 'ngủm củ tỏi' đó!"-it: "Cho tớ tương tác với nó như đang ở trong terminal của nó vậy!"-p 8000:8000: "Mở cổng 8000 của cái hộp, rồi 'ánh xạ' nó ra cổng 8000 của máy bạn. Vậy là ai truy cập máy bạn ở cổng 8000 thì sẽ nói chuyện được với Pixtral trong hộp!"pixtral-vllm: "À, chạy từ cái hộp mẫu tên này nè!"python3 -m vllm.entrypoints.openai.api_server --model <đường_dẫn_tới_pixtral>: "Trong cái hộp, hãy chạy ngay server API của vLLM, và nhớ chỉ đường cho nó đến Pixtral nha!" (Bạn nhớ thay <đường_dẫn_tới_pixtral> bằng tên repo trên Hugging Face hoặc đường dẫn local nhé!)Khi lệnh này chạy, bạn sẽ thấy Pixtral được tải lên và vLLM server bắt đầu lắng nghe ở cổng 8000 bên trong container.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Run_Container.png' alt='Chạy Docker container với GPU và Pixtral'>🌐 Bước 4: "Thử Gõ Cửa" API (Test the API Endpoint)"Nhà hàng" Pixtral đã mở cửa rồi, giờ phải thử xem có "gọi món" được không chứ! Mở một terminal mới (không phải terminal đang chạy Docker nhé) và thử gửi một yêu cầu curl đơn giản:curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{"model": "pixtral", "prompt": "Hello Pixtral!"}'curl: "Đây là công cụ để 'gọi món' từ nhà hàng API!"http://localhost:8000/v1/completions: "Gọi đến địa chỉ này, cổng 8000 nhé!"-H "Content-Type: application/json": "Nói cho nhà hàng biết là mình gửi dữ liệu dạng JSON!"-d '{"model": "pixtral", "prompt": "Hello Pixtral!"}': "Đây là 'món' mình muốn gọi: mô hình Pixtral, và câu hỏi là 'Hello Pixtral!'"Nếu mọi thứ "ngon lành", bạn sẽ nhận được một phản hồi kiểu như thế này:{ "id": "cmpl-1234", "object": "text_completion", "choices": [ {"text": "Hello there!"} ]}<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Test_API_Curl.png' alt='Kiểm tra API bằng Curl'>📡 Bước 5: "Mở Cửa" cho Cả Thế Giới (Expose for External Access)Bạn muốn bạn bè, hay các ứng dụng khác bên ngoài máy bạn cũng có thể "nói chuyện" với Pixtral? Dễ thôi!Mapping cổng: Chúng ta đã làm ở Bước 3 rồi đó (-p 8000:8000). Điều này có nghĩa là, khi ai đó truy cập http://<IP_máy_của_bạn>:8000, thì yêu cầu đó sẽ được chuyển vào cổng 8000 của chiếc hộp Pixtral.Tường lửa/Security Group: Đảm bảo rằng tường lửa của máy bạn hoặc Security Group (nếu bạn dùng Cloud) cho phép các kết nối đến cổng 8000. Coi chừng nó "chặn cửa" nha!Lưu ý quan trọng cho "chế độ sản xuất" (production):Reverse Proxy (NGINX/Traefik): Nếu bạn muốn chạy thực tế, hãy dùng NGINX hay Traefik làm "người gác cổng" nhé. Chúng sẽ giúp quản lý các yêu cầu tốt hơn, thêm HTTPS, v.v.Xác thực: Đừng bao giờ "mở toang" API ra Internet mà không có xác thực! Hãy thêm một lớp bảo mật để chỉ những ai được phép mới có thể truy cập Pixtral của bạn.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/External_Access.png' alt='Mở truy cập ngoại vi cho API'>✅ Kết Luận "Ngọt Ngào"Vậy là xong! Giờ đây, bạn đã tự tay "triển khai" thành công mô hình Pixtral với vLLM ngay trong một chiếc hộp Docker. Nghe có vẻ "pro" quá trời đất đúng không? Với setup này, bạn có thể dễ dàng "nhân bản" các container Pixtral để xử lý lượng yêu cầu lớn hơn, mà vẫn đảm bảo tính di động và dễ quản lý. Chúc mừng bạn đã lên "level" mới trong hành trình chinh phục AI nhé!
Chào các bạn, mấy nay, AI tạo sinh (GenAI) cứ như một cơn bão đổ bộ vào thế giới công nghệ, đặc biệt là mảng phát triển phần mềm, đúng không? Ai ai cũng rần rần muốn "nhảy vào" làm AI hết. Nhưng mà nè, có bao giờ bạn thấy việc "chế tạo" mấy ứng dụng AI này không hề dễ dàng như mình tưởng tượng không? Thực ra, nó "gai góc" hơn nhiều đó! Đầu tiên nhé, cái thế giới AI hiện tại nó cứ "tan đàn xẻ nghé" kiểu gì ấy. Bạn phải lọ mọ ghép nối đủ thứ thư viện, framework, nền tảng mà chúng nó sinh ra vốn chẳng phải để "yêu nhau" đâu. Cứ như ráp một cái xe từ linh kiện của đủ hãng khác nhau vậy, khó nhằn! Thứ hai, chạy mấy em "Siêu Não Ngôn Ngữ" (Large Language Models – LLMs) cho mượt mà á? Đòi hỏi phần cứng chuyên biệt đấy, mà mỗi nền tảng lại đòi cấu hình khác nhau nữa chứ. Rồi, cái khâu chạy mô hình AI thì lại tách rời hẳn khỏi quy trình "đóng gói" (container) truyền thống. Thế là team phát triển phải đau đầu duy trì hai môi trường riêng biệt: một cho code ứng dụng, một cho mô hình AI. Cứ như nhà có hai gian, mà mỗi gian lại dùng một loại chìa khóa khác nhau vậy! Chưa hết! Làm sao để lưu trữ, quản lý phiên bản (versioning) và "phục vụ" (serving) mấy cái mô hình AI một cách chuẩn mực đây? Thiếu quy trình thống nhất nên anh em cứ triển khai mỗi người một kiểu, loạn xà ngầu luôn. Rồi, cứ phụ thuộc vào mấy dịch vụ AI "trên mây" (cloud-based) thì chi phí nó cứ nhảy múa theo mức độ sử dụng, không kiểm soát được. Cứ như bạn đi ăn buffet mà không biết lúc nào thì hết tiền vậy. Mà gửi dữ liệu nhạy cảm lên dịch vụ bên ngoài á? Rủi ro về bảo mật, quyền riêng tư cao lắm chứ đùa! Tất cả những cái "khó chịu" này gộp lại làm cho trải nghiệm của các dev cứ như "đấm vào không khí" vậy đó, vừa nản vừa tốn thời gian, kìm hãm luôn cả việc thử nghiệm những ý tưởng mới. Trong khi đó, doanh nghiệp thì đang cần tăng tốc "ôm" AI lắm rồi! Đừng lo lắng nữa! Chính lúc này, "người hùng" của chúng ta xuất hiện: Docker Model Runner! Đây chính là "liều thuốc tiên" giúp bạn giải quyết tất tần tật những vấn đề trên. Nó mang đến một giải pháp siêu gọn nhẹ để chạy các mô hình AI ngay trên máy tính của bạn, và đặc biệt là, nó được tích hợp "ngọt xớt" vào quy trình làm việc với Docker mà bạn đã quen thuộc. Tức là, bạn không cần phải học cái gì quá mới mẻ mà vẫn có thể triển khai AI ngon lành! Trong series hướng dẫn này, chúng ta sẽ cùng nhau xây dựng một ứng dụng GenAI "xịn sò" từ A đến Z. Cụ thể hơn là một giao diện chatbot "đỉnh cao" được cấp sức mạnh bởi Docker Model Runner. Đặc biệt hơn nữa, chúng ta sẽ còn "trang bị" cho nó những công cụ quan sát chuyên nghiệp (kiểu như Prometheus, Grafana và Jaeger) để bạn có thể theo dõi và tối ưu hóa các mô hình AI của mình một cách dễ dàng nhất. Nghe có hấp dẫn không nào?<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/L8qS8zY.png' alt='Minh họa giao diện chatbot AI thân thiện'> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/dK1r9g3.png' alt='Sơ đồ quy trình làm việc với Docker Model Runner và AI'> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/Z4cR3zM.png' alt='Dashboard giám sát hiệu suất với Prometheus, Grafana và Jaeger'> Bạn muốn biết cách biến những ý tưởng AI phức tạp thành hiện thực một cách dễ dàng và hiệu quả? Hãy cùng khám phá bài viết gốc nhé!
Ê, bạn có biết tin tức 'nóng hổi' từ nhà Docker không? Với phiên bản Docker Desktop 4.43 mới toanh (ra mắt vào 3/7/2025), việc triển khai các mô hình AI, các 'đặc vụ' (agents) thông minh, hay các công cụ MCP giờ đây dễ như ăn kẹo! Bạn chỉ cần 'khai báo' tất tần tật chúng vào một file `compose.yaml` duy nhất, sau đó gõ một lệnh `docker compose up` là mọi thứ tự động 'khởi động' lên hết! Cực kỳ tiện lợi phải không nào?Chưa hết, một tính năng 'đỉnh của chóp' đang trong giai đoạn beta là Docker Offload còn cho phép bạn 'đẩy' cả project Compose của mình chạy thẳng trên 'mây' với sức mạnh từ GPU NVIDIA L4 khủng bố. Điều này có nghĩa là, dù bạn chỉ sở hữu một chiếc laptop 'khiêm tốn', bạn vẫn có thể 'cân' được những mô hình AI khổng lồ đòi hỏi tài nguyên cực lớn!Trong bài viết này, tôi sẽ 'mổ xẻ' chi tiết cách chúng ta sử dụng ví dụ A2A Multi-Agent Fact Checker chính thức từ dự án `docker/compose-for-agents` chỉ với Compose thôi. Và tất nhiên, tôi cũng sẽ 'khoe' cách để 'tải' công việc tính toán nặng nề lên đám mây bằng Docker Offload nhé!Vậy thì, cái 'Hệ thống kiểm chứng thông tin đa đặc vụ A2A' này hoạt động ra sao nhỉ? Nó là một hệ thống multi-agent (đa đặc vụ) được xây dựng dựa trên Bộ công cụ phát triển đặc vụ (ADK) của Google và giao thức A2A. Tưởng tượng nó như một đội thám tử siêu đẳng với ba thành viên chủ chốt: **Auditor (Kiểm toán viên)**, **Critic (Phê bình gia)**, và **Reviser (Người chỉnh sửa)**. Cả ba 'điệp viên' này hợp sức lại để điều tra, kiểm chứng và chỉnh sửa một 'tuyên bố' mà bạn đưa ra, sau đó đưa ra kết luận cuối cùng.<ul><li>**Auditor (Kiểm toán viên):** Như một vị sếp tổng, anh chàng này sẽ 'xé nhỏ' yêu cầu của bạn thành các nhiệm vụ nhỏ hơn và giao cho Critic và Reviser. Sau cùng, anh ấy là người thu thập kết quả cuối cùng và trả về cho bạn qua giao diện người dùng.</li><li>**Critic (Phê bình gia):** Là 'thám tử' chuyên đi tìm kiếm thông tin bên ngoài. Anh ta dùng công cụ DuckDuckGo MCP để 'lùng sục' trên web, tìm kiếm các bằng chứng hỗ trợ cho tuyên bố.</li><li>**Reviser (Người chỉnh sửa):** Nắm trong tay bằng chứng từ Critic và bản nháp ban đầu từ Auditor, cô nàng Reviser sẽ 'tút tát' lại, xác minh kỹ càng và hoàn thiện kết quả.</li></ul>Đặc biệt, anh Critic sẽ liên lạc với thế giới bên ngoài thông qua 'cánh cổng' MCP Gateway, và mô hình suy luận AI (chính là Gemma 3 4B‑Q4) của chúng ta được 'nuôi dưỡng' bởi Docker Model Runner đó nha!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y8b4nrr7s7l37h9m4u4.png' alt='Sơ đồ hệ thống đa đặc vụ kiểm chứng thông tin A2A'>Giờ thì, hãy cùng nhìn vào trái tim của hệ thống này – file `compose.yaml` thần thánh! Nó trông có vẻ 'hack não' nhưng thực ra cực kỳ logic đó nha. Đây là cách chúng ta định nghĩa toàn bộ 'biệt đội' đặc vụ AI của mình:<br>• **Dịch vụ (services):**<br> • `auditor-agent-a2a`: Đây là 'sếp tổng' điều phối mọi thứ. Nó sẽ xây dựng từ `auditor-agent`, lắng nghe ở cổng `8080`. Đặc biệt, nó cần biết địa chỉ của `critic-agent-a2a` và `reviser-agent-a2a` để giao việc. Nó còn "phụ thuộc" vào hai anh em kia, nghĩa là Critic và Reviser phải "dậy" trước thì Auditor mới làm việc được.<br> • `critic-agent-a2a` và `reviser-agent-a2a`: Hai 'đặc vụ' này được xây dựng từ `critic-agent` và `reviser-agent`. Chúng kết nối với 'cổng thông tin' MCP Gateway để làm việc.<br> • `mcp-gateway`: Đây là 'người bảo vệ' các máy chủ MCP của bạn (như DuckDuckGo). Nó dùng image `docker/mcp-gateway:latest` và thiết lập kết nối Server-Sent Events (SSE). Dòng `--interceptor` chỉ là để 'ghi nhật ký' các phản hồi đầu ra thôi, tiện cho việc gỡ lỗi đó!<br>• **Mô hình (models):**<br> • `gemma3`: Đây là nơi chúng ta 'khai báo' mô hình LLM (mô hình ngôn ngữ lớn) mà chúng ta muốn kéo về và sử dụng. Ở đây là `ai/gemma3:4B-Q4_0` với kích thước ngữ cảnh (context size) là 10000, tương đương 3.5 GB VRAM. Bạn có thể thay đổi `context_size` nếu muốn mô hình 'nhớ' nhiều hơn (nhưng cũng tốn VRAM hơn đó!).À, có một điểm mới cực hay ho từ Compose v2.38 đó là: bạn có thể 'khai báo' các hình ảnh mô hình LLM (chính là OCI Artifacts) ngay dưới trường `models` ở cấp cao nhất của file `compose.yaml`. Docker Model Runner sẽ tự động tải hình ảnh đó về và 'biến' nó thành một điểm cuối API sẵn sàng để các dịch vụ khác gọi tới. Nghe 'vi diệu' không?Còn các `models` khai báo riêng cho từng dịch vụ thì sao? Mỗi dịch vụ (như Auditor, Critic, Reviser) sẽ 'chỉ định' nó muốn dùng mô hình nào và cách để 'tiêm' URL và tên mô hình vào các biến môi trường như `MODEL_RUNNER_URL` và `MODEL_RUNNER_MODEL`. Điều này giúp ứng dụng của bạn không cần 'nhúng chết' đường dẫn mô hình vào code, mà chỉ cần đọc từ biến môi trường là xong, tiện lợi cho việc thay đổi sau này!**Cổng kết nối MCP Gateway:** Cái `docker/mcp-gateway` này hoạt động như một 'trạm trung chuyển' an toàn cho các máy chủ MCP (như DuckDuckGo). Nó trò chuyện với 'điệp viên' Critic của chúng ta bằng giao thức Server-Sent Events (SSE) 'siêu tốc'. Cái cờ `--interceptor` chỉ đơn giản là một 'thám tử' nhỏ, giúp ghi lại toàn bộ phản hồi thô vào nhật ký lỗi (stderr) để bạn dễ dàng kiểm tra thôi.**Quản lý Phụ thuộc:** Cũng giống như các dự án Compose truyền thống, chúng ta dùng `depends_on` để 'sắp xếp thứ tự' khởi động cho các dịch vụ. Thứ tự vàng ở đây là: MCP Gateway phải 'tỉnh' trước, rồi đến Critic và Reviser, và cuối cùng mới là Auditor. Nhờ vậy, chúng ta không cần phải 'mệt mỏi' với việc viết các đoạn mã kiểm tra và thử lại kết nối nữa – mọi thứ cứ thế mà chạy trơn tru!Vậy làm sao để 'chạy thử' ngay trên máy của bạn? Đơn giản cực kỳ! Bạn chỉ cần gõ lệnh 'thần chú' này vào terminal:`docker compose up --build`Và 'chứng kiến' điều kỳ diệu! Hệ thống sẽ tự động xây dựng và khởi động các 'đặc vụ' của bạn. Đặc biệt, mô hình Gemma 3 4B-Q4 đã được 'lượng tử hóa' (quantized), nên nó 'nhẹ nhàng' đến mức có thể chạy được ngay trên chiếc MacBook Air M2 'mong manh' của tôi đó!Khi mọi thứ đã 'lên sóng', bạn chỉ cần mở trình duyệt và truy cập vào `http://localhost:8080`. Sau đó, hãy thử gõ một câu hỏi nào đó, ví dụ như:'Mặt trăng cách Trái đất bao xa?'Và xem điều gì xảy ra nhé! 'Điệp viên' Critic sẽ ngay lập tức 'bay' đi tìm kiếm trên DuckDuckGo, 'cô nàng' Reviser sẽ 'tút tát' lại câu trả lời cho thật hoàn hảo, và cuối cùng, 'sếp tổng' Auditor sẽ trả về kết quả cuối cùng cho bạn. Quá ngầu luôn!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4ijldzlfcbtjrdzymy4.png' alt='Ảnh chụp màn hình quá trình khởi chạy Docker Compose'><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxprbyo375ihwa53gjqku.png' alt='Giao diện web của Fact-Checker hiển thị kết quả'>Nhưng nếu bạn muốn 'chơi lớn' hơn, ví dụ như dùng mô hình Gemma 27B Q4 'khổng lồ' thì sao? Lúc này, GPU trên máy tính của bạn có thể 'kêu cứu' đấy! Đây chính là lúc **Docker Offload** 'ra tay nghĩa hiệp'! Bạn chỉ việc bật tính năng này lên và 'ghi đè' cấu hình mô hình của bạn để nó chạy thẳng tắp trên đám mây.**Kích hoạt Docker Offload:**Đầu tiên, bạn cần đăng ký quyền truy cập beta trên trang chính thức của Docker nhé. (Tôi may mắn là một Docker Captain nên được trải nghiệm sớm hơn tí!).Sau đó, hãy vào phần `Settings` (Cài đặt) > `Beta Features` (Tính năng Beta) và bật hai tùy chọn này lên:<ul><li>`Enable Docker Offload` (Bật Docker Offload)</li><li>`Enable Docker Offload GPU Support` (Bật hỗ trợ GPU cho Docker Offload)</li></ul>Cuối cùng, chỉ cần 'chuyển công tắc' Docker Desktop sang biểu tượng đám mây là Docker Offload sẽ 'nhảy' vào làm việc ngay (hoặc bạn có thể gõ lệnh `docker offload start`).<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevkna2eq0f2yr8r53e3d.png' alt='Cài đặt bật Docker Offload và hỗ trợ GPU'><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl73pj8mpk1gp18q2as8r.png' alt='Nút chuyển đổi Docker Desktop sang chế độ Cloud'>Để 'ghi đè' cấu hình mô hình, bạn hãy chuẩn bị thêm một file `compose.offload.yaml` riêng biệt như thế này:```yamlmodels: gemma3: model: ai/gemma3-qat:27B-Q4_K_M context_size: 10000 # ~18.6 GB VRAM```File này sẽ 'nói' cho Docker biết rằng chúng ta muốn dùng phiên bản Gemma 3 lớn hơn (27B Q4 K M) và nó cần nhiều VRAM hơn (khoảng 18.6 GB).**Chạy với Docker Offload:**Giờ thì, để 'phóng' dự án của bạn lên đám mây, chúng ta sẽ 'kết hợp' hai file Compose lại với nhau bằng lệnh sau:`docker compose -f compose.yaml -f compose.offload.yaml up --build`Lệnh này sẽ 'ghi đè' trường `models` ở cấp cao nhất của `compose.yaml` bằng cấu hình 'đám mây' mà bạn đã định nghĩa trong `compose.offload.yaml`.À, có một tin vui nữa là Docker Offload tặng bạn 300 GPU credits miễn phí để 'vọc vạch' thoải mái đó! Nếu dùng quá, chi phí sẽ là $0.015 cho mỗi giây GPU. Nhớ là sau khi dùng xong, đừng quên 'tắt điện' bằng lệnh:`docker offload stop`Để tránh tốn tiền oan nha!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4lev95v9c20d7eaep4c.png' alt='Kết quả chạy Docker Compose với Docker Offload'><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6tx1tyvsxmveck69enn.png' alt='Trạng thái Docker Desktop khi Offload đang hoạt động'><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih4zm6jkwi7fmvp0dhg1.png' alt='Thông báo dừng dịch vụ Docker Offload'>**Lời cuối:**Trải nghiệm kết hợp Compose và Docker Offload thực sự đã 'phô diễn' sức mạnh của việc 'điều phối' các đặc vụ AI, mô hình và công cụ một cách thống nhất. Thật khó tin là chỉ với một lệnh `docker compose up` quen thuộc, bạn có thể triển khai dự án của mình cả trên máy cục bộ lẫn trên đám mây! Quá tiện lợi phải không nào?Thế giới của các 'đặc vụ' AI đang phát triển nhanh như vũ bão, nên nếu bạn có workflow nào hay ho hơn hay mẹo vặt nào 'độc chiêu', đừng ngần ngại chia sẻ với tôi nhé!Và nếu bạn tò mò về Docker Offload, hãy cứ mạnh dạn 'vọc vạch' trong giới hạn tín dụng miễn phí xem sao – biết đâu bạn sẽ bất ngờ với những gì mình làm được đó!
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' -
Hướng dẫn chi tiết cách thiết lập môi trường AI cục bộ mạnh mẽ với Docker, Ollama, và Open-WebUI để chạy các LLM như DeepSeek Coder và Mistral ngay trên máy tính của bạn. Dễ dàng cài đặt, tối ưu hiệu năng.
Chào các bạn lập trình viên thân mến! Có bao giờ bạn muốn một 'sân chơi' riêng để vọc vạch code Go ngay lập tức, không cần cài đặt lằng nhằng không? Hôm nay, mình sẽ dẫn các bạn đi một vòng khám phá cách mình đã "hô biến" ý tưởng đó thành hiện thực – một phiên bản Go Playground REPL "xịn sò" từ A đến Z! Từ backend Go mạnh mẽ biên dịch code trong Docker siêu bảo mật, đến frontend Vue.js đẹp mắt, rồi cả quy trình triển khai tự động lên AWS Elastic Beanstalk bằng GitHub Actions nữa. Nghe thôi đã thấy hấp dẫn rồi đúng không?<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/go_playground_intro.png' alt='Giới thiệu Go Playground REPL'> Ngôi nhà Go Playground này được xây dựng từ những viên gạch công nghệ nào nhỉ?Để xây dựng nên 'đứa con tinh thần' này, mình đã kết hợp một dàn 'siêu anh hùng' công nghệ đó:Backend: Go (Golang) – Ngôn ngữ quốc dân cho hiệu năng cao, cùng với Docker để 'đóng gói' môi trường chạy code an toàn, framework Gin siêu tốc, và gói os/exec thần thánh để 'điều khiển' các lệnh hệ thống.Frontend: Vue 3 – Ngôi sao sáng trong thế giới giao diện người dùng, đi kèm Pinia để quản lý trạng thái mượt mà, Tailwind CSS giúp 'trang điểm' cho giao diện lung linh, và Monaco Editor (cái trình soạn thảo code mà các bạn thấy trong VS Code ấy) để mang lại trải nghiệm gõ code cực đỉnh.DevOps: GitHub Actions – Người 'quản gia' tự động hóa mọi thứ, từ build Docker image, đẩy lên DockerHub, đến triển khai 'phù phép' lên AWS Elastic Beanstalk một cách tự động.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/tech_stack_collage.png' alt='Các công nghệ sử dụng trong dự án'> Vậy sản phẩm cuối cùng của chúng ta 'chất' như thế nào?Cái Go Playground REPL này không chỉ là một trình soạn thảo code thông thường đâu nhé, nó còn có:Một "sân vận động" code tương tác, nơi bạn có thể tha hồ viết và chạy các chương trình Go của mình.Một 'hậu trường' siêu an toàn, có khả năng:Chạy code của người dùng bên trong một "chiếc hộp" Docker riêng biệt, đảm bảo không ảnh hưởng gì đến hệ thống chính.Thiết lập 'hẹn giờ' để chặn đứng các đoạn code bị lỗi vòng lặp vô tận (đừng lo, không còn cảnh chờ đợi mòn mỏi nữa!).Xử lý mọi loại 'đầu vào' bất thường hay có ý đồ xấu một cách duyên dáng.Một 'mặt tiền' lung linh, tự động thay đổi giao diện sáng/tối, kèm theo các ví dụ code Go có sẵn để bạn thử nghiệm và xem kết quả ngay tức thì.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/repl_features.png' alt='Các tính năng chính của Go Playground REPL'> Xây Dựng 'Trái Tim' Hệ Thống: Backend Go và DockerĐể biến code của bạn thành kết quả, backend chính là 'bộ não' trung tâm. Mình đã dùng gói os/exec trong Go để biên dịch và chạy code của người dùng ngay bên trong một 'thùng' Docker riêng biệt, tách biệt hoàn toàn khỏi máy chủ. Tưởng tượng nó như một khu vực thử nghiệm được cô lập vậy đó!Đây là một đoạn code Go handler đơn giản minh họa cách xử lý yêu cầu chạy code:func RunCode(c *gin.Context) { var payload struct { Code string `json:"code"` } if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Định dạng yêu cầu không hợp lệ"}) return } output, err := runInDocker(payload.Code) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"output": output, "error": ""})} Và đây là 'công thức' Dockerfile để xây dựng môi trường chạy ứng dụng Go của chúng ta:FROM golang:1.24-alpineWORKDIR /appCOPY . .RUN go build -o repl-server ./cmdCMD [ "./repl-server" ] Đơn giản là: lấy ảnh Go, tạo thư mục làm việc, copy code vào, biên dịch thành file chạy được, rồi 'bảo' Docker chạy file đó khi container khởi động.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/backend_docker_flow.png' alt='Sơ đồ luồng xử lý Backend Go với Docker'> Tự Động Hóa Triển Khai: GitHub Actions 'Gánh Team'Bạn có chán nản với việc cứ phải làm đi làm lại các bước build rồi deploy mỗi khi có thay đổi không? Mình đã dùng GitHub Actions để tự động hóa toàn bộ quy trình CI/CD (Continuous Integration/Continuous Deployment)! Tưởng tượng thế này: mỗi khi mình push code lên GitHub, GitHub Actions sẽ tự động 'đóng gói' ứng dụng thành Docker image, đẩy nó lên ECR (Elastic Container Registry) của AWS, và cuối cùng là 'gửi' nó đến AWS Elastic Beanstalk – dịch vụ giúp triển khai ứng dụng lên mây dễ dàng.Các bước chính trong luồng CI/CD của chúng ta trông như thế này:Build Docker Image: Tạo ra 'bản sao' Docker của ứng dụng.Push to ECR: Đẩy 'bản sao' đó lên kho chứa của AWS.Deploy to EBS: 'Hô biến' ứng dụng lên máy chủ trên AWS Elastic Beanstalk.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/github_actions_pipeline.png' alt='Quy trình CI/CD với GitHub Actions'> Xây Dựng 'Gương Mặt' Dự Án: Frontend Vue.js và Monaco EditorSau khi có một backend vững chắc, giờ là lúc 'trang điểm' cho giao diện người dùng thật lung linh. Mình đã chọn Vue 3 kết hợp với Pinia để quản lý dữ liệu một cách gọn gàng, và Monaco Editor để mang lại trải nghiệm soạn thảo code giống hệt như VS Code yêu thích của bạn! Còn Tailwind CSS thì khỏi phải nói, giúp giao diện trở nên đẹp mắt và responsive (thân thiện với mọi kích thước màn hình) chỉ trong nháy mắt.Đây là một đoạn code Vue siêu đơn giản để tích hợp Monaco Editor:<template> <MonacoEditor v-model="code" :theme="isDark ? 'vs-dark' : 'vs-light'" language="go" /></template><script setup>import MonacoEditor from '@monaco-editor/vue'import { ref } from 'vue' // Đừng quên import ref nhé!const code = ref(`package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Xin chào thế giới!")\n}`)</script> Đơn giản không? Cứ như bạn đang 'kêu gọi' một trình soạn thảo code chuyên nghiệp vào ứng dụng của mình vậy!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/frontend_vue_monaco.png' alt='Giao diện Frontend với Monaco Editor và Vue.js'> Menu Các Ví Dụ Code Go Có SẵnĐể các bạn không phải vò đầu bứt tóc nghĩ xem nên viết gì, mình đã 'khéo léo' thêm một menu thả xuống với các ví dụ code Go có sẵn cực kỳ thú vị như:Tính dãy Fibonacci (số thỏ đẻ con kiểu gì?)Số nguyên Peano (một cách định nghĩa số học rất 'hack não'!)Kiểm tra số nguyên tố (ai là số độc thân?)Thuật toán Bubble Sort (sắp xếp 'nổi bọt' như thế nào?)Tính toán số Pi (con số 'thần thánh' trong hình học!)<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/go_examples_dropdown.png' alt='Menu các ví dụ code Go có sẵn'> Bảo Mật Hệ Thống: Không Lo 'Phá Hoại'Khi cho phép người dùng chạy code trên máy chủ của mình, vấn đề bảo mật là CỰC KỲ QUAN TRỌNG. Lỡ người dùng tinh nghịch viết một đoạn code 'vô tận' hoặc có ý đồ xấu thì sao? Đừng lo, mình đã áp dụng vài 'chiêu' để đảm bảo 'sân chơi' này luôn an toàn:Hẹn giờ (Timeout) bằng context.WithTimeout: Giống như một chiếc đồng hồ đếm ngược vậy đó. Nếu code chạy quá thời gian quy định, nó sẽ bị 'ngắt' ngay lập tức. Bye bye vòng lặp vô tận!Giới hạn tài nguyên Docker (--memory, --cpus): Đảm bảo mỗi 'chiếc hộp' Docker chỉ được dùng một lượng tài nguyên nhất định (RAM, CPU), tránh việc một đoạn code 'đói' tài nguyên làm ảnh hưởng đến cả hệ thống.Giới hạn kích thước đầu ra/lỗi: Tránh trường hợp code in ra hàng tỉ dòng chữ làm 'ngập lụt' hệ thống.Xác thực cấu trúc JSON đầu vào: Đảm bảo chỉ những yêu cầu hợp lệ mới được xử lý, tránh các kiểu tấn công khai thác lỗi định dạng.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/code_security_sandbox.png' alt='Các biện pháp bảo mật khi chạy code người dùng'> Tương Lai 'Rạng Ngời' của Go PlaygroundDự án này được xây dựng với tư duy mở rộng nên vẫn còn rất nhiều tiềm năng để 'phát tướng' đó các bạn:Thêm hỗ trợ cho các ngôn ngữ khác như JavaScript, Java, Python, v.v. (tưởng tượng có một playground đa ngôn ngữ thì sao nhỉ?).Cho phép nhập liệu từ bàn phím (stdin) trong Docker để code có thể tương tác với người dùng.Tạo tài khoản người dùng và lưu lại lịch sử các phiên code (để khoe thành tích với bạn bè!).Sử dụng WebSocket cho các luồng biên dịch thời gian thực (nhìn code chạy 'từng bước' luôn thì sao ta?).<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/future_roadmap_ideas.png' alt='Những ý tưởng phát triển trong tương lai'> Trải Nghiệm Ngay và Lời KếtThôi nói nhiều làm gì, tự tay trải nghiệm là thích nhất! Các bạn có thể ghé thăm 'sân chơi' của chúng ta tại đây:<a href="https://bit.ly/4iFifWT">🌐 Go Playground Trực Tuyến</a>Đây thực sự là một dự án cực kỳ thú vị, kết hợp giữa hiệu năng backend, trải nghiệm người dùng frontend mượt mà, và sự tự động hóa thần kỳ của DevOps. Một 'sân chơi' hoàn hảo để các bạn vừa thử nghiệm Go, vừa xây dựng một công cụ lập trình 'full-stack' hoàn chỉnh.Đừng ngần ngại fork (tạo bản sao) và mở rộng dự án này nhé! Hy vọng bài viết này sẽ giúp ích cho hành trình học hỏi của các bạn.Mã nguồn đầy đủ có ở đây nha: <a href="https://github.com/gedons/go_Repl">🔗 Github Repo</a><img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/happy_gopher_coding.png' alt='Kết luận về dự án Go Playground'>
Khám phá Docker Desktop 4.43 với khả năng định nghĩa AI model và agent trong compose.yaml. Tìm hiểu Docker Offload giúp bạn chạy mô hình AI khổng lồ trên GPU đám mây ngay từ laptop của mình.
Bạn có tò mò về Seccomp? Khám phá 'vệ sĩ' thầm lặng giúp bảo vệ các container Docker của bạn bằng cách giới hạn chính xác các lệnh ứng dụng gửi đến nhân Linux. Bài viết sẽ đi sâu vào cách Seccomp hoạt động, tầm quan trọng của nó trong môi trường sản xuất, và cách bạn có thể kiểm soát bảo mật 'từng syscall một' để đạt được nguyên tắc đặc quyền tối thiểu.
Chào các bạn, Tim và Juri lại tái xuất đây! 👋 Còn nhớ cái "siêu phẩm" AI Resume Matcher mà chúng ta cùng nhau xây dựng ở bài trước không? (Nếu bạn lỡ mất hoặc muốn ôn lại, đừng ngại ngần ghé xem nhé!). Chúng ta đã biến ứng dụng Java Spring Boot kết hợp Google Vertex AI (Gemini) thành một chuyên gia "soi" CV, đối chiếu với mô tả công việc, tất cả gói gọn cực kỳ ngăn nắp bằng Docker Compose. 🐳 Đấy là một khởi đầu quá tuyệt vời, biến ý tưởng thành sản phẩm mẫu chạy ro ro. Nhưng từ sản phẩm mẫu đến phiên bản "đỉnh cao" chạy thực tế thì sao? Làm thế nào để em nó mạnh mẽ hơn, "chịu tải" tốt hơn và sẵn sàng cho những thử thách lớn hơn? Câu trả lời chính là: Local Kubernetes đã xuất hiện!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/prototype_to_production.png' alt='Hành trình từ prototype đến production'>📝 Vậy thì, hôm nay chúng ta sẽ cùng "xắn tay áo" làm gì nào?* **Đóng gói "chiến binh" AI của chúng ta:** Biến ứng dụng Java Spring Boot AI thành một container Docker gọn gàng bằng cách viết file Dockerfile.* **Thiết lập "sân chơi" Kubernetes tại chỗ:** Khởi động một môi trường Kubernetes ngay trên máy tính của bạn (chúng ta sẽ xem xét các lựa chọn như Minikube và Docker Desktop).* **Vẽ bản thiết kế Kubernetes:** Tạo các file cấu hình Kubernetes (YAML) để hướng dẫn Kubernetes cách "vận hành" ứng dụng AI Resume Matcher và cả cơ sở dữ liệu PostgreSQL (có pgvector) của chúng ta.* **Bảo mật "cửa ngõ" Google Cloud:** Thiết lập xác thực an toàn cho Google Cloud để Vertex AI có thể "giao tiếp" với ứng dụng của chúng ta từ bên trong cụm Kubernetes.* **Mở cửa cho ứng dụng:** Phơi bày ứng dụng của bạn để có thể truy cập từ trình duyệt web bằng một Kubernetes Service.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/learning_path.png' alt='Những điều bạn sẽ học'>🤔 **Động lực nào để "lên đời" Kubernetes cục bộ?**Bạn có thể thắc mắc: "Docker Compose vẫn chạy ngon lành mà, sao phải tốn công làm quen với Kubernetes làm gì?" Đúng là một câu hỏi rất hay! Mặc dù Docker Compose cực kỳ tuyệt vời cho việc phát triển nhanh chóng, nhưng việc "nâng cấp" lên Kubernetes cục bộ mang lại những lợi ích then chốt cho "hành trình ra biển lớn" (sản phẩm thực tế) của chúng ta:* **"Giả lập" môi trường thật:** Bạn sẽ được trải nghiệm cảm giác ứng dụng chạy như thế nào trong các cụm cluster thực tế, giống hệt môi trường sản phẩm. Cứ như chơi game mô phỏng vậy!* **Chấp nhận "tăng trưởng":** Kubernetes được trang bị tốt hơn để quản lý các ứng dụng phức tạp và kiến trúc microservices trong tương lai. Nó giống như việc bạn chuyển từ một chiếc xe đạp sang một chiếc xe tải vậy.* **Kỹ năng "sẵn sàng lên mây":** Giúp bạn chuyển đổi mượt mà sang các nền tảng đám mây như Google Cloud và SAP BTP. Học cái này là bạn có "vé VIP" lên cloud rồi đó!* **Triển khai "chuẩn hóa":** Thúc đẩy các đợt triển khai ứng dụng nhất quán và đáng tin cậy hơn. Không còn chuyện "trên máy tôi thì chạy được"!Tóm lại, việc sử dụng Kubernetes cục bộ giúp chúng ta xây dựng những kỹ năng "sẵn sàng cho sản xuất" và chuẩn bị cho AI Resume Matcher của chúng ta "bay cao" hơn trong tương lai trên đám mây! ☁️<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_vs_k8s.png' alt='So sánh Docker Compose và Kubernetes'>⚙️ **Chuẩn bị "hành trang" gì đây?**Để bắt đầu cuộc phiêu lưu này, bạn cần chuẩn bị vài thứ nho nhỏ sau:* **Dự án AI Resume Matcher của chúng ta:** Bạn sẽ cần mã nguồn ứng dụng từ bài viết trước nhé. Nếu chưa có, bạn có thể ghé thăm kho mã nguồn của chúng tôi để "rinh" về ngay!* **Docker Desktop:** Công cụ "tất cả trong một" này đã bao gồm Kubernetes cục bộ chỉ với một cú nhấp chuột. Tiện lợi hết sức!* **Tài khoản & Dự án Google Cloud:** Vì ứng dụng của chúng ta "nhờ cậy" Google Vertex AI, nên bạn cần:* Một tài khoản Google Cloud Platform (GCP).* Một dự án GCP mà bạn đã bật API Vertex AI (như chúng ta đã thực hiện ở bài trước đó).<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/prerequisites_icons.png' alt='Các công cụ cần thiết'>📦 **Bước 1: "Đóng gói" AI Resume Matcher của chúng ta vào Container**Đầu tiên và quan trọng nhất, chúng ta cần "đóng gói" ứng dụng Spring Boot của mình vào một Docker container. Việc này giống như việc bạn gói quà vậy, giúp ứng dụng của chúng ta trở nên di động, và quan trọng hơn là đảm bảo nó sẽ chạy y hệt nhau ở mọi nơi, từ máy tính cá nhân cho đến một cụm Kubernetes "khổng lồ".<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/springboot_to_docker.png' alt='Đóng gói Spring Boot vào Docker'>**Biên dịch ứng dụng với Gradle**Vì dự án của chúng ta "kết thân" với Gradle, nên bước đầu tiên là biên dịch ứng dụng để tạo ra một file JAR có thể chạy được. Bạn hãy mở terminal (hoặc Command Prompt/PowerShell trên Windows) tại thư mục gốc của dự án smarthire-blog và chạy lệnh sau nhé:`./gradlew build`Lệnh này sẽ biên dịch mã nguồn của bạn, chạy các bài kiểm tra và đóng gói mọi thứ vào một file JAR. Bạn sẽ tìm thấy "thành phẩm" này trong thư mục build/libs/.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/gradle_build.png' alt='Lệnh gradlew build'>**"Chế tác" Dockerfile "Thần Thánh"**Tiếp theo, chúng ta cần tạo một file tên là `Dockerfile` (nhớ là không có đuôi mở rộng nhé!) ngay tại thư mục gốc của dự án. File này chứa tất cả các "công thức" mà Docker sẽ dùng để xây dựng image của chúng ta. Đây là nội dung cho `Dockerfile` của chúng ta:`FROM eclipse-temurin:21-jre-jammy RUN groupadd --system spring && useradd --system --gid spring spring USER spring:spring COPY build/libs/*.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"] EXPOSE 8080`Cùng "mổ xẻ" từng dòng một xem chúng làm gì nhé:* `FROM eclipse-temurin:21-jre-jammy`: Dòng này nói với Docker rằng chúng ta sẽ dùng image chính thức của Eclipse Temurin Java 21 JRE (Java Runtime Environment) dựa trên Ubuntu Jammy làm nền tảng. Dùng JRE thì hay ho lắm, vì nó nhỏ hơn một image JDK đầy đủ, giúp container của chúng ta nhẹ nhàng hơn vì chỉ chứa những gì cần thiết để chạy mã Java đã biên dịch thôi.* `RUN groupadd --system spring && useradd --system --gid spring spring`: Ở đây, chúng ta tạo một group hệ thống tên là `spring` và sau đó là một user hệ thống cũng tên là `spring`, gán nó vào group đó. User "chuyên biệt" và không có nhiều đặc quyền này sẽ chạy ứng dụng của chúng ta, giúp tăng cường bảo mật đó!* `USER spring:spring`: Lệnh này chuyển đổi người dùng hiện hành bên trong Docker image sang user `spring` mà chúng ta vừa tạo. Tất cả các lệnh tiếp theo, bao gồm cả `ENTRYPOINT`, sẽ chạy dưới quyền của user này.* `COPY build/libs/*.jar app.jar`: Dòng này sao chép file JAR được tạo bởi Gradle (từ build/libs/ – dấu *.jar giúp bắt được file JAR có phiên bản cụ thể mà không cần biết chính xác tên) vào hệ thống file của container và đổi tên thành app.jar.* `ENTRYPOINT ["java", "-jar", "/app.jar"]`: Dòng này chỉ định lệnh sẽ được chạy khi container khởi động. Nó sẽ thực thi ứng dụng Spring Boot của chúng ta bằng lệnh java -jar. Đường dẫn /app.jar chính là file JAR mà chúng ta đã sao chép ở bước trước (nếu không có WORKDIR được chỉ định, nó mặc định được đặt ở thư mục gốc của hệ thống file trong container).* `EXPOSE 8080`: Dòng này "thông báo" cho Docker biết rằng ứng dụng bên trong container sẽ lắng nghe trên cổng 8080 khi chạy. Thực ra nó không phải là "mở cổng" đâu, mà giống như một tài liệu hướng dẫn cho người dùng và một "gợi ý" cho các công cụ khác thôi.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/dockerfile_explained.png' alt='Giải thích Dockerfile'>**"Đúc" Docker Image của chúng ta**Khi đã có `Dockerfile` và file JAR của ứng dụng, giờ là lúc chúng ta "đúc" ra Docker image! Tại terminal (vẫn ở thư mục gốc của dự án), bạn chạy lệnh này nhé:`docker build -t smarthire-app:v1 .`Cùng "giải phẫu" lệnh này một chút:* `docker build`: Đây là lệnh để xây dựng một image từ file Dockerfile.* `-t smarthire-app:v1`: Cờ `-t` giúp chúng ta gắn "nhãn" (tag) cho image. `smarthire-app` là tên của image, và `:v1` là phiên bản (tag). Hãy dùng một tag cụ thể như `v1` thay vì `:latest` nhé! Vì chính sách mặc định `imagePullPolicy` của Kubernetes cho `:latest` là `Always` (luôn cố gắng kéo từ xa), trong khi với `v1` là `IfNotPresent` (sẽ dùng image cục bộ của bạn nếu có). Nhớ kỹ điểm này để tránh phiền toái nha!* `.`: Dấu chấm ở cuối nói với Docker rằng hãy tìm file Dockerfile trong thư mục hiện tại.Sau khi lệnh này chạy xong, bạn sẽ có một Docker image sẵn sàng để chạy cục bộ! Bạn có thể kiểm tra danh sách các image của mình bằng lệnh `docker images`.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_build_output.png' alt='Lệnh docker build'>⚙️ **Bước 2: Khởi động Kubernetes với Docker Desktop**Giờ thì ứng dụng của chúng ta đã được "đóng gói" cẩn thận vào container, đến lúc thiết lập môi trường Kubernetes cục bộ rồi! Trong hướng dẫn này, chúng ta sẽ tận dụng cụm Kubernetes có sẵn trong Docker Desktop. Vừa nhanh, vừa tiện!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_desktop_k8s.png' alt='Docker Desktop với Kubernetes'>**Kích hoạt Kubernetes trong Docker Desktop**Nếu bạn chưa bật Kubernetes trong Docker Desktop, đừng lo, chỉ vài cú click chuột là xong ngay thôi:* Đầu tiên, bạn vào **Settings** (biểu tượng bánh răng ⚙️ quen thuộc).* Tìm đến mục **Kubernetes** ở thanh bên.* Đảm bảo rằng ô **Enable Kubernetes** đã được tích chọn.* Cuối cùng, nhấn **Apply & Restart**. Docker Desktop sẽ tự động tải xuống các thành phần Kubernetes cần thiết và khởi động cụm cluster một node của bạn. Quá trình này có thể mất vài phút, bạn cứ thư giãn chút nhé!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/enable_k8s_docker_desktop.png' alt='Kích hoạt Kubernetes trong Docker Desktop'>**Kiểm tra xem cụm Kubernetes của bạn đã "lên sóng" chưa**Để "trò chuyện" với Kubernetes, chúng ta sẽ dùng một công cụ dòng lệnh tên là `kubectl` (công cụ này sẽ tự động được cài đặt khi bạn kích hoạt Kubernetes trong Docker Desktop).Giờ hãy cùng kiểm tra xem cụm cluster của bạn đã hoạt động trơn tru chưa nhé. Mở terminal lên và gõ:`kubectl get nodes`Vì Docker Desktop chạy một cụm cluster chỉ có một node, bạn sẽ thấy một node duy nhất được liệt kê, thường là `docker-desktop`, với trạng thái `Ready`.Xin chúc mừng! 🎉 Bạn đã có một cụm Kubernetes cục bộ đang chạy và sẵn sàng để "tiếp đón" AI Resume Matcher của chúng ta rồi đó!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/kubectl_get_nodes.png' alt='Kiểm tra node Kubernetes với kubectl'>✨ **Bước 3: "Chỉ huy" với Kubernetes - Viết các "kịch bản" Manifest của chúng ta**Cụm Kubernetes đã sẵn sàng, image cũng đã "nấu" xong, đến lúc chúng ta "ra lệnh" cho Kubernetes biết ứng dụng và cơ sở dữ liệu của chúng ta sẽ chạy như thế nào rồi! Chúng ta sẽ dùng các file Kubernetes manifest – giống như những "bản thiết kế" YAML mô tả chi tiết cách bạn muốn hệ thống của mình hoạt động. Chúng ta sẽ tạo hai file chính cho phần này.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/k8s_orchestrator.png' alt='Chỉ huy với Kubernetes'>**"Bản thiết kế" cho Cơ sở dữ liệu (PostgreSQL + pgvector)**Đầu tiên, hãy định nghĩa cơ sở dữ liệu PostgreSQL của chúng ta, bao gồm cả việc giữ dữ liệu bền vững và cách truy cập nội bộ. Bạn hãy tạo file `k8s/postgres-k8s.yaml` với nội dung sau:`apiVersion: apps/v1 kind: Deployment metadata: name: postgres spec: replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: pgvector/pgvector:pg17 ports: - containerPort: 5432 env: - name: POSTGRES_DB value: "smarthire" - name: POSTGRES_USER value: "YOUR_USER" - name: POSTGRES_PASSWORD value: "YOUR_PASSWORD" # ❗ Trong môi trường sản xuất, nhớ dùng K8s Secrets nhé! volumeMounts: - name: postgres-data mountPath: /var/lib/postgresql/data volumes: - name: postgres-data persistentVolumeClaim: claimName: postgres-pvc---apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi---apiVersion: v1 kind: Service metadata: name: postgres spec: type: ClusterIP ports: - port: 5432 targetPort: 5432 selector: app: postgres`Những phần quan trọng trong `postgres-k8s.yaml` là:* **Deployment:** Sẽ chạy image `pgvector/pgvector:pg17`. Chúng ta đã đặt các biến môi trường quan trọng như `POSTGRES_DB`, `POSTGRES_USER`, và `POSTGRES_PASSWORD` trực tiếp vào đây để đơn giản (nhưng với mật khẩu, trong môi trường sản xuất, `Kubernetes Secrets` là lựa chọn được khuyến nghị nhé!). Nó cũng "gắn" một persistent volume (ổ đĩa bền vững) cho dữ liệu.* **PersistentVolumeClaim (PVC):** Với tên `postgres-pvc`, nó yêu cầu 1Gi bộ nhớ, đảm bảo dữ liệu cơ sở dữ liệu của chúng ta không bị mất nếu pod bị khởi động lại. Cứ như có một ổ cứng riêng cho database vậy!* **Service:** Được đặt tên là `postgres` và có `type: ClusterIP`, điều này cung cấp cho cơ sở dữ liệu của chúng ta một địa chỉ IP nội bộ, ổn định và tên DNS (`postgres:5432`) để ứng dụng của chúng ta có thể "liên lạc" với nó từ bên trong cụm cluster.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/postgres_k8s_diagram.png' alt='Bản thiết kế database trên Kubernetes'>**"Bản thiết kế" cho Ứng dụng AI Resume Matcher (app-k8s.yaml)**Tiếp theo, là file manifest cho ứng dụng Spring Boot AI Resume Matcher của chúng ta. Bạn hãy tạo file `k8s/app-k8s.yaml` với nội dung sau:`apiVersion: apps/v1 kind: Deployment metadata: name: smarthire-app spec: replicas: 1 selector: matchLabels: app: smarthire-app template: metadata: labels: app: smarthire-app spec: containers: - name: smarthire-app image: smarthire-app:v1 # Image của chúng ta từ Bước 1 imagePullPolicy: IfNotPresent # Ưu tiên dùng image cục bộ nếu có ports: - containerPort: 8080 env: - name: SPRING_DATASOURCE_URL value: "jdbc:postgresql://postgres:5432/smarthire" - name: SPRING_DATASOURCE_USERNAME value: "YOUR_USER" - name: SPRING_DATASOURCE_PASSWORD value: "YOUR_PASSWORD" - name: GOOGLE_APPLICATION_CREDENTIALS value: /etc/gcp-auth/key.json volumeMounts: - name: gcp-sa-key-volume mountPath: /etc/gcp-auth readOnly: true volumes: - name: gcp-sa-key-volume secret: secretName: gcp-sa-key # Secret GCP (sẽ tạo ở Bước 4)---apiVersion: v1 kind: Service metadata: name: smarthire-app spec: type: LoadBalancer ports: - port: 8090 # Cổng ngoài targetPort: 8080 # Cổng nội bộ của ứng dụng selector: app: smarthire-app`Những phần quan trọng trong `app-k8s.yaml` là:* **Deployment:** Sẽ chạy image `smarthire-app:v1` của chúng ta (với `imagePullPolicy: IfNotPresent` để ưu tiên các image cục bộ). Nó đặt các biến môi trường cho việc kết nối cơ sở dữ liệu (`SPRING_DATASOURCE_URL`, username, password) và cho thông tin xác thực Google Cloud (`GOOGLE_APPLICATION_CREDENTIALS`). Thông tin xác thực Google Cloud sẽ được "gắn" từ một Secret tên là `gcp-sa-key` (mà chúng ta sẽ tạo ở Bước 4).* **Service:** Được đặt tên là `smarthire-app` và có `type: LoadBalancer`, điều này giúp ứng dụng của chúng ta có thể truy cập được từ bên ngoài (ví dụ, qua `localhost:8090` trong Docker Desktop). Nó "ánh xạ" cổng bên ngoài 8090 tới cổng nội bộ 8080 của ứng dụng.Với các file manifest này đã định nghĩa cấu trúc ứng dụng của chúng ta trong Kubernetes, chúng ta đã sẵn sàng chuyển sang bước cực kỳ quan trọng: xử lý xác thực Google Cloud.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/app_k8s_diagram.png' alt='Bản thiết kế ứng dụng trên Kubernetes'>🔑 **Bước 4: "Mở khóa" Google Cloud từ Kubernetes**Ứng dụng AI Resume Matcher của chúng ta cần "trò chuyện" với các dịch vụ Google Vertex AI. Khi chạy cục bộ (không trong container), nó thường tự động lấy thông tin xác thực người dùng từ gcloud CLI. Tuy nhiên, bên trong một Kubernetes pod, mọi chuyện lại khác một trời một vực! Nó cần một cách riêng để xác thực an toàn.Để làm được điều này, chúng ta sẽ sử dụng một Tài khoản Dịch vụ (Service Account) của Google Cloud và file JSON key của nó. Sau đó, chúng ta sẽ lưu trữ khóa này như một Kubernetes Secret và "gắn" nó vào pod của ứng dụng. Cứ như trao "chìa khóa vàng" vậy đó!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/gcp_auth_k8s.png' alt='Xác thực Google Cloud từ Kubernetes'>**Những gì cần chuẩn bị cho xác thực GCP*** **Tài khoản Dịch vụ Google Cloud** với các quyền thích hợp để sử dụng Vertex AI (ví dụ: vai trò "Vertex AI User").* **File JSON key** của tài khoản dịch vụ này, đã được tải xuống máy tính cục bộ của bạn. Hãy giữ file này thật cẩn thận và bảo mật nhé!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/service_account_key.png' alt='Key tài khoản dịch vụ Google Cloud'>**Tạo Kubernetes Secret**Giờ thì, hãy lấy file JSON key bạn vừa tải xuống và tạo một Kubernetes Secret từ nó. Secret này sẽ lưu trữ khóa một cách an toàn bên trong cụm Kubernetes của bạn.Mở terminal và chạy lệnh `kubectl` sau. Đảm bảo bạn đã thay thế `/path/to/your-downloaded-service-account-key.json` bằng đường dẫn thực tế đến file key của bạn nhé. Tên của file key không quá quan trọng, mà là nội dung và đường dẫn bạn cung cấp trong lệnh.`kubectl create secret generic gcp-sa-key --from-file=key.json=/path/to/your-downloaded-service-account-key.json`Cùng "giải mã" lệnh này một chút:* `kubectl create secret generic gcp-sa-key`: Lệnh này nói với Kubernetes rằng hãy tạo một generic secret có tên là `gcp-sa-key`. Đây chính xác là tên (`gcp-sa-key`) mà file manifest triển khai ứng dụng của chúng ta (`app-k8s.yaml`) mong đợi cho secret này.* `--from-file=`: Cờ này cho `kubectl` biết rằng hãy tạo secret từ nội dung của một hoặc nhiều file.* `key.json`: Đây sẽ là tên file bên trong dữ liệu secret. Triển khai ứng dụng của chúng ta được cấu hình để tìm kiếm tên file cụ thể này (`key.json`) khi secret được gắn làm một volume.* `/path/to/your-downloaded-service-account-key.json`: Đường dẫn thực tế đến file JSON key trên hệ thống cục bộ của bạn.Sau khi chạy lệnh này, Kubernetes sẽ lưu trữ nội dung của file JSON key của bạn vào secret `gcp-sa-key`.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/kubectl_create_secret.png' alt='Tạo Kubernetes Secret với kubectl'>🚀 **Bước 5: "Thả neo" - Triển khai ứng dụng với các Manifest của chúng ta!**Chúng ta sẽ sử dụng lệnh `kubectl apply` để triển khai ứng dụng của mình bằng cách áp dụng các file YAML từ Bước 3. Hãy đảm bảo terminal của bạn đang ở thư mục gốc của dự án, nơi chứa thư mục `k8s` nhé.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/k8s_deploy.png' alt='Triển khai ứng dụng trên Kubernetes'>**Áp dụng các Manifest**Bạn hãy áp dụng file manifest của cơ sở dữ liệu trước, sau đó mới đến file manifest của ứng dụng nhé:`kubectl apply -f k8s/postgres-k8s.yaml``kubectl apply -f k8s/app-k8s.yaml`Những lệnh này sẽ "ra lệnh" cho Kubernetes tạo ra các deployment, service và các tài nguyên khác mà chúng ta đã định nghĩa. Nghe có vẻ phức tạp nhưng thực ra chỉ là "bảo" Kubernetes làm theo bản thiết kế thôi mà!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/apply_manifests.png' alt='Áp dụng các file manifest'>**Kiểm tra "sản phẩm" đã triển khai**Giờ hãy cùng "ngó nghiêng" nhanh xem mọi thứ đã chạy đúng như ý chưa nào:<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/verify_deployment.png' alt='Kiểm tra triển khai'>**Kiểm tra trạng thái của các Pod:**Để xem các pod của ứng dụng và cơ sở dữ liệu đã "lên sóng" và chạy tốt chưa:`kubectl get pods`Bạn sẽ thấy các pod cho `postgres` và `smarthire-app` cuối cùng đạt trạng thái `Running`. Quá trình này có thể mất một chút thời gian, bạn cứ kiên nhẫn nhé!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/kubectl_get_pods.png' alt='Kiểm tra trạng thái Pods'>**Kiểm tra Service của ứng dụng:**Để biết cách truy cập vào ứng dụng đã triển khai của bạn:`kubectl get services`Tìm service `smarthire-app` trong danh sách. Nếu bạn đang dùng Docker Desktop, `TYPE` của nó sẽ là `LoadBalancer`, và `EXTERNAL-IP` nên là `localhost`. Hãy chú ý cột `PORT(S)` – nó sẽ hiển thị dạng `8090:XXXXX/TCP`. Điều này có nghĩa là ứng dụng của bạn sẽ có thể truy cập được tại `http://localhost:8090`. Ngon lành cành đào rồi!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/kubectl_get_services.png' alt='Kiểm tra Services với kubectl'>**Kiểm tra nhật ký (Logs) của ứng dụng:**Nếu pod `smarthire-app` không "hợp tác" như mong đợi (hoặc đơn giản là bạn muốn xem nó "nói gì", ví dụ như các thông báo khởi động của Spring Boot), bạn có thể kiểm tra nhật ký của nó.Đầu tiên, hãy lấy tên chính xác của pod (nó sẽ bắt đầu bằng `smarthire-app-`):`kubectl get pods`Sau đó, hiển thị nhật ký cho pod cụ thể đó (nhớ thay thế `<smarthire-app-pod-name>` bằng tên thực tế từ lệnh trên nhé!):`kubectl logs <smarthire-app-pod-name>`Để theo dõi nhật ký trong thời gian thực (rất hữu ích để xem các yêu cầu hoặc lỗi trực tiếp), hãy sử dụng cờ `-f`:`kubectl logs -f <smarthire-app-pod-name>`Nếu pod `smarthire-app` của bạn đang `Running` và nhật ký hiển thị thông báo khởi động thành công (như "bức tranh ASCII" của Spring Boot và thông báo "Tomcat started on port(s): 8080"), vậy thì xin chúc mừng một lần nữa! Ứng dụng AI Resume Matcher của bạn giờ đây đã chạy "bon bon" trên cụm Kubernetes cục bộ rồi đó! 🎉<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/kubectl_logs.png' alt='Xem nhật ký ứng dụng với kubectl'>🧪 **Bước 6: Thời gian "lái thử" - Kiểm tra ứng dụng chạy trên K8s của chúng ta!**Đến lúc kiểm tra xem em nó có hoạt động đúng như mong đợi không bằng cách gửi một yêu cầu thử nghiệm.Đầu tiên, hãy nhớ rằng ứng dụng của chúng ta sẽ có thể truy cập được tại `http://localhost:8090` (dựa trên `smarthire-app Service` với `type LoadBalancer` mà chúng ta đã kiểm tra ở Bước 5). Điểm cuối (endpoint) để tải lên CV vẫn là `/api/candidates/upload`.Bây giờ, hãy dùng `Bruno` (hoặc bất kỳ API client nào bạn yêu thích, ví dụ như Postman hay Insomnia) để gửi một yêu cầu `POST` tới `http://localhost:8090/api/candidates/upload`. Đảm bảo phần thân yêu cầu (request body) chứa nội dung văn bản thuần túy của một CV mẫu từ bài viết trước của chúng ta nhé.Bạn sẽ nhận được một phản hồi JSON thành công! Phản hồi này sẽ liệt kê các vị trí công việc phù hợp nhất với CV bạn đã cung cấp, giống hệt như phản hồi mẫu mà chúng ta đã thấy khi cài đặt bằng Docker Compose lần đầu. Tuyệt vời ông mặt trời!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/api_test_success.png' alt='Kiểm tra API ứng dụng'>🎯 **Tổng kết: AI Resume Matcher của chúng ta đã "lên đời" Kubernetes!**Chúng ta đã thành công đưa ứng dụng AI Resume Matcher từ thiết lập Docker Compose lên một tầm cao mới: triển khai nó trên cụm Kubernetes cục bộ!Cùng nhau, chúng ta đã đi qua một hành trình đầy thú vị:* **Đóng gói (Containerizing)** ứng dụng Java Spring Boot của chúng ta bằng Docker.* **"Chế tác" (Crafting)** các file Kubernetes manifest cần thiết cho cả ứng dụng và cơ sở dữ liệu PostgreSQL của nó.* **Cấu hình (Configuring)** xác thực Google Cloud từ bên trong Kubernetes bằng Tài khoản Dịch vụ (Service Account) và Secrets.* **Triển khai (Deploying)** tất cả các thành phần vào cụm cluster.* **Kiểm tra (Testing)** ứng dụng để đảm bảo nó đang chạy đúng như mong đợi.Qua việc thực hiện từng bước này, bạn không chỉ khiến AI Resume Matcher của chúng ta chạy trong một môi trường mạnh mẽ hơn, được điều phối chặt chẽ hơn, mà còn tích lũy được kinh nghiệm thực tế với các kỹ thuật DevOps cốt lõi và công nghệ cloud-native.Bạn có gặp phải bất kỳ thách thức nào không, hay có gợi ý gì để cải thiện không? Hãy cho chúng tôi biết trong phần bình luận bên dưới nhé!👉 **Bạn có thể xem mã nguồn dự án trên GitHub tại đây!**👨💻 Chúc bạn code thật vui vẻ!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/happy_coding_success.png' alt='Chúc mừng thành công'>
Chào các bạn lập trình viên! Bạn có đang tìm kiếm một "ngôi nhà" mới cho những ứng dụng Docker của mình, vừa mạnh mẽ, vừa tiết kiệm, lại siêu nhanh? Vậy thì hãy làm quen với Cloudflare Containers – một "vũ khí" mới toanh vừa được Cloudflare tung ra, hứa hẹn sẽ thay đổi cách chúng ta triển khai ứng dụng ở "rìa mạng" (edge) đó!Tưởng tượng thế này: bạn có thể chạy bất kỳ ứng dụng đóng gói Docker nào của mình ngay tại hơn 300 địa điểm siêu gần người dùng của Cloudflare trên toàn cầu. Điều đặc biệt là bạn chỉ cần vài dòng JavaScript "thần thánh" trong một Cloudflare Worker để điều khiển chúng. Và đây mới là điểm "ăn tiền" nè: chúng có khả năng "ngủ đông" (scale to zero) khi không ai dùng, nghĩa là bạn chỉ bị tính phí theo từng lát 10ms khi ứng dụng thực sự "tỉnh giấc" làm việc thôi! Đúng là "xài bao nhiêu, trả bấy nhiêu"!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/cloudflare_containers_edge.png' alt='Cloudflare Containers tại các địa điểm rìa mạng'>Cloudflare Containers "lấp đầy khoảng trống" giữa:* **Cloudflare Workers:** Siêu nhanh (khởi động dưới mili giây), có mặt khắp nơi, nhưng chỉ hỗ trợ V8 (JavaScript/Wasm) và RAM khá khiêm tốn (128 MB). Ngon cho các tác vụ nhỏ gọn, siêu tốc.* **Các nền tảng PaaS "luôn bật" (như Sliplane):** Đơn giản, dễ dùng, nhưng bạn phải "móc hầu bao" trả tiền 24/7, kể cả khi ứng dụng đang "ngủ khò". Phù hợp cho những ứng dụng cần hoạt động liên tục, không ngừng nghỉ.* **Kubernetes / Fargate "tự làm":** Cho bạn quyền kiểm soát tối đa ở mọi quy mô, nhưng đi kèm với hàng tá công việc "đau đầu" như quản lý cụm, cân bằng tải, phân quyền (IAM) – đủ làm bạn "toát mồ hôi hột"!Containers chính là "đứa con lai" hoàn hảo, thừa hưởng tốc độ "cận kề người dùng" và mô hình tính phí linh hoạt "dùng bao nhiêu trả bấy nhiêu" từ Workers, nhưng lại dành cho những ứng dụng cần một "ngôi nhà" Linux đầy đủ, rộng rãi hơn nhiều. **Tại sao Cloudflare Containers lại "hot" đến vậy?** <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/containers_languages_tools.png' alt='Các ngôn ngữ và công cụ hỗ trợ trên Cloudflare Containers'>1. **Chạy được mọi "chiêu trò":** Từ các chương trình biên dịch gốc (native binaries) như FFmpeg (xử lý video), thư viện khoa học dữ liệu Pandas, cho đến cả những bộ công cụ AI phức tạp – tất tần tật đều "chơi được"!2. **Đa dạng ngôn ngữ "thần sầu":** Không còn bó buộc với JavaScript hay WebAssembly nữa! Giờ đây, bạn có thể "bung lụa" với Go, Rust, Python, Java, Ruby, hay bất cứ ngôn ngữ nào mà Dockerfile của bạn hỗ trợ. Tha hồ sáng tạo nhé!3. **Tài nguyên "khủng" hơn:** Với "ví tiền" rộng rãi hơn, bạn có thể có tới 4 GiB RAM và nửa vCPU cho mỗi instance (và Cloudflare hứa hẹn sẽ có những gói lớn hơn nữa trong tương lai).4. **Trạng thái riêng biệt cho từng người dùng:** Mỗi container có thể gắn với một Durable Object ID, giúp bạn dễ dàng duy trì "phiên làm việc dính" (sticky sessions) cho từng khách hàng, cứ như họ có một "hộp cát" riêng vậy.5. **Lý tưởng cho các tác vụ "bùng nổ":** Các công việc định kỳ (cron jobs), chạy thử code theo yêu cầu, hay xuất video "on-demand" là những ứng dụng hoàn hảo cho Containers. Nếu code của bạn hay "ngủ" thì việc "scale to zero" sẽ tiết kiệm chi phí hơn rất nhiều so với việc trả tiền cho một container "luôn bật" (dù là Sliplane, VPS hay dyno quản lý). Tiền điện không hề tốn khi máy tắt đúng không nào?<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/scale_to_zero_sleep.png' alt='Mô hình scale to zero tiết kiệm chi phí'>**Vậy nó hoạt động "thần kỳ" như thế nào?**Cực kỳ đơn giản! Cloudflare đã tối giản mọi thứ để bạn không phải "vật lộn" với cấu hình phức tạp.1. **Khởi tạo + Triển khai (chỉ vài giây):** Mở terminal và gõ:`npm create cloudflare@latest -- --template=cloudflare/templates/containers-template`Sau đó, để triển khai:`wrangler deploy`2. **Định tuyến yêu cầu trong Worker của bạn (đoạn code "quyền năng"):** Bạn chỉ cần thêm vài dòng JavaScript vào Worker của mình để điều khiển:```javascriptimport { Container, getRandom } from "@cloudflare/containers";class API extends Container { defaultPort = 8080; sleepAfter = "10m"; // Container sẽ "ngủ" sau 10 phút không hoạt động}export default { async fetch(req, env) { const instance = getRandom(env.API, 3); // Một "trợ lý" nhỏ giúp chia đều yêu cầu đến 3 container return instance.fetch(req); },};```Lần đầu tiên có yêu cầu, container sẽ trải qua một "khởi động lạnh" (cold-start), mất khoảng 2-3 giây (trong giai đoạn beta). Đừng lo, sau đó nó sẽ "ấm áp" và sẵn sàng phục vụ cho đến khi "ngủ thiếp đi" sau khoảng thời gian `sleepAfter` bạn cài đặt.Và điều thú vị nhất là: Đằng sau hậu trường, mỗi container được "gắn kết" với một Durable Object, thứ này sẽ lo liệu toàn bộ "vòng đời" và định tuyến cho container của bạn. Không cần "đau đầu" với file YAML, không cần quản lý các "node" phức tạp – tất cả chỉ là code! Thật gọn gàng phải không?<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/containers_pricing.png' alt='Minh họa mô hình tính phí của Cloudflare Containers'>**Làm rõ chuyện "tiền nong": Một cái nhìn tổng quan về giá**Mô hình tính phí của Cloudflare Containers cực kỳ chi tiết, theo từng giây:* **Bộ nhớ:** 0.0000025 USD / GiB-giây* **CPU:** 0.000020 USD / vCPU-giây* **Disk:** 0.00000007 USD / GB-giâyTrong giai đoạn beta, có các kích thước instance là "dev" (256 MiB), "basic" (1 GiB) và "standard" (4 GiB). Các kích thước lớn hơn sẽ sớm ra mắt nhé. **Một ví dụ "đau đầu" về chi phí:**Hãy giả định bạn chạy một instance "standard" (4 GiB RAM, nửa vCPU, 4 GB disk) liên tục 24/7 trong 30 ngày và xử lý 2 TB traffic. Thực ra, trường hợp này lại phù hợp hơn với một PaaS "luôn bật" đó.Tổng chi phí ước tính sẽ rơi vào khoảng 77.44 USD/tháng (chưa kể 5 USD phí đăng ký Workers Paid). Tổng cộng khoảng 82.44 USD.Trong khi đó, một PaaS "luôn bật" tương đương (như Sliplane hay một VPS nhỏ) có thể chỉ tốn từ 7 đến 15 USD/tháng. **Mẹo nhỏ quan trọng:**Nếu ứng dụng của bạn có xu hướng "ngủ" phần lớn thời gian trong ngày thì dùng Cloudflare Containers sẽ tiết kiệm hơn rất nhiều. Ngược lại, những dịch vụ chạy liên tục, tiêu thụ băng thông cao, thì PaaS "luôn bật" vẫn là lựa chọn kinh tế hơn. Cứ hình dung như bạn bật đèn phòng ngủ cả ngày thì tốn điện hơn là chỉ bật khi dùng thôi!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/containers_comparison.png' alt='So sánh Cloudflare Workers, Containers và PaaS'>**Những "điểm trừ" nhỏ (giới hạn beta hiện tại):** * **Mở rộng thủ công:** Bạn vẫn cần tự "ra lệnh" mở rộng bằng cách gọi `get(id)`. Cloudflare đang phát triển tính năng tự động mở rộng và định tuyến theo độ trễ, nên hãy kiên nhẫn nhé!* **Disk "ảo diệu":** Sau mỗi lần "ngủ dậy", bạn sẽ có một file system mới toanh. Tức là dữ liệu trên disk không được lưu giữ sau khi container "ngủ". Cần lưu ý nếu ứng dụng của bạn cần lưu trữ dữ liệu.* **Giới hạn tài khoản tạm thời:** Mức giới hạn hiện tại là 40 GiB RAM và 20 vCPU cho mỗi tài khoản.* **Chỉ Linux/amd64:** Hiện tại chỉ hỗ trợ kiến trúc này, chưa có ARM.* **Không TCP hay UDP trực tiếp:** Mọi thứ đều phải đi qua các lời gọi HTTP thông qua một Worker. **Khi nào thì "triển" Cloudflare Containers, khi nào thì "trung thành" với PaaS "luôn bật"?** * **Tạo ảnh AI ở rìa mạng, nhưng hầu hết thời gian nhàn rỗi:** ✅ Cloudflare Containers (tiết kiệm khi không chạy).* **API REST chạy 24/7 với hơn 70% mức sử dụng:** ✅ PaaS "luôn bật" (đơn giản, chi phí ổn định, thấp hơn).* **Sandbox riêng biệt cho từng người dùng (mỗi người một container):** ✅ Cloudflare Containers (linh hoạt, scale theo nhu cầu).* **Database cần lưu trữ liên tục (persistent volumes):** ✅ PaaS "luôn bật" (vì Containers disk là ephemeral – tức thời). **Mô hình "lai" thường thắng lớn!**<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/hybrid_architecture.png' alt='Mô hình kiến trúc hỗn hợp Cloudflare Containers và PaaS'>Bạn hoàn toàn có thể kết hợp những điểm mạnh của cả hai: chạy database cần lưu trữ liên tục trên một nền tảng PaaS như Sliplane, còn các tác vụ tính toán "bùng nổ" thì đẩy sang Cloudflare Containers, sau đó kết nối tất cả lại bằng một Cloudflare Worker. Thật là một "combo" mạnh mẽ! **Lời kết "ngọt ngào":**Cloudflare vừa giới thiệu một dịch vụ mà về cơ bản là "Fargate serverless" phiên bản "rìa mạng": chạy ảnh Docker, tính phí theo mili giây, có mặt ở khắp mọi nơi trên toàn cầu, và bạn không cần "đau đầu" với việc quản lý các cụm máy chủ phức tạp. Nếu bạn từng thấy "chiếc hộp" V8 của Workers hơi "chật chội", hoặc container "luôn bật" của bạn cứ "ngủ" suốt ngày, thì hãy thử ngay Cloudflare Containers beta xem những gì "rìa mạng" có thể mang lại nhé!Chúc bạn coding vui vẻ và "hack" thành công!Thân ái,Jonas, Đồng sáng lập Sliplane
Hướng dẫn chi tiết cách cài đặt và sử dụng Gemini CLI cùng Docker MCP Toolkit để tối ưu quy trình phát triển AI. Khám phá lợi ích vượt trội về hiệu suất, linh hoạt và hiệu quả.
Khám phá cách tối ưu WordPress để xử lý 5000 người dùng đồng thời chỉ với 1GB RAM VPS giá $6/tháng, đạt thời gian phản hồi dưới 200ms nhờ chiến lược 4 lớp cache và stack Docker hiệu quả.
Bạn đã sẵn sàng nâng cấp ứng dụng AI Resume Matcher từ Docker Compose lên Kubernetes chưa? Bài viết này sẽ hướng dẫn từng bước container hóa, triển khai và bảo mật ứng dụng Java Spring Boot AI với Google Vertex AI trên Kubernetes cục bộ, biến ý tưởng thành sản phẩm sẵn sàng cho môi trường thực tế.
Hướng dẫn chi tiết cách triển khai ứng dụng AI sử dụng LangChain và FastAPI, đóng gói gọn gàng trong Docker container. Học cách tạo LangChain agent, xây dựng API với FastAPI và deploy dễ dàng.
Hướng dẫn chi tiết, vui vẻ cách cài đặt và cấu hình Minikube trên Arch Linux, sử dụng KVM/QEMU để tạo cụm Kubernetes "bỏ túi" ngay trên máy tính cá nhân. Thích hợp cho lập trình viên và DevOps muốn thử nghiệm Kubernetes dễ dàng.
Chào mừng các bạn đến với thế giới "phù thủy" của lập trình! Bạn đã sẵn sàng "nâng cấp" khả năng code của mình lên một tầm cao mới với sự trợ giúp của AI chưa? Trong bài viết này, chúng ta sẽ cùng nhau khám phá một bộ đôi "siêu phẩm" giúp việc phát triển phần mềm có AI hỗ trợ trở nên dễ dàng và mạnh mẽ hơn bao giờ hết: đó chính là **Gemini CLI** kết hợp với **Docker MCP Toolkit**. Tưởng tượng xem, không cần IDE cồng kềnh, không cần cấu hình phức tạp, bạn vẫn có thể biến terminal của mình thành một "trung tâm điều khiển AI" cực đỉnh! Nghe hấp dẫn đúng không? Cùng tôi "nhảy" vào tìm hiểu ngay thôi! Đầu tiên, phải kể đến **Gemini CLI** – "trợ lý AI" đến từ Google, một tay chơi mã nguồn mở cực kỳ xịn sò, đưa sức mạnh của Gemini 2.5 Pro thẳng vào terminal của bạn. Thôi rồi cái thời phải chuyển tab qua lại giữa trình duyệt và cửa sổ code nữa nhé! Gemini CLI mang lại những lợi ích không tưởng: * Hòa mình vào terminal: Tích hợp trực tiếp vào quy trình làm việc của bạn, không cần rời khỏi màn hình đen huyền bí! * "Bộ nhớ" siêu khủng: Với cửa sổ ngữ cảnh lên đến 1 triệu token, nó có thể "ngấu nghiến" cả những codebase đồ sộ nhất mà không sợ "não cá vàng". * Hỗ trợ công cụ "xịn": Nhờ có Model Context Protocol (MCP), Gemini CLI có thể tương tác với hàng loạt công cụ phát triển khác. Cứ như có cả một đội quân hậu cần vậy! * "Chơi" miễn phí: Cung cấp gói miễn phí với giới hạn sử dụng cực kỳ "hào phóng". Ngại gì không thử? * Thực thi code và quản lý file "thần tốc": Làm việc trực tiếp với code và file của bạn theo thời gian thực. Đúng chuẩn "làm đến đâu, kiểm tra đến đó"! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijij2ylz22baghy7jflw.png' alt='Giao diện Gemini CLI'> Tiếp theo là **Docker MCP Toolkit** – "phù thủy" biến cách các AI agents tương tác với công cụ phát triển trở nên dễ dàng hơn bao giờ hết! Thay vì phải loay hoay cấu hình từng server MCP một cách thủ công (nghe thôi đã thấy đau đầu rồi!), bạn sẽ có ngay: * Hơn 100+ server MCP: Một kho tàng server được cấu hình sẵn, đủ mọi thể loại trong "danh mục" cho bạn tha hồ lựa chọn! * Cài đặt công cụ chỉ với một cú click: Đúng vậy, "một cú click" thôi là xong, không cần đợi chờ mỏi mòn. * Môi trường chạy an toàn, "đóng hộp": Mọi thứ được gói gọn trong container, đảm bảo an toàn và không gây xung đột. * Kiến trúc Gateway "thân thiện": Giúp việc kết nối từ client trở nên siêu đơn giản. * Quản lý tài khoản "tự động": Tích hợp OAuth và quản lý thông tin đăng nhập, khỏi lo quên mật khẩu hay rắc rối xác thực. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7k1gr69u2xcuwdc1p6p.png' alt='Cài đặt Docker Desktop và kích hoạt MCP Toolkit'> Vậy tại sao cặp đôi "hoàn hảo" Gemini CLI và Docker MCP Toolkit lại được tôi hết lời ca ngợi như vậy? Đơn giản vì nó mang lại những lợi thế "độc quyền" mà bạn khó lòng tìm thấy ở nơi khác, biến nó thành lựa chọn lý tưởng cho mọi quy trình phát triển hiện đại: * Về hiệu suất (Performance Benefits): Khởi động "chớp nhoáng"; Truy cập hệ thống "tẹt ga"; Tiết kiệm bộ nhớ. * Về sự linh hoạt (Flexibility Advantages): Thích ứng mọi terminal; Nói không với xung đột; Di động mọi nơi; Độc lập cập nhật. * Về hiệu quả làm việc (Workflow Efficiency): Giao diện "tất cả trong một"; Chuyển đổi ngữ cảnh "mượt mà"; Thực thi lệnh trực tiếp; Hòa nhập tự nhiên. Ok, bây giờ là phần mà ai cũng mong chờ: **Hướng dẫn cài đặt "siêu tốc"**! Đừng lo, tôi sẽ chia sẻ từng bước một, dễ như ăn kẹo! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/setup_guide_icon.png' alt='Hướng dẫn cài đặt từng bước'> **Đầu tiên, chuẩn bị "hành trang" (Prerequisites):** 1. **Cài đặt Docker Desktop:** Nếu bạn chưa có, hãy tải và cài đặt Docker Desktop ngay nhé. Đây là "cửa ngõ" để chúng ta mở khóa sức mạnh của MCP Toolkit. 2. **Kích hoạt Docker MCP Toolkit:** Mở Docker Desktop lên, vào phần cài đặt và tìm mục MCP Toolkit để bật nó. 3. **Kích hoạt ít nhất 1 MCP Server:** Trong MCP Toolkit, bạn cần bật ít nhất một server. Ví dụ như Docker, GitHub, Firecrawl, Kubernetes, hay Slack. Cái này giống như bạn chọn công cụ mà AI sẽ được phép "chọc ghẹo" và làm việc cùng vậy. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0ucjrflkuwwectzds9k.png' alt='Kích hoạt MCP Servers trong Docker Desktop'> 4. **"Đồ nghề" cần có:** Node.js (phiên bản 18 trở lên); Tài khoản Google; Hiểu biết sơ bộ về GitHub gemini/gemini-cli. **Bước 2: Cài đặt Gemini CLI – Đơn giản như đang giỡn!** Mở terminal lên và gõ lệnh sau: `npm install -g @google/gemini-cli` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/npm_install_gemini.png' alt='Lệnh cài đặt Gemini CLI qua npm'> **Bước 3: Khởi động và Xác thực – Điểm cuối cùng!** Sau khi cài đặt xong, chỉ cần gõ lệnh "thần chú" này để khởi động Gemini và thực hiện xác thực: `gemini` Nó sẽ dẫn bạn đến trang đăng nhập Google để cấp quyền. Xong xuôi là bạn đã sẵn sàng "hô biến" rồi đó! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijij2ylz22baghy7jflw.png' alt='Giao diện Gemini CLI sau khi xác thực'> Vậy là xong! Giờ thì bạn đã có một "trợ lý AI" siêu cấp ngay trong terminal của mình rồi. Hãy bắt đầu khám phá và biến những ý tưởng code phức tạp thành hiện thực một cách dễ dàng hơn bao giờ hết nhé!
Bạn đã sẵn sàng "nâng cấp" ứng dụng AI của mình lên một tầm cao mới chưa? Trong thế giới AI ngày càng phức tạp, việc triển khai một ứng dụng AI mượt mà, dễ quản lý và sẵn sàng cho môi trường sản phẩm luôn là ưu tiên hàng đầu. Đừng lo, hôm nay chúng ta sẽ khám phá một công thức "thần thánh" để biến ý tưởng đó thành hiện thực: kết hợp Docker, FastAPI và LangChain vào một quy trình đóng gói duy nhất, siêu mượt mà! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/AI_app_deployment.png' alt='Mô hình triển khai ứng dụng AI với Docker, FastAPI, LangChain'> 🧠 Tổng Quan: Biến Ứng Dụng AI Của Bạn Thành "Siêu Phẩm Đóng Hộp"! Tưởng tượng ứng dụng AI của bạn là một siêu đầu bếp tài năng. Để đầu bếp này làm việc hiệu quả mọi lúc mọi nơi mà không lo thiếu nguyên liệu hay dụng cụ, chúng ta cần một "nhà bếp di động" hoàn hảo. Đó chính là lúc bộ ba Docker, FastAPI và LangChain tỏa sáng! Trong bài viết này, bạn sẽ học cách "đóng hộp" (dockerize) một LangChain agent thông minh bằng FastAPI – tạo ra một container sẵn sàng để triển khai, cực kỳ thân thiện với môi trường sản xuất. Khi kết thúc hành trình này, bạn sẽ tự tin: * Tạo ra một "trợ lý AI" siêu thông minh – đó chính là LangChain agent. * Biến trợ lý ấy thành một "người phục vụ" chuyên nghiệp với giao diện REST rõ ràng bằng FastAPI. * "Đóng gói" toàn bộ hệ thống này vào một "chiếc hộp thần kỳ" mang tên Docker. * Và cuối cùng, chỉ cần một câu lệnh đơn giản là có thể "mở hộp" và chạy ứng dụng của bạn ở bất cứ đâu! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/dream_team_devops.png' alt='Sự kết hợp hoàn hảo của Docker, FastAPI và LangChain'> 📦 Chuẩn Bị Hành Trang: Những Thứ Bạn Cần Có Trước khi chúng ta bắt tay vào cuộc phiêu lưu này, hãy đảm bảo bạn đã có đầy đủ "đồ nghề" nhé: * **Docker:** Hãy cài đặt Docker! Nó như chiếc "hộp công cụ vạn năng" của chúng ta vậy. * **Python 3.10+:** Để chạy và kiểm tra ứng dụng "tại nhà" (trên máy tính của bạn). * **API Key của OpenAI (hoặc LLM khác):** Đây là "chìa khóa" để trợ lý AI của chúng ta có thể làm việc với các mô hình ngôn ngữ lớn (LLM). 📁 Cấu Trúc Dự Án: Sắp Xếp Ngăn Nắp Là Nửa Thành Công! Việc sắp xếp dự án gọn gàng sẽ giúp bạn dễ quản lý hơn rất nhiều. Dưới đây là cấu trúc mà chúng ta sẽ theo dõi: ``` langchain-agent-api/ ├── agent_app/ │ ├── main.py │ └── agent.py ├── requirements.txt ├── Dockerfile └── .env ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/project_structure.png' alt='Cấu trúc thư mục dự án'> ✨ Bước 1: "Triệu Hồi" LangChain Agent – Trợ Lý AI Đa Năng! Đầu tiên, chúng ta sẽ tạo ra trái tim của ứng dụng: một LangChain agent. Hãy nghĩ về nó như một "bộ não" thông minh có khả năng sử dụng nhiều công cụ khác nhau để trả lời câu hỏi của bạn. File: `agent_app/agent.py` ```python from langchain.agents import initialize_agent, Tool from langchain.llms import OpenAI from langchain.utilities import SerpAPIWrapper import os def create_agent(): # Khởi tạo mô hình ngôn ngữ lớn (LLM) của OpenAI. # Nhiệt độ (temperature=0) đảm bảo câu trả lời nhất quán, ít "sáng tạo". llm = OpenAI(temperature=0, openai_api_key=os.getenv("OPENAI_API_KEY")) # Công cụ tìm kiếm: SerpAPIWrapper giúp agent "tra cứu" thông tin trên internet. # Tưởng tượng nó như "Google" riêng của agent vậy! search = SerpAPIWrapper() # Định nghĩa các công cụ mà agent có thể sử dụng. # Ở đây chúng ta có công cụ "Search" để tìm kiếm thông tin chung. tools = [Tool(name="Search", func=search.run, description="Useful for answering general questions.")] # Khởi tạo agent với các công cụ và LLM. # "zero-shot-react-description" là một loại agent có thể tự suy luận để chọn công cụ phù hợp. # "verbose=True" giúp chúng ta nhìn thấy quá trình agent "suy nghĩ" gì. agent = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True) return agent ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/langchain_agent.png' alt='LangChain Agent và các công cụ'> 🚀 Bước 2: Bọc "Bộ Não" AI Bằng FastAPI – Giao Diện Giao Tiếp Siêu Tốc! Sau khi có "bộ não" LangChain agent, làm sao để người dùng có thể "nói chuyện" với nó? Chính là nhờ FastAPI! FastAPI là một framework web cực kỳ nhanh, giúp chúng ta dễ dàng tạo ra các API (giao diện lập trình ứng dụng) để ứng dụng của mình có thể giao tiếp với thế giới bên ngoài. File: `agent_app/main.py` ```python from fastapi import FastAPI from pydantic import BaseModel from agent import create_agent # Nhập agent mà chúng ta vừa tạo # Khởi tạo ứng dụng FastAPI. Nó giống như việc "mở cửa" cho nhà hàng của bạn. app = FastAPI() # Tạo agent khi ứng dụng khởi động. agent = create_agent() # Định nghĩa một "mẫu" dữ liệu cho yêu cầu gửi đến. # Ở đây, chúng ta mong đợi một "question" (câu hỏi) dưới dạng chuỗi. class Query(BaseModel): question: str # Định nghĩa một endpoint (điểm cuối) API. # Khi có ai đó gửi yêu cầu POST đến "/ask", hàm ask_question sẽ được gọi. @app.post("/ask") async def ask_question(query: Query): # Agent sẽ chạy câu hỏi và tạo ra phản hồi. response = agent.run(query.question) # Trả về kết quả dưới dạng JSON. return {"response": response} ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/fastapi_workflow.png' alt='Workflow của FastAPI'> 📄 Bước 3: Liệt Kê "Nguyên Liệu" – File `requirements.txt` Để đảm bảo ứng dụng của chúng ta hoạt động trơn tru trong mọi môi trường, chúng ta cần một danh sách tất cả các thư viện Python cần thiết. Đây chính là công dụng của file `requirements.txt`. File: `requirements.txt` ``` fastapi uvicorn langchain openai python-dotenv ``` **Lưu ý:** Bạn có thể cần thêm `serpapi` hoặc các thư viện khác nếu agent của bạn sử dụng thêm công cụ nhé! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/requirements_txt.png' alt='File requirements.txt và các thư viện'> 🛠️ Bước 4: "Bản Thiết Kế" Của Chiếc Hộp Thần Kỳ – File `Dockerfile` Dockerfile là "bản đồ chi tiết" để Docker có thể xây dựng "chiếc hộp" (container) chứa toàn bộ ứng dụng của bạn. Nó định nghĩa mọi thứ từ hệ điều hành cơ bản cho đến cách cài đặt các thư viện và chạy ứng dụng. File: `Dockerfile` ```dockerfile # Sử dụng một ảnh Python 3.10 phiên bản nhỏ gọn làm nền. # Đây là "nền móng" cho chiếc hộp của chúng ta. FROM python:3.10-slim # Đặt thư mục làm việc bên trong container là /app. # Mọi thao tác sau đó sẽ diễn ra trong thư mục này. WORKDIR /app # Sao chép file requirements.txt vào thư mục /app trong container. COPY requirements.txt . # Cài đặt tất cả các thư viện từ requirements.txt. # --no-cache-dir giúp giữ kích thước ảnh nhỏ gọn. RUN pip install --no-cache-dir -r requirements.txt # Sao chép toàn bộ thư mục agent_app vào trong container. COPY agent_app ./agent_app # Sao chép file .env (chứa các biến môi trường) vào trong container. COPY .env . # Định nghĩa lệnh mặc định sẽ chạy khi container được khởi động. # Ở đây là chạy ứng dụng FastAPI của chúng ta bằng Uvicorn trên cổng 8000. CMD ["uvicorn", "agent_app.main:app", "--host", "0.0.0.0", "--port", "8000"] ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/dockerfile_concept.png' alt='Mô tả các lớp của Dockerfile'> 🔑 Bước 5: Giữ Bí Mật "Chìa Khóa" – File `.env` Để ứng dụng AI của chúng ta hoạt động với OpenAI và SerpAPI, nó cần các khóa API. Chúng ta sẽ lưu chúng trong một file `.env`. File: `.env` ``` OPENAI_API_KEY=your_openai_key_here SERPAPI_API_KEY=your_serpapi_key_here ``` **⚠️ Cực kỳ quan trọng:** Đừng BAO GIỜ đẩy file `.env` lên các kho mã công khai như GitHub nhé! Đây là nơi chứa những "bí mật" của bạn. Trong môi trường sản phẩm, hãy sử dụng các cơ chế quản lý biến môi trường an toàn hơn như Docker secrets hoặc biến môi trường của CI/CD. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/secure_api_keys.png' alt='Bảo mật API keys'> 🧪 Bước 6: "Đúc" Chiếc Hộp và "Khởi Chạy" Nó! Đã đến lúc biến bản thiết kế Dockerfile thành hiện thực và chạy ứng dụng của chúng ta! 🧱 Đúc ảnh Docker (Build the Docker image): Mở terminal của bạn và gõ lệnh này. Nó sẽ "đúc" toàn bộ ứng dụng của bạn vào một "chiếc hộp" di động: ```bash docker build -t langchain-agent-api . ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_build.png' alt='Lệnh Docker build'> 🚀 Khởi chạy container (Run the container): Khi chiếc hộp đã được "đúc" xong, hãy "mở hộp" và chạy nó! Lệnh này sẽ khởi động ứng dụng của bạn bên trong một container độc lập. ```bash docker run --env-file .env -p 8000:8000 langchain-agent-api ``` Lệnh này nói với Docker: "Hãy chạy cái hộp tên `langchain-agent-api` này, dùng các biến môi trường từ file `.env` và 'kết nối' cổng 8000 bên trong hộp với cổng 8000 trên máy tính của tôi nhé!" <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_run.png' alt='Lệnh Docker run'> 📬 Thử Nghiệm "Trợ Lý" Của Bạn! Khi container đã chạy, hãy thử "hỏi" trợ lý AI của bạn xem sao! Mở một terminal/command prompt khác (để container vẫn chạy ở terminal trước) và gõ lệnh `curl` này: ```bash curl -X POST http://localhost:8000/ask \ -H "Content-Type: application/json" \ -d '{"question": "Who is the CEO of OpenAI?"}' ``` Chỉ trong vài giây, "trợ lý" LangChain được đóng gói của bạn sẽ đưa ra câu trả lời! Thật vi diệu phải không nào? <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/api_test_curl.png' alt='Kiểm tra API với Curl'> 🤖📦 Bonus: Thêm Docker Compose (Tùy Chọn) Nếu bạn muốn quản lý nhiều container cùng lúc hoặc muốn quy trình khởi động đơn giản hơn, Docker Compose là một công cụ tuyệt vời! Nó cho phép bạn định nghĩa toàn bộ ứng dụng của mình (kể cả nhiều dịch vụ) trong một file YAML duy nhất. File: `docker-compose.yml` ```yaml version: "3.8" services: langchain: # Tên dịch vụ của chúng ta build: . # Docker sẽ tìm Dockerfile trong thư mục hiện tại để build ảnh ports: - "8000:8000" # Ánh xạ cổng env_file: # Sử dụng biến môi trường từ file .env - .env ``` Sau đó, chỉ cần chạy một lệnh duy nhất: ```bash docker-compose up --build ``` Lệnh này sẽ tự động build ảnh (nếu cần) và khởi động container cho bạn. Tiện lợi hơn rất nhiều phải không? <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_compose_orchestration.png' alt='Docker Compose quản lý nhiều container'> 🏁 Lời Kết: Chinh Phục Triển Khai Ứng Dụng AI! Tuyệt vời! Giờ đây, bạn đã có trong tay một "trợ lý AI" LangChain sẵn sàng cho môi trường sản xuất, được phục vụ qua FastAPI và đóng gói gọn gàng trong Docker. Dù bạn đang xây dựng các công cụ AI nội bộ hay triển khai lên đám mây, thiết lập này mang lại cho bạn khả năng lặp lại (luôn chạy giống nhau), tính di động (chạy được mọi nơi) và sức mạnh để bạn tập trung vào việc tạo ra những ứng dụng AI đỉnh cao mà không lo về hạ tầng. Hãy tiếp tục sáng tạo nhé!
Khám phá Docker MCP: Giải pháp đột phá giúp đóng gói, triển khai và quản lý các mô hình AI/ML dưới dạng container Docker chuẩn hóa, an toàn và dễ dàng tái sử dụng. Đơn giản hóa quy trình tích hợp và tăng tốc phát triển AI.