สวัสดีครับ เคยเบื่อกันบ้างไหม เขียนโค้ดใหม่ทีหนึ่ง ต้อง Remote เข้า FTP Protocols แล้วลากไฟล์ลงแบบดิบเถื่อน แล้วต้อง build อีกทีหนึ่ง โลกนี้มีระบบที่เรียกว่า CI/CD หรือ Continuous Integration and Continuous Delivery ซึ่งมันจะช่วยให้แค่เรา Push Code เข้า Github แล้วตัวมันจะฟังการเปลี่ยนแปลงของ Repository แล้ว Clone ลงมา Build ให้เอง เรียกได้ว่าสะดวกและรวดเร็วสุด ๆ

ในวงการพัฒนาซอฟต์แวร์ใช้ระบ CI/CD มานานมาก ๆ แล้วครับ ซึ่งตัวผมก็มาทาง Experimental จ๋า ๆ ไม่ค่อยจะรู้เรื่องอะไรกับเขาหรอกครับ จนได้ฝึกงานที่บริษัทซอฟต์แวร์ชื่อดังแห่งหนึ่งในเชียงใหม่ จนได้รู้ว่า โอ้ววว มันก็คือ Standard อย่างหนึ่งในการผลิตซอฟต์แวร์ที่ขาดไม่ได้

Jenkins จะเป็นตัว Softwares ที่ผมจะมาแนะนำการใช้งานในบทความนี้แบบสั้น ๆ ใช้งานได้

Jenkins คืออะไร ?

ไอ้เจ้า Jenkins เนี้ยเป็น Open-source automation server หรือเรียกง่าย ๆ ว่าเป็นตัว ตั้งงานอัตโนมัติบนเซิร์ฟเวอร์ ที่เป็น Open-source ซึ่งเราสามารถนำตัว Software ดังกล่าวมาติดตั้งใช้งานได้ทันที นั่นแหละครับ

ซึ่งเจ้า Jenkins นี่ลูกเล่นค่อนข้างเยอะ ทำได้ตั้งแต่ Clone (Build), Unit Tests ยัน Deploy ให้ได้เลย โดยบทความนี้เราจะเอาแค่ว่า เรา Push Code เข้า Git หน้าเว็บ Production แก้ไขตามทันที งี้ก็พอครับ ส่วนลูกเล่นอื่น ๆ เพื่อน ๆ ลองไปอ่าน Docs และลองเล่นกันเอาเอง

ก่อนแรกเราต้องติดตั้ง Jenkins อะไรให้เรียบร้อยก่อนครับ ด้วยคำสั่ง

sudo apt install -y fontconfig openjdk-21-jre
# เพิ่ม Jenkins GPG key
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null

# เพิ่ม Jenkins repository เข้าไปใน apt sources list
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/" | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt update
sudo apt install -y jenkins

จากนั้นเราก็เปิดใช้งานมันเลยครับ

sudo systemctl start jenkins
sudo systemctl enable jenkins

ตัว Web Interface ของ Jenkins จะอยู่ที่ Port 8080 ครับ เมื่อเราเข้าไป การติดตั้งครั้งแรกของมัน มันจะถามที่ให้เราไปเอารหัสเริ่มต้นมาใส่ก่อนครับ ซึ่งเราก็ cat มันออกมาแบบนี้ได้เลย

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

จากนั้นก็รอมันติดตั้งไปสักพักเลยครับ เมื่อเสร็จสิ้น เราก็จะไปเชื่อมต่อมันกับ GitHub กัน

การเชื่อมต่อ Jenkins เข้ากับ GitHub

ขั้นตอนนี้ขาดไม่ได้ ถ้าเราจะทำให้มัน Automate งาน Build เราจริง ๆ

ในฝั่งของ Github ให้เราไปที่ https://github.com/settings/tokens แล้วสร้าง access token (classic) ขึ้นมา โดยเราสามารถตั้ง Expiration และ Scope ของ Token นี้ได้ครับ

แล้วเราก็จะไปที่หน้า Interface ของ Jenkins ที่ /manage/credentials/ แล้วให้เรา "Add Credentials" แบบ Global และเลือก "Username and Password" โดย Username ให้เราใส่ GitHub Username ของเราเลยครับ และส่วน Password ให้ใส่ Token ที่เราได้มาจากการสร้าง access token ใน GitHub

แค่นี้เราก็สามารถ Clone Repository โดยเฉพาะ Private Repository จาก GitHub ของเรามาได้แล้วครับ

การเพิ่ม Repository เข้าไปใน Jenkins

เมื่อไปที่หน้าหลักของ Jenkins เพื่อน ๆ จะเห็นคำว่า "New Item" ตรงแท็บ Sidebar ด้านซ้ายครับเมื่อเรากดเข้าไปแล้วเราก็ตั้งชื่อ Item ตัวนี้เลยครับ 

แล้วทีนี้เราจะเลือก item type เป็น Freestyle project ไปก่อนครับ เพราะมันง่าย และเขียน Build Step ใกล้เคียงกับ Bash มากที่สุด 

เมื่อเรากด Next ตัว Jenkins ก็จะพาเรามาหน้า configure ของ item ตัวนั้นซึ่งตรงนี้แหละที่เราจะตั้งค่ามันว่า git repo url เราอยู่ที่ไหนยังไง จะดึงข้อมูลมาด้วยวิธีไหนยังไง

Source Code Management ตรงนี้จะเป็นส่วนที่เราจะใส่ Repository URL ของเราเข้าไปครับ โดยการเลือก Git และกรอกลิงค์ Repo ของเรา (ที่ลงท้ายด้วย .git) ลงไปในช่องนี้ ซึ่งหากเป็น Private Repository เราก็จะเลือก Credentials เป็นตัวที่เราเพิ่งเพิ่มเข้าไปนี่แหละครับ เพราะโดยปกติแล้ว Jenkins มันสามารถ Clone แล้ว Deploy Public Repository ได้อยู่แล้วครับ แต่ถ้าเป็น Private Repo เราก็จะต้องใช้ Credential ที่ถูกต้อง

เสร็จแล้วถ้าไม่มี Error ใด ๆ เราก็ไปเลือก Branches to build ได้ต่อเลยครับ แล้วสุดท้ายสำคัญทีุ่ดคือเราจะไปตรง Triggers แล้วเลือก GitHub hook trigger for GITScm polling

สุดท้ายสำคัญมาก คือ การกำหนด Build Steps ซึ่งสำหรับผมจะใช้ Execute shell ครับเพราะมันง่ายดีและใกล้เคียงกับคำสั่งที่เรารันบ่อย ๆ มากที่สุด

ซึ่งปกติเราก็จะรันประมาณว่า npm i && npm run build อะไรประมาณนี้ใช่ปะครับ เราก็ใส่เข้าไปเลยในช่อง Command

แค่นี้เวลาเรากด Build มันก็จะ Clone -> Deploy ให้เรียบร้อยครับ

** เพิ่มเติม **

หากเพื่อน ๆ ลองกด Build ครั้งแรกแล้วไม่ผ่าน โดยมี Error ประมาณนี้

+ npm i npm ERR! code EACCES npm ERR! syscall unlink npm ERR! path /var/lib/jenkins/workspace/logistic/node_modules/.package-lock.json npm ERR! errno -13 npm ERR! Error: EACCES: permission denied, unlink '/var/lib/jenkins/workspace/logistic/node_modules/.package-lock.json' npm ERR! [Error: EACCES: permission denied, unlink '/var/lib/jenkins/workspace/logistic/node_modules/.package-lock.json'] { npm ERR! errno: -13, npm ERR! code: 'EACCES', npm ERR! syscall: 'unlink', npm ERR! path: '/var/lib/jenkins/workspace/logistic/node_modules/.package-lock.json' npm ERR! } npm ERR! npm ERR! The operation was rejected by your operating system. npm ERR! It is likely you do not have the permissions to access this file as the current user npm ERR! npm ERR! If you believe this might be a permissions issue, please double-check the npm ERR! permissions of the file and its containing directories, or try running npm ERR! the command again as root/Administrator.

แสดงว่าโฟล์เดอร์ /var/lib/jenkins ยังเป็นของ user อื่น เช่น root อยู่ เนื่องจากตัว Jenkins จะต้องมาแก้ไขโฟล์เดอร์ workspace/ ข้างในนี้โดยตรงเราจึงจำเป็นต้องให้ jenkins เป็นเจ้าของโฟล์เดอร์ /var/lib/jenkins ก่อน โดยใช้คำสั่ง

sudo chown -R jenkins:jenkins /var/lib/jenkins
sudo chown -R jenkins:jenkins /var/cache/jenkins

กรณีที่ใช้งานเริ่มกับ PM2 โดย PM2 รันอยู่ใน User Root เราก็จำเป็นจะต้อง Restart Node App ของเราบน PM2 หลัง Clone มาใหม่เช่นกัน โดยเราจะเพิ่ม user jenkins เข้าไปใน sudo list โดยให้ทันสามารถรัน sudo pm2 restart ได้เลย โดยที่ไม่ต้องใส่รหัสผ่าน

ก่อนแรกเราก็จะใช้คำสั่ง

sudo visudo

จากนั้นเราก็เพิ่มบรรทัดข้างล่างนี้ลงไปได้เลย

jenkins ALL=(ALL) NOPASSWD: ALL

ตรงนี้ก็จะอันตรายหน่อย เพราะ jenkins ก็จะเสมือนมีสิทธิเท่า root ได้เลย ถ้า account jenkins โดน Hacker ยึดไปก็คือจบกัน ดังนั้นเราก็จะกำหนดว่า มันจะใช้ sudo ได้โดยไม่ต้องถามรหัส เฉพาะตอนรัน /usr/bin/pm2 (หรือ path pm2 ของเพื่อน ๆ) เท่านั้น แบบนี้ก็ได้

jenkins ALL=(ALL) NOPASSWD: /usr/bin/pm2

การเพิ่ม GitHub Webhook Trigger สำหรับ Jenkins

ต่อไปเราจะกลับไปที่ Repository ที่เราเพิ่งเพิ่มเข้าไปใน Jenkins ครับ โดยเราจะไปตั้งค่า Webhook ในฝั่งของ GitHub ครับ โดยที่ไปที่ Repository นั้น ๆ ของเราแล้วไปใน Settings แล้วก็ Webhooks แล้วกดเพิ่ม Webhook ใหม่เลยครับ

โดยเราจะใส่ Payload URL เป็น URL Jenkins Public ของเรา เช่น http://[IP]:8080 แล้วใส่ /github-webhook/ ไว้ข้างหลังเลยครับ แล้วเลือก Content type เป็น application/json

ส่วนของ Which events would you like to trigger this webhook? เราสามารถให้มัน Trigger แค่ตอน Push อย่างเดียวก็ได้ หรือเอาทุก Events เลยก็ได้ โดยผมจะเอาแค่ตอน Push ก็จะเลือก Just the push event. ครับ

เสร็จแล้วเราก็กด Add Webhook เลยครับ แค่นี้เมื่อเรา Push Commit ใหม่เข้า Repo ไปมันก็จะรัน Build Steps แล้วก็จัดการอะไรให้เสร็จสับ