มาลดความซับซ้อนของไฟล์ config กัน

Visarut Phusua
Avangelist
Published in
5 min readAug 4, 2020

--

Table of Contents 📑

· ปัญหา Duplication ของไฟล์ config
· มาลองรู้จัก Jsonnet กันดีกว่า
· การติดตั้ง Jsonnet
· แล้ว Jsonnet ทำไรได้บ้าง
· ก่อนอื่นมาดู Syntax กันก่อน
· Feature ที่น่าสนใจ
· Test ก็มีนะ
· มาจัด Format Jsonnet กัน
· เราจะมาทำ template config ด้วย Jsonnet กันเริ่ม!

ปัญหา Duplication ของไฟล์ config

เคยเจอไหมครับปัจจุบันเรามีไฟล์ Environment Configs มากมายเช่น Dev, Test/QA, UAT, Staging และ Production

หน้าตาก็จะประมาณนี้

แต่!!!ถ้าเรามี config แต่ละประเทศด้วยละ

ถ้ามี 4 ประเทศหน้าตาก็จะประมาณนี้

เราจะเห็นได้ว่าของซ้ำกันเยอะมากๆ😱 และในแต่ละ folder จะมีไฟล์ที่ชื่อว่า config.json อีก~~~ ลองคิดดูว่าถ้าต้องเพิ่ม Environment และประเทศล่ะจะเป็นยังไง

สมมุติเราอยากแก้ของในไฟล์ config.json เช่น:

  • อยากแก้ port ของ redis ของประเทศ MY , SG
  • อยากแก้ password ของ mongo ของประเทศ USA, TH , MY
  • อยากแก้ host ของ mongo ของทุกประเทศ
  • อยากแก้ token ของ rest api ของทุกประเทศ

จากตัวอย่างที่สมมุติขึ้นมา จะเห็นได้เลยว่าเวลาเราแก้อะไรเราก็ต้องแก้หลายไฟล์ไปหมดแก้มือหงิก5555555555นี่แค่ 4 ประเทศนะ ก็มี 20 ไฟล์แล้ว

มาลองรู้จัก Jsonnet กันดีกว่า

Jsonnet เป็นโปรเจคของ Google ซึ่งได้รับแรงบรรดาลใจมาจากประสบการณ์ที่ต้อง config ระบบของตัวเองที่ซับซ้อนมาก ๆ และได้ออกมาให้นักพัฒนาได้ใช้กันอย่างแพร่หลายในช่วงกลางปี 2014 และมีบริษัทใหญ่ ๆ หลายบริษัทที่ได้นำ Jsonnet ไปใช้ เช่น MasterCard, Grafana, Bitnami, Deepmind

ส่วนตัวผมนั้นได้รู้จัก Jsonnet จากงาน CNCF 2018 ก็เลยมาลองเล่นดู พอได้ลองใช้แล้วก็รู้สึกว่าช่วยลดความซ้ำซ้อนของ config ที่ต้องเขียนลงไปได้เยอะเลย

การติดตั้ง Jsonnet

# Homebrew
brew install jsonnet
# The Python binding is on pypi:
pip install jsonnet

แล้ว Jsonnet ทำไรได้บ้าง

วิธีการทำงานของ Jsonnet คือการที่เราเขียนไฟล์ .jsonnet ขึ้นมา แล้วใช้ command ของ Jsonnet เพื่อจะได้ output ตามที่เราต้องการ

เราสามารถรัน command ได้ 2 แบบคือ

รันโดยเขียน Jsonnet บน command

jsonnet -e <แบบใส่โค้ด jsonnet ตรงนี้ได้เลย>

รันโดยอ่านไฟล์ Jsonnet

jsonnet <ตามด้วยไฟล์ jsonnet>

ตัวอย่างแบบต้องการ ouput file

jsonnet main.jsonnet -o main.json

ก่อนอื่นมาดู Syntax กันก่อน

หากใครเคยใช้ JSON เคยเขียนมาก่อนน่าจะไม่ยากนักเพราะหน้าตามันจะคล้าย JSON ปกติเลย

  • key ของ object ไม่จำเป็นต้องใส่ quotes
  • ถ้าประกาศตัวแปรหรือฟังก์ชั่น อย่าลืมปิดด้วย semicolon
  • สามารถทำ Text blocks (multiple lines) ได้ โดยใช้ “|||
  • การใช้ self คือการอ้างอิง object ที่อยู่ภายใต้ {} นั้นๆ

Feature ที่น่าสนใจ ⭐️

Comments

ปกติ json ธรรมดาใส่ comment ไม่ได้

จะเห็นได้ว่าตัว Jsonnet เนี่ยมัน comment ได้หลาย style เลย

Hidden

ใน Jsonnet เราสามารถ hidden key ของ object ได้โดยการใช้เครื่องหมาย “::” ต่อท้าย key ที่เราอยากให้ hidden เวลาตอน output จะไม่แสดง key นั้นๆ ดังรูปด้านขวา

Variables

การประกาศตัวแปรนั้นใช้ local ขึ้นต้น ตามด้วยชื่อแปรที่เราต้องการได้เลยและหลังจากใส่ value แล้วอย่าลืมปิดด้วย semicolon

Functions

การประกาศ function นั้นใช้ local ขึ้นต้นเหมือนกับตัวแปร ต่างกันตรงต้องใส่ () ด้วย function ใน jsonnet สามารถใส่ parameter ได้ตามปกติ

Merge Object

แบบที่ 1

โดยใช้เครื่องหมาย “+” จะทำให้ 2 objects มา merge รวมกันดั่งภาพด้านบน

แบบที่ 2

ใช้เครื่องหมาย “+” เช่นเดิมแต่อันนี้ต้องต่อหลัง key ที่ต้องการ เพื่อบอกว่า key นี้เวลา merge กัน มันจะ deeply nested fields นั้นๆให้

Loop

ตัว loop ใน Jsonnet หน้าตามันจะคล้ายๆ list comprehension ของ python

Conditionals

Syntax if-else จะเป็น “if เงื่อนไข then ถ้าเป็นจริงทำตรงนี้ else ถ้าไม่เป็นจริงทำตรงนี้

Import

ใน Jsonnet มีสิ่งที่เรียกว่า libsonnet มันคือการทำ library ของ Jsonnet นั้นเองและ syntax ก็เขียนเหมือน Jsonnet ทั่วไปได้เลย

วิธีใช้เรียก library ก็ง่ายๆ ก็แค่ import เข้ามาในไฟล์ที่เราต้องการใช้ได้เลย

Parameterize Entire Config

สามารถรับ parameter ได้ 2 แบบ ดังนี้

1. External variables

วิธีรับค่า arguments จาก command มาในโค้ด โดยใช้ build-in function ของ Jsonnet ได้เลย คือ std.extVar(‘ตามด้วยชื่อตัวที่เราต้องการ’)

จากตัวแบบภาพด้านบนรับ arguments มา 2 ตัว คือ env กับ commitId

วิธีการส่ง arguments เข้าไปใน jsonnet

วิธีการส่ง arguments เข้าไปใน Jsonnet โดยใช้ — ext-str ตามด้วยชื่อ argument ที่เราใส่ไว้ในไฟล์ Jsonnet ของเรา

2. Top-level arguments

ถ้าแบบ Top-level จะใช้ง่ายกว่า และไม่ต้องเอาตัวแปรมารับ วิธีใช้เราสามารถประกาศ “function(ตามด้วย arguments ที่เราต้องการได้เลย)

เช่น function(env, commitId) จากตัวอย่างคือรับ arguments มา 2 ตัว คือ env กับ commitId ตัว env จะมี default value ด้วย

วิธีการส่ง arguments เข้าไปใน jsonnet

วิธีการส่ง arguments เข้าไปใน jsonnet จะคล้ายกับแบบแรกโดยใช้ — tla-str ตามด้วยชื่อ argument ที่เราใส่ไว้ในไฟล์ Jsonnet ของเรา

ข้อดี

  • สามารถมี Default value ได้
  • สามารถ Implement ให้เป็น Custom library ได้

และมี features อื่นๆที่สามารถใช้ได้ ไปอ่านได้ที่ที่เลย https://jsonnet.org/learning/tutorial.html

Test ก็มีนะ 🛠

ถ้ามีหลายๆ test ให้ใช้เครื่องหมาย “&&” เป็นตัวเชื่อม test ถัดไป

เพิ่มเติม framework สำหรับ Unit testing ของ Jsonnet ก็มีนะ -> jsonnetunit

มาจัด Format Jsonnet กัน💡

command นี้จะจัด format ทุกไฟล์ที่นามสกุล .jsonnet

เราจะมาทำ template config ด้วย Jsonnet กันเริ่ม!

ก่อนอื่นให้เรามาสร้าง libsonnet ใน Jsonnet กันก่อน

หลังจากที่เราสร้าง libsonnet ทั้ง 2 ไฟล์เสร็จแล้วจะเอามา import ใส่ไฟล์ main.jsonnet ดังนี้ 👇

มาลองรันกันดู😀

เพียงแค่นี้เราก็สามารถมีตัว template config ที่ generate ตาม country และ env ที่เราต้องการไว้ใช้กันแล้ว 🎉

Conclusion

ผมคิดว่า Jsonnet มีประโยชน์มากในการทำพวก template config ต่างๆ และยังช่วยลดความซับซ้อนของ config ได้อีกด้วย นอกจากนี้ยังประยุกต์ทำอะไรได้เยอะเลยเช่นการทำ Refactoring ไฟล์ต่างๆเช่น YAML, JSON, CONF, etc. ส่วนตัวลองเอาไปใช้กับ Kubernetes รู้สึกว่าลดจำนวนไฟล์ deployment ได้เยอะเลย

You can see all source code from this blog here

Reference

--

--