Jenkins Pipeline for beginners
Bài đăng này đã không được cập nhật trong 3 năm
Khi xây dựng CI/CD trên jenkins, chúng ta thường sử dụng các plugins cài sẵn hoặc bash/bat script để config build, test, report, deploy vv..
Với Bash/Bat script tiếp cận nó đã khó, sử dụng nó còn khó khăn hơn. Chưa kể với cách truyền thống còn hạn chế về mặt môi trường build / run project , mặc dù Slave Node có thể giải quyết vấn đề môi trường chạy ,nhưng nó cũng chưa thực sự là giải pháp tối ưu. Hoặc không thể sử dụng đa luồng chạy song song, cũng không thể sử dụng nhiều môi trường chạy trong 1 build...vvv
Vì vậy trong bài viết này, mình sẽ giới thiệu Jenkins Pipeline
- một workflow CI trên Jenkins 2.0, cái sẽ trợ giúp chúng ta khắc phục những khó khăn đó.
Jenkins Pipeline là gì
Như trang https://jenkins.io/doc/book/pipeline/#overview có trình bày :
Jenkins Pipeline (or simply "Pipeline" with a capital "P") is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins
Nó sẽ hỗ trợ chúng ta trong việc tạo và cấu hình một hệ thống CI/CD trên Jenkins với concept Pipeline
Jenkins Pipeline dựa trên syntax của DSL (Domain Specific Language), mang đậm phong cách coder
. Vì vậy được gọi là Pipeline as Code
, ngôn ngữ chính là : Groovy sử dụng Groovy Script
Dưới đây là một ví dụ :
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make'
}
}
stage('Test'){
steps {
sh 'make check'
junit 'reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh 'make publish'
}
}
}
}
stage
: Được hiểu như từng chức năng con, trong đó nó sẽ phân rẽ các chức năng cụ thể tùy thuộc vào bài toán của bạn. Chẳng hạn như stage Init, Build , Test, Deploy ..vv
Việc trình bày Pipeline code cũng có 2 cách từ đơn giản đến phức tạp như sau :
1. Declarative Pipeline
(Là ví dụ bên trên) - nó được gọi là Presents a more Simplified
. Dựa trên các methods / functions dựng sẵn, việc của chúng ta sử dụng và tuân thủ theo các rule và syntax được định nghĩa sẵn theo các steps và funtions như vậy để implement theo các stages (từng đoạn trong pipeline)
2. Scripted Pipeline
Sử dụng Groovy script
là kỹ thuật nâng cao hơn khi cần sử dụng code để implement vài tasks nào đó hoặc logic phức tạp tùy thuộc bài toán.
Một ví dụ đơn giản Scripted Pipeline như sau
def mavenBuild = {
profile, output ->
sh('git reset --hard ')
sh("mvn clean compile exec:java package -P ${profile} -Dskiptests=true")
sh("mv target/bookstore.war ${output}")
}
if (TOMCAT_USER == 'true') mavenBuild(mavenProfileUser, WAR_USER_BUILD_PATH)
if (TOMCAT_ADMIN == 'true') mavenBuild(mavenProfileAdmin, WAR_ADMIN_BUILD_PATH)
if (TOMCAT_STUDENT == 'true') mavenBuild(mavenProfileStudent, WAR_STUDENT_BUILD_PATH)
Lợi ích Jenkins Pipeline mang lại
So với cách thông thường, sử dụng các plugin build có sẵn phải lệ thuộc theo cách làm việc của plugin đó. Hoặc là viết Bash / Bat script để customize theo project của mình. Thì cá nhân mình thấy nó quá là bó hẹp phạm vi mở rộng, cũng như những gì mà mình đã nói ở phần đầu.
Do đó cá nhân mình đề cao những lợi ích sau mà Jenkins Pipeline mang lại:
Sử dụng ngôn ngữ Groovy
: mang đến sự thân thiện, dễ dàng implement hơn là sử dụng Bash / Bat ScriptHỗ trợ Docker
: Tùy biến môi trường phát triển cho từng project riêng biệt.Cấu trúc DSL chặt chẽ
: sử dụng cú pháp DSL (Domain Specific Language) thuận tiện , cùng với đó là các methods/functions general của jenkins pipeline mang đến sự implement tối giản hơn.Dễ dàng mở rộng với Shared Libraries
: Tự do xây dựng library common để sử dụng trong khi runtime.Hỗ trợ build đa luồng, đa môi trường phân tán trong cùng 1 build
Pipeline Syntax
Như mình đã nói ở trên, chúng ta có 2 cách cấu hình Pipeline code là Declarative Pipeline
và Scripted Pipeline
. Do đó syntax cũng có sự sắp xếp khác biệt đôi chút.
Declarative Pipeline
pipeline {
/* insert Declarative Pipeline here */
}
pipeline{ }
Đây là top-level khi bắt đầu pipeline code, tất cả các thành phần sections, stages, khai báo docker image , run sh script ..vv đều phải nằm trong cặp thẻ này.
Tiếp theo bên trong cặp pipeline
là sub-level của các sections
agent
agent
là môi trường để chạy pipeline code, các đoạn shell script vv...
Agent có thể hiểu chính jenkins master, slave machine nào đó, hoặc docker image.
Options :
any
: Chỉ định cho bất cứ master/slave nào available sẽ được pick để chạy.none
: Không sử dụng, thay vì đó các stages sẽ phải tường minh chỉ định agent cho chính nó ( Xem phầnstages
ở phía dưới )label
: tên của agent (master hoặc slave machine) sẽ dụng để thực thi nhiều stages đã khai báo. ( Khác vớiany
ở chỗ -any
sẽ tự động pick master/slave machine available)node
: Giống nhưlabel
nhưng có thể thêm nhiều options hơn. Hay nói cách khác optionnode
này là phụ trợ cholabel
docker
: sử dụng docker image cho việc chạy pipeline - https://hub.docker.com/explore/
Ví dụ :
pipeline {
// agent any
// agent none
// agent { label 'master' }
// agent { label 'manhnv-slave' }
/* agent {
node {
label 'manhnv-slave'
customWorkspace '/path/to/custom/workspace'
}
}
*/
// Hoặc với docker image
agent { docker 'maven:3-alpine' }
stages {
stage('Example Build') {
steps {
sh 'mvn clean compile exec:java package -P staging'
}
}
}
}
post
Tiếp theo sub section của pipeline
là post
. Cái sẽ được chạy cuối cùng khi kết thúc Pipeline hoặc stage (giống như finally
trong Java đó) Nhằm handle kết quả của pipeline hoặc chạy các tác vụ cần chạy sau cùng.
//Một ví dụ từ https://jenkins.io/doc/pipeline/tour/post/
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'One way or another, I have finished'
deleteDir() /* clean up our workspace */
}
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before...'
}
}
}
always
: luôn luôn được gọi, bất kể kết quả buidl là gìchanged
: chỉ chạy khi kết quả của lần build này khác với lần build trướcsuccess
: khi kết quả buildSUCCESS
unstable
: khi kết quả build làUNSTABLE
failure
: khi kết quả build làFAILURE
aborted
: khi kết quả build làABORTED
Class define kết quả build có thể refer tại Java doc - http://javadoc.jenkins-ci.org/hudson/model/Result.html
stages
Lại là một sub section nữa bên trong pipeline
, đó là stages
.
Một stages
có thể hiểu là chứa nhiều stage
con. Mỗi stage sẽ đóng một vai trò đảm nhiệm khác nhau tùy bài toán của bạn. Nó giống như việc bạn viết nhiều method trong 1 class vậy
pipeline {
agent any
stages {
stage('Clone') {
//implement pipeline code
}
stage('Build') {
//implement pipeline code
}
stage('Test') {
//implement pipeline code
}
}
}
P/s : Nếu agent là none
bạn phải chỉ định agent tương ứng cho các stage( refer section agent
ở mục trên)
steps
steps
là một thành phần nằm bên trong stage
con. Nơi mà chúng ta đặt các xử lý logic bên trong nó .
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
}
Scripted Pipeline
Nói lại một chút về Declarative Pipeline ở trên:
Nếu bạn muốn chèn một đoạn groovy script, bạn phải dùng thẻ script {}
như ví dụ steps
ở bên. Thì Scripted Pipeline
không phải làm điều đó, bởi vì nó implement theo cách mang nghĩa Scripted
Giống như Declarative Pipeline, Scripted Pipeline
cũng sử dụng cú pháp DSL và build với Groovy. Nhưng nó có thể sử dụng các functions cung cấp bởi Groovy, và "coding" theo cách của một coder
Ở đây là một ví dụ tiếp theo về Scripted Pipeline
node{
stage("Prepare") {
// Tạo function validate input param
def checkParam = {
val -> ('' == val || val == 'false')
}
if (checkParam(TOMCAT_USER) && checkParam(TOMCAT_STUDENT)) {
error('You must select environment(s) to deploy')
}
// clean old build
sh("rm -rf ${WAR_USER_BUILD_PATH}")
sh("rm -rf ${WAR_STUDENT_BUILD_PATH}")
// change dir và clone source vào folder src
dir('source') {
git url: 'https://github.com/path/to/project', branch: 'develop', credentialsId: "${env.GITHUB_ID}"
}
}
}
node trong scripted
node
là gi ?
node
chính là agent theo cách hiểu củaDeclarative Pipeline
. Bạn có thể thấy nó ởhttp://<jenkins-url>/computer/
- Một node có thể là master / slave machine / hoặc kết hợp node đó với docker
- Dùng để khai báo môi trường sẽ chạy jenkins job, hoặc chạy các stage vv. ( Giống như
agent
)
Syntax :
node(<node_name_or_label>) {
<processor>
}
Trong đó :
node_name_or_label
: Tên hoặc label của nodehttp://<jenkins-url>/computer/
. Nếu để trống, xử lý này sẽ take một node bất kỳ đang available. Giống nhưagent any
của Declarative Pipelineprocessor
: Implement các logic xử lý trong các stage, hoặc shell script ..vv
Ví dụ :
node { // sẽ take một node bất kỳ đang available
stage('Prepare'){
//TODO
}
}
node('manhnv1_slave') { // sẽ chạy trên slave machine manhnv1_slave
stage('Log1'){
//TODO
}
}
node('manhnv2_slave') { // sẽ chạy trên slave machine manhnv2_slave
stage('Log2'){
//TODO
}
}
node('master') { // Hoặc chạy trên jenkins master
stage('Log'){
//TODO
}
}
Một stage
có thể trong một node
, 1 node
cũng có thể sử dụng trong 1 stage
tùy thuộc vào bài toán của bạn.
Agent vs Node
Sau khi đi qua những phần cơ bản về Declarative vs Scripted Pipeline.
Mình cùng compare lại cách sử dụng giữa agent
(Declarative) vs node
(Scripted) bằng hình ảnh dưới để thấy rõ trong cách sử dụng
Sư khác biệt giữa Declarative vs Scripted Pipeline
Declarative | Scripted |
---|---|
Bắt đầu bằng pipeline {} | Không quy định |
Khai báo stage con nằm trong section stages {} cha | Không cần stages cha, chỉ cần khai báo các stage |
Groovy script phải viết trong section script | Viết groovy script ở bất kỳ đâu - không ràng buộc |
Bắt buộc phải có stage trong các xử lý | Stage là không bắt buộc |
Các section phải theo đúng level cha con | Không cố định level , linh động phụ thuộc từng bài toán |
Không phải xử lý logic - đơn giản hơn với các section định nghĩa sẵn (section post {} là một ví dụ) |
Phải xử lý logic, nâng cao hơn - sử dụng script code để kiểm tra tính đúng đắn |
Tổng kết
Như vậy, mình đã giới thiệu xong những thành phần cơ bản và cũng là thành phần bắt đầu để xây dựng Jenkins Pipeline Build. Và mình nghĩ đó là những thứ cần thiết cho các bạn sẽ/bắt đầu tìm hiểu Jenkins Pipeline code.
Ngoài những thành phần kể trên, còn rất nhiều thành phần nâng cao khác như sử dụng tools tích hợp, schedule/ trigger job, build condition , build paralle..vv mà không thể giới thiệu hết trong một bài.
Hy vọng qua bài viết sẽ giúp ich được phần nào đó!
All rights reserved