Issue VPN sập toàn bộ hệ thống CI/CD

Hôm nay vừa mới fix issue mà tốn công, tốn của, tốn thời gian và thêm trải nghiệm và đang làm cái report issue nên thuận tay viết ngay cho mọi người để chẳng may bạn nào có gặp sẽ có thêm tham khảo hoặc giúp bạn nào đang cần những kinh nghiệm fix production có thể xem nhé.

Hệ thống của chúng tôi ở giai đoạn chuyển đổi:

  • App & CI/CD: chạy trên AWS (GitLab self-hosted EC2 + runner autoscale).
  • Artifact & registry nội bộ: vẫn đặt on-prem trong datacenter công ty, cùng với LDAPproxy internal.
  • Kết nối: qua đường IPSec VPN site-to-site, có dự phòng nhưng chưa active–active, chỉ failover thủ công.

Mô hình hybrid: pipeline trên cloud chỉ việc kéo base image và artifact từ on-prem rồi push ngược kết quả lại. Chạy ổn suốt gần nửa năm, cho đến khi vừa sáng nay gặp sự cố.

Sự cố

8:47 sáng, đang đi pha cốc cafe, slack DevOps channel bắn liên tục:

  • “Pipeline stuck ở step build.”
  • “Runner báo context deadline exceeded.”
  • “Docker pull base image failed: connection timeout.”

Ban đầu tưởng còn tưởng do ông cloud provider lại downtime hay do registry on-prem quá tải. Nhưng nhìn nhanh thì load vẫn thấp, storage không đầy. Lúc SSH vào con GitLab runner EC2, thử pull tay:

docker pull registry.internal.local/base/python:3.11

và kết quả là:

Error response from daemon: Get "https://registry.internal.local/v2/": dial tcp 10.10.20.5:443: i/o timeout

=> không resolve nổi host on-prem.

Kiểm tra route thì thấy VPN tunnel tunnel1 trạng thái DOWN. Đáng sợ là monitoring Prometheus chưa cảnh báo gì vì metrics thu thập chạy… qua chính tunnel đó.

Khi pipeline CI/CD không kéo được base image, tất cả job build đều fail. Team dev tưởng lỗi transient, nên spam “retry” liên tục. Hàng trăm pipeline đồng thời chạy lại, mỗi cái tốn vài vCPU và pull image không thành công.

EC2 runner autoscale bùng lên 5x so với bình thường, làm AWS cost tăng gần gấp đôi chỉ trong 30 phút, nhìn rén luôn.

Tệ hơn là do GitLab được cấu hình autoscale runner theo queue size, việc retry liên tục tạo ra backlog job, khiến ngay cả pipeline deploy cũng kẹt, không ai có thể release hotfix gì.

Một vài job cố gắng fallback sang mirror public trên Docker Hub nhưng image base của nội bộ không có public version, build sai dependency, dẫn đến binary lỗi mà không ai nhận ra.

Nguyên nhân

Đến tầm 9:30, sau khi hỏi bên network, mới biết đường VPN chính (qua router Cisco ASA) bị đứt route do thay đổi static route ở bên on-prem (một ông Network Engineer thêm route test mà quên commit vào config-sync).

Tunnel failover chưa tự động, vì chúng tôi “chưa kịp” triển khai BGP dynamic routing.

Tóm lại là một thay đổi mạng nhỏ (chẳng biết nhỏ không) ở on-prem, và toàn bộ CI/CD trên cloud đứng luôn.

Xử lý

1. Chặn pipeline mới, stabilize runner

  • Dừng GitLab Runner autoscale bằng cách chỉnh max-runners = 0, tránh tốn thêm tiền.
  • Dọn queue CI/CD để tránh build rác lặp đi lặp lại.

2. Bật VPN backup thủ công

  • Dùng command crypto isakmp enable để kick tunnel dự phòng lên. 5 phút sau, route sang on-prem ổn định.
  • Nhưng registry on-prem vẫn chưa phục hồi hoàn toàn do DNS cache TTL 300s trong GitLab runner container.

3. Tạo local cache tạm

  • Chúng tôi triển khai ngay một con Harbor mirror tạm trên AWS, dùng registry.mirror.local làm upstream cache cho registry.internal.local.
  • Pipeline build sửa lại .gitlab-ci.yml để pull từ mirror trước, fallback sang registry gốc nếu có kết nối.

Ví dụ:

variables:
  DOCKER_REGISTRY: registry.mirror.local

build:
  script:
    - docker pull $DOCKER_REGISTRY/base/python:3.11 || docker pull registry.internal.local/base/python:3.11

Cách này cứu được 80% pipeline build ngay đến tầm đầu giờ chiều.

4. Giảm retry flood

Thêm limit retry cho pipeline, thay vì cho dev tự bấm “retry” vô hạn:

retry:
  max: 1
  when:
    - runner_system_failure

Hậu quả sau sự cố

  • Downtime CI/CD: 1 giờ 45 phút.
  • Cost tăng: dự tính phải tăng khoảng 63% chi phí AWS EC2 runner của tháng (còn chính xác thì để tôi chỉnh sau cho mọi người biết rõ).
  • Developer productivity: gần 150 job fail, 40% commit trong ngày phải re-run build.
  • Niềm tin: các team dev đang chat “CI/CD giờ còn fragile hơn staging.” :)))

Bài học

  1. Hybrid không có “tạm thời” Nếu kết nối giữa cloud và on-prem là single point of failure, thì đừng gọi nó là hybrid gọi đúng tên là “fragile setup”.

  2. Mirror registry là bắt buộc Artifact, base image, helm chart phải có cache ở cả hai đầu. Dùng Harbor hoặc Nexus với chế độ proxy, TTL hợp lý.

  3. Monitoring không nên đi qua cùng đường. Metrics phải thu từ cả hai phía, có exporter chạy ở hai đầu VPN. Prometheus nên có remote_write ra cloud để không bị “mù” khi tunnel down.

  4. Pipeline phải biết khi nào dừng. Một job không pull được image do network thì retry 10 lần cũng vô ích. Cần timeout hợp lý, và fail fast thay vì flood retry.

  5. Failover mạng phải được simulate. Đừng tin BGP, đừng tin router. Phải simulate đứt tunnel ít nhất mỗi quý một lần để thấy CI/CD, registry, và auth phản ứng ra sao. Cái này chưa làm được cứ viết vào đây để thúc đẩy team làm :)))

Sau cùng

Sau vụ này, tôi viết một rule bất thành văn là: “Nếu pipeline phụ thuộc vào thứ gì ngoài control của các ông hãy cache nó trước khi quá muộn.”

Hybrid architecture không sai, chỉ sai khi mình coi nó là cloud. Vì cloud mà không có isolation, resilience, và automation thì chỉ là datacenter của người khác.

Thông tin nổi bật

Báo cáo quan trọng

Sự kiện đang hiện hành

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