Babel Coder

พัฒนาแอพบน Kubernetes ให้เป็นเรื่องง่ายด้วย Skaffold

beginner

เกือบ 4 ปีที่เหล่าออเจ้าได้รู้จักหนทางแห่งการ deploy แอพพลิเคชันที่เลิศ scale ระบบได้ดี และจัดการแอพพลิเคชันได้ชิค ๆ คูล ๆ กับเครื่องมือที่ชื่อว่า Kubernetes ที่ดังกระฉ่อนไปทั่วทั้งพระนคร

ทว่า deployment โดยทั่วไปยังฝากชะตากรรมนี้ไว้กับ devops ทีม โปรแกรมเมอร์นั้นฤางานกรรมกรมีหน้าที่โค้ดก็โค้ดไปซิ จะกระสันไปจุ้นส่วนอื่นทำไม

โลกเปลี่ยนคนเปลี่ยน เมื่อสภาพแวดล้อมในการพัฒนาเช่นระบบปฏิบัติการไม่ตรงกับการทำงานบน production บางครั้งปัญหาจึงเกิด โปรแกรมเมอร์จึงต้องขายวิญญาณด้วยการสร้าง containerized app ผ่าน Docker ส่วนงาน deploy นั้นไซร์ให้ทีมอาวุโสอย่าง devops ทำต่อไปเถอะ

ถึงกระนั้นปัญหายังคงราวี แม้ตอนนี้โปรแกรมเมอร์จะควบคุมและจัดการ dependencies ในการพัฒนาให้ใกล้เคียงกับการใช้งานจริงได้แล้วก็ตาม แต่ขั้นตอนของการ deploy นั้นก็ไม่การันตีว่าจะทำให้แอพพลิเคชันของเราไม่นิพพานก่อนวัยอันควร โปรแกรมเมอร์ที่น่าสงสารจึงต้องเรียนรู้เพื่อใช้งาน Kubernetes จะได้ทดสอบและตรวจสอบการทำงานให้ถูกต้องก่อนการส่งมอบงาน

สารบัญ

กระบวนการพัฒนาซอฟต์แวร์ด้วย Kubernetes

โดยทั่วไปกระบวนการพัฒนาจึงเป็นเช่นนี้ [1]

  1. จัดหาคลัสเตอร์ของ Kubernetes ซะก่อนไม่ว่าจะเป็น GKE AKS หรือเครื่องมืออย่าง kops
  2. Build Docker Image และทำการอัพโหลดไปยัง registry เพื่อใช้งานบนคลัสเตอร์
  3. สร้าง Kubernetes manifest เพื่อใช้งานตามเอกสารของ Kubernetes
  4. Deploy แอพพลิเคชันผ่าน kubectl CLI หรือ Kubernetes Dashboard
  5. ทำซ้ำข้อ 2 - 4 จนกว่ากาพัฒนาฟีเจอร์หรือการแก้ข้อผิดพลาดจะเสร็จสิ้น
  6. โยนโครมลง CI เพื่อทำ Unit testing Integration testing และ deploy ลงสภาพแวดล้อมแบบ test หรือ staging

แค่อ่านก็หาวแล้วใช่ไหม โดยปกติขั้นตอนที่ 2 - 5 มักเป็นกระบวนการทำมือผ่านเครื่องมือต่าง ๆ และบางอย่างก็เป็นเรื่องที่ต้องตบตีกันเอาเองว่าจะใช้ท่าไหน เช่น บางคนกำหนดเลข tag ด้วยการนับเพิ่มเรื่อยๆ จาก 1 ไปถึงไหนก็ไม่รู้ แต่ถ้าวันนึง deploy เยอะมากนับเลขคงไม่ไหว เอา tag เป็นเลข commit ของ GIT แล้วกัน เป็นต้น

รู้จัก Skaffold เครื่องมือที่จะทำให้การพัฒนาเป็นเรื่องง่าย

ทั้งหมดทั้งมวลจะเห็นว่าขั้นตอนที่ 2 - 5 นั้นยุ่งยากและเป็นส่วนที่สามารถ automate ได้ จึงเกิดเครื่องมือสนอง need ต่าง ๆ มากมาย เช่น Draft จาก Microsoft Forge จาก Datawire และ Flux จาก Weavework

และล่าสุดกับ Skaffold เครื่องมือสุดเจ๋งที่จะช่วยเหล่าออเจ้าลดความยุ่งยากในการพัฒนาแอพพลิเคชันและ deploy ด้วย Kubernetes

ไม่ต้องพูดพร่ำทำเพลงเยอะ ลอง build push และ deploy แอพพลิเคชัน Node.js ด้วย Skaffold ดูซิว่ามันเจ๋งแค่ไหนแล้วค่อยกลับมาสรุปกันอีกครั้ง

build/push/deploy แอพพลิเคชัน Node.js ด้วย Skaffold

ก่อนที่จะทดลองใช้งาน Skaffold ขอให้มั่นใจก่อนว่าเพื่อน ๆ ได้ทำการติดตั้ง Docker Minikube และ kubectl เป็นที่เรียบร้อยแล้ว หลังจากนั้นจึงทำการติดตั้ง Skaffold ในลำดับถัดไป สำหรับระบบปฏิบัติการ MacOS ดำเนินการติดตั้งผ่านคำสั่งนี้

curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin

เมื่อทุกอย่างพร้อม เราก็พร้อมไปต่อกับการสร้างเซิฟเวอร์อย่างง่าย ๆ ด้วย express.js ผ่าน package.json ดังนี้

{
  "name": "skaffold",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.3"
  }
}

และนี่คือโค้ดหลักของโปรแกรมเราใต้ index.js

const express = require('express')
const app = express()

app.get('/', function(req, res) {
  res.send('Babel Coder!')
})

app.listen(3000, err => {
  if (err) throw err

  console.log('server is listening');
})

แอพพลิเคชันพร้อม! ต่อไปก็เตรียมความพร้อมสำหรับการสร้าง Docker Image ไว้ใช้กับคลัสเตอร์ผ่านการนิยามด้วย Dockerfile

FROM node:8.10.0-alpine

WORKDIR /usr/src/app

COPY package.json .
COPY package-lock.json .
RUN npm install

COPY . .

EXPOSE 3000

CMD npm start

โดยปกติเมื่อถึงขั้นตอนนี้เราต้องทำการ build และแปะ tag เพื่อสร้าง Docker Image สำหรับนำไปใช้ต่อ แต่ตอนนี้เราฉลาดขึ้นแล้วผลักภาระไปให้ Skaffold ทำเองเถอะแม่หญิง

ถัดไปจึงสร้าง deployment และ service ผ่านไฟล์ k8s.yml ดังนี้ [2]

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: node-app
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: node-app
    spec:
      containers:
      - name: node-app
        image: IMAGE_NAME
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: node-app
  labels:
    app: node-app
spec:
  selector:
    app: node-app
  ports:
  - port: 3000
    protocol: TCP
    nodePort: 30003
  type: LoadBalancer

โปรดสังเกตว่าเราจะไม่ระบุ Docker image ลงไปในส่วนของ containers นั่นเพราะถ้าเราทำมือเองเมื่อ tag เปลี่ยนเราก็ต้องเปลี่ยนส่วนนี้เองตลอด แต่เราจะระบุเป็น IMAGE_NAME แทนเพื่อให้ Skaffold จัดการสร้าง image พร้อม tag ใหม่ให้กับเราทุกครั้งที่โค้ดเราเปลี่ยน และทำการแทนที่ IMAGE_NAME ด้วยชื่อ image นั้น

คำถาม แล้ว Skaffold รู้ได้อย่างไรว่า IMAGE_NAME ควรมีชื่อเป็นอะไร? เราจึงต้องสร้างไฟล์ชื่อ skaffold.yaml เพื่อนิยามการทำงาน ดังนี้

apiVersion: skaffold/v1alpha1
kind: Config
build:
  artifacts:
  - imageName: node-app
    workspace: .
  local: {}
deploy:
  kubectl:
    manifests:
    - paths:
      - k8s.yml
      parameters:
        IMAGE_NAME: node-app

เมื่อโค้ดของเราเกิดการเปลี่ยนแปลง Skaffold จะทำการ build image ใหม่ทุกครั้งด้วยชื่อที่เราระบุใน IMAGE_NAME พร้อมแปะ tag ให้กับเราอัตโนมัติ ส่วนโปรแกรมเมอร์นั้นหรือ จิบกาแฟไปซิ~

เอาหละตอนนี้ก็ถึงเวลาทดลองกันแล้ว เมื่อเป็นการพัฒนาบนเครื่องจงอย่าลืมที่จะปลุกชีพ Minikube ขึ้นมาก่อนด้วยคำสั่ง minikube start จากนั้นจึงสั่ง Skaffold ให้ทำการ build/push/deploy ให้กับเราด้วยคำสั่ง skaffold dev

Starting build...
Found minikube or Docker for Desktop context, using local docker daemon.
Sending build context to Docker daemon  1.944MB
Step 1/8 : FROM node:8.10.0-alpine
 ---> adc4b0f5bc53
Step 2/8 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 484a011055f1
Step 3/8 : COPY package.json .
 ---> 48c848a33ca8
Step 4/8 : COPY package-lock.json .
 ---> b467f6dff7ee
Step 5/8 : RUN npm install
 ---> Running in 9da273042e8f
added 49 packages in 1.165s
 ---> b6e8c2d170b4
Step 6/8 : COPY . .
 ---> 2d977d626ad8
Step 7/8 : EXPOSE 3000
 ---> Running in f32b155a225e
 ---> 44b426f66723
Step 8/8 : CMD npm start
 ---> Running in 521fc2ea13ca
 ---> 68e3745c9338
Successfully built 68e3745c9338
Successfully tagged 05cd3c1f9adb2e24f0660a3eb51071ea:latest
Successfully tagged node-app:68e3745c933892673713541009428c3f1e4e7437f6f2c33ddd7aa5f706e2bfc7
Build complete.
Starting deploy...
Deploying k8s.yml...
Deploy complete.

จะสังเกตได้ว่า Skaffold ทำการแปะ tag ให้กับเราอัตโนมัติด้วยชื่อจาก IMAGE_NAME และค่า hash ออกคำสั่งล่างนี้เพื่อดูผลลัพธ์เป็นการแสดงผล Babel Coder! ออกหน้าจอ

minikube service node-app

ยังไม่จบแค่นั้นครับ Skaffold ยังตรวจจับการเปลี่ยนแปลงโค้ดได้ด้วย เมื่อไหร่ที่โค้ดของเราเปลี่ยนมันจะทำการ build/push/deploy ให้กับเราอย่างอัตโนมัติ ลองเปลี่ยนโค้ดของ index.js จาก

app.get('/', function(req, res) {
  res.send('Babel Coder!')
})

เป็น

app.get('/', function(req, res) {
  res.send('Babel Coder Rocks!')
})

แล้วชะโงกหน้าดูที่ terminal ก็จะพบความจริงว่า…

Starting build...
Found minikube or Docker for Desktop context, using local docker daemon.
Sending build context to Docker daemon  1.944MB
Step 1/8 : FROM node:8.10.0-alpine
 ---> adc4b0f5bc53
Step 2/8 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 484a011055f1
Step 3/8 : COPY package.json .
 ---> Using cache
 ---> 48c848a33ca8
Step 4/8 : COPY package-lock.json .
 ---> Using cache
 ---> b467f6dff7ee
Step 5/8 : RUN npm install
 ---> Using cache
 ---> b6e8c2d170b4
Step 6/8 : COPY . .
 ---> 41d5b84d7feb
Step 7/8 : EXPOSE 3000
 ---> Running in 12da84409ecb
 ---> 8831c0755173
Step 8/8 : CMD npm start
 ---> Running in ebe7b8e815d0
 ---> b4a1e92c24b7
Successfully built b4a1e92c24b7
Successfully tagged 8d090121e01a29734b5b997f9bc114fb:latest
Successfully tagged node-app:b4a1e92c24b76f7507713806e02496e27e8d67236130dbf72e22246e569585ca
Build complete.
Starting deploy...
Deploying k8s.yml...
Deploy complete.

เมื่อ Skaffold ตรวจพบการเปลี่ยนแปลงจึงทำการ build/push/deploy ใหม่อีกครั้ง หากออกคำสั่ง minikube service node-app เราก็จะพบผลลัพธ์ที่เปลี่ยนไป

Skaffold ดีอย่างไร

หลังจากลองเล่น Skaffold กันไปแล้ว ถึงเวลาแล้วหละที่จะสรุปว่า Skaffold นั้นควรค่าแก่การขึ้นหิ้งอย่างไร

  • Skaffold นั้นตรวจจับการเปลี่ยนแปลงแล้วทำการ build/push/deploy ให้ใหม่ ไม่กวนเวลาจิบกาแฟยามเที่ยงและงีบหลับในเวลางานของเรา
  • จัดการ image tag ให้กับเราผ่าน sha256 จากโค้ดของเรา แต่ถ้าเป็นบน CI/CD ก็จะใช้ git commit แทน

Skaffold นั้นให้สิทธิ์ในการเลือกเครื่องมือที่เหมาะสมสำหรับเรา เช่น deploy บน Minikube บน development และใช้ GKE กับ Helm บน production

Pluggability

สรุป

Skaffold นั้นเป็นอีกหนึ่งเครื่องมือที่ช่วยให้งานพัฒนาและการส่งมอบเป็นเรื่องง่าย จากงานมือทำซ้ำซากให้เป็นงานหน่อมแน้มทำอัตโนมัติ หากเพื่อน ๆ คนไหนสนใจสามารถอ่านเพิ่มเติมได้จาก Repo ของ Skaffold ครับ

เอกสารอ้างอิง

Daniel Bryant. (2018) Google Releases “Skaffold”, a Tool That Facilitates Continuous Development with Kubernetes. Retrieved Mar, 23, 2018, https://www.infoq.com/news/2018/03/skaffold-kubernetes

Vic Iglesias. (2018) Introducing Skaffold: Easy and repeatable Kubernetes development. Retrieved Mar, 23, 2018, from https://cloudplatform.googleblog.com/2018/03/introducing-Skaffold-Easy-and-repeatable-Kubernetes-development.html

Gergely Nemeth. (2018) Using Kubernetes for Local Development. Retrieved Mar, 23, 2018, https://nemethgergely.com/using-kubernetes-for-local-development/


  1. https://cloudplatform.googleblog.com/2018/03/introducing-Skaffold-Easy-and-repeatable-Kubernetes-development.html

  2. https://nemethgergely.com/using-kubernetes-for-local-development/


แสดงความคิดเห็นของคุณ


No any discussions