Kinh nghiệm thực tế: Observability cho Automation trong Production

Đây là một bài viết có thể rất dài, là kinh nghiệm của tôi đã từng mắc sai lầm và những cách khắc phục.

Nhìn vào báo cáo State of DevOps Vietnam 2026 của cộng đồng chúng ta thấy 74,1% hệ thống thiếu Observability chuyên nghiệp, mà tận 56,3% sử dụng Terraform, nghĩ cũng đúng mà cũng lo, cái này thì vấn đề không của riêng ai rồi cũng chính vì thế mà DevOps lương cao đấy : D

Nên nảy ra luôn vấn đề đã từng trải nghiệm, xin phép các bác chia sẻ một chút sai lầm quá khứ liên quan đến Observability mong rằng sẽ giúp ích cho các bạn mới đụng nhé.

e9c2161f-18b8-45d2-9d30-acd6751e55b1

Automation là một phần không thể thiếu trong DevOps, mục đích rất rõ ràng là giúp anh em trong công ty bớt chân tay đi. Và chính xác, thường nó chạy rất ổn trong một thời gian đủ dài để cả team bắt đầu tin rằng mọi thứ đã XONG. Và automation không hỏng ngay từ ngày đầu.

Đến một ngày nó fail.

Lúc đó mới lộ ra thứ hệ thống đang thiếu không phải thêm một cái tool mới, đoạn script mới, cũng không phải thêm vài dòng retry. Thứ thiếu thường là observability cho chính automation đó, ngày đó gần như là tôi cũng không có khái niệm như vậy. Gánh hậu quả ngay : D

Một cleanup job đụng nhầm resource. Một đợt secret rotation lệch nhịp làm rollout fail dây chuyền. Một autoscaler đọc sai signal rồi tự tạo retry storm. Một script sync config chạy chậm vài nhịp khiến state bắt đầu drift mà không ai nhận ra lúc đó.

Nhìn lại thì phần lớn những vụ như vậy không phải vì automation tệ hay flow viết sai hoàn toàn. Đa số nó vẫn làm đúng thứ tôi bảo nó làm. Vấn đề nằm ở chỗ khác:

  • Sssumption ban đầu sai
  • Input không còn đúng
  • Context vận hành đã đổi
  • Scope thực tế rộng hơn thứ team tưởng tượng

Và đến lúc incident xảy ra, thứ làm team mất thời gian nhất lại là mấy câu tưởng như rất cơ bản:

  • Ai trigger run này?
  • Nó chạy từ đâu?
  • Nó đang chạy version nào?
  • Nó đang ở env nào, region nào, account nào?
  • Nó dùng identity gì?
  • Nó đã action lên những target nào?
  • Nó retry bao nhiêu lần?
  • Nó bắt đầu lệch từ bước nào?

Nếu mấy câu này còn phải mò log thủ công, grep từng chỗ, hoặc đoán theo kiểu “chắc là cron”, “chắc là pipeline”, “chắc là script A gọi sang job B”… thì lúc đó team đang debug trong trạng thái gần như bị bịt mắt.

Một automation run tốt phải truy ngược được trong vài phút

Với cá nhân tôi nhìn thì khá đơn giản: một run automation không nhất thiết phải hoàn hảo, nhưng phải truy ngược được nhanh.

Tức là tới lúc on-call mở incident ra, người cầm ca không phải đi lục mò từng thứ đã xây. Chỉ cần mở đúng dashboard, đúng trace hoặc đúng log stream là lần ra được gần hết bối cảnh của run đó trong vài phút.

Một run nên cho phép trả lời được các câu sau:

  • Nó được trigger bởi schedule, webhook, pipeline hay con người?
  • Nó chạy code version nào, image tag nào, config version nào?
  • Nó chạy ở env nào, account nào, region nào, workspace nào, cluster nào?
  • Nó dùng identity nào, role nào, token scope nào?
  • Input của nó là gì, target list là gì, allowlist là gì?
  • Nó đã quyết định theo rule nào, threshold nào, branch logic nào?
  • Nó đã action gì lên những resource nào?
  • Nó mất bao lâu, retry bao nhiêu lần, fail ở đâu?

Nghe có vẻ nhiều, nhưng thực ra thiếu chỉ một hai mảnh thôi là lúc sự cố xảy ra bạn sẽ bị kẹt ngay ở đoạn xác định phạm vi ảnh hưởng.

Và một khi chưa biết rõ phạm vi, mọi quyết định phía sau đều chậm đi: rollback chậm hơn, kill switch chậm hơn, thông báo cho team khác cũng chậm hơn.

Vấn đề không phải “nó fail”, mà là “nó fail mà chẳng biết tại sao”

Automation fail là chuyện bình thường. Không hệ thống nào chạy mãi mà không có ngày fail đột ngột.

Cái mệt không nằm ở chuyện fail. Cái mệt nằm ở chuyện fail mà không nhìn ra được nó vừa làm gì.

Đây là khác biệt rất lớn giữa một hệ thống có automation có chạy và một hệ thống có automation vận hành được.

Một job có thể pass 99 lần rồi fail lần thứ 100. Điều đó chưa nói lên nó tốt hay tệ. Nhưng nếu lần fail đó mà team mất 2 tiếng mới dựng lại được:

  • Ai là người hoặc service đã trigger
  • Run đang dùng config nào
  • Nó đã đụng tới bao nhiêu target
  • Có bao nhiêu action đã thực hiện xong
  • Có action nào destructive hay chưa

Thì automation đó về mặt vận hành vẫn đang rất đắt… rất rất đắt…

Nó tiết kiệm công ở ngày thường, nhưng trả lại thành nợ rất lớn vào ngày có sự cố. Nên cả trong báo cáo ưu tiên 12 tháng tới của anh em vẫn là tối ưu chi phí hạ tầng thì đúng thôi.

Bộ tối thiểu tôi thấy đáng tiền

Với tôi thì không cố biến automation thành một hệ thống quan sát hào nhoáng. Tôi chỉ muốn đến lúc có vấn đề thì team lần dấu được nhanh và biết phải dừng ở đâu. Có vài kinh nghiệm chia sẻ với mọi người nhé.

1. Mỗi run phải có run_id, và correlation phải đi xuyên suốt

Nguyên tắc đầu tiên là mỗi lần chạy phải có một định danh rõ ràng. Không phải để đẹp, mà để mọi tín hiệu liên quan tới cùng một run nối được với nhau.

Ít nhất nên có:

  • run_id cho toàn bộ execution
  • correlation_id đi xuyên các step và các service liên quan
  • Nếu có sub-task thì có thêm step_id hoặc attempt_id

Điểm quan trọng không phải là đặt tên gì, mà là ID này phải được propagate xuyên suốt:

  • Từ trigger
  • Sang orchestrator
  • Xuống từng step
  • Qua từng API call
  • Và xuất hiện trong log, metric, audit event

Nếu có tracing distributed thì quá tốt. Không có tracing vẫn sống được, nhưng correlation_id phải hiện diện ở mọi nơi có giá trị điều tra. Không thể để log trigger một chỗ, action một chỗ, API call một chỗ mà không nối được với nhau.

Đây là thứ cực kỳ đơn giản nhưng lại quyết định bạn mất 5 phút hay 45 phút để hiểu một run.

2. Structured logs, nhưng là log để query được

Nhiều hệ thống nói là “có log”, nhưng tới lúc cần tra thì log chỉ là một đống text dài, viết cho con người đọc từng dòng chứ không phải để máy query theo field.

Với automation, log phải có cấu trúc. Không cần cầu kỳ, chỉ cần đủ để lọc, group, join theo những trục điều tra phổ biến.

Những field tối thiểu tôi thường muốn có:

  • run_id
  • trigger_source
  • actor
  • version
  • config_version
  • env
  • account
  • region
  • workspace
  • runner_identity
  • target
  • action
  • result
  • duration
  • error_type

Giá trị của structured log không nằm ở chuyện đẹp trong report (mà cũng một phần :D). Nó nằm ở việc khi incident xảy ra, bạn có thể trả lời cực nhanh các câu như:

  • Tất cả target bị đụng bởi run này là gì
  • Những run nào dùng cùng config_version
  • Lỗi đang tập trung ở action nào
  • Actor nào trigger nhiều run bất thường
  • Run nào đang dùng sai identity ở prod

Log mà không query được thì lúc bình thường nhìn tưởng ổn, nhưng lúc có chuyện sẽ biến thành thứ buộc on-call phải đọc từng dòng trong tuyệt vọng.

3. Audit trail phải đủ để dựng lại thay đổi, nhưng bắt buộc redaction

Automation đã đụng vào hạ tầng hoặc tài nguyên quan trọng thì phải để lại audit trail. Không có audit trail, postmortem rất khó tử tế vì team chỉ đoán được “chắc nó đã làm gì đó”, chứ không dựng lại được thay đổi thực tế.

Tuy nhiên audit trail cũng là nơi rất dễ trở thành ổ rò rỉ dữ liệu nếu làm ẩu.

Cách tôi thấy cân bằng nhất là:

  • Giữ change summary hoặc diff link
  • Ghi rõ resource nào bị tác động
  • Ghi rõ action loại gì: create, update, delete, rotate, grant, revoke, restart…
  • Luôn redaction phần nhạy cảm

Không nên để token, secret, raw credential, payload nhạy cảm hoặc diff lộ nội dung bí mật xuất hiện trong log hay audit event.

Một audit trail tốt là thứ giúp trả lời câu hỏi “run này đã đổi cái gì”, chứ không phải nơi copy nguyên dữ liệu nhạy cảm ra thêm một lần nữa.

4. Metrics và alert để biết nó lệch, nhưng đừng tự DDoS team

Observability không chỉ để soi từng run. Nó còn phải cho team thấy toàn bộ automation đang hành xử ra sao theo thời gian.

Các metrics tối thiểu tôi thấy rất đáng giữ:

  • Số run theo thời gian
  • Success rate
  • Số lần retry
  • Duration
  • Error types
  • Số target bị ảnh hưởng
  • Queue depth hoặc pending executions nếu có orchestrator

Mấy chỉ số này không chỉ hữu ích khi có sự cố. Nó còn giúp thấy drift hành vi trước khi incident thực sự nổ lớn. Ví dụ:

  • Duration tăng dần theo tuần
  • Retries tăng nhưng success rate chưa sập hẳn
  • Số target mỗi run bắt đầu to lên bất thường
  • error_type đổi pattern sau một lần deploy config

Alert cũng quan trọng, nhưng phải cẩn thận. Một hệ thống alert cho automation mà cứ fail là ping cả team thì sớm muộn mọi người cũng tắt cảm xúc với cảnh báo.

Tôi thường ưu tiên những trigger kiểu:

  • Fail liên tiếp vượt ngưỡng
  • Duration vượt baseline rõ rệt
  • Affected targets vượt ngưỡng
  • Retry tăng bất thường
  • Một action destructive xuất hiện ngoài khung giờ hoặc ngoài scope quen thuộc

Nói ngắn gọn: alert phải giúp team phát hiện điều đáng lo, chứ không phải biến chính observability thành nguồn gây nhiễu.

Observability cho biết chuyện gì đã xảy ra. Guardrails mới giúp giảm blast radius.

Đây là chỗ nhiều team làm chưa tới.

Nếu chỉ đầu tư khá ổn vào việc nhìn thấy automation, nhưng lại chưa đầu tư tương xứng vào việc giới hạn phạm vi phá hoại khi automation chạy sai.

Nếu chỉ có observability mà không có guardrails, thì bạn chỉ đang nhìn căn nhà cháy rõ hơn. Bạn chưa chắc dập được nhanh hơn, điều này cực quan trọng.

Phần đáng tiền nhất của automation an toàn, theo tôi là phải vừa trace được, vừa giới hạn được, vừa dừng được.

Dry-run hoặc plan mode

Với các action có ảnh hưởng rộng, automation nên có chế độ preview đủ tử tế trước khi chạy thật.

Ít nhất, team cần nhìn ra được:

  • Nó sắp đụng tới bao nhiêu target
  • Loại action là gì
  • Phạm vi nằm trong allowlist hay chưa
  • Change summary nhìn có hợp lý không

Dry-run không cứu được mọi thứ, nhưng nó chặn rất nhiều tai nạn kiểu query target sai, filter sai, hoặc config scope rộng hơn dự kiến.

Batch, rate limit và pause giữa các lô

Một automation chạy sai mà đụng 5 target thì còn cứu tương đối dễ. Một automation chạy sai mà đụng 5.000 target trong 2 phút thì câu chuyện khác hẳn.

Bởi vậy batch và rate limit gần như là lớp bảo hiểm cơ bản.

Thay vì bắn thẳng toàn bộ target cùng lúc, nên:

  • Chia nhỏ theo lô
  • Có pause giữa các batch
  • Cho phép dừng giữa chừng
  • Có cap số lượng target tối đa mỗi run

Khi có sự cố, mấy phút “trì hoãn có kiểm soát” này thường đáng tiền hơn rất nhiều so với vài phút tối ưu tốc độ ở ngày thường.

Scope constraint: phải chặn phạm vi ngay từ đầu

Nhiều automation không chết vì logic action sai. Nó chết vì scope sai.

Do đó, scope constraint nên là lớp bảo vệ bắt buộc chứ không phải lựa chọn thêm.

Những thứ tôi thấy hữu ích:

  • Allowlist theo tag, owner, namespace, environment
  • Denylist cho resource critical
  • max_targets_per_run
  • Chặn action nếu target count tăng đột biến
  • Chặn action nếu resource nằm ngoài boundary dự kiến

Một filter “thiếu một điều kiện nhỏ” trong code có thể biến thành incident rất lớn. Scope constraint giúp biến lỗi logic đó từ thảm họa thành lỗi khó chịu nhưng còn trong tầm xử lý.

Kill switch phải thật sự bấm được

Rất nhiều team có runbook ghi “nếu có sự cố thì disable automation X”. Nhưng đến lúc cần disable thật, thao tác đó lại đòi:

  • Sửa config
  • Merge code
  • Chờ pipeline
  • Hoặc SSH vào chỗ ít người nắm

Như vậy là chưa có kill switch đúng nghĩa.

Kill switch nên tồn tại ở dạng:

  • Feature flag
  • Config toggle
  • Control plane đơn giản
  • Hoặc một cơ chế pause đủ rõ, đủ nhanh, có phân quyền

Mục tiêu là người trực sự cố có thể dừng tác nhân gây hại ngay, không cần mở thêm một incident khác chỉ để đi tìm cách tắt nó.

Retry phải có backoff và jitter

Retry là một ví dụ điển hình cho chuyện một thứ sinh ra để tăng độ bền có thể tự biến thành thủ phạm.

Không có backoff và jitter, một cụm automation cùng retry rất dễ biến một lỗi tạm thời thành retry storm. Điều này đặc biệt nguy hiểm nếu automation có liên quan tới API quota, lock contention, hoặc downstream vốn đang chậm sẵn.

Bởi vậy retry muốn an toàn thì ít nhất phải có:

  • Backoff
  • Jitter
  • Retry budget
  • Và lý tưởng là awareness về error type để biết khi nào nên dừng

Không phải lỗi nào cũng đáng retry. Và không phải retry nào cũng vô hại.

Idempotency, hoặc ít nhất phải có checkpoint

Một automation tốt không chỉ chạy được. Nó còn phải chạy lại được mà không làm tình hình tệ hơn.

Idempotency là trạng thái lý tưởng. Không đạt được hoàn toàn thì ít nhất phải có:

  • Checkpoint
  • Đánh dấu step đã hoàn tất
  • Compensating action cho những chỗ rollback được
  • Khả năng resume từ giữa thay vì làm lại từ đầu

Lúc incident xảy ra, khả năng chạy lại an toàn là khác biệt rất lớn giữa khôi phục nhanhcàng thao tác càng lo.

Approval theo risk, không phải theo cảm giác

Không phải mọi automation đều cần approval. Nhưng các action destructive, khó rollback hoặc có blast radius lớn thì nên có lớp phê duyệt phù hợp với mức độ rủi ro.

Ví dụ:

  • Delete hàng loạt
  • Rotate secret diện rộng
  • Migrate config trên nhiều cluster
  • Revoke permission ở scope lớn

Approval ở đây không nên hiểu là thêm giấy tờ thủ tục. Mục tiêu là thêm một điểm dừng hợp lý trước những action mà sai một lần là trả giá rất đắt.

ChatOps tiện, nhưng đừng biến chat thành nơi rò rỉ thông tin

Nhiều team thích đẩy trạng thái automation ra chat vì tiện cho on-call theo dõi. Cách này hoàn toàn ổn, miễn là nhớ hai chuyện:

  • Access control phải rõ
  • Redaction phải nghiêm

Đừng đẩy raw diff, secret hoặc payload nhạy cảm lên kênh chat chung chỉ vì “cho tiện theo dõi”. Chat rất giỏi trong việc lan thông tin nhanh. Đó cũng chính là lý do nó nguy hiểm nếu bạn đẩy nhầm thứ không nên xuất hiện ở đó.

Câu hỏi tôi hay dùng trước khi merge automation

Trước khi merge một automation có ảnh hưởng thật lên hệ thống, tôi hay tự hỏi một câu khá đơn giản:

Nếu nó chạy sai, team có mất quá lâu để biết nó đã action gì, lên targets nào, từ lúc nào, và bằng identity nào không?

Nếu câu trả lời là “không, mở ra vài phút là lần được”, thì hệ thống thường đã ở mức chấp nhận được về mặt vận hành.

Nếu câu trả lời là “chắc phải mò log”, “chắc phải hỏi người viết script”, “chắc phải grep lại pipeline”, thì gần như chắc chắn đến lúc incident sẽ rất mệt.

Vì lúc đó vấn đề không chỉ là automation chạy sai. Vấn đề là team không có đủ ngữ cảnh để phản ứng nhanh.

Kết

Dài quá rồi, cũng nói hết các ý gạch đầu dòng note ra rồi, chia sẻ hết tâm can rồi. Kết thôi 😀

Cơ bản thì automation giúp hệ thống chạy nhanh hơn, đều hơn, đỡ phụ thuộc thao tác tay hơn. Nhưng chuyện nó có an toàn hay không lại thường không nằm ở số lượng job bạn viết, hay số lượng tool bạn cài thêm.

Nó nằm ở hai thứ rất thực tế:

  • Bạn có nhìn ra nhanh nó vừa làm gì hay không
  • Và bạn có chặn được nó trước khi blast radius quá lớn hay không

Nói cách khác, observability giúp team không bị mù, còn guardrails giúp team không bị muộn.

Một automation thiếu observability không chỉ khó debug. Nó còn làm incident response chậm đi đúng vào lúc từng phút đều đáng tiền.

Và đó là lý do tôi nghĩ observability cho automation không phải nice-to-have. Với bất kỳ team nào đang để automation đụng vào production, nó nên được coi là một phần bắt buộc của thiết kế, không phải phần trang trí thêm sau này.

Good look anh em có một năm mới tốt đẹp nhé, bài viết đầu tiên của năm mới 😀

Thông tin nổi bật

Sự kiện phát trực tiếp​

Event Thumbnail

Báo cáo quan trọng

Article Thumbnail
Article Thumbnail
Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận

Tiêu điểm chuyên gia