Jenkins Pipeline as Code: เขียน Jenkinsfile สำหรับ Build และ Deploy

Jenkins Pipeline as Code เป็นแนวทางการเขียน Build Pipeline ในรูปแบบ Code ที่เก็บไว้ใน Version Control System ร่วมกับซอร์สโค้ดโปรเจกต์ของคุณ Jenkinsfile คือไฟล์ที่เขียน Pipeline Definition ซึ่งช่วยให้ Continuous Integration และ Continuous Deployment มีประสิทธิภาพและสามารถบำรุงรักษาได้ดีขึ้น

Jenkinsfile คืออะไร และข้อดีของ Pipeline as Code

Jenkinsfile เป็นไฟล์ที่อยู่ในไดเรกทอรี่ root ของโปรเจกต์ เขียนด้วยภาษา Groovy ซึ่งทำงานบน Java Virtual Machine ข้อดีสำคัญของ Pipeline as Code:

  • Version Control: Pipeline Definition เก็บใน Git Repository ร่วมกับซอร์สโค้ด สามารถ Track History ได้
  • Reusability: สามารถนำ Pipeline ไปใช้ซ้ำได้ในหลายโปรเจกต์
  • Code Review: Pull Request จะรวม Pipeline Changes ด้วย
  • Disaster Recovery: สามารถ Restore Pipeline ได้จาก Git History

Declarative Pipeline vs Scripted Pipeline

Declarative Pipeline

Declarative Pipeline มี Structure ที่ชัดเจน ใช้ง่าย ปลอดภัยกว่า แนะนำสำหรับ Pipeline ส่วนใหญ่

pipeline {
  agent any
  stages {
    stage('Build') {
      steps {
        sh 'npm install'
      }
    }
  }
}

Scripted Pipeline

Scripted Pipeline ให้ความยืดหยุ่นมากขึ้น ใช้ Groovy Script ทั้งหมด เหมาะสำหรับ Pipeline ที่ซับซ้อน

node {
  stage('Build') {
    sh 'npm install'
  }
}

ส่วนใหญ่แนะนำให้ใช้ Declarative Pipeline เพราะให้ความเสถียรและสะดวกกว่า

โครงสร้างพื้นฐาน: Pipeline, Agent, Stages, Steps

Agent

ระบุว่า Pipeline จะรันบน Agent ใด

agent any         // รันบน Agent ใดก็ได้
agent { label 'linux' }   // เฟ้น Agent ตาม Label
agent { docker { image 'node:18' } }  // รันใน Docker

Stages และ Steps

stages {
  stage('Build') {
    steps {
      sh 'npm install'
      sh 'npm run build'
    }
  }
  stage('Test') {
    steps {
      sh 'npm test'
    }
  }
  stage('Deploy') {
    steps {
      sh './deploy.sh'
    }
  }
}

Environment Variables และ Credentials

pipeline {
  agent any
  environment {
    APP_NAME = 'my-app'
    BUILD_ENV = 'production'
    SERVER_IP = '203.150.xxx.xxx'
    SSH_KEY = credentials('vps-ssh-key')  // ได้จาก Jenkins Credentials
  }
  stages {
    stage('Build') {
      steps {
        echo "Building ${APP_NAME} for ${BUILD_ENV}"
        sh 'npm install'
      }
    }
    stage('Deploy') {
      steps {
        sh 'ssh -i ${SSH_KEY} user@${SERVER_IP} "cd /app && pm2 restart app"'
      }
    }
  }
}

When Conditions: Branch และ Environment Triggers

stages {
  stage('Deploy to Production') {
    when { branch 'main' }
    steps { sh './deploy-prod.sh' }
  }
  stage('Deploy to Staging') {
    when { branch 'develop' }
    steps { sh './deploy-staging.sh' }
  }
  stage('Run only on tag') {
    when { tag 'v*' }
    steps { sh './release.sh' }
  }
}

Parallel Stages: การรันงานพร้อมกัน

Parallel Stages ช่วยลดเวลา Build ด้วยการรันหลาย Stage พร้อมกัน

stages {
  stage('Test and Build') {
    parallel {
      stage('Unit Tests') {
        steps { sh 'npm test' }
      }
      stage('Lint') {
        steps { sh 'npm run lint' }
      }
      stage('Build') {
        steps { sh 'npm run build' }
      }
    }
  }
}

Post Actions: always, success, failure

post {
  always {
    echo 'Pipeline execution completed'
    cleanWs()  // ลบ Workspace
  }
  success {
    echo 'Build successful!'
    // แจ้ง Slack
  }
  failure {
    echo 'Build failed!'
    mail to: '[email protected]',
         subject: "Build ${BUILD_NUMBER} Failed",
         body: "Check Jenkins: ${BUILD_URL}"
  }
  unstable {
    echo 'Build is unstable'
  }
}

ตัวอย่าง Jenkinsfile สมบูรณ์สำหรับ Node.js Deploy ไปยัง Cloud VPS ด้วย SSH

ตัวอย่าง Jenkinsfile ที่สมบูรณ์สำหรับ Node.js Application Deploy ไปยัง Cloud VPS ผ่าน SSH:

pipeline {
  agent any

  environment {
    APP_NAME = 'nodejs-app'
    NODE_ENV = 'production'
    SERVER_IP = '203.150.xxx.xxx'
    SERVER_USER = 'deploy'
    APP_PATH = '/var/www/nodejs-app'
  }

  options {
    buildDiscarder(logRotator(numToKeepStr: '10'))
    timeout(time: 30, unit: 'MINUTES')
    timestamps()
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Install Dependencies') {
      steps {
        sh 'npm install'
      }
    }

    stage('Test and Lint') {
      parallel {
        stage('Unit Tests') {
          steps { sh 'npm test' }
        }
        stage('Linting') {
          steps { sh 'npm run lint' }
        }
      }
    }

    stage('Build') {
      steps {
        sh 'npm run build'
      }
    }

    stage('Deploy to Cloud VPS') {
      when { branch 'main' }
      steps {
        withCredentials([sshUserPrivateKey(credentialsId: 'vps-ssh-key', keyFileVariable: 'SSH_KEY')]) {
          sh '''
            ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} "
              cd ${APP_PATH} && \
              git pull origin main && \
              npm install --production && \
              npm run build && \
              pm2 restart ${APP_NAME} || pm2 start npm --name '${APP_NAME}' -- start
            "
          '''
        }
      }
    }

    stage('Health Check') {
      when { branch 'main' }
      steps {
        retry(3) {
          sh 'curl -f http://${SERVER_IP}:3000/health || exit 1'
        }
      }
    }
  }

  post {
    always { cleanWs() }
    success {
      echo 'Deployment successful!'
    }
    failure {
      mail to: '[email protected]',
           subject: "Build ${BUILD_NUMBER} Failed",
           body: "Check the build logs at ${BUILD_URL}"
    }
  }
}

Shared Libraries

Shared Libraries ช่วยให้สามารถเขียน Code ที่ใช้ร่วมในหลาย Pipeline โครงสร้าง:

shared-library/
├── src/
├── vars/
│   ├── deployVPS.groovy
│   └── runTests.groovy
└── resources/

ตัวอย่างไฟล์ vars/deployVPS.groovy:

def call(String server, String appPath) {
  withCredentials([sshUserPrivateKey(credentialsId: 'vps-ssh-key', keyFileVariable: 'SSH_KEY')]) {
    sh """
      ssh -i ${SSH_KEY} deploy@${server} "
        cd ${appPath} && git pull && npm install --production && pm2 restart app
      "
    """
  }
}

ใช้ Shared Library ใน Jenkinsfile:

@Library('shared-library') _

pipeline {
  agent any
  stages {
    stage('Deploy') {
      steps {
        deployVPS('203.150.xxx.xxx', '/var/www/app')
      }
    }
  }
}

สรุป

Jenkins Pipeline as Code ด้วย Jenkinsfile ช่วยให้ทีมพัฒนามี CI/CD Pipeline ที่สามารถสอบทาน ตรวจสอบ และ Deploy ได้อย่างมีประสิทธิภาพ

สำหรับการ Deploy Application ไปยัง Server บนระบบ Cloud ที่มีประสิทธิภาพและเสถียร Cloud VPS ของ ผู้ให้บริการโฮสติ้ง คือตัวเลือกที่ยอดเยี่ยม พร้อมทีม Support ภาษาไทยดูแลตลอด 24 ชั่วโมง