หลังจากที่ได้รู้วิธีการเขียน gitlab-ci กับของ golang แล้วก็ nodejs กันมาแล้ว คราวนี้เราจะมาลองเพิ่มระดับความยากอีกระดับ นั้นก็คือการเขียน gitlab-ci ให้ใช้งานกับ Microservice กัน
โดยผมเลือกใช้ mono-repo ในการจัดการกับ Microservice เพื่อเป็นการง่ายในการจัดการแต่ละ service ที่จะเกิดขึ้นให้อยู่ใน Repository เดียวไปเลย
ตัวอย่างจากรูปบนให้สังเกตุรูปด้านขวาจะเป็นการสร้างมา 1 repository จากนั้นข้างในจะแยกโฟลเดอร์เป็นแต่ละ service เพื่อให้เราจัดการไฟล์ต่างๆ ได้ง่ายขึ้น
ก่อนจะมาลุยกันถ้าใครยังไม่เคยใช้ gitlab-ci ให้ไปศึกษาพื้นฐานมาก่อนจาก https://area51.twinsynergy.co.th/tag/gitlab/ ซึ่งเป็นบทความที่ผมเคยเขียนมาแล้ว เพราะในบทความนี้ผมจะไม่อธิบายโค็ดทั้งหมด จะอธิบายในส่วนแค่เพิ่มเติมของการทำ Micro service เท่านั้น ซึ่งหลังจากข้อความนี้ไปผมถือว่าทุกคนที่อ่านเคยเขียน gitlab-ci มาแล้ว
ผมได้ทำโค็ดตัวอย่างมาแล้วสามารถ clone ได้เลยจาก https://gitlab.com/twin-opensource/microservice-cicd ซึ่ง repo อันนี้จะมี 2 services ได้แก่ nodejs และ golang จะเป็นการเขียนที่ต่างภาษากันจะเห็นภาพชัดเจนขึ้น ซึ่งโครงสร้างภายในจะเป็นดังนี้
microservice-cicd/
├── .gitignore
├── .gitlab-ci.yml
├── golang
│ ├── Dockerfile
│ ├── .gci.yml
│ ├── .gitignore
│ ├── Gopkg.lock
│ ├── Gopkg.toml
│ ├── main.go
│ └── README.md
├── nodejs
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── .gci.yml
│ ├── .gitignore
│ ├── package.json
│ ├── README.md
│ └── server.js
└── readme.md
golang/
และ nodejs/
ทั้งสองโฟลเดอร์นี้จะเป็น service ที่เราจะทำ ซึ่งเราควรตั้งชื่อตาม service ที่จะใช้งานจริง.gitignore
เอาไว้บังคับไม่ให้เอาไฟล์บ้างไฟล์ขึ้นบน Git โดยผมใช้ gitignore.io ช่วยสร้างขึ้นมา.gitlab-ci.yml
เป็นไฟล์ไว้ทำ CI/CD ของ gitlabstages:
- test
- dep
- build
- release
- deploy
include:
- '/nodejs/.gci.yml'
- '/golang/.gci.yml'
จะเห็นว่าในไฟล์นี้ผมจะกำหนดแค่สองค่าเท่านั้นคือ
คราวนี้เราลองมาดูไฟล์ .gci.yml
ที่ผมทำการ include เข้ามา ซึ่งวิธีการเขียนมันก็คือการเขียนไฟล์แบบ .gitlab-ci.yml
แต่ผมแค่เปลี่ยนชื่อไฟล์มันเฉยๆ
โฟลเดอร์ nodejs ไฟล์ .gci.yml
จะเป็นดังนี้
variables:
NODEJS_PATH: "nodejs"
DEV_NODEJS_BRANCH: "dev-release-nodejs"
.dev_only: &dev_only
only:
- dev-release-nodejs
nodejs-npm:
stage: build
image: node:10-alpine
script:
- cd $NODEJS_PATH
- '[ -f package-lock.json ] && rm package-lock.json'
- npm i
cache:
key: npm-cache
paths:
- $NODEJS_PATH/node_modules
<<: *dev_only
nodejs-npm job นั้นผมจะให้มัน install node_modules แล้วก็ caching เก็บไว้ โดยจะเฉพาะ dev-release-nodejs branch เท่านั้น สังเกตุจาก <<: *dev_only
ที่ผมใส่เข้ามา ซึ่งมันจะไปดึงค่าจาก anchor ข้างบนมาใช้ โดยเวลามันอ่านจริงจะเป็นแบบนี้
nodejs-npm:
stage: build
image: node:10-alpine
script:
- cd nodejs
- '[ -f package-lock.json ] && rm package-lock.json'
- npm i
cache:
key: npm-cache
paths:
- nodejs/node_modules
only:
- dev-release-nodejs
ส่วน job อื่นก็จะถูกกำหนดจาก <<: *dev_only
เหมือนกัน
หลักการเขียนเหมือนกับของ nodejs service ข้างต้นเลย คือผมจะทำ archor
.dev_only: &dev_only
only:
- dev-release-golang
กำหนดไว้แล้วนำไปใช้กับทุก job เพื่อให้แต่ละ job นั้นทำงานเฉพาะ dev-release-golang เท่านั้น
โดยปกติผมจะสร้าง dev branch ขึ้นมาก่อนเป็นตัวหลัก แล้วให้ developer แต่ละคน แตก branch ตาม issue ที่ตัวเองได้ไปจาก dev branch นี้ แล้วไปทำงานของตัวเองกัน จากนั้นทำการสร้าง Merge request จากหน้าเว็บ gitlab เข้ามายัง dev branch นี้ แล้วผมจะตรวจสอบ code ต่างๆ ถ้าโอเค ก็จะกดปุ่มให้มัน Merge เข้า dev branch
หลังจากที่ merge เข้า dev branch แล้วตัว pipeline จะยังไม่ทำงาน เพราะเราได้กำหนดไปแล้วว่าให้ทำงานเฉพาะ dev-release-nodejs หรือ dev-release-golang
เราก็แค่สร้าง merge request จาก dev มายัง dev-release-nodejs ถ้าอยากให้ nodejs service ทำงาน
และสร้าง merge request จาก dev มายัง dev-release-golang ถ้าอยากให้ golang service ทำงาน
เพียงเท่านั้นเราก็จะสามารถสั่งการทำงาน CI/CD ให้แต่ละ service ได้แล้ว
ขอให้สนุกกับการเขียน gitlab-ci นะครับ 🙂