Finops: Journey to improve and manage the cost of BigQuery

Nitit Taepant
CJ Express Tech (TILDI)
5 min readAug 9, 2023

จากที่เราได้เริ่ม project Finops มาระยะหนึ่ง ก็มีการ monitor cost ของ BigQuery ได้ดีขึ้น รวมถึงทำให้เกิด transparency ในการใช้งาน BigQuery ของคนในองค์กร และเจอ cost จาก query ที่ไม่จำเป็นทำให้ลดค่าใช้จ่ายไปได้เยอะ และถ้าใครที่ยังเคยเห็นสิ่งที่เราทำก่อนหน้านี้ สามารถดูได้จาก blog ข้างล่างเลย 😊

แต่เราก็เจอปัญหาบางอย่าง รวมถึงเราต้องการป้องกันการใช้เงินมากเกินไป โดยที่ไม่รู้ตัวก่อนที่งบจะพุ่งจนล้มละลาย 🤪

สิ่งที่เราต้องการแก้ไขรวมถึงสิ่งที่ต้องทำให้ดีขึ้นคือ

  • Dashboard เราไม่สามารถ track cost จาก Query ที่ใช้ table ที่มีการ set row-level security
  • เริ่มการใช้งาน BigQuery Editions เพื่อควบคุม cost
  • ต้องรู้ตัวก่อนที่ค่าใช้จ่ายจะเกินงบที่ตั้งไว้ รวมถึงถ้าเกินแล้วต้องมีการควบคุม และวิธีจัดการ

Cost หายไปจาก Dashboard ทั้งๆที่ Cloud billing พุ่งกระฉูด

query ใช้ table ที่ใช้ row-level security

สิ่งที่ทำให้เราไม่สามารถเห็น cost บน BQ’s information_schema จากวิธีที่เราใช้ก่อนหน้านี้ได้เพราะ มี Query job บางอันมีการใช้ข้อมูลจาก table ที่มีการใช้ row-level security ซึ่ง table นี้เป็นของ team ทีมอื่นเราจึงไม่มีสิทธิ์ในการเปลี่ยนเงื่อนไขการใช้งาน

row-level security จะทำกำหนดให้คนเห็นเฉพาะบางแถวจาก filter

ทำให้เราต้องหาวิธีการหาข้อมูลที่หายไปมาเติม และเราก็เจอว่าที่ Audit logs มี JobStats (Audit logs old version ดู JobStatistics) ที่มีข้อมูลที่เราต้องการเช่น

  • totalBilledBytes
  • totalSlotMs
  • outputRowCount
  • cacheHit
  • query -> ใน jobConfig.Query

เราสามารถตรวจสอบเนื้อหาของ log ได้ใน Logs Explorer โดยใช้ Filter log ดังนี้

resource.type="bigquery_project" AND
log_name="projects/<project_id>/logs/cloudaudit.googleapis.com%2Fdata_access" AND
(protoPayload.methodName="google.cloud.bigquery.v2.JobService.InsertJob" OR
protoPayload.methodName="google.cloud.bigquery.v2.JobService.Query")

โดยเราจะสนใจ methodName ที่เป็น jobService 2 ตัวนี้

  • google.cloud.bigquery.v2.JobService.InsertJob
  • google.cloud.bigquery.v2.JobService.Query

แต่เราพึ่งมารู้ว่าข้างบนเป็น version ใหม่หลังจากเราทำไปเสร็จแล้วโดยใช้ log ที่เป็น version เก่า 😓 ทำให้เราต้องใช้ version เก่าก่อนใน Phase นี้

ซึ่งเราจะสนใจ methodName ที่เป็น jobservice.jobcompleted โดยใช้ filter ดังนี้

resource.type="bigquery_resource" AND
log_name="projects/<project_id>/logs/cloudaudit.googleapis.com%2Fdata_access" AND
protoPayload.methodName="jobservice.jobcompleted"

จะเห็นว่าจากที่เราไม่เห็นในข้อมูล job บน BQ’s information_schema เราสามารถเห็นมันได้ในข้อมูลของ log เช่นข้างล่าง

job completed log

หลังจากนี้เราก็เลยจะเอาข้อมูล log มาทำ data pipeline เป็นอีก data source นึงสำหรับ Finops dashboard ของเรา

แต่เรายังได้ข้อมูลไม่ครบ ดังนั้นมาดูในส่วนที่เหลือกัน

ใช้ BigQuery Editions เพื่อปรับวิธีการใช้งานและควบคุมค่าใช้จ่าย

BigQuery จะมีการใช้งานสองแบบคือ

  1. On-demand compute (analysis) pricing
  2. Capacity compute (analysis) pricing หรือ BigQuery Editions

โดยปกติ Default ของการใช้งานจะเป็นแบบ On-demand ซึ่งจะคิดค่าใช้จ่ายตามขนาดของข้อมูลที่ถูก processed ไป แต่ในวันที่ 5/7/2023 จะมีการขึ้นราคาจาก ~6.75 ไปที่ ~8.44 (region asia-southeast1- singapore) ซึ่งถือว่าขึ้นมาค่อนข้างมากเลยทีเดียว 🥶 เราเลยมองหาตัวเลือกที่ 2 ซึ่งก็คือ BigQuery Editions นั่นเอง โดยเป็นการใช้งานแบบ การจอง slots เแบบใหม่ที่ Google ปล่อยมาให้ใช้ไม่นานนี้เอง แทนการจอง slots แบบเดิม โดยที่แบบเดิมจะเป็นการ fix slots (flat-rate) ไว้คงที่และจะคิดเงินตาม slots ที่จองนั้นเลย ซึ่งทาง Google ได้ยกเลิกไปแล้ว และเปลี่ยนไปเป็นแบบ Editions อย่างเดียว ซึ่งเราสามารถให้มัน autoscale slots ในระหว่าง baseline และ max slots ได้ ทำให้ค่าใช้จ่ายจะขึ้นอยู่กับการใช้งานจริงประมาณหนึ่งใน range slots ที่เราตั้งไว้

ซึ่งเราจะนำ BigQuery Editions มาใช้ไปคู่กับ On-demand เนื่องจากสามารถที่จะควบคุมการใช้จ่ายได้ดีขึ้น จากการที่เราคาดเดาค่าใช้จ่ายได้ประมาณหนึ่ง ซึ่งเดี๋ยวเราจะพูดถึงในหัวข้อของ Budget and Policy อีกทีนึง

ในส่วนของข้อมูล การที่เราเอา BigQuery Editions มาใช้ทำให้ Dashboard ของเราต้องมีการแยกค่าใช้จ่ายที่เกิดขึ้นระหว่าง On-demand และ BigQuery Editions ออกให้ได้ เนื่องจากวิธีการคิด Cost ไม่เหมือนกัน

แต่… เราก็เจอว่าในข้อมูล log ที่เราเอามาเพิ่อแก้ปัญหา row-level security นั้นไม่มีข้อมูลว่า Query นั้นใช้ reservation (BigQurty Editions reservation) ตัวไหนทั้งๆที่จากทั้ง AuditLog ตัวเก่า และ AuditLog ตัวใหม่ น่าจะมีข้อมูลนี้ ซึ่งภายหลังได้ติดต่อถาม support ไปก็เหมือนกับว่าตัวข้อมูลของ API ตัวนี้ Deprecated ไปแล้ว 😱

แต่ว่าาา ในตัวของ INFORMATION_SCHEMA.JOBS_BY_PROJECT มีข้อมูลของ Reservation

Query job ใน INFORMATION_SCHEMA.JOBS_BY_PROJECT ที่ใช้ reservation

ดังนั้นเราเลยต้องผสมข้อมูลจากทั้งสองที่ ออกมาเป็น pipeline แบบนี้

Data Pipeline สำหรับ Finops

summary pipeline

โดย pipeline จะมี 2 ส่วน

  1. Initial project logs สำหรับเพิ่ม project ใหม่ลงใน pipeline
  2. Data pipeline

Initial project logs

ในส่วนนี้เราจะทำ script สำหรับการสร้าง sink logs รวมถึงดึง logs ย้อนหลัง จาก log bucket _Default โดยใช้ filter จากที่เราทดลองใน log explorer ข้างบนหัวข้อแรก ซึ่งจะทำให้เรามี raw data ของ logs อยู่ใน bucket ที่เราสร้างไว้ในแต่ละ project

โดยเราจะวาง script นี้ไว้บน Cloud run และทำการฝัง Service Account ที่ถือสิทธิ์สำหรับจัดการ Cloud Logging และเพิ่ม Service Account ไว้ใน IAM ของ project ต่างๆ

Data Pipeline

ในส่วนนี้เราจะแยก Service Account ของ pipeline เป็นอีกอันเพื่อที่จะแบ่งสิทธิ์ pipeline ไม่ให้ยุ่งเกี่ยวกับส่วนของ Cloud Logging ของ project อื่นๆ เพราะข้อมูลใน Cloud Logging มีข้อมูล sensitive เยอะ

เราจึงเพิ่ม Service Account ตัวนี้ใน bucket ที่ไว้สำหรับ sink log เท่านั้น ทำให้ Service Account ตัวนี้จะไม่ได้มีสิทธิ์ในการไปใช้งาน Bucket อื่นๆใน project ต่างๆ ด้วย และเพิ่มสิทธิ์ในการจัดการ BigQuery ใน IAM ของแต่ละ project ด้วยเพื่อที่จะทำให้สามารถดูข้อมูลจาก INFORMATION_SCHEMA.JOBS_BY_PROJECT ได้

ในส่วนของ pipeline ก็จะเริ่มด้วยการรวบรวมข้อมูล log มาไว้ใน data lake ของ data-platform bucket และทำการรวบรวมข้อมูลจาก INFORMATION_SCHEMA.JOBS_BY_PROJECT ของแต่ละ project มาไว้ที่ table เดียวใน BigQuery ของ data-platform

หลังจากนั้นก็จะทำการ merge & process ข้อมูลต่างๆด้วย tools ของ data-platform ของเราให้พร้อมใช้งานแล้วนำขึ้น BigQuery เป็นสอง table คือ

  1. table ส่วนที่ใช้สำหรับ dashboard ที่เป็น personal cost
  2. table สำหรับ dashboard ที่เป็นของ admin เพื่อดูข้อมูลของทุก project

ในส่วนนี้เราก็ซ่อมข้อมูลที่หาย และเพิ่มข้อมูลของการใช้ Reservation เรียบร้อยสำหรับใช้งานใน Dashboard 🎊

เราก็มาถึงส่วนสุดท้ายนั่นคือ…

Budget and Policy เพื่อควบคุมค่าใช้งานใน BigQuery

ช่วงนี้ Tildi ได้มีการใช้งาน Dashboard มากขึ้น ซึ่งจะมีการใช้ Data ผ่าน BigQuery เป็นส่วนใหญ่ รวมถึงเริ่มมี Business Unit เข้ามาใช้งานมากขึ้นเรื่อยๆ ทำให้ cost ของบาง project นั้นมากขึ้นเรื่อยๆ เพราะเรายังไม่ได้ควบคุม cost การใช้งานโดยที่ปล่อยให้ใช้แบบ On-demand มาตลอด 🫣

เราเลยมีประเด็นที่ต้องการแก้ไขดังนี้

  1. อยากให้ทีม DE รู้ตัวโดยเร็วว่ามีการใช้ cost เยอะแล้วนะใน project นั้นๆเพื่อหาแนวทางในการจัดการต่อไป
  2. อยากจะคุมค่าใช้จ่ายได้ดีขึ้น
  3. ไม่อยากให้หยุดการใช้งานไปเลยเพราะจะเสีย productivity ของการใช้งาน data

เราจึงเริ่มใช้งาน cost plan แบบอื่น นั่นก็คือการ reserve slots หรือ BigQuery Editions และการตั้ง budgeting ใน Cloud Billing เพื่อสร้าง events เวลาค่าใช้จ่ายถึงจำนวนหนึ่ง

วิธีที่เอามาใช้คือ เมื่อ cost ถึง threshold ที่เราตั้งไว้จะมีการส่ง notification มาที่ทีม DE ของเราและเมื่อถึง threshold ที่เกิน budget ที่เราตั้งไว้ project นั้นจะทำการปรับการใช้งานเป็นแบบ BigQuery Edition อัตโนมัติ เพื่อที่จะลด cost ที่เกิดจากการใช้งานให้ขึ้นช้าลง แต่ไม่ได้หยุดการใช้งาน data

สุดท้ายแล้วเราจึงได้ flow การทำงานดังนี้

working flow

อธิบายเพิ่มเติม เริ่มต้นเราจะคุยกับ stakeholders ของ project ต่างๆเพื่ออธิบาย process ที่จะเกิดขึ้นถ้าเริ่มใช้ policy และทำการตั้ง budget ร่วมกัน แล้วหลังจากนั้นเราก็จะมี Finops admin ไปตั้ง budget ให้กับ project นั้นๆ และเมื่อมี alert เกิดขึ้นก็จะมีการเตือน และคุยกับคนที่เกี่ยวข้องกันว่า ยังโอเคกับ cost ที่เกินหรือกำลังจะเกิน budget รึเปล่า ถ้ารับได้เราก็จะปรับให้ใช้ On-demand หรือ Reservation slots ที่เยอะขึ้น โดยจัดการเป็นเคสๆไป

เย้! นี่ก็คือทั้งหมดที่เราทำเพิ่มในรอบนี้ และเรายังคงมีสิ่งที่ต้องทำเพิ่มใน project Finops อีกเยอะเลย 🤯 ถ้าสนใจ project นี้ หรืออยากเห็น blog อื่นๆ ของ CJ Express ก็ติดตามได้ที่ CJ Express Medium ครับ

Conclusion

  • เราได้แก้ปัญหาที่ไม่สามารถเห็น cost ของ query table ที่ใช้ row-level security policy โดยการใช้ข้อมูล Audit log มาเติมข้อมูลที่หายไป
  • เริ่มมีการใช้งาน Reservation slots (BigQuery Edition) และหาข้อมูลเพิ่มเพื่อแยกวิธีคิด cost โดยดูที่ INFORMATION_SCHEMA.JOBS_BY_PROJECT
  • ทำ Pipeline ใหม่ที่รวบรวมข้อมูลที่ถูกต้องมากขึ้นมาใช้งานสำหรับ dashboard Finops
  • ทำการสร้าง Process สำหรับการทำ Budget & Policy เพื่อควบคุมค่าใช้จ่ายของ BigQuery ไม่ให้มากเกินไปโดยไม่รู้ตัว

References

--

--