Disclaimer: Nếu bạn dùng giải pháp của tôi nhưng không được promote 🐵, đấy không phải lỗi của tôi nhé.

Tối qua trong lúc chờ con ngủ, lướt qua được bài “A one-line Kubernetes fix that saved 600 hours a year”, một cái tilte rất “kêu” và thu hút người đọc. Đọc lướt qua thì thấy dek ổn rồi, nên quyết định sáng nay đọc kỹ lại, chia sẻ với anh em kỹ sư Đông Lào tí, làm ăn vầy chết tôi.

Với tư cách là một kỹ sư khởi động server lâu năm, tôi thấy mấy anh engineer ở Cloudflare giải quyết vấn đề nếu đúng thật như bài viết này thì thật rất 3 chấm. Nhưng tôi cũng thừa biết, blog đôi khi chỉ là công cụ marketing, cũng chỉ là bề nổi của tảng băng, có nhiều thứ … “khó” nói lắm.

Tóm tắt cho ae vài thông tin nhé:

  • Cloudflare như các kỹ sư Đông Lào, dùng Terraform để IaC, và có vẻ họ có rất nhiều Terraform repo, mỗi repo có thể tương ứng với một project, infra.
  • Cũng như các kỹ sư Đông Lào, họ dùng Atlantis để tự động hóa quá trình plan/apply. Khi có ai đó submit MR code Terraform, Atlantis sẽ chạy plan, submit comment trong MR để xem Infra có gì thay đổi hay không.
  • Vấn đề là, mỗi khi họ restart Atlantis, tốn 30ph để restart, không có infra code nào có thể plan/apply trong 30 phút này.
  • Mỗi tháng, họ restart cỡ 100 lần (khi rotate creds hoặc on/offboard repo mới) -> Trung bình một tháng blocked 50h làm việc của engineer.

=> Đọc tới khúc trên, tôi đù *** liền, restart gì lắm thế, lại còn tốn hơn 2 ngày/tháng ko làm ăn gì được, tới đây là tôi thấy sức chịu đựng của engineer ở Cloudflare hơi khỏe rồi đấy, là tôi, bắt đợi 30ph mỗi ngày để chờ server restart là tôi lật cmn bàn rồi.

Đoạn dài dài phía dưới nói về cách đi tới log, debug flow ra sao, chắt lọc trong bài viết, tiện chỉ vài chỗ có thông tin về internal Infra của Cloudflare cho mấy fen rõ luôn:

  • Họ chạy Atlantis trên Kubernetes ở dạng StatefulSet và dùng PersistentVolume để keep track repository state on disk.
  • Mỗi khi có một Terraform project on/offboard hoặc creds được sử dụng bởi Terraform được rotate hoặc add mới -> Cần restart để Atlantis để cập nhật mấy cái creds mới này.
  • Mấy thím này chắc dùng bare-metal để deploy Kubernetes dùng cho internal infra (ví trong bài viết có nhắc tới kubelet chạy qua systemd), nên PV của mấy thím ấy là một cái Ceph, chắc cũng tự deploy.
  • Mấy thím ấy restart, check status thấy pod init tốn 30 phút, xong cầm cái hostname, Atlantis service label, đem lên Kibana để trace log -> Tới đây tôi nghĩ ngay, Kibana thì lại ELK rồi, cơ mà kỳ thế nhờ, chẳng phải CF dùng ClickHouse để storage log hay sao, sao lại Kibana thế này, hóa ra tôi nhớ nhầm, đúng là Cloudflare có dùng ClickHouse để storage log, cơ mà log đó là HTTP Analytics, một feature ở phía customer, chứ không phải cho hệ thống nội bộ. Như các cty khác, nội bộ mỗi team dùng một tool, tùm lum thứ có cùng mục đích…

Mò mẫm một hồi giữa một bể log, cuối cùng mấy bạn ấy tóm được đoạn log này

[volume_linux.go:49] Setting volume ownership for /state/var/lib/kubelet/pods/83089f13-2d9b-46ed-a4d3-cba885f9f48a/volumes/kubernetes.io~csi/pvc-94b75052-8d70-4c67-993a-9238613f3b99/mount and fsGroup set.

If the volume has a lot of files then setting volume ownership could be slow, 

see https://github.com/kubernetes/kubernetes/issues/69699

Đoạn này đại ý là nếu trong PV có rất rất nhiều file nhỏ, thì quá trình change ownership sẽ chậm. Khi rollout Atlantis Statefulset, sẽ có một signal gửi tới pod Atlantis cũ, bảo, mời bạn về hưu nhé, sau đó spining up một pod mới, mount PV vào. Sau khi PV đã được mount, kubelet sẽ chạy một lệnh để change onwership của các tệp trên PV chgrp -R, cái param -R là đệ quy, mút từ đầu tới móng chân không chừa thằng nào. Và không có gì bất cmn ngờ nó chậm vãi.

Okie, thế tại sao phải chgrp -R để làm gì? Pod spec.securityContext có cái đoạn vầy fsGroup: 1. Đại ý là để đảm bảo các process chạy dưới GID 1 vẫn có thể đọc mấy cái tệp trong volume. Atlantis process trong pod chạy với user non-root, nên phải có cái spec đó, không là không có đọc/ghi file gì được hết trơn, vậy nên Kubernete phải đảm bảo Atlantis có thể đọc ghi được bằng cách mỗi lần PV được mount, nó chạy đệ quy để thay đổi ownership của 8 tỉ tệp và thư mục (cũng do CF có nhiều repo).

Đoạn này chợt nhớ tới mấy cái chart của Bitnami, có dùng một cái os-shell để change ownership tương tự thì phải, chart của PostgreSQL nó cũng chậm như vậy nếu bật cái mode này.

Giờ fix sao mấy fen, Kubernetes verion 1.20 có một field là fsGroupChangePolicy, mặc định nó Always tức là như nãy giờ nói ở trên, nhưng nó có một tùy chọn khác là OnRootMismatch, ý là chỉ chạy chgrp -R nếu root directory của PV có permission không đúng (nhưng làm sao biết đúng hay không thì tôi không biết). Mấy thím ấy bảo, sure, perm của mấy thím ấy ko sai, nên ko cần phải Always, nên cuối cùng, cách fix là:

spec:
  template:
    spec:
      securityContext:
-       fsGroupChangePolicy: Always
+       fsGroupChangePolicy: OnRootMismatch

Thật bất cmn ngờ, từ 30ph -> 30s, tôi không còn gì để nói, nhờ ngay con Gemini tính dùm xem đợt này tăng được bao nhiêu lương:

  • Cải thiện tốc độ lên tới 60 lần
  • Cắt giảm được 98.33% thời gian chờ đợi

Ngon, viết vào performance review thôi.

Tới đây thì mấy fen có thể sẽ nghĩ, ơ kìa thế thì có gì đâu mà nói, dịch lại bài viết thôi mà, :shame: nhưng là một kỹ sư Đông Lào chân chính, k8s hater, tôi sẽ không sinh ra vấn đề để bán giải pháp.

Mấu chốt của đoạn chậm là do change ownerhip, mà cái dẫn tới việc change ownership là do rollout nhiều (nhưng chrgp chỉ là lớp phòng hờ, chứ rollout thì đụng gì tới PV đâu, tự dưng restart phải đi change ownership là hết sức tào lao). Việc thay đổi hành vi fsGroupChangePolicy: OnRootMismatch chỉ đúng khi không phát sinh ad-hoc case và giả định là permission không sai, nếu phát sinh debug case mà clone repo ở user khác user chạy Atlantis, thì Atlantis process sẽ không thể đọc được code repo đó và Terraform command sẽ lỗi trên các folder đó. Việc tìm ra lỗi sai permission này với k8s vô cùng mệt mỏi khi phải vật lộn với mớ log nhiễu, pod restart … càng làm người vận hành mất nhiều thời gian hơn.

Tôi chợt nghĩ, nếu tôi deploy Atlantis kiểu vầy thì sao:

  • Kiếm một con máy vừa một tí, deploy Atlantis lên đó qua systemd.
  • Thay vì dùng Ceph, mount network volume, tôi kiếm cái ổ cứng SSD có phải nhanh hơn là mount cái network disk không.
  • Volume dùng để chứa code Terraform, chứ state lưu cloud, thì dùng ZFS lâu lâu snapshot cái disk, ko thì RAID 1, RAID 6 gì đó, đỡ phải pull code mới về là ổn, nghĩ nhiều chi cho mệt.
  • Mỗi lần tôi cần restart, thì tôi chỉ cần systemctl restart atlantis là xong, không mount lại, ko cần chrgp vì process vẫn ở đó, perm vẫn là non-root, volume vẫn ở đó, đâu có gì thay đổi đâu mà phải chrgp.
  • Xong

Một giải pháp, công nghệ lỗi thời, nhàm chán, nhưng deploy một lần, không cần phải nghĩ về nó, ai đời một ngày đi bấm nút restart 3 lần, đợi 90ph làm gì bao giờ, hehe.

NHƯNG, nhớ nhé, bạn sẽ không bao giờ được promote nếu dùng giải pháp nhàm chán, không buzzword nhé. shet

Và trong một cty to, thứ cản trở việc triển khai một giải pháp tốt không phải là vấn đề kỹ thuật mà là cấu trúc, con người, quy trình …Một giải pháp nhàm chán sẽ khó “sale” cho khách hàng lắm :v. Còn tôi, tôi không làm như thế nhé.