Sự cố Pipeline lúc 2 giờ sáng
Đã 2 giờ sáng. Bạn đang đẩy một bản vá lỗi (hotfix) quan trọng lên môi trường production. Pipeline CI/CD bắt đầu chạy, nhưng thay vì một dấu tích xanh, bạn lại nhận được một loạt dòng chữ đỏ rực. Hoặc có thể bạn chỉ đang thiết lập laptop cho nhân viên mới. Bạn chạy lệnh go mod tidy, và mọi thứ bỗng khựng lại. Terminal hiển thị lỗi này:
go get github.com/acme-corp/secret-api: git ls-remote -q https://github.com/acme-corp/secret-api.git: exit status 128
Lỗi exit status 128 này là cách thông báo chung của Git cho lỗi "Truy cập bị từ chối" (Access Denied). Nó xảy ra vì Go toolchain đang cố gắng truy cập vào một repository riêng tư (private) như thể đó là một repository công khai (public). Quá trình bắt tay xác thực (authentication handshake) thất bại, và Git ngắt kết nối.
Tại sao bản Build của bạn bị lỗi
Theo mặc định, Go giả định mọi module đều là công khai. Điều này tạo ra hai rào cản lớn đối với mã nguồn riêng tư:
- Google Proxy: Go thường tải các module từ
proxy.golang.org. Vì repository của bạn là riêng tư, proxy của Google không thể nhìn thấy nó. Nó sẽ trả về lỗi 404 hoặc 410, và bản build của bạn thất bại. - Bẫy HTTPS: Go cố gắng tải các module thông qua HTTPS. Nếu máy tính của bạn sử dụng SSH keys cho GitHub nhưng không có trình hỗ trợ lưu thông tin xác thực (credential helper) cho HTTPS, Git sẽ lỗi. Nó không thể yêu cầu bạn nhập mật khẩu khi đang thực hiện một lệnh Go không tương tác (non-interactive).
Bước 1: Xác định không gian riêng tư của bạn
Hãy bắt đầu bằng cách thông báo cho Go biết những module nào nên bỏ qua proxy công khai và cơ sở dữ liệu tổng kiểm (checksum database). Sử dụng biến môi trường GOPRIVATE cho việc này. Nó chấp nhận các mẫu (pattern) phân tách bằng dấu phẩy.
Chạy lệnh này trong terminal của bạn. Thay thế github.com/acme-corp bằng tổ chức (organization) thực tế của bạn:
go env -w GOPRIVATE=github.com/acme-corp/*
Cờ (flag) đơn giản này mang lại hiệu quả rất lớn. Nó báo cho Go rằng bất kỳ thứ gì nằm trong đường dẫn acme-corp đều là riêng tư. Go giờ đây sẽ bỏ qua hoàn toàn Google proxy và cơ sở dữ liệu tổng kiểm công khai (GOSUMDB) đối với các repository này.
Bước 2: Ép Git sử dụng SSH
Việc thiết lập GOPRIVATE giúp Go kết nối trực tiếp với GitHub, nhưng nó vẫn mặc định sử dụng HTTPS. Nếu bạn dựa vào SSH keys để phát triển, bạn cần ép Git chuyển đổi giao thức ngay lập tức.
Thêm quy tắc ghi đè này vào file .gitconfig toàn cục của bạn:
git config --global url."git@github.com:".insteadOf "https://github.com/"
Giờ đây, khi Go chạy lệnh git ls-remote https://github.com/acme-corp/secret-api.git, Git sẽ chặn lệnh đó. Nó chuyển đổi URL thành git@github.com:acme-corp/secret-api.git. SSH agent của bạn sẽ xử lý việc xác thực, và quá trình tải về sẽ thành công ngay lập tức.
Giải pháp thay thế cho CI/CD: Personal Access Tokens
Trong các môi trường như GitHub Actions hoặc Jenkins, việc quản lý SSH keys thường khá rắc rối. Sử dụng Personal Access Token (PAT) qua HTTPS thường là giải pháp gọn gàng hơn.
Thay vì ghi đè bằng SSH, hãy sử dụng lệnh này trong script CI của bạn:
# Dành cho GitHub Actions
git config --global url."https://${GH_TOKEN}:x-oauth-basic@github.com/".insteadOf "https://github.com/"
Mẹo bảo mật: Đừng bao giờ viết cứng (hardcode) token này. Hãy sử dụng các biến môi trường hoặc trình quản lý bí mật (secrets management) để chèn ${GH_TOKEN} vào lúc thực thi nhằm giữ an toàn cho thông tin xác thực của bạn.
Bước 3: Kiểm tra kết quả
Xóa cache module cục bộ để đảm bảo bạn không dùng metadata cũ bị lỗi. Sau đó, hãy thử giải quyết các phụ thuộc (dependencies) một lần nữa.
go clean -modcache
go mod tidy
Nếu lệnh kết thúc mà không báo lỗi gì, bạn đã thành công. Bạn có thể kiểm tra lại các thiết lập của mình bất cứ lúc nào bằng cách chạy lệnh go env GOPRIVATE.
Các lỗi thường gặp cần lưu ý
1. Bẫy Dấu hai chấm so với Dấu gạch chéo
Khi sử dụng insteadOf, cú pháp rất khắt khe. Nếu bạn dùng git@github.com: (có dấu hai chấm), hãy đảm bảo phía HTTPS khớp với https://github.com/ (có dấu gạch chéo ở cuối). Một sai sót nhỏ ở đây sẽ dẫn đến các URL bị hỏng như git@github.com:/user/repo, điều này sẽ gây ra một lỗi 128 khác.
2. Cơ sở dữ liệu Checksum
Một số lập trình viên cố gắng chỉ sử dụng GONOPROXY một mình. Đừng làm vậy. Nếu bạn bỏ qua proxy nhưng không bỏ qua cơ sở dữ liệu tổng kiểm (GOSUMDB), Go vẫn sẽ cố gắng xác minh mã nguồn riêng tư của bạn dựa trên một sổ cái công khai. Nó sẽ thất bại vì mã nguồn riêng tư của bạn không có trong sổ cái đó. GOPRIVATE là lựa chọn an toàn hơn vì nó cấu hình cả hai thiết lập cùng một lúc.
3. Các vấn đề với Docker Container
Các bản build Docker là nguồn cơn thường xuyên của lỗi 128. Container là một môi trường sạch hoàn toàn; nó không biết về file .gitconfig hay SSH keys của máy chủ (host). Bạn phải thiết lập rõ ràng các biến môi trường bên trong Dockerfile hoặc truyền chúng dưới dạng đối số khi build (build arguments).
# Ví dụ đoạn mã Dockerfile
ARG GITHUB_TOKEN
ENV GOPRIVATE=github.com/acme-corp/*
RUN git config --global url."https://${GITHUB_TOKEN}:x-oauth-basic@github.com/".insteadOf "https://github.com/"
Tóm tắt
- Thiết lập mặc định của Go giả định mọi mã nguồn là công khai. Đối với các dự án nội bộ,
GOPRIVATElà trợ thủ đắc lực nhất của bạn. - Lỗi
exit status 128hầu như luôn xuất phát từ việc xác thực Git thất bại. - Thủ thuật
insteadOflà cách gọn gàng nhất để khắc phục sự không khớp về giao thức mà không cần chạm vào filego.modcủa bạn.

