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

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 แต่ละประเทศด้วยละ

เราจะเห็นได้ว่าของซ้ำกันเยอะมากๆ😱 และในแต่ละ 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

จะเห็นได้ว่าตัว 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 โดยใช้ — ext-str ตามด้วยชื่อ argument ที่เราใส่ไว้ในไฟล์ Jsonnet ของเรา
2. Top-level arguments

ถ้าแบบ Top-level จะใช้ง่ายกว่า และไม่ต้องเอาตัวแปรมารับ วิธีใช้เราสามารถประกาศ “function(ตามด้วย arguments ที่เราต้องการได้เลย)”
เช่น function(env, commitId) จากตัวอย่างคือรับ arguments มา 2 ตัว คือ env กับ commitId ตัว env จะมี default value ด้วย

วิธีการส่ง 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 กัน💡

เราจะมาทำ 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 ได้เยอะเลย