อีเวนท์แรกหลัง covid AWS Summit Bangkok 2024

ยังจำความว้าวตอนได้ฟังเรื่อง AWS Well-architected ครั้งแรกจาก AWS Summit 2018 Keynote ได้ติดตาค่ะ (จำได้ว่าว้าว แต่ลืมจุดที่ทำให้ว้าวไปหมดเกลี้ยง) จำได้ว่าหลังจากนั้นก็ fangirl AWS event หนักมากกก งานเล็กงานใหญ่คือไปหมด ไม่ได้ไปก็ดูย้อน

จนกระทั่งย้ายงานค่ะ ซึ่งทำให้เกิดการย้าย cloud เกิดขึ้น 5555

ก็เลยไม่ได้ไปงาน AWS อีกเลย ประกอบกับ covid situation เลยห่างหายไปจากการสะสมสติ๊กเกอร์ใน tech event แบบสุดๆไปเลย แถมค้นพบว่าการจอยอีเวนท์ออนไลน์มันบ่ด้ายจีจี 😔


AWS Summit 2024

Keynote

ได้เห็นว่า Keynote ปีนี้มีนายกเศรษฐามาด้วยค่ะ รู้สึกผิดแผกแปลกตาอย่างมากกับธีมงานปกติที่ Keynote จะเอาเหล่า Tech Executive มายืนพูดปลุกระดม สร้างแรงบันดาลใจ ขายฝัน (อยากฟัง อยากมาโดนตกมาก)

ก็กลายเป็นว่ามีท่านนายกมาเป็นประธานเปิดงานให้ค่ะ อ่ะ แต่ก็มีข่าวน่ายินดี

“ปีหน้ารอใช้ AWS region Bangkok เต็มรูปแบบกันได้เลยนะครับ”

นี่สิคะท่าน ข่าวดี!!

คุณวัตสัน Country manager ออกมาประกาศเรื่อง AWS Partner โดยเฉพาะที่เป็นสาย generative AI ก็ถือว่าเป็นอีกมูฟเพื่อการเตรียมพร้อมสู่การบุกตลาดไทยในปีหน้าอย่างชัดเจน

และคนที่เราฟังแล้วรู้สึกถึงความตื่นเต้น และพลังงานบางอย่างคือคุณ Mai Lan VP AWS ที่ออกมาพูดถึงการตอกย้ำว่าการเกาะขบวน generative AI มันสำคัญขนาดไหน มันไม่ใช้แค่ software embedded ตัว generative AI ลงไป แต่ครอบคลุมถึงเรื่องพื้นฐานที่ผู้ใช้งาน และผู้สร้างไม่ควรลืมเกี่ยวกับการใช้ AI ให้ทรงประสิทธิภาพ

“Good Data = Good AI”

ปีนี้แบ่งเป็น 4 track เลื่อน agenda ตาแตก บอกเลยว่าเราแทบไม่มีเวลาเลือก session ให้ดีๆเลยค่ะ ทำการบ้านมาน้อยจริงๆสำหรับงานนี้

แต่สำหรับเราโทนของงานแบ่งออกเป็นสองสายชัดๆค่ะ Generative AI กับ Cyber Security แบ่งแบบนี้ด้วยความ bias ของเราล้วนๆเลยค่ะ 555


Generative AI

การเข้ามาของ Generative AI เรียกได้ว่าเปลี่ยนโลก มาถึงยุคที่ถ้าไม่ใช้ก็จะเสียเวลาทำงาน โลกใบนี้มันถูกออกแบบมาเพื่อเกิดการแข่งขัน และการแข่งขันจะเลือกผู้ที่แข็งแกร่งกว่าเสมอ

NVIDIA

หลายคนอาจจะรู้จักชื่อนี้อยู่แล้วโดนเฉพาะเกมเมอร์ทั้งหลายว่านี่คือเจ้าแห่งการ์ดจอที่ไว้ใจได้ และหลายปีมาแล้วที่ NVIDIA พา technology stack ของตัวเองมาฝากฝังไว้กับ AWS ถ้าใครเคยใช้ AWS มาก่อนอาจจะได้เห็น EC2 Instance G series, P series ที่กำลัง GPU เดือดทะลุโลกจนน่าสงสัยว่าทำมาให้ใครใช้ ก็ NVIDIA นี่แหละค่ะคุณ

ในปีนี้ทั้งสองก็จับมือยกระดับไปอีกขั้นด้วยการออก Project Ceiba

project-ceiba
https://ceibaerc.tech/

เป็นที่รู้กันว่าการเทรน model เป็นเรื่องที่ใช้เงินและเวลา ด้วยความล้ำหน้าของ technology virtual GPU ที่ผสมผสานกำลังของ GPU และ CPU เป็นเนื้อเดียวกันเพื่อรีดประสิทธิภาพการเทรนและรัน model ให้ถึงขีดสุด (จะ gen ฉากใน Cyberpunk ไม่ง่ายนะคะคุณ)

ทีน้ีนึกสภาพกราฟฟิกการ์ดเชื่อมโยงขุมพลังกันเป็น Supercomputer ในขนาดหนึ่งตึกดูค่ะ

ทั้งนี้ก็เพื่อรองรับ demand ของการเทรน LLM model มหาศาลที่กำลังผุดขึ้นทั่วทุกมุมโลก

เค้าว่ากันว่าลดเวลาการเทรนโมเดลได้มหาศาล และมันจะช่วยลด cost ประหยัดพลังงานได้ในระยะยาว

Amazon CodeCatalyst + Amazon Q

แน่นอนว่ามันเป็นงานขายของอ่ะเนอะ 555

สมัยที่ยังใช้ cloud AWS ก็ตื่นตาตื่นใจเมื่อได้รู้จัก CloudFormation เป็นครั้งแรก จากการตั้ง server ที่คุ้นชิน เช่า rack ซื้อ hardward ต่อสาย กว่าจะเรียบร้อยใช้เวลาอย่างน้อยๆก็เป็นวัน

แล้วนี่อะไรกัน เราสามารถ spin server ตามเสป็คขึ้นมาได้ใน 30 นาทีเหรอ บ้าาาาาา 😆

แต่แล้วปีนี้ AWS ก็มี feature มาเหนือค่ะ

aws-code-catalyst

ก็ช็อปปิ้งกันไปเลยสิคะ อยากสร้างระบบอะไร

สำหรับเดฟอย่างเราที่ใช้ Copilot กันมาคงจะเคยชินกับให้ Copilot ช่วย generate code กันอยู่แล้ว และวันนี้ CodeCatalyst ให้เราได้มากกว่าด้วยการ gen ทั้ง software และ (virtual) hardware ให้เราซะเลย

แล้วถ้า gen ออกมาแล้วมันติดบัคอ่ะทำไง

amazon-q

ก็ fix, merge, build, deploy ใหม่กันไปเลยจ้าาาา ด้วย Amazon Q

แต่เหนือสิ่งอื่นใด เราไม่คิดว่า tool นี้จะมาแทนที่ developer ได้นะคะ

จากที่คลุกคลีกับ ChatGPT, Copilot, Midjourney กันมา generative AI เหล่านี้ยังไม่สามารถสร้างสิ่งใหม่ หรือช่วยเราวิจัยในเชิงลึกได้อย่างจริงจัง แก้ design ที่ซับซ้อนด้วยตัวเองยังไม่ได้ generative AI ยังเป็น tool ที่ต้องการ prompt engineer ที่จะใช้งานเค้าได้อย่างเต็มประสิทธิภาพ

ตั้งแต่รู้จักกันมา ก็ทำให้เราเป็น full stack engineer ที่เขียนได้ไม่จำกัดภาษาเลยค่ะ 😆


Cyber Security

เรื่องความปลอดภัยที่เราเคยได้ยินครั้งแรกคือเมื่อ 9 ปีที่แล้ว (ไม่นับตอนเรียนนะ เพราะลืมไปหมดแล้ว 555) ได้ตระหนักถึงความน่ากลัวของภัยทางโลกดิจิตอลจากงาน AI ยังจำได้ขึ้นใจว่า

สงครามทางโลกไซเบอร์มันเกิดขึ้นและจบลงในไม่กี่นาที

วันนั้นเมื่อ 9 ปีที่แล้วคือวันแรกที่เราเห็นภาพว่า

Hacker ไม่ได้ใช้ตาเจาะระบบกันนะครับ แต่ใช้ AI ถ้าที่ไหนยังอัพ patch เดือนละครั้งก็ถือว่าช้าไปแล้ว

และในงานวันนี้เราก็เลยเน้นเข้าร่วม session ที่เกี่ยงข้องด้าน security และได้ไปยืนคุยกับหลายๆบูธที่เกี่ยวข้องกับงานด้านนี้ และที่ประทับใจจนอยากพูดถึงก็คือ CrowdStrike และ Palo Alto ค่ะ

CrowdStrike: Global Threat 2024 report

เปิดฉากด้วยการสร้างควรกลัวก่อนเลยว่าโลกเราในปีนี้มีระดับ eCrime ที่ไวขึ้น เยอะขึ้นขนาดไหน ซึ่งการวัดตรงนี้จริงๆ target เฉพาะเหล่า hacker ชื่อดังที่โลกรู้จัก หากใครสนใจอ่านต่อเข้าไปดาวน์โหลดรีพอทกัยได้เลยค่ะที่ > https://go.crowdstrike.com/global-threat-report-2024.html

แค่เพียงครึ่งปีผ่านไประดับการเจาะระบบทั่วโลกก็ทะยานกว่า 110%

การจู่โจม cloud เติบโตจากปีก่อน 75% และข้อมูลรั่วไหลก็มากขึ้นถึง 76%

ได้เรียนรู้หนึ่งในพฤติกรรมของเหล่า hacker ที่เปลี่ยนไปจากในอดีต ไม่ว่าจะเป็น…

การเจาะระบบแล้วนิ่งไว้ รอวันที่มีคนอยากได้ข้อมูลขององค์กรเป้าหมายแล้วค่อยขายทางเข้าให้

หรือสร้าง library ให้เหล่า developer ดึงไปใช้แล้ว code, build, deploy ขึ้น cloud แล้วค่อยแผลงฤทธิ์

การพัฒนาระบบจึงจำเป็นมากๆที่จะเริ่มจากอุปนิสัยที่ดีของนักพัฒนา

และที่น่าตื่นตาตื่นใจ คือเป้าหมายการเจาะในปีนี้ของเหล่า hacker ไม่ใช่ bank or financial business แต่กลับกลายเป็น

23% ของการเจาะระบบเล็งไปที่ Technology vendor เหล่าที่ปรึกษา และผู้รับเหมาทางเทคโนโลยีอย่างพวกเรานี่เอง 😱

ซึ่งมันก็ make sense มากเลยนะคะ เพราะเมื่อคุณเจาะเข้าระบบของคนกลุ่มนี้ได้ คุณสามารถเข้าถึงฐานข้อมูลลูกค้าอีกหลากหลายที่ที่ปรึกษาเหล่านี้ให้ความช่วยเหลืออยู่อีกมากมายหลากหลาย industry

ยิงหนึ่งได้เกินสิบ

Palo Alto AI Security Posture

สำหรับเหล่าโปรแกรมเมอร์น่าจะคุ้นเคยกันดีเรื่อง script injection ที่สมัยก่อนเราต้องพยายามป้องกันไม่ให้คนทั่วไปสามารถอาศัยช่องโหว่ของการพิมพ์ได้อิสระในช่อง search หรือ content generator ต่างๆเพื่อแอบใส่ script หรือโปรแกรมใดใดเข้ามาทำร้ายระบบเรา หรือผู้ใช้งานคนอื่นๆได้

แต่ในวันนี้ที่โลกอนุญาตให้มี prompt ที่คุณจะใส่อะไรลงไป เพื่อ generate อะไรใดใด กระทั่ง code ชุดใหม่ขึ้นมาเองก็ยังได้

ระดับความปลอดภัยเลยยิ่งต้องครอบคลุม

อย่างที่เข้าใจกันว่าระบบคือ node หลายๆ node ที่ต่อกันจนเป็นกลุ่ม และแต่ละ node ก็ส่งข้อมูลที่อาจจะเป็น sensitive data ผ่านช่องทางเชื่อมต่อถึงกันตลอดเวลา การหลุดรอด การโจมตี อาจเกิดขึ้นใน node ใด หรือจุดเชื่อมระหว่าง node ไหนก็ได้

การมาของ AI เป็นการเพิ่ม node และจุดเชื่อมขึ้นมาเป็นปริมาณมากที่เราต้องคอยเฝ้าจับตาให้ดี เริ่มตั้งแต่กดูดข้อมูลเข้าระบวนการเทรน AI

  • มีใครใส่ code ประหลาดเข้ามามั้ย
  • เราให้สิทธิ์ data pipelline มากไปในการดึงข้อมูลรึเปล่า
  • เรา mark ข้อมูลที่เป็น sensitive data เช่น บัตรประชาชน ชื่อลูกค้า เบอร์โทรศัพท์แล้วรึยัง

และเมื่อเทรน model เรียบร้อยและ deploy ขึ้นเพื่อใช้งาน

  • ข้อมูลภายในขององค์กรที่ model ต้องดึงมาวิเคราะห์ไม่มี sensitive data และไม่ได้ล้วงมากเกินใช้
  • ผลจากโมเดลไม่ได้ละเมิดลิขสิทธิ์ หรือมี code ที่ฝังไวรัสไว้แน่นะ?
  • ไม่ได้มีความพยายามที่จะดูดข้อมูลออกจากโมเดลแบบผิดปกติใช่หรือไม่

ถ้าเราจินตนาการถึงปริมาณ prompt มากมายที่วิ่งผ่านระบบก็พบสัจธรรมที่ว่ามันเป็นไปไม่ได้เลยที่เฝ้าระวังแบบแมนวล

เพื่อให้ทันเวลามันก็ต้อง auto เท่านั้น!!

และการได้ไปยืนดู solution Cloud & AI security posture กับตาก็เปิดหูเปิดตาดีไม่ใช่น้อยเลย!!

Deploy ขึ้น Github Pages ด้วย Hexo one command แล้ว custom domain หาย!!

สืบเนื่องจากการฟื้นคืนชีพเพราะโดนค่า Domain name เตือนสติ เลยได้กลับมาเขียน blog อีกครั้งเมื่อไม่กี่วันก่อน หลังจากอัพเดต blog engine สุดเลิฟ Hexo เรียบร้อยก็มาถึงความพยายามรื้อถอน CD pipeline ค่ะ

ก่อนเริ่มทำก็สำรวจก่อนเลยว่าตอนนี้ Hexo มีวิธีการ deploy แบบไหน

ซึ่งสองปีผ่านไป Hexo ไม่ทำให้ผิดหวัง มี 1 one command deployment ที่จบทุกประเด็นการ deploy ขึ้น Github Pages ด้วย configuration และ hexo deploy command เดียว

เราก็จัดเลย

ผ่าม

404 github page not found

😱

แตก ถถถถถถ

งงมากแม่ นี่มันเกิดอะไรขึ้น อยู่ดีๆเว็บที่ใช้งานได้มาตลอดก็เข้าไม่ถึงอีกต่อไป ไปดูที่ Github ก็ปกติ branch ที่ใช้ลง blog ทุกอย่างก็อัพเดตปกติ branch เก็บ code hexo ก็อัพเดตปกติ

ทุกอย่างปกติ

วิ่งกลับมาดู github page setting เอาละเจอของแปลก custom domain name ที่ผูกไว้ตอนนี้คือหายไป

Google สักพักก็เจอทางออก ที่แท้การจะ auto deploy github page ต้องสร้างไฟล์ CNAME ไว้ที่ root folder ด้วยค่ะ

และสำหรับ hexo เราต้องวางไว้ใน source

1
2
3
4
source
|_drafts
|_posts
CNAME

เอาละ มาลองกันค่ะ

ถ้าทุกคนยังเห็นโพสนี้ แปลว่าวิธีนี้ work 5555555

Hexo มันอัพเดตง่ายแบบนี้เลยเหรอ

ห่างหายการเขียนบล็อคไปนานมากจนคิดว่าแทบจะพูดคุยกับมนุษย์ไม่รู้เรื่องแล้วค่ะช่วงนี้ และการกลับมาครั้งนี้ก็เพราะ GoDaddy ส่ง email มาต่ออายุโดเมนพร้อมตัดบัตรไปเรียบร้อย 5555

เลยระลึกขึ้นมาได้ว่าเรามี blog นี่นา

วันนี้จัดว่าพอมีเวลา ไหนๆก็ไหนๆละ กลับมาอัพเตตมันสักหน่อยค่ะให้คุ้มค่า domain กับ ssl ที่เสียไป

เริ่มที่การตั้งเป้า

กลับมาครั้งนี้ตั้งใจทำสองอย่างค่ะ

  1. Upgrade blog engine ใหม่
  2. รื้อ pipeline ใหม่ จะลองใช้ Github Actions

มาเริ่มกับในส่วนที่ 1

ปรากฎว่า Hexo อัพเกรดง่ายดายมากค่ะ ง่ายจนต้องยอมใจใช้ต่อไป (คือในความง่ายก็มีความหมายเบาๆที่ว่า แกไม่อัพเดตอะไรที่ breaking change เลยใช่มั้ย ถถถถ)

(ล้อเล่นนะคะ จริงๆมี breaking change อยู่ค่ะ แค่ไม่โดนที่เราใช้มาในบทความทั้งหมดเลยรอดแบบใสๆ)

แล้วมันง่ายระดับไหนคือเชิญชม

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"hexo": {
"version": "7.2.0"
},
"dependencies": {
"hexo": "^7.0.0",
"hexo-asset-link": "^2.2.3",
"hexo-deployer-git": "^4.0.0",
"hexo-filter-emoji": "^2.2.2",
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-index": "^3.0.0",
"hexo-generator-tag": "^2.0.0",
"hexo-renderer-ejs": "^2.0.0",
"hexo-renderer-marked": "^6.3.0",
"hexo-renderer-stylus": "^3.0.1",
"hexo-server": "^3.0.0"
}

เพียงแค่ปรับ lib ทั้งหมดให้ใหม่ล่าสุดเท่าที่จะทำได้ compatible ร้อยล้านเปอเซนต์ไปเลยค่ะ

ก็ถ้าจะง่ายแบบนี้ เราจะหนีไปไหร่รอดอ่ะ <3

เปลี่ยน netcoreapp2.2 เป็น netcoreapp3.1 บทเรียนที่ได้มาจากการฝ่าหลุมอากาศ!!

เนื้อหาในบทความ

  1. ตรวจสอบอายุขัยของ Technology เสมอ
  2. Build ≠ Rebuild ≠ Clean
  3. จัดเรียง Middleware Pipeline ให้ดี
  4. Middleware Pipeline จะ wrap error message ของเราไว้จนดูเหมือนทุกอย่างเป็น CORS policy บน Production environment

คนเราจะทำอะไรมันต้องมีแรงบันดาลใจ ว่ามั้ย?

ถ้าถามว่าอะไรบันดาลใจให้ลุกขึ้นมาเปลี่ยน framework คำตอบที่ให้กับผู้บริหารด้วยกัน คือ

“ได้เรื่องใช้ resource น้อยลง code อ่านง่ายขึ้น = เวลาเดฟก็สั้นลง และ performance ก็เร็วขึ้นนิดนึง แถมยังได้ LTS อีกด้วยค่ะ”

แต่ความจริงในใจก็คือ

อยากใช้ Syntax C# version 8

55555555

netcoreapp2.2 to 3.1

การเปลี่ยนครั้งนี้นำมาซึ่งความเข้าใจ การ refactor ให้รองรับ lib ใหม่ (รอยยิ้ม และน้ำตา) แต่ละบรรทัดของ commit นี่แทบจะเป็น blog ได้ 1 ตอน และได้ให้สัญญากับน้องๆในบริษัทเอาไว้ ว่าถ้าทำเสร็จแล้วจะเอามาถ่ายทอดให้ครบทุกความเจ็บปวด(?)กันไปเลย แต่แน่นอนว่า ไม่ใช่เนื้อหาของ blog นี้ เพราะแต่ละแอพก็คงมี lib ที่ใช้แตกต่างกัน เพราะฉะนั้น technical dept เป็นเรื่องราวของใครของมันนะคะ 5555

Technical dept เมื่อเกิดขึ้นแล้ว ดอกเบี้ยแพงเสมอ 😱

และ blog นี้ขอนำเสนอการหลีกเลี่ยงหลุมอากาศที่อาจจะได้เจอเมื่อตัดสินใจเปลี่ยน ASP.NET framwork version ค่ะ รู้ไว้ แล้วจะไม่เสียเวลา 😉

บทเรียนที่ 0 ก่อนจะเริ่มลงมือ migrate ขอแนะนำว่าจงทำตาม Official Walkthrough อย่างเคร่งครัด


หลุมที่ 1 ตรวจสอบอายุขัยของ Technology เสมอ

บังเอิญเวอร์ชั่นที่ใช้อยู่คือ ASP.NET Core 2.2 อยากเรียกว่าเป็นเวอร์ชั่นลูกเมียน้อย เพราะทุกวันนี้ ASP.NET Core 2.1 ยังซัพพอร์ตอยู่เลย เราที่รีบร้อนเลี้ยวตามเทคโนโลยีก็ โดนเทคโนโลยีพามาเจอทางตัน เพราะเค้าพากันไป 3.1 หมด (และในวันนี้ .NET 5 ก็มาจ่อคอหอยแล้วค่ะ 555555)

สำหรับใครที่คิดจะเปลี่ยน asp.net framework อย่าลืมมาเช็คอายุขัยในนี้ก่อนนะ .net core support

เพราะทุกเทคโนโลยีมีวันหมดอายุ

ไม่มีใครรับ support อะไรไปได้ตลอดกาล ขนาด Microsoft ยังต้องประกาศไม่รับดูแล windows บางเวอร์ชั่นเมื่อเวลาผ่านไป คือคุณยังใช้ได้แหละ แต่มีปัญหาอะไรก็ต้องดูแลกันเอาเอง

บทเรียนที่ 1 พาตัวเองไปอยู่ใน LTS (Long Term Support) อยู่เสมอ


หลุมที่ 2 Build ≠ Rebuild ≠ Clean

เป้าหมายของการเปลี่ยน framework ครั้งนี้คือ API โปรเจค ก้าวแรกไม่ยากเลย มุ่งตรงไปเปลี่ยน TargetFramework จาก netcoreapp2.2 เป็น netcoreapp3.1 เปลี่ยนปั๊บ Run พังนิดหน่อย เปลี่ยน lib บางตัว อัพเวอร์ชั่นบางตัว ปรับ code ใน startup.cs Run อีกรอบ เฮ้ย ผ่านละ อะไรมันจะง่ายปานนั้น สบายใจโยนลง branch เตรียมเทส ไปนอน กะว่าพรุ่งนี้ตื่นมาได้เทสชัวร์

แต่เช้ามาพบกับความพัง

เฮ้ย ทำไมพังได้ ก็ complier บอกเราว่าผ่านใสๆนี่นา เล่นแง่อะไรกันอีกละเนี่ย หรือ IDE ก็มีวันเบลอๆเหมือนกัน?

แต่เทคโนโลยีไม่มีวันโกหก (อาจจะเป็นเราที่โกหกตัวเอง 555)

ขยายความประโยคข้างบนนิดนึงกลัวทัวร์ลง คือทุกผลลัพท์ที่ผิดคาดของ machine มันมีสาเหตุเสมอค่ะ อยู่ที่เรารู้หรือไม่รู้เท่านั้นเอง (และอยู่ที่มันจะบอกเราหรือไม่ หรืออาจถูก wrap ไว้ใต้ error message อะไรบางอย่างเพื่อไม่ให้มักเกิ้ลอย่างเรางง)

และสาเหตุที่ทำให้ทุกอย่างมันผ่านใสกิ๊งขนาดนี้ก็เป็นเพราะเราลืมไปว่าการกดปุ่ม run บน visual studio 2019 มันคือการ build และ run แบบใส่ debugging tool ลงไป ประเด็นมันอยู่ตรงที่ โดยปกติการ build ทำงานแบบที่ว่า ถ้า file ไหนมี code เปลี่ยนก็เอามา build ใหม่ ถ้าไม่ ก็ข้ามไป พูดไปอาจงง ดูรูปประกอบ

build-rebuild-clean

เพราะฉะนั้นการ run อย่างเดียวเลยทำให้โปรเจคอื่นๆที่ไม่ได้มี code เปลี่ยนยังคงใช้ configuration แบบเดิม และส่งผลให้เราไม่เห็นผลกระทบที่โปรเจคมีต่อกันเมื่อโปรเจคนึงมีการเปลี่ยนแปลง เนื่องจากการเปลี่ยน frameworks ของ C# โปรเจคที่ reference ต่อต้องเปลี่ยนตามหรืออย่างน้อยๆต้องใช้ framework ที่ compatible ดังนั้นการเปลี่ยน framework แค่โปรเจคเป้าหมาย โดยไม่ได้เปลี่ยนปลายทางด้วยเลยก่อให้เกิด error ตามมา

บทเรียนที่ 2 เมื่อทำอะไรยิ่งใหญ่ให้ Clean Solution ก่อนเสมอ (ถ้า frontend ก็ลบ node_module 555)


หลุมที่ 3 จัดเรียง Middleware Pipeline ให้ดี

สำหรับคนที่อยู่บน .net core มาแต่แรกอาจจะเข้าใจ concept การใช้ middleware pipeline เป็นตัวคัดกรอง HTTP Request ที่ไหลเข้ามา ซึ่งแต่ละชั้นของ pipeline มีหน้าที่ที่ตัวเอง focus เช่น HTTPsRedirection แปลงทุก request ให้เป็น HTTPS, Authentication จัดการเรื่อง Authentication (ใช้ JWT หรืออะไรก็ว่ากันไป) เป็นต้น

และเมื่อ middleware ชั้นนั้นๆทำงานเสร็จก็จะส่ง request ให้ชั้นต่อๆไป แต่ถ้าไม่ผ่านก็ drop ได้เลยไม่เสียเวลา โดยปลายทาง API Controller ของเราจะอยู่ในชั้นในสุด Endpoint

middleware pipeline

ถามว่าวางผิดแล้วเป็นอะไรมั้ย จริงๆไม่เป็นไรค่ะ 5555 แต่บางครั้งปัญหาที่แท้จริงอาจถูกปัดตกไปเพราะ middleware ชั้นที่ควรทำหลังดันทำก่อน และการทำงานผิดลำดับยังทำให้แอพต้อง process งานที่ไม่จำเป็นทั้งๆที่บาง request อาจปัดตกไปตั้งแต่ชั้นแรกๆได้ด้วยซ้ำ

บทเรียนที่ 3 วาง middleware pipeline ดี มีชัยไปกว่าครึ่ง


หลุมที่ 4 Middleware Pipeline จะ wrap error message ของเราไว้จนดูเหมือนทุกอย่างเป็น CORS policy บน Staging/Production environment

หลังจาก upgrade libraries ไปจนหมด ถึงเวลาเอาขึ้น staging แต่ทันทีที่เอาขึ้นไปก็พบว่า API พังยับอย่างที่คิดไว้ แต่ที่แปลกคือทั้งหมดทั้งมวลบ้วนออกมาเป็น CORs problem ถึงตอนนี้ถ้าอ่านผ่านข้อข้างบนมาแล้วคงเริ่มเข้าใจแล้วว่า Middleware ขั้น CORs อาจไม่ใช่ผู้ร้ายตัวจริง

กลับมาหาเครื่องมือที่ช่วยเราสืบหาสาเหตุได้ที่ไม่ใช่ Browser inspection/Docker error message เพราะล้วนแล้วแต่เป็น tool ที่ถูก wrap มาแล้วหลายชั้น

สำหรับเราชาว C# การ Debug ExceptionHandler class OnException ไว้จะพาเราไปสู่ปัญหาที่แท้จริงได้เร็วที่สุด เป็นเครื่องมือ basic ที่ทำหน้าที่แบบเดียวกับการ try catch ด้วยการใช้ attribute ครอบ controller เป้าหมายไว้ก็ประหนึ่งว่ามี try catch อันใหญ่ครอบทุก API Controller ของเรา ทั้งยังเปิดโอกาสให้แปลง response ให้อยู่ในรูปแบบที่ต้องการก่อนส่งออกไปอีกด้วย

บทเรียนที่ 4 ยึดหลักการ StackTrace ไว้ให้มั่น error ที่ใกล้ต้นตอมากที่สุด เป็นของที่ real ที่สุด


ก็จบลงแล้วกับมหากาพย์การเปลี่ยน framework ที่หายตัวไม่ได้งอกบล็อคเลยก็คือมัวแต่ทำสิ่งนี้อยู่นี่แหละค่ะ 555 กะว่าพอทำเสร็จแล้วคงมีเรื่องเล่ามากมาย แต่เอาตรงๆการมานั่งแจกแจงว่าต้องทำอะไรบ้างมันหาอ่านได้ทั่วไป จริงๆถ้าอ่าน Official Migration 2.2 to 3.0 ก็จะเห็นเกือบหมดแล้วว่าต้องทำอะไรบ้าง หรือการบอกว่าต้องเปลี่ยน libraries อะไรบ้างก็เช่นกัน มันมี Official Breaking Change ที่แจกแจงรายละเอียดมาเพียบแล้วว่าต้องเปลี่ยนตัวไหน และการใช้ lib ของเราก็คงไม่เหมือนกัน

แต่ความผิดพลาด เป็นสิ่งที่ถ่ายทอดออกมา แล้วช่วยลดเวลาการงมของเพื่อนมนุษย์ด้วยกันได้ดีที่สุด 5555

Happy refactoring ค่ะ 😉

JMeter Series - [2] Recording Template บันทึกรูปแบบการท่องเว็บไว้ยิงรัวๆ

กลับมาตามคำสัญญา ภาค 2 ของ JMeter: Performance Testing Tool ตามแผนเราตั้งใจจะรุมยำ server ว่าจะไหวไปสักกี่น้ำ แต่ก่อนจะกระหนำยิง เราต้องมีรูปแบบการใช้งานที่สมจริงเป็นต้นแบบซะก่อน ซึ่ง JMeter เองมีตัวช่วยให้เราสร้าง test plan อย่างว่องไวได้ด้วยการบันทึกรูปแบบการท่องเว็บด้วย Recording Template

JMeter Series:

  1. Intro to JMeter ทำไมใครๆบอกว่าง่าย
  2. Recording Template บันทึกรูปแบบการท่องเว็บไว้ยิงรัวๆ
  3. Work in Progress..

เนื้อหาในบทความ

  1. สร้าง Recording Template
  2. เริ่มต้นบันทึกรูปแบบการท่องเว็บ
    1. ตั้งค่า Browser ให้ส่ง HTTP protocol ผ่าน Proxy Server
    2. ตั้งค่า SSL Certificate
  3. ตรวจสอบ และบันทึก testplan.jmx

การทดลองมีความเสี่ยง ผู้ทดลองโปรดใช้วิจารณญาณในการรับชม


สร้าง Recording Template

ก่อนจะรันเทสบนเว็บเป้าหมาย เราต้องมี test plan กันก่อนเพราะฉะนั้นเปิด JMeter GUI ขึ้นมา

เลือก Files > Templates… > Recording

jmeter-recording-template

มันมี 2 เหตุผลที่เลือก Google เป็นเป้าหมายในวันนี้

  1. ขอสารภาพว่าป็อด 5555 ไม่กล้าเอาเว็บบริษัทมาเป็นตัวอย่างจริงๆค่ะ คือมัน scale out ได้แหละ แต่ก็อย่าเลย เราเล่นกับ google นี่แหละ
  2. google.com เป็นบรรทัดฐานที่ดีค่ะ ผลลัพท์ที่ได้จากการยิงเว็บที่รับมือคนได้ทั้งโลกระดับนี้ จะเป็น benchmark ที่ดีเวลาเอามาเทียบกับเว็บของเรา

หลังกด Create สิ่งที่ได้มาจะเป็นดังรูปข้างล่าง

jmeter-recording-testplan

บล็อคที่แล้วอธิบายอย่างนึง ทำไมมาทำจริง test plan เปลี่ยนเป็นอีกแบบนึง 5555 ใจเย็นๆนะทุกคน มันยัง concept เดิมแหละ ในกรอบสีเขียว Thread Group > Elements(Recording Controller) > Listener(View Results Tree)

แล้วข้างบนอีก 2 ก้อนสีที่งอกมามันคืออะไรกันละ!!

มาว่ากันที่กรอบสีแดงก่อน User Defined Variables, HTTP request Defaults, HTTP Cookie Manager ทั้ง 3 อันนี้เป็น Config elements มีมาเพื่อประกาศตัวแปร/ตั้งค่าทั่วไปให้ test plan หรือกำหนดค่า default สำหรับใช้ใน test plan หนึ่งๆค่ะ เช่น เราสามารถเอาค่า host ใน User Defined Variables ไปใช้ในจุดอื่นๆของ test plan ได้ในลักษณะนี้

1
${host}

ช่องนั้นก็จะถูกแทนค่าด้วย www.google.com ทันทีที่ runtime ซึ่งไม่ค่อยมีประโยคกับการรันเทสก็อกๆแก๊กๆของเราเท่าไหร่ค่ะ แต่มีคุณประโยชน์มหาศาลหากเราจะเอาไปใช้ใน Production และแน่นอนว่า มีอีกหลาย Config elements ให้เราใช้งาน แล้วแต่ use case ที่ต้องการเทส

กรอบสีฟ้า ถ้ายังจำได้จากบล็อคที่แล้วมีการพูดถึงคอนเซป workbench (ที่ไม่มีอีกต่อไปแล้วในเวอร์ชั่น 5.3) มันคือการทำงานที่อยู่นอก test plan นึกภาพ flow ของสิ่งที่เรากำลังจะทำในตอนนี้คือการทดสอบยิงเว็บของเราแบบมีลำดับ action โดยทั่วไป Test plan ต้องการข้อมูลแค่ ยิงไปไหน ปริมาณเท่าไหร่ ลำดับเป็นยังไง การได้มาซึ่งข้อมูลเหล่านั้นคือการนั่งสร้าง test plan elements มาประกอบทีละอันๆ

แต่ไม่ ครั้งนี้สิ่งที่เราจะทำคือการวานให้ JMeter บันทึกรูปแบบการเดินทางท่องเว็บของเราให้หน่อย แล้วเราจะเอาลำดับที่ได้นั้นไปเป็น elements ต่างๆของ test plan อีกที

เพราะฉะนั้น เรื่องราวของการตั้งค่าเพื่อบันทึกการเดินทางครั้งนี้ จึงไม่ถือเป็นส่วนหนึ่งของ test plan และจะไม่ถูกเรียกใช้เมื่อเราสั่งรันเทสค่ะ ถ้างง ดูรูปประกอบค่ะ

jmeter-recording-flow

เพราะทุกครั้งที่รันเทส เราไม่จำเป็นต้องบันทึก behavior ใหม่ แค่รันตาม behavior pattern เดิมที่เรามี 100 รอบ 1000 รอบก็พอ


เริ่มต้นบันทึกรูปแบบการท่องเว็บ

เลือก HTTP(S) Test Script Recorder

jmeter-test script recorder

จะเห็นปุ่ม Start ถ้าใจร้อนกด Start แล้วไปเปิด browser เล่นเว็บเป้าหมาย แล้วหวังว่า JMeter จะ record ให้เลยก็จะผิดหวังค่ะ 5555 (เราทำมาแล้ว บอกแล้วเป็นสายลองก่อน ไม่ได้ค่อยถอยกลับมาอ่าน)

มันมี 2 อย่างที่เราต้อง setting เพิ่มเติมเพื่อให้ JMeter ทำงานได้

  1. ตั้งค่าให้ browser ส่ง HTTP package ผ่าน Proxy Server
  2. ตั้งค่า Certificate ให้เว็บปลายทาง handshake กับ browser ของเราที่ถูกดักผ่าน proxy server แล้วยังผ่าน (เพราะเราคุยกันกับเว็บปลายทางด้วย HTTPS protocols)

jmeter-recording-how it works

เพราะเราต้องการบันทึก pattern ของการยิง http request เอาไว้ลูปใส่ server เป้าหมายในภายหลัง จึงต้องทำให้ JMeter อ่าน http package ที่เดินทางเข้าและออกจาก browser ที่เราเปิดเว็บเป้าหมายได้ ถ้าใครเคยเล่น wireshark จะเข้าใจว่ามันคือหลักการเดียวกันค่ะ

และปุ่ม Start ที่เห็นใน Configuration ของ HTTP(S) Test Script Recorder นั้น คือการสั่งเปิด proxy server เพื่อดักจับ package ค่ะ

และนอกจากนี้จะส่วนให้ config เพิ่มเติมอีก 2 ส่วน

Test Plan Creation ตรงนี้คือตัวกำหนดว่าจะเราจะบันทึก pattern ที่ได้ไปไว้ที่ไหนของ test plan, จะแบ่งกรุ๊ปของ Sampler (transactions) ด้วยอะไร

Requests Filtering ระหว่างที่เราทำการบันทึกมันอาจมี package อื่นปลอมปนเข้ามา เช่น chrome ของเราเอง มีการ login ค้างเอาไว้ดังนั้นจะมี package ที่ยิงไป googleapis อยู่เสมอ เราสามารถกรองของแบบนี้ออกได้

1. Setting browser & Proxy server

browser ที่เลือกมาเป็น Chrome ค่ะ

เลือก Settings > Advanced > System > Open your computer’s proxy settings

เลือก Use a proxy server

ใส่ Address เป็น http://localhost

ใส่ Port เป็น 8888 (ตรงกับ port ใน HTTP(S) Test Script Recorder)

Save ปั๊บ เน็ตหลุดปุ๊บค่ะ 555 เพราะเรายังไม่ได้ start proxy server เลย

2. ตั้งค่า Certificate

กด start ใน HTTP(S) Test Script Recorder จะเจอ popup หน้าตาแบบนี้

jmeter-certificate

ทันทีที่กด start เราจะได้ CA certificate มา 1 อัน อยู่ใน folder bin เราต้องเอาสิ่งนี้ไป import ให้ browser เราอีกทีค่ะ

เลือก Settings > Privacy and security > Security > Manage certificates

เลือก tab Trusted Root Certificate Authorities > Import > เลือกไฟล์ ApacheJMeterTemporaryRootCA.crt

Restart Chrome

cert ตัวนี้มีอายุเพียง 7 วัน และเมื่อหมดอายุต้องเอาออกก่อนจะใส่ตัวใหม่ลงไปเสมอ ไม่งั้น browser เบลอ

เพียงเท่านี้เราก็พร้อมแล้วที่จะ record การเล่นเว็บของเรา

3. Start Proxy Server และเริ่มท่องหน้าเว็บเป้าหมายจนกว่าจะพอใจ

ในขั้นตอนนี้ เราทำ 2 อย่างค่ะ

  1. เข้าหน้า www.google.com
  2. พิมพ์ jmeter ในกล่อง search แล้วค้นหา

4. Stop

สิ่งที่ได้ออกมามี 2 ส่วนนะคะ

ส่วนที่ 1

HTTP request ทั้งหมดที่ record ไว้ระหว่างเปิด proxy server จะถูกบันทึก pattern ไว้ใน Thread Group > Recording Controller ภายใต้ recording controller จะมี transaction controller นี่คือการพยายามจัดกรุ๊ปของ http request transaction

jmeter-google search pattern

สีฟ้าอันแรก - GET request www.google.com
สีฟ้าแผงที่สองและสาม - GET request ตอนที่พิมพ์ jmeter แบบรายตัว
สีแดง - GET request ผลลัพท์การเซิส jmeter

ส่วน transaction ล่างที่ไม่ได้กล่าวถึงอันนั้นเป็นส่วนที่เรา login google account ไว้และไม่เกี่ยวกับการทดสอบของเราวันนี้

ส่วนที่ 2

google-recording.xml ไฟล์นี้คือผลลัพท์ของการรันรอบนี้ สามารถดูได้ที่ HTTP(S) Test Script Recorder > View Results Tree > Write result to file / Read from file

อันนี้เป็นตัวอย่างผลจากการรันทดสอบภายใต้ Thread Group 1 concurrent/1 loop เท่านั้น ขนาดไฟล์ก็ปาไป 1 Mb คือเก็บละเอียดมากค่ะ ลองเปิดดูได้

ซึ่งเวลาเอาไปรันจริง แนะนำว่าให้บันทึกไฟล์ลงในไดร์ฟที่ว่างนะคะ เพราะเราต้องใช้ไฟล์นี้แหละในการ generate ผลลัพท์รูปแบบต่างๆ

และด้วยความที่บันทึกละเอียดขนาดนี้ จึงไม่แนะนำให้ใช้ View Results แบบใดใดเลยระหว่างที่ทำการรันเทส

และยิ่งแผนเราจะยิง 1000 concurrent ไม่ควรอย่างยิ่งที่จะใช้ GUI เพราะถ้าเรา process response ที่ server ตอบกลับมาไม่ทัน write file ได้ช้า จะทำให้ผลการวัด performance คลาดเคลื่อนไปเลยด้วยค่ะ


ตรวจสอบ และบันทึก testplan.jmx

คลิกขวาที่ Thread Group > Validate เพื่อตรวจสอบดูว่ามีข้อมูลส่วนไหนที่เวลาเอาไปรันแล้วจะไม่ได้ผลเหมือนที่ทำการบันทึกไว้รึเปล่า บอกเลยว่า ถ้าถึงเวลาไปรันบนเว็บจริงของเราที่มีระบบ authentication จะมีประเด็นค่ะ เพราะ Recording จับไว้ และเอามาใช้ต่อได้เฉพาะ token ที่เห็นติดมากับ package เท่านั้น เพราะฉะนั้นแนะนำให้ login ใหม่ไปเลยระหว่าง record นะคะในคราวนี้

หลังจากนั้นค่อย implement User Defined Variables, Pre/Post Processors เพื่อกระทำการขอ token แทนการ login ในอนาคตค่ะ

แต่สำหรับ google search ครั้งนี้คือผ่านค่ะ save ได้

เลือก Test plan > Files > Save

จากนั้นไปนอนค่ะ 5555555555


ยังเหลืออีก 1 ตอนนะคะ สำหรับซีรี่ย์ JMeter จากเดิมกะว่า 2 ตอนจบ ไม่ได้จริงๆค่ะ รายละเอียดมันเยอะเกินไป ไม่เพียงแค่คนอ่านจะย่อยไม่ไหว เราเองก็ขยายให้ฟังได้ไม่หมด

นี่ยังไม่ได้พูดถึงการใช้ Pre processors / Post processors / Timer / Assertion และอีกล้านแปดที่ยังมีอีกเพียบใน JMeter ซึ่งเราเองก็ยังอ่านไปไม่ถึงไหนค่ะตอนนี้

พอดีมีเป้าหมายว่าต้องเทส Performance เว็บตัวเองให้ได้ เลยเอาแค่เท่าที่ต้องการก่อน ซึ่งตอนนี้ก็ได้ผลนั้นออกมาแล้ว ไว้จะเอา result มาเทียบกับผล google คราวนี้ในบล็อคหน้านะคะ

มันก็จะเขินๆกับผลลัพท์นิดนึง 55555

C# 8.0 - switch case From Statement to Expression

c# and switch case expression

เรื่องมันเริ่มมาจาก เมื่อคืนลง extension ใน visual studio code แล้วพบว่า warning งอกทั่วทุกหัวระแหงก่อนจะค่อยๆ rebuild แล้วกลับเป็นปกติยกเว้นบางไฟล์ที่ยังแดงเถือกไม่หาย และ 1 ในนั้นก็พาให้เราไปเจอกับ code ชุดนึงเข้า

unreachable code

มีคนวาง break ไว้หลัง return

ใจดำอำมหิตอะไรเยี่ยงนี้ 5555 break จะไม่มีวันได้เห็นเดือนเห็นตะวัน ไม่มีวันได้สัมผัสการ debug ใดใด เพราะ cursor ทั้งหลายจะโดน return ดีดออกจาก method ไปจนหมด

แต่ก่อนจะใจบุญลบ code ออกไป สมองดันสงสัยซะก่อน

แต่ switch case มันใช้กับ break และ default นี่หว่า 4 สหายที่ใช้กันมาแต่โบราณกาล แล้วถ้าไม่มี break ทิ้งไว้แค่ return มันก็ได้แหละ แต่ทำไมมันรู้สึกขัดใจแปลกๆวะ หรือเพราะมันทำงานคนละอย่างกัน…

(สาระ - break จะดีดเราออกจาก switch case แล้วไปต่อบรรทัดล่างของ method นี้ ในขณะที่ return จะดีดเราออกจาก method ไปเลย)

งั้น use case ที่ใช้ switch case + return นี่มันใช้กับอะไรละเนี่ย พอสงสัยก็เลยเริ่มเซิสค่ะ แต่พอเซิสกลับพบว่ามันมีรูปแบบการเขียน switch case ใหม่ๆโผล่มาอีกมากมาย และจุดเปลี่ยนล่าสุดแห่งวงการ switch case ก็เป็น feature ที่เปิดกว้างให้ concept ของ switch case มีความเหมาะสมกับการใช้งานหลากหลายมากขึ้น ซึ่งมาพร้อมกับ C# version 8.0 ที่เพิ่งจุดระเบิดเปิดตัวไปเมื่อไม่นานนี้เอง

From Statement to Expression

ใน C# version ใหม่ จริงๆมีหลาย feature แต่ที่อยากพูดถึงก่อนก็คือ switch case

อยากที่เกริ่นไว้ ปกติเราใช้ switch case คู่กับ break มาจนเป็นธรรมชาติ แล้วถ้าไปอ่าน doc ของมันก็จะพบแมสเสจบางอย่างที่ก่อให้เกิดความสงสัย (คนอื่นอาจรู้อยู่แล้ว 555)

switch is a selection statement…

The switch statement is often used as an alternative to an if-else construct…

ขีดเส้นใต้ตรง statement

พอลองเซิสต่อไป ก็ย้อนกลับไปถึงคำนิยามของ Statement ใน C# ว่ามันคือ กลุ่มก้อนของ keywords (codes) ที่ก่อให้เกิด action อาจจะมีหรือไม่มี expressions และ operators ก็ได้

และ expression เป็นซับเซตของ statement แต่เป็น action ที่มีการคำนวณ หรือทำอะไรบางอย่างให้เกิด value และมีการเก็บลงในตัวแปร

ถ้างง ดูตัวอย่าง code ดีกว่า

1
2
3
int cats; //statement (declaration statement)
cats = 2; //ก็ยัง statement
cats++; //เนี่ย EXPRESSION statement มีการ + แล้วเก็บลง cats

และ switch case ดั้งเดิม เกิดมาพร้อมคำนิยามว่าเป็น statement นั่นหมายความว่าเราสามารถใช้ switch case เพื่อส่ง value บางอย่างกลับ (return) หรือแค่ให้เข้ามาทำงานบางอย่างแล้วผ่านไปโดยไม่ส่ง value อะไรกลับก็ได้เช่นกัน (break)

แต่พอมาถึง C# 8.0 ในรายการ feature ใหม่มีสิ่งที่เรียกว่า switch expression

ของเดิมก็ทำได้อยู่แล้ว ดังนั้นคราวนี้จึงเป็นการปรับ syntax ใหม่เพื่อให้เราเห็นความแตกต่างระหว่าง case ได้ง่ายขึ้น

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string statusDisplay;
switch (projectStatusId)
{
case (int)ProjectStatus.Active:
statusDisplay = "Working";
break;
case (int)ProjectStatus.Finished:
statusDisplay = "Finish";
break;
case (int)ProjectStatus.Terminated:
statusDisplay = "Terminated";
break;
case (int)ProjectStatus.Pending:
statusDisplay = "Pending";
break;
default:
statusDisplay = "Not supported status id";
break;
};
return statusDisplay;

สามารถเปลี่ยนใหม่ให้สั้นลงได้เป็นแบบนี้

1
2
3
4
5
6
7
projectStatusId switch {
ProjectStatus.Active => return "Working",
ProjectStatus.Finished => return "Finished",
ProjectStatus.Terminated => return "Terminated",
ProjectStatus.Pending => return "Pending",
_ => throw new ArgumentException(message: "Not supported status id")
};
  1. การเอา case : ออกไปแล้วใช้ => แทน
  2. การเอา default ออก เหลือแค่ _
  3. การสลับ match มาวางหน้า switch

ทั้งหมดทั้งมวลมันทำให้เกิดการจัดวางบรรทัดแบบใหม่ที่ส่งผลให้เราอ่าน code และเห็นผลลัพท์ที่จะได้จาก case ต่างๆง่ายขึ้นมาก สมแล้วที่ทาง C# 8.0 เคลมว่าเป็น feature ที่เพิ่มเติมความพิเศษให้ switch case พร้อมเป็น Expression statement อย่างสมศักดิ์ศรี

นอกจากนี้ก็ยังมีวิธีการเขียน switch case เพิ่มเติมใน version นี้อีกหลายรูปแบบ แนะนำให้เข้าไปลองอ่าน document ต้นทางกันดูแล้วจะรู้ว่า switch case เป็นได้มากกว่าที่คุณคิด 😉

JMeter Series - [1] Intro to JMeter ทำไมใครๆบอกว่าง่าย

เนื้อหาในบทความ

  1. ทำไม JMeter?
  2. JMeter ทำงานยังไง?
  3. โครงสร้างภายในของ JMeter
  4. Run GUI JMeter
  5. องค์ประกอบของ Test plan

เมื่อวันนึงเกิดสงสัยว่าเว็บที่เราทำมามันรับ user พร้อมๆกันได้ขนาดไหน

เพื่อจะหาคำตอบให้ได้ มันคงไม่ใช่การเอาพนักงาน 100 กว่าชีวิตในบริษัทมานั่งกดปุ่ม login พร้อมๆกัน นอกจากจะไม่ productive แล้วยังเป็นไปไม่ได้ในชีวิตจริง แต่การจะให้ไปจัดงาน workshop ระดม user ตัวเป็นๆมานับพันแล้วมากดปุ่ม login พร้อมกันก็คงไม่ใช่ทางออกที่ดี เพราะบางทีเว็บเราอาจรับได้แค่ 500 แล้วตายไปต่อหน้าต่อตา user นับพันก็เป็นได้

ก็มาถึงจุดที่ต้องหา tool อะไรสักอย่างมาทดสอบให้รู้เรื่องกันไป

เครื่องมือมันมีหลากหลายแต่ในวันนี้ที่เลือกมาคือ JMeter

jmeter-conceptual

JMeter เป็นเครื่องมือในการทำ Performance testing อย่างนึง ที่ประด้วย Load test (ทดสอบว่ารับ concurrent ได้เยอะแค่ไหน) + Stress test (ทดสอบว่ารับ concurrent และ data package ได้เยอะแค่ไหน) เหมาะมากที่จะเอาไว้เป็นเครื่องมือหาคอขวดในระบบ


ทำไม JMeter?

  1. เพราะมันฟรี เป็น open source
  2. เป็น Java ซึ่งเป็นหนึ่งในภาษาที่เร็วชิบหายวายป่วงถ้าใช้เพียวๆ เหมาะมากที่จะเอามาเล่นอะไรแบบนี้ จะทดสอบ performance คนอื่น ตัวเองก็ต้องเร็วก่อนอ่ะเนอะ
  3. และเพราะมันเป็น Java จะรันมันขึ้นมา ขอแค่ลง jdk ซึ่งลงได้ทุกระบบปฏิบัติการณ์อยู่แล้ว เลยคิดว่าไม่น่าจะติดอะไร (ซึ่งหมายความว่าพอถึงเวลาต้อง production จริงจัง ก็น่าจะไม่ติดด้วยเช่นกัน)
  4. คนทั่วไปเค้าเคลมกันว่า หน้าตามันเป็นมิตร ใช้งานง่าย ซึ่งเราก็ตกหลุม หลงเชื่อคนอื่นไป
  5. มี extension libraries ที่หลากหลาย และเอามาต่อกับ Selenium ได้ด้วย ดีเลยที่บริษัทมี Robot testing ถ้าถึงเวลาเอามาพ่วงท้าย robot testing ของทีม QA ของเรามันคงจะดีไม่น้อย
  6. มันแสดงผลได้หลากหลาย แต่เอาตรงๆไม่ได้คาดหวังสิ่งนั้นจาก tool นี้ค่ะ เพราะ grafana หรือ tool สร้างกราฟอื่นๆน่าจะกินขาด

ด้วยข้อดีประมาณนี้ เลยตัดสินใจให้เราเลือกสิ่งนี้มาเป็นของเล่นในวันนี้ค่ะ

แต่พอได้ลองเล่นจริง มันไม่ง่ายค่ะ 555555555 แม่งเอ้ย

ต้องขอออกตัวก่อนว่าเราเป็นสาย เจอปุ๊บ ลองปั๊บ ถ้าติดขัดถึงจะกลับมาอ่าน doc ทีหลัง 55555 (ไม่ดี อย่าหาทำ) จริงๆมันก็ไม่ถึงกับลงมือเลยขนาดนั้นหรอกค่ะ เพราะในจังหวะที่เลือกก็ research มาระดับนึงแล้ว แต่ตอนลงมือเนี่ย ชอบลงไปจับทุกอย่างก่อน มันมีเสน่ห์ในการได้ลองมั่วดูก่อน ไม่รู้ทำไม นิสัยนี้แก้ไม่หายจริงๆ

แต่เพราะไปลองมั่วมาแล้ว เลยตัดสินใจว่า เอ้อ สำหรับ tool นี้ ถ้าไม่อ่านก่อน ทำความเข้าใจมันก่อน ไม่เวิร์คหรอก ทำไปอ่ะทำได้ แต่จะไม่เข้าใจถึงการได้มาซึ่งผลลัพธ์ มันไม่สนุก

เป็นที่มาที่เราเปลี่ยนใจ ตอนแรกจะอัด tutorial ให้จบใน blog เดียว ไม่เอาละ ขอพื้นที่อธิบายความเป็น JMeter ก่อนดีกว่า ถ้าเราจะหอบหิ้วไปสอนน้องๆต่อ ก็อยากจะสอนเพื่อให้น้องๆเข้าใจไส้ เข้าใจที่มาที่ไป ก่อนลงมือทำเหมือนกัน


JMeter ทำงานยังไง?

การทำงานของ JMeter มีหลักการคร่าวๆคือ การสร้าง Test Plan แล้วรัน Test Plan นั้นผ่าน proxy ไปหา Server เพื่อป้อน request หน้าตาและปริมาณตามที่กำหนดให้ server เป้าหมายเพื่อเก็บ response ของ server นั้นๆมาสร้างผลลัพท์ค่ะ ดูรูปประกอบ (อาร์ตเวิร์คทุกอย่างสร้างด้วย google slides)

jmeter-workflow

(สำหรับเวอร์ชั่นเก่า JMeter มีคอนเซปท์ที่เรียกว่า workbench เอาไว้แสดงผลที่เป็นผลลัพท์จากการรัน Test plan รอบนั้นๆ แต่ตอนนี้ไม่มีแล้วนะคะ ถือเป็น non-testing element ทั้งหมด)

หลักการสั้นมาก แต่การสร้าง Test plan นั้น config ได้หลากหลายตามความซับซ้อนของระบบที่เราต้องการทดสอบ มันยากเพราะเว็บของเรามันมี authentication มันมี ssl มันมี WAF การจะออกแบบ request ไปหลอกเว็บเราที่ก็มี security ระดับนึงว่านี่คนนะ ไม่ใช่บอทต้องสงสัยที่ไหน (แต่จริงๆเราคือบอท 555) ก็เลยกลายเป็นเรื่องยากไปเลยโดยอัตโนมัติ


โครงสร้างภายในของ JMeter

มาดูกันหน่อยว่าพอ download JMeter มาแล้วเราได้อะไรมาบ้าง

jmeter-structure

ด้วยความที่ไม่ต้อง install เวลาเรียกใช้งานจะเป็นการเรียก script ใน bin เพราะงั้นอย่าวางไว้ใน folder ลึกมากนะคะ เวลาเขียน command จะเสียใจภายหลัง 5555

โฟลเดอร์ที่เราสนใจในตอนนี้มีแค่นี้

  • bin - script ต่างๆที่เราใช้รันให้ JMeter ทำงานจะอยู่ในนี้ และรวมถึง template test plan ต่างๆที่ JMeter มีให้มาเป็นเบื้องต้นด้วย
  • lib - core libraries, protocols, components ของ JMeter และ extension อย่างที่บอกว่า JMeter สามารถเอา extension มาใส่เพิ่มเติมเพื่อใช้งานได้ในรูปแบบการเอา libraries นอกมาใส่ ถ้าเปิดดูข้างในก็จะเห็นว่ามี lib เบื้องต้นอยู่เพียบไปหมด (มีกระทั่ง font-awesome 555)

ส่วนที่เหลือเป็นอื่นๆ และพวก document การใช้งาน JMeter


Run GUI JMeter

jmeter-gui

JMeter นี่มันเปิดได้ 3 วิธี

  • GUI - Run bin/jmeter.bat เพื่อเปิด JMeter GUI ก็จะได้หน้าตาแบบข้างบน
  • CLI - Run command ข้างล่างในโฟลเดอร์ bin
1
jmeter -n -t <ชื่อ test plan> -l <ชื่อไฟล์ที่จะใช้เก็บผลลัพท์> -H <proxy server> -P <port>
  • Server mode Run bin/jmeter-server.bet ถ้ารันในโหลดนี้จะสามารถกระจาย thread ได้ เทสได้ไวขึ้น

มันมีสาเหตุที่เราควรใช้ cli มากกว่า gui เพราะตอนลองรันโดยใช้ gui แบบ thread เดียว รอบเดียว ยิง https request 2 หน้าจอ ผลที่ออกมา 300 responses นี่ทำคอมเรา RAM 16GB ถึงกับค้างไป (แต่ตอนนั้นก็ทำอย่างอื่นอีกหลายๆอย่างๆไปด้วยแหละ แหะๆ) เพราะฉะนั้นถ้าจะ load test ใส่สัก 1000 thread ก็อย่าใช้ gui เลยค่ะ สงสารเครื่องเนอะ

ถึงตรงนี้ขอพูดถึงตัว GUI นิดนึงนะคะ หลังจากเปิดมาจะมี 2 panels ซ้ายมือคือ Test plan และขวามือคือ configuration หรือ detail ของสิ่งที่เราเลือกมาจากทาง panel ซ้ายค่ะ


องค์ประกอบของ Test plan

นี่คือหัวใจของ JMeter ค่ะ

Test plan เกิดจากการเอา JMeter element ต่างๆมาประกอบกันเป็นลำดับ activity เพื่อให้ Thread Group ที่เปรียบเสมือน user เอา activity ดังกล่าวไปกระทำบน server เป้าหมาย จากนั้นตั้ง Listener รอรับผลมาแสดงตามรูปแบบที่กำหนด

เห็นตัวหนามั้ยคะ

นั่นคือ องค์ประกอบทั้ง 3 ที่ทำให้เกิด test plan ขึ้นมา 1 plan

jmeter-elements

Thread Group - กำหนดปริมาณของ user และพฤติกรรมเบื้องต้นเช่น ทำแต่ละ action ช้าเร็วขนาดไหนได้ 1 test plan มีมากกว่า 1 thread group ได้นะ เพื่อนำเสนอกิจกรรมของ user แต่ละกลุ่มที่แตกต่างกัน แต่ถ้าทำแบบนั้นอาจทำให้ตัวแปรมันเยอะเกินจะวิเคราะห์ได้ ต้องระวัง

Elements - ลำดับกิจกรรมต่างๆที่เราสามารถกำหนดให้ user ไปทำได้ โดยประกอบไปด้วย 2 elements ที่เราจะใช้บ่อย

  1. Sampler ตัวกิจกรรมหลักๆ (แปล: ตัวอย่างกิจกรรม) ถ้าสมมุติเราจะทดสอบการเข้าเว็บ เราจะสร้าง Sampler > HTTP Request เป็นการบอกว่าเรากำลังจะยิง HTTP Request ไปหา Server เป้าหมาย
  2. Controller ตัวควบคุมลำดับของ Sampler อีกที เพราะการท่องเว็บมีลำดับของมัน เช่น อาจต้องยิง HTTP Request login ก่อน เพื่อผ่านหน้า login เข้าไปในเว็บให้ได้ แล้วค่อยยิง HTTP Request logout ออกมา (แล้วจะเข้าไปทำไม..)

Listener - ตัวแสดงผลลัพท์ มีมากกว่า 1 ได้เหมือนกันค่ะในแต่ละรอบการเทส เช่น อยากได้ผลลัพท์กราฟ 1 อัน กับผลลัพท์แบบตารางอีก 1 อัน ก็สร้างไว้เลย 2 listener แต่หลักการจริงๆคือ listener จะอ่าน response ที่ถูกบันทึกไว้ในไฟล์ xml แล้วมาแสดงผลเฉยๆค่ะ ไม่ต้องกลัวว่ามันจะเก็บผลแยกกัน


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

“บันทึกการท่องเว็บของเราเองด้วย JMeter Recording script แล้วเอามารันในระดับ 1000 concurrent”

จะตื่นเต้นขนาดไหน server จะล่มมั้ย โปรดติดตามตอนต่อไปค่ะ 😉

สร้างระบบ Ticket Issuing ด้วย JIRA Issue Collector + Jira Automation

ที่ออฟฟิศใช้อะไรจัดการงาน develop กันคะ

สำหรับ Builk One Group เราถนัดใช้ JIRA Cloud และวันนี้ จะพาทุกคนทัวร์ระบบรับแจ้งบัค/แจ้งปัญหาการใช้งานแอพพลิเคชั่นโดยใช้ JIRA เพียวๆไม่พึ่ง code กันค่ะ

jira-automation-stack

JIRA จากตระกูล Atlassian (เจ้าของเดียวกับ Bitbucket) เป็น Solution สำหรับบริหาร+จัดการการพัฒนา Software ในรูปแบบ Agile ที่พาวเวอร์ฟูลตรงที่สามารถสร้าง workflow ได้ดั่งใจปรารถนา (ก็ยังไม่ค่อยเจอข้อจำกัดนะคะ มีทรมานนิดหน่อยตอนที่ย้าย JIRA Cloud 2 organization มารวมกันเป็นอันเดียว) ล่าสุดก็ควบรวม Trello ไปแล้ว ก็แฮปปี้สำหรับคนใช้ Kanban สุดๆไปเลย

เอาละเข้าเรื่องของเราดีกว่า

เนื่องจากได้รับโจทย์ให้ทำระบบ Ticket issuing หรือก็คือระบบแจ้งปัญหาการใช้งานที่สามารถฝังตัวเองไว้ในหน้าเว็บได้ แต่ถ้าคุณโทรมาใน 30 นาทีนี้ เราแถมให้ไปเลย ระบบตอบกลับความเคลื่อนไหวทางอีเมล ฟรี!

ขอเล่าถึงที่มาว่าทำไมถึงใช้ tool นี้นิดนึงก่อนเข้า tutorial นะคะ ใครไม่สนใจ อยากดู tutorial แล้ว ข้ามเส้น __ ไปได้เลย


เมื่อนานแสนนานมาแล้ว เคยทำระบบเก็บบัคด้วย JIRA Issue Collector ฝังไว้หลังบ้านให้ usercare ที่ทำหน้าที่ซัพพอร์ตลูกค้าได้ใช้งาน เพราะย้อนกลับไป 8 ปีก่อน JIRA เหมือนป่าดิบชื้นค่ะ ไม่เป็นมิตรต่อสายตาของสายอาชีพอื่นอย่างมาก เรียกได้ว่าเข้ามาทางไหนก็หลง ตอนนี้คิดว่าดีขึ้นเยอะแล้วนะ 5555

แต่ไม่ว่าจะหลงยังไง เวลามีบัคมา เขาเหล่านั้นก็ยังต้องแจ้งเราอยู่ดี แล้วที่ที่เราเหล่าเดฟอาศัยอยู่ก็คือใจกลางป่าดิบชิ้น JIRA scrum board นั้นน่ะเอง ถ้าใครเคยใช้จะรู้ว่าเมื่อก่อนกว่าจะเข้าไปถึงจุดนั้นได้คือผ่านหลายคลิกมาก

สุดท้ายเลยตัดสินใจทำทางเข้าไว้ให้เข้าถึงได้ง่ายด้วย JIRA Issue Collector ซึ่งเป็น feature นึงที่มีอยู่ใน Project classic type เท่านั้น (ยังไม่ได้เช็คว่า new gen จะทำมั้ย แต่ภาวนาขอให้ทำ) แล้วเอา code ที่ได้ไปฝังไว้ในเว็บเพื่อให้คนแจ้งบัคใช้งาน

ทีนี้คนแจ้งก็สามารถกด float tab แจ้งบัคจากในเว็บ กรอก field ต่างๆใน popup ที่เด้งขึ้นมาแล้วส่ง card ให้คนที่สิง backlog เห็นได้ทันท่วงที

ตรง requirement เด๊ะ

และไม่นานมานี้ เราได้สังเกตเห็นปุ่มนึงโผล่ขึ้นมาใน JIRA เป็นรูปสายฟ้า พอลองเล่นดูก็พบว่า อ่อ มันคือ automation rules ที่สามารถใส่เพิ่มได้แบบสะดวกมาก อิจฉาคนใช้งานสมัยนี้จริงๆ 555555

(ยังจำภาพความลำบากเมื่อก่อนที่ต้องผสมผสาน workflows+notification scheme เพื่อจะส่งเมลแบบเดียวกันเนี่ยแหละได้เป็นอย่างดี)

งั้นเราจะรับบัคมาอย่างเดียวทำไม ก็ออกแบบให้พอแก้เสร็จก็แจ้งกลับได้อัตโนมัติกันไปเลยดีกว่า!


1. สร้าง field Email

งงละสิ ทำไมสร้าง Field อีเมลก่อน มันค่อนข้างเป็นหลักการ programming นิดนึง ในทางโปรแกรมเมอร์เราเรียกกันว่า การประกาศตัวแปร (เพื่อจอง memory เอาไว้ใช้)

ซึ่ง field Email นี้เราเตรียมจะเอาไปใช้ใน form ที่จะให้ user กรอกในภายหลัง ดังนั้นเราเลยจะสร้างเตรียมไว้ก่อน (จริงๆสร้างทีหลังก็ได้ แต่ก็ต้องกลับมาแก้ไข form ใหม่อีกรอบ)

และแน่นอน field นี้สามารถเอาไปใช้ได้อีกหลายโปรเจค และจะสร้างอีกกี่ field ก็ได้ตามอัธยาศัย

ไปที่ JIRA Settings > Issues > Custom fields > Create custom field

custom-fields-JIRA

เลือก Text Field (single line) แล้วกรอกชื่อ field ไว้ว่า Email

2. ใส่ field ที่สร้างใหม่ให้โปรเจค

**ไปที่ Custom fields > เลือก field ใหม่ที่เพิ่งสร้าง > … > Associate to Screens **

ติ๊กเลือกโปรเจคที่ต้องการสร้างระบบ Issue Collector กด update ที่ท้ายหน้าจอด้วยนะคะ 5555 โปรเจคเราเยอะมากจนลืมกด update แล้วก็มานั่งงงว่าทำไมมันไม่เปลี่ยนวะ? เป็นประจำ

ฝันให้ Jira มี autosaved แต่ก็ยังไม่เป็นจริง…

3. Setting JIRA Issue Collector

โปรเจค classic เท่านั้นที่จะมี feature นี้นะคะ

ไปที่ Project เป้าหมาย > Project Settings > Issue Collector > Add issue collector

add-issue-collector-JIRA

JIRA Issue Collector ประกอบไปด้วย 3 ส่วน

  • Issue Collector Setting (สีแดง) กางกรอบแดงพลาดค่ะ จริงๆมันเริ่มตั้งแต่ name แหละส่วนแรก จุดสำคัญคือ Reporter ตรงนี้จะกรอกลงไปได้แค่คนที่มี account jira ค่ะ จึงเป็นเหตุให้เราต้องไปสร้างกล่อง email เพื่อรับ email ของลูกค้าเพิ่มเพื่อทำการตอบกลับ
  • Trigger Setting (สีน้ำเงิน) สามารถปรับแต่งการแสดงผลได้หลากหลายค่ะ ยิ่งถ้าเลือก custom คือแก้ behavior แก้จุดจัดวางปุ่มกันได้เลยทีเดียว
  • Issue Form (สีเขียว) สามารถเลือกได้ว่าอยากให้คนแจ้งกรอกข้อมูลอะไรบ้าง และที่สำคัญคือสามารถเพิ่มกล่อง email ของเราลงไปได้ และถ้าเลื่อนลงไปท้ายดูในส่วน preview ก็จะเห็นสภาพ form ที่จะโผล่มาให้ผู้ใช้งานกรอกอีกด้วย

สร้างเสร็จแล้วอย่าลืม save

4. Setting JIRA Automation Rules

ไปที่ Project เป้าหมาย > Project Settings > Automation > Create rule

project-automation-JIRA-std

บอกเลยว่า endless possibilities ค่ะ 55555 (มี incoming webhook ด้วย คือสบายใจละ)

แต่ flow ของเราครั้งนี้จะลองทำอย่างง่าย rule เดียวก่อน คือเมื่อปัญหาแก้เสร็จเรียบร้อยค่อยส่งเมลแจ้ง

  1. เลือก Issue transitioned
  2. เลือก To สถานะ Done
  3. เลือก New Action > Send email

setting-automation-rule

มาถึงตรงนี้จะ tricky นิดนึงค่ะ

ช่อง To เนี่ย ถ้าเลือกไปมันจะไม่มีกล่อง Email ที่เราสร้างมาใหม่ แต่เราสามารถเอาค่าจาก custom fields มาใส่ได้โดยการใช้ feature smart value แต่ลำบากนิดนึงค่ะ คือต้องเอา custom field ID มาใส่ในรูปแบบนี้

1
{{issue.customField_xxxxx}}

ไหนบอกไม่มี code 5555 นิดนึงอ่ะเนอะ

แล้วจะไปเอา ID นั้นมาจากไหน ให้กลับไปที่หน้ารวม Custom fields ของเราที่ JIRA Settings > Issues > Custom fields > field เป้าหมาย > … > Edit Custom Field Details

jira-custom-field-id

นั่นแหละไอดีของ field ของเรา ย้อนกลับไปเติมเต็มข้อมูลให้เรียบร้อยแล้วลองทดสอบการใช้งานดูได้เลยค่ะ

test-transition-email-noti

ก่อนส่งให้ลูกค้า อย่าลืมปรับหน้าตาอีเมลกันนะคะ ^^

คืนชีวิตให้ Technical Document บน ASP.NET Core 3.1 ด้วย XML Comments + SwashBuckle + Redoc

มือก็ต้องเกี่ยวข้าว(coding) เท้าก็ต้องไกวเปล(documenting that sourcecode)

ใครเป็นเดฟคงรู้ดีว่าชีวิตนี้มันปวดร้าว ที่ไหนมีทีม SA เราชาวเดฟยังพออาศัยพึ่งพาให้ผู้กล้าเหล่านั้นช่วยดูแล Technical document ทั้งหลายได้บ้าง แต่สำหรับทีมเดฟล้วน ที่ต้องทั้งเขียน code แล้วยังต้องมาดูแล Technical document ด้วยนั้น

บอกเลยว่า Trilogy แถม Drama Comedy สุดๆ

จะไม่ทำก็ไม่ได้… แถมพอทำไป requirement ก็ยังเปลี่ยนแปลงให้ต้องกลับมาแก้กันอยู่ตลอดๆ แอพก็เหมือนเด็กที่ต้องมีวันเติบโต ต้องเปลี่ยนแปลงตามกาลเวลา ตามเทคโนโลยีที่เปลี่ยนไป ตามธุรกิจที่ต้องปรับตัว เพราะงั้นต่อให้ทำแอพมาดีแค่ไหน ก็ไม่มีแอพใดที่อยู่ได้ค้ำฟ้า
ทางออกที่พอรับได้สำหรับเดฟปากกัดตีนถีบก็คงจะหนีไม่พ้น..

Living Document

Technical Document ที่เปลี่ยนแปลงตามระบบที่เปลี่ยนไปอยู่ตลอดเวลา

สมัยเรียนหลายคนคงเคยชินกับวิชา SA ที่จัดให้เรียนพร้อมๆกับวิชาโปรเจค และ 2 วิชานี้ก็จะบูรณาการกันในเทอมนั้น วิชานึงส่งรายงาน Technical Specification อีกวิชาพรีเซนโปรแกรม และนั่นคือตัวอย่างของการทำ spec ชิ้นแรกที่พอทำเสร็จก็ไม่ได้กลับไปมองอีกเลย

แต่ในหลายองค์กรที่เดฟหลายคน จากหลายทีมต้องทำงานร่วมกัน บางครั้งแค่ code มันไม่พอ มันจำเป็นที่จะต้องมีเอกสารประกอบที่สามารถอธิบายที่มาที่ไปของ code ชุดนั้นได้ หลายทีมพึ่งพา Wikipedia และหลายทีมก็ทำเว็บขึ้นมาเอง

แต่ที่ถือว่าเปลี่ยนแปลงโลกของเดฟไปสู่มิติแห่งการเขียน spec คือการมาถึงของ XML (Extensible Markup Language) โดยองค์กร W3

ความสามารถหลักๆคือการต่อขยายจาก programming language ใดก็ได้ เพราะทุก Compiler มีหน้าที่ที่ต้องอ่านมันออก และยังสามารถปรับโครงสร้างของตัวเองเพื่อรองรับการสื่อสารระหว่างระบบโดยเฉพาะ
และแน่นอน C# ก็ adopt สิ่งนี้มาใช้ในระบบให้เราสามารถใส่ XML comment ในจุดใดของโปรแกรมก็ได้ และยัง export ออกมาใช้งานได้สะดวกทุกครั้งที่มีการ Build โปรแกรมใหม่

สังเกตเห็นอะไรมั้ยคะ…

export XML comment ทุกครั้งที่มีการ Build โปรแกรมใหม่

นั่นหมายความว่าเราจะมีเสป็คใหม่ทุกครั้ง ถ้าเราขยันขันแข็ง maintain comment ที่กระจายอยู่ทั่วโปรแกรม

และเมื่อรวมมันเข้ากับ Swagger ละ…

(Swagger Specification หรือ OpenAPI Specification คือเครื่องมือที่สามารถสร้างไฟล์ JSON จาก API Route เพื่อทำเป็น API Document ได้ อีกหนึ่งเทคโนโลยีที่ใช้ทำ Living Document)


เป็นที่มาของสิ่งที่เราจะทำกันในวันนี้ค่ะ

living-document-tech-stack
ใหญ่นิดนึงนะคะ เอามาจากสไลด์สอนน้องที่ออฟฟิศ

Flow >> หลักการของมันไม่มีอะไรมาก เราจะใช้ swagger.json เพื่อสร้าง API Specification ซึ่งลำพัง Swagger ถือเป็น Living API Documentation อยู่แล้วเพราะจะ generate ใหม่ทุกครั้งที่ build จากนั้นเราจะแล้ว inject XML comment ที่เรา comment ทิ้งไว้ทั่ว code ลงไป

ถ้าถามว่าทำไมต้อง Redoc ก็บอกได้คำเดียวว่าดูแล้วปวดตับน้อยกว่า SwaggerUI เยอะอยู่ค่ะ 5555

Pre-requisites (ของต้องมีก่อนเริ่ม code)

  1. ASP.NET Core API Server (version ไหนก็ได้)
  2. Visual Studio Community 2019

สิ่งแรกที่ต้องทำคือการ comment code โดยเฉพาะในส่วน API Controller ที่เราต้องการทำ Spec โดยอย่างน้อยให้ใช้ comment syntax ที่ครอบคลุมข้อมูลดังต่อไปนี้

  1. คำอธิบาย
  2. request parameters
  3. responses
  4. ตัวอย่าง model

เตรียม XML Comments

API Controller

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// อัพเดตข้อมูลพยากรณ์อากาศ
/// </summary>
/// <param name="id">ID ชุดข้อมูลที่ต้องการแก้ไข</param>
/// <param name="weatherForecast">โมเดลของข้อมูลที่ต้องการแก้ไข</param>
/// <returns>HTTP Status Code</returns>
/// <response code="200">Successfully saved</response>
/// <response code="500">Bad Request</response>
[HttpPut]
public IActionResult Put(int id, WeatherForecast weatherForecast)
{

Model

1
2
3
4
5
6
7
8
9
10
11
12
13
public class WeatherForecast
{
/// <summary>
/// วันที่
/// </summary>
/// <example>3/1/2008 7:00:00</example>
public DateTime Date { get; set; }

/// <summary>
/// อุณหภูมิ องศาเซลเซียส
/// </summary>
/// <example>36.6</example>
public int TemperatureC { get; set; }

จากนั้นให้เตรียม export ไฟล์ XML Comment ทุกครั้งที่ทำการ build ดังนี้

export xml setting
สิ่งสำคัญคือต้อง set ให้หยุดเตือน error 1591 ด้วยนะคะ ไม่งั้นต้องไล่ใส่ XML Comment ทุกที่

Install Swashbuckle.AspNetCore + Redoc

1
2
Install-Package Swashbuckle.AspNetCore -Version 5.5.0
Install-Package Swashbuckle.AspNetCore.ReDoc -Version 5.5.0

Redoc: Up and running!

Swashbuckle ประกอบไปด้วยการทำงาน 3 ส่วน

swashbuckle structure
ซึ่งเราจะ replace SwaggerUI ด้วย Redoc

จะเริ่มใช้งาน Swagger ต้อง register service ให้กับ API container ของเราก่อนที่ Startup.cs

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

services.AddSwaggerGen();
}

จากนั้นสั่งให้ทำงานเมื่อ runtime จงสร้าง swagger.json ขึ้นมา

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseSwagger();
app.UseReDoc(config=> {
config.SpecUrl("/swagger/v1/swagger.json");
});

app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

โดยปกติคำสั่ง UseSwagger() จะทำให้ swagger.json เกิดขึ้นมาที่ default path ../swagger/v1/swagger.json แต่ Redoc มี default URI ของตัวเองที่ ../api-docs ดังนั้นจำเป็นต้อง config path ที่ถูกต้องที่ Redoc จะเข้าถึง swagger.json ได้ ไม่งั้น Something went wrong

ถึงตรงนี้ ถ้าเปิด browser เรียก localhost:port/api-docs ขึ้นมาดูจะเห็น API Document ขึ้นมาแบบเรียบๆ ซึ่งถ้าใช้งานกันภายใน ปล่อยไว้อย่างนี้ก็ถือว่าพอใช้การได้แล้วค่ะ แต่อย่างที่เกริ่นไว้ เราจะเอา comment ที่อุตส่าห์เตรียมไว้ทั่วโปรแกรมมาใช้งานด้วย เพราะงั้น เข้าสู่โซนปรับแต่ swagger.json กันค่ะ

swagger.json configuration

เป้าหมายของ blog นี้มีอย่างเดียวคือเอา xml comment มาใส่ ง่วงแล้วด้วยค่ะ สำหรับการปรับแต่งอื่นๆ เช่น ใส่ CSS, Authentication ปรับ index.html ขอละไว้โอกาสหน้า(ถ้ามี)

ยังจำได้ path XML ที่เรา export ไว้ได้เนอะ >> bin\redoc-lesson-2.xml

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

services.AddSwaggerGen(config=>{
var filepath = Path.Combine(AppContext.BaseDirectory, "redoc-lesson-2.xml");
config.IncludeXmlComments(filepath, includeControllerXmlComments: true);
});
}

run แล้วเข้าไปยลโฉมอีกครั้งจะได้เห็นอะไรประมาณนี้

redoc ui
แอบเอา logo ที่ออฟฟิศมาใช้ ไม่ได้ตั้งใจขายตรงจริงๆนะอย่ามองอย่างนั้น ส่วน logo ถ้าอยากใส่ให้ใช้ extension x-logo

จบตามเป้าหมาย ไว้พบกันใหม่บล็อคหน้าค่ะ

Start a jouney with Hexo | NodeJS + Hexo + Travis CI + GitHub Pages

มันเริ่มจากอารมณ์ที่ว่า “อยากจะมี Blog ที่ไม่ใช่ Wordpress/Medium” วันดีคืนดีอยากเปลี่ยนหน้าตาเองอย่างใจก็ทำได้ (แต่ทำมั้ยนี่อีกเรื่อง 5555) โดยมีเงื่อนไขสำคัญอยู่ 2 เรื่อง

“ฟรี และเป็น markup language”

แล้วจากการที่คุ้นเคยกับการทำ README.md อยู่บ้าง แล้วชอบความซิมเปิ้ลของมันเหลือเกิน เหมาะมาก สำหรับคนที่ code ได้แต่ไม่มีปัญญาเลือกคู่สีอย่างเรา เลยตัดสินใจว่ายังไงบล็อคนี้คงเขียนด้วย markup language ที่เป็น markdown (อย่าเพิ่งงง 555)

เพราะอยากจะเขียนไปเรื่อยๆ แบบไม่ต้องสนใจอะไร อยากใช้ visual code เขียนบล็อค สองเดือนหลังนี้มีอาการตาแห้ง ไม่สามารถจ้องแสงสีขาวของพื้นพลัง Medium ได้นานๆอีกต่อไป แต่ก็คงไม่ได้ทิ้ง Medium นะคะ เผลอๆ บล็อคนี้อาจทำมาเพื่อลองของอย่างเดียวก็เป็นได้ 555 (ล้อเล่นๆ) ไหนๆก็ไหนๆ ฝาก Medium ไว้ในอ้อมอกอ้อมใจอีกสักอัน

เกริ่นมานาน เข้าเรื่อง geekๆ ของเราดีกว่า


Stack ที่เลือกมาวันนี้ ตอนแรกว่าจะเล่น Jekyll แต่กลัวโลกไม่จำ เล่น Hexo ที่ไม่ค่อยมีคนใช้นี่ดีกว่าค่ะ 555

เพราะกำลังมองหา Blog Engine ที่รันบน NodeJS ได้ Deploy ง่าย และที่สำคัญคือมี Theme บอกแล้วว่ามันจำเป็นสำหรับคน code ได้แต่ลงสีไม่เป็น เอาละ งั้นเรามาเริ่มลงมือกันเลยดีกว่าค่ะ อยากมี blog ก็เริ่มที่ลง NodeJS ลงเสร็จก็ต่อด้วยพระเอกของเรา Hexo

Install Hexo

1
npm install hexo-cli -g

เพียงเท่านี้ก็ได้ Hexo cli มาใช้รันคำสั่งต่างๆของ Hexo งั้นลงมือสร้าง blog แรกกัน หา folder เหมาะๆ แล้วรัน

1
2
3
hexo init <folder>
cd <folder>
npm install

ถ้าเข้าไปดูด้านใน จะเห็นโครงสร้างของ blog ดังนี้

1
2
3
4
5
6
7
> node_modules <--- ก้อนนี้เดฟทุกคนรู้จักดี 5555 แต่ไม่ได้เกี่ยวข้องกับโครงสร้าง blog เราในวันนี้ค่ะ
> scaffolds <--- ที่เก็บ template ต่างๆของ post ที่เราสามารถสร้างได้ เราสร้างเพิ่มเองได้เช่นกัน
> source <--- ทุกโพส รวมทั้งรูปประกอบจะกองกันอยู่ในนี้
> themes <--- เปลี่ยน theme ได้ ทำ theme เองมาเพิ่มก็ได้
_config.yml <--- สิ่งอื่นๆที่เป็น configuration ของเว็บ blog และ meta data ของ blog เรา
.gitignore <--- คนใช้ git รู้กันดีว่ามีไว้เมินไฟล์ที่ไม่อยาก push ขึ้น remote
.package.json <--- มีไว้เก็บ version lib ต่างๆที่เราใช้

และถ้าเข้าไปดูใน source จะพบว่า Hexo ได้แถมโพสแรกมาให้เราแล้วในชื่อ hello-world.md เพราะฉะนั้นอย่ารอช้า รัน localhost ขึ้นมาดูหน้าตากัน

Run Hexo on localhost

1
hexo server

Hexo จะถูกรันขึ้นมาที่ localhost port 4000 จังหวะนี้เปิด browser ดูก็จะเห็น Hexo theme Landscape พร้อม post แรกปรากฎขึ้นมา

พอเห็นบน localhost ก็เริ่มอยากเห็นบน production 5555

อยากได้ url .dev .io บ้าง แต่โอ้โห 40-50 เหรียญต่อปี (นี่แค่ domain ยังมีค่า SSL อีก) เดือนนี้ช็อตจริงๆ คงต้องหาทางฟรีๆ ที่มี .io ให้เราไปก่อน ก็มาพบว่า GitHub มี feature GitHub Pages ที่ให้เดฟ Host scalable web ของตัวเองได้ เพียงแค่ deploy ขึ้นไปไว้ใน branch master ของ repository ที่ตั้งชื่อว่า username.github.io

Create new repository on github

  1. สร้าง repository ตั้งชื่อว่า username.github.io สำคัญมากที่ต้องตรงกับ username นะคะ
  2. push code ของเราขึ้นไปวางบน branch master ก่อนค่ะ มันจำเป็นต้องมี branch master เป็น branch แรก
1
2
3
4
5
git init
git add .
git commit -m "initial commit"
$ git remote add origin https://github.com/username/new_repo
$ git push -u origin master

ถ้าใครใช้ SSH ก็เป็น git@github.com:username/new_repo เอาแทนนะ

CI/CD to automatically deploy site when changes

ธรรมดาโลกไม่จำ งั้นเราต้องทำ CI/CD มาทั้งทีเอาให้สุดค่ะ 5555 เรามีแผนอย่างงี้ จะเอา CI tools ที่ขึ้นได้ไวๆ ฟรีๆ (ฟรีอีกแล้ว) มารันของจาก branch hexo_source พอ build เสร็จค่อยใส่กลับลง master ประหลาดกว่าการทำ CI ทั่วๆไปแต่มันเป็นสิ่งที่ github pages ต้องการ

ตอนแรกจะเลือก jenkins แต่การต้องตั้ง server + config อีกมหาศาลจะผิดหลักการฟรีและเร็วของเรา 555 กรรมเลยมาตกที่ travis CI ที่เป็นอีกเจ้าที่เปิดให้ CI open source ได้ฟรี แถมง่าย แน่นอนสำหรับ blog ของเราที่ไม่ต้องโหดร้ายแบบ api server เท่านี้ก็ถือว่าเพียงพอค่ะ

  1. ไปที่ github marketplace แล้วแอด Travis CI
  2. ไปที่ github setting > Application > Travis Configure
  3. ตรงนี้สามารถ config ให้เฉพาะ repo ที่เป็น blog ของเราก็ได้นะคะ จากนั้นก็ save จะถูก redirect ไป travis ให้ activate github account จะเริ่มเชื่อม webhook กันด้วยการเอา token จาก github ไปใส่ เพราะงั้นเราจะไป github ก่อน
  4. ไปที่ github setting > Developer settings > Personal access tokens ตรงนี้เราจะสร้าง token เพื่อให้ travis สามารถทำงานแทนเราในการ push/pull ภายใน repo ที่เรากำหนดได้ ด้วยการ generate new token ใส่ชื่อกันลืมเป็น travis.ci ใส่ลิทธิ์ในการดับ repo ก็พอค่ะ ถ้างงก็ดูรูป

  1. จบขั้นนี้ได้ token มา 1 ชุด กลับไปหา travis ci ไปที่ repo’s setting ในเครื่องหมาย hamburger > Environment Variables ตั้งชื่อ GH_TOKEN แล้วเอา token ใส่ลง value กด Add เป็นอันจบ
  2. สร้างไฟล์ .travis.yml ไว้ในระดับเดียวกับ _config.yml เพื่อทำหน้าควบคุม flow CI/CD ของเรา ใส่ code ชุดนี้ลงไป
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sudo: false
language: node_js
node_js:
- 10
cache: npm
branches:
only:
- hexo-source
script:
- hexo generate
deploy:
provider: pages
skip-cleanup: true
github-token: $GH_TOKEN
keep-history: true
target_branch: master
on:
branch: hexo-source
local-dir: public

code ข้างบนบอกว่า จับตาดู branch ที่ชื่อ hexo-source ถ้ามีการเปลี่ยนแปลงเข้ามาให้รันคำสั่ง

1
hexo generate

อันเป็นคำสั่งของการสร้าง file HTML/CSS/JS ตามที่เว็บ blog เราต้องการจากบรรดาไฟล์ .md ทั้งหลายที่เราส่งขึ้นไปให้โดยใช้ NodeJS v.10 เป็น build engine ผลพวงจากการ build ให้นำไป deploy ลงใน branch master โดยใช้ GH_TOKEN เป็นใบผ่านทางนั่นเองค่ะ

  1. สร้าง branch ใหม่ชื่อ hexo-source และ push ขึ้นไป
  2. นั่งยิ้มดู travis ci ทำงาน
  3. พอเสร็จแล้วให้ไปที่ Github repo setting ค่ะ เวรกรรมเรายังไม่จบ ไปเช็คก่อนค่ะว่า GitHub Pages เรามีตัวตนขึ้นมาแล้ว

  1. กดลิงค์ไปดูเว็บ blog ของตัวเองได้เลยค่ะ

หลังจากนี้เมื่อเราสร้าง blog ใหม่แล้ว commit+push ขึ้นไป Travis CI ก็จะทำการ build .md ไฟล์ให้โดยอัตโนมัติ


การเขียนบล็อคมันโหดมากจริงๆค่ะ ทั้งหมดนี่ทำไปแค่ 3 ชม. แต่เขียนบล็อคนี่คือ เขียนมาตั้งแต่วันอาทิตย์ 555555 โห อยากจะร้อง เอาเป็นว่าไว้ครั้งหน้ามีอะไรสนุกๆจะมาแชร์ใหม่ ไว้เจอกัน!!!