---
id: cicd-jenkins
title: "Jenkins"
shortTitle: "Jenkins"
track: cicd
trackTitle: "CI/CD"
order: 2
level: important
status: published
estimatedMinutes: 35
prerequisites:
  - cicd-github-actions
next: cicd-gitlab
reviewedAgainst: 2026-10-12
sources:
  - title: "Jenkins Pipeline"
    url: https://www.jenkins.io/doc/book/pipeline/
  - title: "Declarative Pipeline syntax"
    url: https://www.jenkins.io/doc/book/pipeline/syntax/
---

# Jenkins

Jenkins là CI tự host. Controller giữ job. Agent chạy build. GitHub Actions làm việc đó trên runner của GitHub. Bài này để đọc một Jenkinsfile khi hệ thống cũ còn Jenkins.

## Để làm gì

Bạn đọc Jenkinsfile declarative, chỉ ra stage nào chạy lệnh, và không chạy build trên controller.

## Gồm những gì

- `pipeline`, `agent`, `stages`, `steps`.
- Controller và agent.
- Vì sao plugin và secret nằm trên server Jenkins, không nằm trong Git hết.

Bài này không cài Jenkins, cũng không mở Script Console.

## Dùng khi nào

Repo có `Jenkinsfile`, hoặc một đội vận hành đã có controller. Pipeline mới của lộ trình này vẫn là GitHub Actions, trừ khi nơi làm việc bắt Jenkins.

Đừng để agent là chính máy controller. Một job `sh 'rm -rf'` hoặc một dependency độc chạy trên controller là đụng cả CI.

## Kiến thức cốt lõi

Declarative pipeline bắt đầu bằng `pipeline { }`. `agent any` lấy một agent rảnh. `agent { label 'linux' }` chỉ agent mang nhãn đó. `stages` chứa `stage`. Mỗi stage có `steps`. `sh` chạy shell trên agent Linux. `bat` hoặc `powershell` trên agent Windows.

Jenkinsfile nằm trong Git thì đồng nghiệp review được. Job cấu hình tay trên web thì máy khác không tái hiện được. Ưu tiên file trong repo.

Credential lưu trong Jenkins, gọi bằng `credentials()`. Đừng viết password vào `sh`. Log của build lưu trên controller. Echo secret là dán secret vào lịch sử build.

Script Console trên controller chạy Groovy với quyền process Jenkins. Docs coi đó là công cụ quản trị, không phải chỗ thử lệnh.

## Lệnh và code mẫu

`Jenkinsfile`:

```groovy
pipeline {
  agent { label 'linux' }
  stages {
    stage('test') {
      steps {
        sh 'echo ok'
      }
    }
  }
}
```

Không có controller trong lab. Đọc file là phần thực hành.

## Thực hành

1. Chỉ ra nhãn agent và câu lệnh trong stage `test`.
2. Đổi nhãn thành `any` trên giấy, rồi giải thích vì sao lab nhỏ vẫn không nên dùng controller làm agent.
3. Không tải war Jenkins và không mở cổng 8080 cho Jenkins trên máy này. Cổng đó bài Tomcat cũng dùng khi bạn chạy lab kia.

## Kết quả mong đợi

Stage `test` chạy `echo ok` trên agent nhãn `linux`.

## Checklist

- [ ] Jenkinsfile nằm trong repo.
- [ ] Build chạy trên agent, không trên controller.
- [ ] Secret lấy từ credential store, không viết trong `sh`.

## Lỗi thường gặp

**`agent any` trên controller vì chưa có agent.** Job chạy process cùng user của Jenkins. Thêm agent, gắn nhãn, rồi sửa Jenkinsfile.

**Cài plugin từ update center trên controller production lúc đang release.** Plugin đổi hành vi pipeline. Cài trên bản sao, chạy một Jenkinsfile, rồi mới nâng controller.

## Nguồn

1. [Pipeline](https://www.jenkins.io/doc/book/pipeline/) — controller, agent, Jenkinsfile.
2. [Syntax](https://www.jenkins.io/doc/book/pipeline/syntax/) — `agent`, `stages`, `sh`.

## Tiếp theo

[GitLab CI](./04-gitlab-ci.md)
