Tạo file .gitlab-ci.yml: stages, jobs, image, services, cache, artifacts và rules
Công cụ sinh file cấu hình pipeline đặt ở gốc repository GitLab, cho phép chọn danh sách stages, image chạy job, service database đi kèm, kiểu cache key, thời hạn artifacts, cách viết điều kiện bằng rules hay only, cơ chế tái sử dụng bằng extends hoặc anchor YAML, và job deploy có environment cùng nút bấm tay.
Tính năng nổi bật
- Chọn stages build, test, deploy độc lập, sơ đồ thứ tự chạy hiện ngay bên dưới
- Khối default đặt image, interruptible và retry dùng chung cho mọi job
- Ba kiểu cache key: băm theo lockfile bằng key files, theo CI_COMMIT_REF_SLUG, hoặc key cố định, kèm chọn policy pull, push, pull-push
- Artifacts có expire_in chọn từ 30 phút tới never, kèm khối reports junit cho kết quả test
- Chuyển qua lại giữa cú pháp rules đang được khuyến nghị và cú pháp only cùng except đời cũ
- Bật needs để job chạy theo đồ thị thay vì chờ hết stage phía trước
- Chọn cơ chế tái sử dụng: extends với job ẩn bắt đầu bằng dấu chấm, hoặc anchor YAML hợp nhất bằng hai dấu bé
- Bốn service database gắn kèm job test kèm biến kết nối theo alias, và ba mẫu dựng sẵn cho Docker, GitLab Pages, test có database
Vì sao viết .gitlab-ci.yml hay sai ở những chỗ không ngờ
Người quen GitHub Actions chuyển sang GitLab thường sai ở ba chỗ. Thứ nhất là trộn lẫn rules với only trong cùng một job, GitLab từ chối ngay vì hai cú pháp này loại trừ nhau. Thứ hai là nhầm cache với artifacts: cache dùng để tăng tốc bằng cách giữ lại thư mục dependency và có thể mất bất cứ lúc nào, artifacts mới là cơ chế truyền kết quả từ job này sang job sau và được lưu chắc chắn. Đặt sản phẩm build vào cache rồi chờ job deploy lấy được là một trong những lỗi khó truy nhất, vì lúc chạy thì được lúc không. Thứ ba là kết nối tới service bằng localhost thay vì bằng alias, khiến job test báo lỗi từ chối kết nối dù cấu hình trông hoàn toàn hợp lý. Khung sinh sẵn ở đây đặt đúng từng khối theo mô hình của GitLab, tách bạch cache và artifacts, dùng alias cho service, và không bao giờ trộn hai kiểu điều kiện trong một job.
Lợi ích khi sử dụng
- Tách bạch rõ cache và artifacts, hai thứ hay bị dùng nhầm cho nhau
- Không bao giờ sinh ra job trộn lẫn rules với only gây lỗi cú pháp
- Service database kèm sẵn biến kết nối trỏ đúng alias thay vì localhost
- Thấy ngay khác biệt giữa extends và anchor YAML trên cùng một cấu hình
- Có expire_in cho artifacts nên dự án không bị đầy dung lượng lưu trữ sau vài tháng
Cách tạo file .gitlab-ci.yml
- 1Chọn một mẫu dựng sẵn nếu phù hợp: build và push Docker image, deploy GitLab Pages, hoặc chạy test kèm database service.
- 2Chọn ngôn ngữ để lấy image mặc định và bộ lệnh cài đặt, test, build, sau đó sửa lại tên image nếu bạn dùng image riêng.
- 3Bật các stage cần dùng, kiểm tra sơ đồ thứ tự chạy, rồi quyết định có dùng khối default hay không.
- 4Cấu hình cache key và policy, bật artifacts kèm expire_in, chọn viết điều kiện bằng rules hay only, bật needs nếu muốn chạy theo đồ thị.
- 5Chọn service database nếu job test cần, nhập biến CI/CD không nhạy cảm, đặt tên environment và bật deploy bấm tay, sau đó chép nội dung vào file .gitlab-ci.yml ở gốc repository.
Mô hình của GitLab: stages xếp thứ tự, jobs nằm ở cấp cao nhất, script là danh sách lệnh shell
File .gitlab-ci.yml không có tầng step. Mọi khóa ở cấp cao nhất mà không phải từ khóa dành riêng đều được hiểu là tên một job, và job bắt buộc phải có script. Các từ khóa dành riêng gồm stages, default, variables, include, workflow, và những tên bắt đầu bằng dấu chấm dùng cho job ẩn. Khóa stages khai báo danh sách tên giai đoạn theo đúng thứ tự chạy, mỗi job gắn vào một giai đoạn bằng khóa stage. Mọi job cùng một giai đoạn chạy song song, và giai đoạn sau chỉ bắt đầu khi toàn bộ job của giai đoạn trước đã xong. Nếu bạn không khai báo stages, GitLab dùng ba giai đoạn mặc định là build, test, deploy. Khóa script nhận một danh sách chuỗi, mỗi chuỗi là một dòng lệnh shell chạy tuần tự trong container, và job thất bại ngay khi một dòng trả về mã thoát khác không. Hai khóa họ hàng là before_script chạy trước script và after_script chạy sau, kể cả khi script đã hỏng, nên after_script hợp để dọn dẹp hoặc thu thập log. Khóa image chỉ định container mà job chạy bên trong, thay cho khái niệm máy ảo runner có sẵn phần mềm.
rules thay thế only và except: mệnh đề if, changes, when và allow_failure
Cú pháp only cùng except là cách cũ, vẫn chạy nhưng không còn được phát triển thêm. Cách hiện nay là rules, một danh sách có thứ tự và GitLab dừng lại ở mệnh đề đầu tiên khớp. Mỗi phần tử có thể chứa if để so sánh biến, changes để kiểm tra tệp nào bị sửa trong commit, exists để kiểm tra sự tồn tại của tệp, kèm when và allow_failure và variables. Giá trị của when gồm on_success, on_failure, always, manual, delayed và never. Sự khác biệt quan trọng so với only nằm ở chỗ rules quyết định job có được thêm vào pipeline hay không, chứ không chỉ quyết định job có chạy hay không, nên một job bị rules loại sẽ không xuất hiện trên giao diện pipeline. Các biến hay dùng trong mệnh đề if gồm CI_PIPELINE_SOURCE để phân biệt push với merge request với schedule, CI_COMMIT_BRANCH để lấy tên nhánh, CI_DEFAULT_BRANCH để so với nhánh mặc định của dự án, và CI_COMMIT_TAG khi pipeline chạy từ một thẻ. Tuyệt đối không viết cả rules lẫn only trong cùng một job vì GitLab báo lỗi cú pháp ngay khi kiểm tra file.
cache và artifacts là hai cơ chế khác nhau, dùng nhầm là hỏng pipeline
Cache tồn tại để tăng tốc. Nó lưu một số thư mục như node_modules hay thư mục cache của trình quản lý gói, gắn với một khóa, và runner cố khôi phục khi job bắt đầu. Cache không có gì đảm bảo: chạy trên runner khác, cache hết hạn, hay khóa đổi đều dẫn tới không có cache, và job vẫn phải chạy đúng. Khóa cache khai báo bằng key với ba dạng phổ biến: chuỗi cố định, biến như CI_COMMIT_REF_SLUG để mỗi nhánh có cache riêng, hoặc key files trỏ tới lockfile để GitLab tự băm nội dung và chỉ đổi khóa khi dependency đổi. Khóa policy nhận pull khi job chỉ đọc, push khi job chỉ ghi, pull-push là mặc định. Artifacts thì ngược lại, đó là kết quả công việc: khai báo paths trỏ tới thư mục hoặc tệp cần giữ, GitLab tải chúng lên máy chủ khi job xong, và mọi job ở giai đoạn sau tự động tải về trừ khi bạn chặn bằng dependencies rỗng. Vì artifacts chiếm dung lượng lưu trữ tính vào hạn mức của dự án, hãy luôn đặt expire_in. Khóa when trong artifacts đặt thành always giúp giữ lại log và báo cáo test ngay cả khi job thất bại, đó chính là lúc bạn cần chúng nhất.
needs biến pipeline thành đồ thị, parallel và dependencies điều khiển thêm hai chuyện khác
Mặc định GitLab chạy theo giai đoạn, nghĩa là một job ở giai đoạn deploy phải chờ mọi job ở giai đoạn test xong, kể cả những job không liên quan gì tới nó. Khóa needs phá thứ tự đó: liệt kê tên các job mà job hiện tại thật sự phụ thuộc, và nó sẽ khởi động ngay khi những job đó xong, bất kể các job khác cùng giai đoạn còn đang chạy. Với pipeline có nhiều nhánh công việc độc lập, cách này rút ngắn tổng thời gian đáng kể. Mỗi phần tử của needs còn nhận khóa artifacts đặt thành true hoặc false để quyết định có tải artifact của job đó về hay không, và optional đặt thành true để không lỗi khi job kia bị rules loại khỏi pipeline. Đừng nhầm needs với dependencies: dependencies chỉ chọn lọc artifact nào được tải về chứ không đổi thứ tự chạy, và khai báo dependencies rỗng là cách gọn nhất để một job không tải bất kỳ artifact nào. Một khóa khác dễ nhầm với matrix của GitHub là parallel: đặt parallel bằng một con số sẽ nhân job đó ra bấy nhiêu bản giống hệt, còn parallel matrix mới là dạng nhân theo tổ hợp biến, và cú pháp của nó là một danh sách các bản đồ biến chứ không phải một bản đồ các danh sách.
extends, anchor YAML, include và biến CI/CD: bốn cách để file không phình ra
Có hai cách tái sử dụng cấu hình trong cùng một file. Cách thứ nhất là job ẩn, tên bắt đầu bằng dấu chấm nên GitLab không chạy nó, rồi job thật khai báo extends trỏ tới tên đó. extends hợp nhất theo từng khóa và đi sâu vào các khóa lồng nhau, cho phép ghi đè một phần, và một job có thể extends nhiều job ẩn cùng lúc. Cách thứ hai là anchor của bản thân YAML: đánh dấu một khối bằng ký hiệu và kèm tên, rồi hợp nhất vào job khác bằng hai dấu bé. Anchor chạy ở tầng cú pháp YAML nên chỉ hoạt động trong một file, không dùng được qua include, và cách hợp nhất của nó nông hơn extends. Với dự án nhiều repository chung quy trình, khóa include kéo cấu hình từ nguồn khác vào, gồm local cho tệp trong cùng repository, project cho tệp ở repository khác, remote cho một đường dẫn công khai, và template cho các mẫu GitLab dựng sẵn. Về biến, khối variables ở cấp cao nhất áp cho mọi job, khối variables trong job chỉ áp cho job đó và ghi đè biến cùng tên. Biến nhạy cảm không bao giờ viết trong file: hãy khai báo ở Settings, CI/CD, Variables và bật masked để giá trị bị che trong log, bật protected để chỉ nhánh và thẻ được bảo vệ mới đọc được.
So sánh với GitHub Actions: cùng mục tiêu nhưng khác từ đơn vị nhỏ nhất
Khác biệt gốc rễ nằm ở đơn vị nhỏ nhất của cấu hình. GitHub có step, và một step có thể là lời gọi tới một action đóng gói sẵn thông qua khóa uses, nghĩa là bạn lắp ghép các mảnh do người khác viết và truyền tham số qua khối with. GitLab không có step và không có kho action: một job chỉ gồm danh sách lệnh shell trong khóa script, muốn đăng nhập registry thì gọi thẳng lệnh docker login, muốn cài công cụ thì tự cài trong before_script. Đổi lại GitLab đưa môi trường chạy vào chính cấu hình bằng image và services, trong khi GitHub chọn máy bằng runs-on và job phải tự cài thứ nó cần thông qua các action setup. Về thứ tự, GitLab mặc định theo stages và dùng needs để phá thứ tự, GitHub thì ngược lại, mặc định mọi job chạy song song và dùng needs để tạo thứ tự. Về điều kiện, GitLab dùng rules với các mệnh đề if, changes, exists, còn GitHub dùng biểu thức if kèm các hàm như contains và startsWith. Cả hai đều có khái niệm environment và cách chạy tay, nhưng GitLab đặt when manual ngay trong job còn GitHub dùng workflow_dispatch ở cấp workflow. Nếu dự án của bạn nằm trên GitHub, hãy dùng công cụ tạo workflow tại /vi/tools/github-actions-generator, trang đó đi sâu vào matrix, hashFiles, permissions và cách ghim phiên bản action.
Câu hỏi thường gặp (FAQ)
File cấu hình của GitLab đặt ở đâu và có đổi tên được không?
Mặc định là file .gitlab-ci.yml đặt ngay gốc repository, có dấu chấm ở đầu tên. Bạn đổi được đường dẫn trong Settings, CI/CD, General pipelines, mục CI/CD configuration file, kể cả trỏ sang một repository khác. Nhưng nếu chưa có nhu cầu cụ thể thì cứ giữ tên mặc định cho người sau dễ tìm.
Vì sao GitLab báo lỗi khi tôi dùng rules và only trong cùng một job?
Hai cú pháp này loại trừ nhau, GitLab không cho phép trộn trong cùng một job. Hãy chọn hẳn một kiểu. Nếu đang viết mới thì dùng rules vì only và except là cú pháp cũ, không còn được bổ sung tính năng. Các job khác nhau trong cùng file thì được phép mỗi job một kiểu.
Cache và artifacts khác nhau thế nào, khi nào dùng cái nào?
Cache để tăng tốc, giữ thư mục dependency và có thể mất bất cứ lúc nào nên job phải chạy đúng kể cả khi không có cache. Artifacts để truyền kết quả sang job sau và để tải về xem, được lưu chắc chắn theo expire_in. Sản phẩm build luôn đi vào artifacts, không bao giờ đi vào cache.
Job test không kết nối được database dù đã khai báo services?
Gần như chắc chắn bạn đang trỏ tới localhost. Container service chạy cạnh container job và được đặt tên theo alias, nên chuỗi kết nối phải dùng chính alias đó làm tên máy, ví dụ db thay cho localhost. Ngoài ra hãy chờ database sẵn sàng trước khi chạy test, vì container khởi động không tức thì.
needs khác dependencies ở điểm nào?
needs đổi thứ tự chạy, cho job khởi động sớm ngay khi các job nó cần đã xong, không phải chờ hết giai đoạn. dependencies không đổi thứ tự, chỉ chọn lọc artifact nào được tải về. Khai báo dependencies rỗng là cách gọn nhất để một job không kéo theo bất kỳ artifact nào của giai đoạn trước.
Đặt expire_in bao lâu là hợp lý?
Artifact trung gian giữa các job chỉ cần vài giờ tới một tuần. Báo cáo test và log nên giữ khoảng ba mươi ngày để còn truy lại khi có sự cố. Bản build phát hành thì nên đẩy sang nơi lưu trữ riêng thay vì để trong artifact. Không đặt expire_in nghĩa là artifact nằm lại rất lâu và dung lượng dự án sẽ đầy dần.
Chọn extends hay anchor YAML thì tốt hơn?
extends tốt hơn trong hầu hết trường hợp vì nó hợp nhất sâu theo từng khóa, cho phép ghi đè một phần, kết hợp được nhiều job ẩn và hoạt động qua include. Anchor là tính năng của bản thân YAML nên chỉ dùng được trong một file và không đi qua include, phù hợp cho những đoạn lặp ngắn trong file nhỏ.
Job pages của GitLab Pages có yêu cầu gì đặc biệt?
Job bắt buộc phải tên là pages và phải xuất artifact vào đúng thư mục tên public ở gốc thư mục làm việc. Nếu công cụ build của bạn xuất ra dist hay site thì phải đổi tên hoặc chép sang public trong phần script. Trang chỉ được xuất bản sau khi job pages chạy xong và thành công.
Biến CI/CD viết trong file và biến khai báo trong Settings khác nhau ra sao?
Biến trong file nằm trong lịch sử Git nên ai đọc được mã là đọc được giá trị, chỉ dùng cho cấu hình không nhạy cảm. Biến trong Settings, CI/CD, Variables hỗ trợ masked để che giá trị trong log và protected để chỉ nhánh hoặc thẻ được bảo vệ mới đọc được, đó là nơi duy nhất nên để token và mật khẩu.
Làm sao để pipeline không chạy hai lần khi mở merge request?
Đó là hiện tượng pipeline trùng lặp, xảy ra khi vừa có rule cho push nhánh vừa có rule cho merge_request_event. Cách xử lý gọn là dùng khóa workflow ở cấp cao nhất với rules loại bỏ pipeline nhánh khi nhánh đó đã có merge request đang mở, dựa trên biến đếm số merge request mở của nhánh.
Job deploy bấm tay có chặn các job phía sau không?
Tùy allow_failure. Job manual mặc định không chặn, pipeline vẫn tính là thành công dù bạn chưa bấm. Muốn pipeline phải chờ và chỉ xanh khi bạn đã bấm chạy thành công, hãy đặt allow_failure là false cho job đó, thường viết ngay trong mệnh đề rules kèm when manual.
Kiểm tra cú pháp file trước khi push bằng cách nào?
Vào mục CI/CD rồi mở Editor, tab Validate sẽ kiểm tra cú pháp và cho xem cấu hình sau khi đã gộp hết include và extends. Đây là cách nhanh nhất để phát hiện lỗi thụt lề, tên khóa sai hoặc job tham chiếu tới một job không tồn tại trong needs, trước khi tạo commit.
Từ khóa liên quan
- gitlab ci generator
- tạo file gitlab-ci.yml
- gitlab ci stages jobs script
- gitlab ci rules if changes
- gitlab ci only except khác rules
- gitlab ci cache key files
- gitlab ci artifacts expire_in
- gitlab ci needs dag
- gitlab ci extends job ẩn
- anchor yaml gitlab ci
- gitlab ci services postgres
- gitlab ci docker dind build push
- gitlab pages deploy public
- biến ci cd gitlab masked protected
- gitlab ci environment manual deploy
- CI_COMMIT_REF_SLUG là gì
- CI_PIPELINE_SOURCE merge_request_event
- gitlab ci parallel matrix
- pipeline trùng lặp gitlab merge request
- gitlab ci lint validate cú pháp
