มาทำ Private Node Module กันเถอะ

Putt Potsawee
LifeatRentSpree
Published in
4 min readDec 3, 2018
Photo by Marcello Gennari on Unsplash

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

ในบทความนี้จะเล่าถึงวิธีการแชร์โค้ดระหว่างโปรเจค ด้วยการทำ Node Module แบบ Private เพื่อช่วยในการ Maintain Code สำหรับ Microservice Architecture และโปรเจคที่ Build บน Docker เอาหละ ลุย!

แชร์โค้ด? ยังไงนะ

ในทีม Rentspree เราค่อนข้างซีเรียสกับคอนเซปต์ DRY (Don’t Repeat Yourself)

อะไรก็ตามที่เป็นโค้ดซ้ำ เราจะไม่ก็อปปี้มันเด็ดขาด เราจึงมีกฎว่า ถ้า Module ไหนใช้ซ้ำ เราจะสร้างมันเป็น Module แยก

Rentspree Screening Handout

ยกตัวอย่างโค้ด Screening Handout ตัวนี้ของ Rentspree เราจำเป็นต้องใช้โค้ดส่วนนี้สองส่วน คือในหน้าเว็บ สำหรับ user สั่งพิมพ์ Handout และในส่วนหลังบ้าน จะมีการ อัพโหลดไฟล์ PDF Handout ตัวนี้ไปยังระบบอื่นที่เชื่อมต่อกับ Rentspree

เราจึงใช้วิธีสร้างโค้ดส่วน PDF Handout นี้เป็น Module แยก เพื่อเวลาเอาไปใช้งานก็เป็นประมาณนี้

import pdf from "@rentspree/pdf"pdf.generate(user, link, address)

วิธีการทำโค้ดเราให้เป็น Node Module สามารถหาอ่านได้ จากนี่

ซึ่งโปรเจคไหนที่ต้องการเอา module นี้ไปใช้ ก็ทำตามปกติของ NPM ได้เลย

// package.json
...
"@rentspree/pdf": "^0.1.1",
...

แต่เดี๋ยวนะ เอาขึ้นไป NPM แบบนี้คนอื่นก็เห็นโค้ดเราหมด เอาไปใช้ได้กันหมดสิ ในกรณีนี้เราไม่ต้องการจะแชร์โค้ดเป็น public library เราต้องการแค่แชร์โค้ดภายในโปรเจคของเรา

งั้นมาทำ Module ของเราให้เป็น private กันเถอะ

Private Module Repository

เอาหละ เรามาดูกันว่ามีวิธีอะไรบ้างในการทำ Module ของเราให้เป็น Private

  1. ใช้ Github Private Repo หรือ set package.json เอา
  2. จัดการ ขึ้น private npm proxy registry เองเลย
  3. ไปใช้ Cloud Private Registry ดีกว่า

Github Private Repo

วิธีนี้ ง่ายและสะดวกที่สุดในด้านของการเซ็ตอัพ สมมติ Module Rentspree PDF อยู่บน Github ที่เป็น Private Repository

เราสามารถเพิ่มโมดูลเข้ามาในโปรเจคได้

// package.json"@rentspree/pdf": "git+https://<github_token>:x-oauth-basic@github.com/<user>/<repo>.git"

ซึ่งตัว github_token สามารถเอาจาก ที่นี่ได้ ส่วน syntax ของ package อันนี้สามารถอ่านเพิ่มเติมได้ ที่นี่ เช่นกัน

ข้อดี:

  • ง่ายและเร็ว ตั้งค่าเสร็จได้ใน 1 นาที (ถ้า Repo อยู่บน Github อยู่แล้ว)

ข้อเสีย:

  • ใช้ได้กับ Github เท่านั้น Gitlab ไม่สามารถใช้ท่านี้ได้
  • ถ้า Code เราต้อง build (เช่นใช้ babel หรือ webpack) โค้ดที่ build แล้วต้องอยู่บน Git Repo ด้วย จึงจะสามารถใช้ได้

Private NPM Proxy Registry

ในเมื่อเราต้องการเก็บ Module เป็น Private อย่าหยุดแค่นั้นเลย เราทำ NPM Registry ที่เป็นของเราเอง แบบ Private ไปเลยดีกว่า

ใน Comminity ตอนนี้ มี Opensource หลายตัวที่สามารถตอบโจทย์ข้อนี้ได้ คือเป็น NPM Registry ทั้งอัน ให้เราไป Deploy เองเลย ขั้นตอนการทำท่านี้ก็คือว่า

  1. Deploy NPM Registry ขึ้นไปซักที่นึง อาจจะเป็น Intranet ภายในองค์กร หรือจะเป็นบน Cloud Provider เช่น AWS หรือ GCP
  2. Publish Private Module ของเราขึ้นไปที่ Registry พวกนั้น
  3. Setup npm ให้ใช้ Proxy Registry ที่ได้เซ็ทอัพไว้

เท่าที่ได้ลองดูมี จะมีตัว Verdaccio ที่กำลังมาแรงในช่วงนี้ ด้วยความง่ายในการ เซ็ตอัพ มี doucment ค่อนข้างครบ มี feature เช่น Dashboard ให้จัดการ repositories ได้ จากที่ลอง Deploy บน local นั้นถือว่าใช้ได้ไม่ยากเลย

ข้อดี

  • ประหยัดค่าใช้จ่าย มีแค่ค่า Host Server ที่ต้องเสีย
  • เราสามารถควบคุม Registry ได้เองทั้งหมด

ข้อเสีย

  • ต้อง deploy ดูแล และ maintain
  • ขั้นตอนเซ็ตอัพเยอะพอสมควร

ในบทความนี้ เราจึงจะเน้นไปที่วิธีสุดท้าย นั่นคือการใช้ Cloud Private Registry นั่นเอง

Cloud Private Registry

วิธีนี้ก็จะเหมือนวิธีข้างบนเลย เพียงแต่ว่า แทนที่เราจะ Self Hosted Registry เอง เราไปใช้ Cloud Provider ที่เค้าดูแลเรื่องนี้

Provider เจ้าใหญ่ที่จะไม่กล่าวถึงไม่ได้เลย ก็คือตัว NPM เอง NPM จะมีบริการ Premium service สำหรับ host private registry ถ้าเราต้องการ publish lib ที่เป็น open source เราสามารถ publish ฟรี ได้บน NPM อยู่แล้ว แต่ถ้าเราต้องการ publish แบบ private จำเป็นจะต้อง upgrade account โมเดลนี้เป็นโมเดลคล้ายกับของ Github เลย

จะเห็นว่า NPM ให้บริการแค่แบบ Developer plan คือคิดราคา 7$/user ซึ่งแพลนนี้อาจจะไม่เหมาะสำหรับทีมที่กำลังขยาย สมมติสมาชิกในทีมมี 5 คนแต่ยังมี Module แค่สอง Module และต้องจ่ายเงินเดือนละ 35$ เงินจำนวนนี้คงแพงเกินไป ทางทีม Rentspree จึงได้เลือก Provider อีกเจ้า นั่นคือ Gemfury

โดย Gemfury จะมีแพลนให้เลือกทั้งแบบ per user และ per project ถ้ามีโปรเจคน้อย ใช้ plan per project น่าจะคุ้มกว่า เพราะสามารถเพิ่ม user ได้ไม่จำกัด

เริ่มโดยการ upload module ขึ้นไปที่ Gemfury ระบบของ Gemfury สามารถ upload module ขึ้นไปได้หลายวิธี อันนี้สามารถดูตามใน document ของ Gemfury ได้ง่ายๆ เลย

Module ที่อัพโหลดขึ้นไปจะถูกแสดงใน dashboard

เมื่ออัพโหลดเสร็จแล้ว module จะถูกแสดงใน dashboard ของ Gemfury เลย

สำหรับการใช้งาน Private module พวกนี้ จำเป็นต้องมีการเซ็ทอัพก่อน

Collaborator dashboard

เริ่มจากเพิ่ม user ที่ต้องการ pull private repo เป็น Collaborators ก่อน

เมื่อได้แล้ว user แต่ละคนจะมี token เป็นของตัวเอง

Gemfury token manager

จากนั้น ไปเพิ่ม config ใน ~/.npmrc เพื่อเซ็ต proxy มาที่ Gemfury และใส่ Access Token ลงไป

ในกรณีนี้ กำหนดให้แค่ module ที่มี namespace rentspree ไปใช้ Gemfury เท่านั้น จึงใช้ setting แบบนี้

//npm-proxy.fury.io/rentspree/:_authToken=<AUTH_TOKEN>@rentspree:registry=https://npm-proxy.fury.io/rentspree/

หลังจากนั้น ก็สามารถ install private module ของเราได้เลย

npm install @rentspree/pdf

เบื้องหลังการทำงานนี้ ตัว npm จะติดต่อไปยัง proxy ซึ่งเป็น server ของ Gemfury โดยตัว proxy จะค้นใน Gemfury เองก่อนว่าเจอ module ที่ install หรือไม่ หากไม่เจอ จะ proxy ไปที่ official npm

เซ็ตอัพแบบนี้จะทำให้เราสามารถอัพโหลดเฉพาะ private module ขึ้นไปที่ Gemfury และ public module อื่นๆ ก็ยังอยู่ที่ npm ต่อไป

ข้อดี

  • เซ็ตอัพง่าย
  • เลือก plan ตามการใช้งานของเราได้
  • ไม่ต้องเสีย effort ในการดูแล server

ข้อเสีย

  • $25 ต่อเดือน :((
  • โค้ดจะถูก อัพโหลด ขึ้นไปที่ server ของ Gemfury

เพิ่มเติม

Gemfury มีฟังก์ชั่นสำหรับการเซ็ต CI CD ด้วยนะเออ

สำหรับแบบที่ build docker บน CI สามารถทำตามนี้ได้เลย

ไปเอา Deploy token มาจาก Gemfury

ถ้า npm install บน Docker build สามารถเพิ่มคำสั่งการเซ็ตอัพ Gemfury บน Dockerfile ดังนี้

# Dockerfile
...
# SETUP Gemfury
ARG GEMFURY_DEPLOY_KEY
RUN echo "//npm-proxy.fury.io/rentspree/:_authToken=$GEMFURY_DEPLOY_KEY" > ~/.npmrc
RUN npm config set @rentspree:registry https://npm-proxy.fury.io/rentspree/
COPY package.json /app/package.json
RUN npm install
...

โดยใส่ deploy key ลงไปที่ $GEMFURY_DEPLOY_KEY

เพียงเท่านี้ Dockerfile นี้ก็จะสามารถ build image ที่ดึงเอา Private module ของเราเข้ามาได้

สรุป

ในทีม Rentspree เราค่อนข้างให้ความสำคัญกับคอนเซปต์ DRY โค้ดที่ซ้ำทั้งหมดมันคือหนี้ (Technical Debt) เราพยายามทำทุกอย่างเพื่อลดหนี้พวกนี้ ทุก process คือความสามารถของทีมที่เพิ่มขึ้น

เราสามารถทำ Private module แล้วเอาขึ้น Gemfury ได้ง่ายๆ

การใช้ Private module จะทำให้เราแชร์โค้ดระหว่างโปรเจคได้ ระบบที่เซ็ตอัพง่ายอย่าง Gemfury สามารถทำให้เรานำเข้า Private module เข้าสู่แต่ละโปรเจคได้เร็ว

การที่ Gemfury เป็น Cloud ทำให้เราไม่ต้องกังวลเรื่อง availability ของ Private module ของเรา

Gemfury สามารถทำให้ Node Private Module ของเรา deploy เข้าไปใน docker ด้วยเซ็ทอัพเพิ่มเติมแค่นิดเดียว

นอกจากนี้ Gemfury ยังมี Dashboard ที่ใช้งานง่ายสำหรับการจัดการให้เราอีกด้วย

--

--

Putt Potsawee
LifeatRentSpree

Lead Developer at RentSpree. Passionate programmer who specialized in Backend and Infrastructure.