"จงให้แล้วเจ้าจะได้รับ"
บันทึกการย้ายโปรดักชั่นเข้า Google Cloud Run กับค่าใช้จ่ายที่ลดลงหลัก 100 เท่า
26 Aug 2019 02:59   [43051 views]

เดือนที่ผ่านมามีโอกาสย้ายงานจำนวนมากเข้า Google Cloud Run ซึ่งถึงแม้มันจะยังเป็น Beta อยู่ แต่ความสามารถของมันก็อยู่ในระดับดีพอแล้วที่จะใช้เป็นโปรดักชั่นได้ (สำหรับงานที่เหมาะสมกับมัน)

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

Cloud Run คืออะไร

ก่อนอื่นก็ขออธิบายสั้น ๆ ก่อนว่า Cloud Run คืออะไร

Cloud Run เป็น Compute Platform แบบ Serverless ที่สามารถสเกลขึ้นลงโดยอัตโนมัติได้ตามจำนวนทราฟฟิกที่เข้ามา ซึ่งแน่นอน การที่จะสเกลขึ้นลงอัตโนมัติแบบนี้ได้ ตัว Service ที่เราเอาขึ้นไปทำงานจึงต้องออกแบบให้เป็น Stateless ด้วยเช่นกัน

เอาจริง ๆ มันก็คือ PaaS คล้ายกับพวก Heroku, Google App Engine นั่นแหละ สามารถ Deploy ได้ด้วยการสั่งบรรทัดเดียวแล้วก็จะมี Server โผล่มาให้ใช้งานเลยทันที เพียงแต่มีอะไรต่างกันไปในรายละเอียดหลายอย่างรวมไปถึงความสามารถในการสเกลอย่างชาญฉลาดและราคาที่เซ็กซี่มากกกก ก็จะพามาดูกันในบล็อกนี้ 

ฮาร์ดแวร์ของ Cloud Run Instance

สำหรับเครื่องที่ถูกสร้างขึ้นมาเราจะเรียกว่า Instance จะมี Hardware ตามนี้

- CPU Intel รุ่นใหม่ ๆ ไม่เกิน 3 ปี แต่ถูก Throttle ไว้ที่ 1 GHz (ไม่มีสเปคบอก อันนี้ทดลองเอง) โดยจะรันในรูปแบบ vCPU ไม่ใช่ของ CPU มาทั้งตัว

- แรมเลือกได้ห้าขนาด 128MB, 256MB, 512MB, 1GB และ 2GB

สำหรับ CPU เราจะเลือกอะไรไม่ได้เลย แต่ RAM จะเลือกได้และราคาก็จะจ่ายตามขนาดที่เราจองไว้ หากมีการสเกลเครื่องขึ้น เครื่องใหม่ก็จะมี Configuration เดียวกันทั้งหมดทุก Instance และจะถูก Route เข้า Load Balancer โดยอัตโนมัติโดยที่เราไม่ต้องทำอะไรเลย 

วิธีการ Deploy

จุดเด่นของ Cloud Run คือ

เราจะเอาไปรันอะไรบนนั้นก็ได้ ขอแค่ห่อมันเป็น Docker Image ได้ก็พอ

ก็คล้ายกับ Google App Engine แบบ Flexible นั่นแหละ ทำให้ข้อจำกัดของงานที่ Deploy บนนั้นได้จึงต่ำมาก จะเขียนด้วยภาษาอะไรก็ได้ จะรันกี่โปรเซสก็ได้ อยากทำอะไรทำแล้วก็ห่อเข้า Docker Image แล้วเอาขึ้นไปเก็บไว้บน gcr.io ซึ่งเป็น Private Docker Repository ของ Google Cloud Platform โดยวิธีก็เหมือนการเอาขึ้นไปเก็บไว้บน Docker Hub นั่นแหละ แค่ติด tag ให้ Docker Image ที่เอาสร้างขึ้นมาซึ่งจะอยู่ในฟอร์แมต

gcr.io/<GCP_PROJECT_ID>/<IMAGE_NAME>

ซึ่งก็ทำด้วยคำสั่ง docker push ได้เลย แล้วตอน Deploy ก็แค่เรียกคำสั่ง gsutil ประมาณว่า

gcloud beta run deploy --image gcr.io/<IMAGE_NAME> \
    --project <GCP_PROJECT_ID> \
    --region us-central1 \
    --memory 2Gi \
    --timeout 600 \
    --concurrency 1 \
    CLOUD_RUN_INSTANCE_ID

จากนั้น Google Cloud ก็จะสร้าง Cloud Run Instance ขึ้นมาโดยใช้ Docker Image ที่กำหนด ง่าย ๆ แค่นี้เลย เรียบร้อย !

เปิดพอร์ต 8080 เพื่อรับรีเควสต์

ก็อาจจะสงสัยว่าแล้วเราจะเปิดพอร์ตให้คนติดต่อเข้ามายัง Instance ที่รันขึ้นได้ยังไง จริง ๆ ก็แค่เปิดพอร์ต 8080 ขึ้นมา แล้วตัว Cloud Run จะ Route เข้าพอร์ต 80 ให้ทันที ก็คือสามารถเข้าด้วย URL ปกติที่ระบบสร้างให้ได้เลย ไม่ต้องกำหนดพอร์ต เพียงแต่ภายใน Docker Container ที่รันขึ้นมาจะเป็นเซอร์วิสที่ Listen ไว้ที่พอร์ต 8080

โดเมนเข้าถึง Service ที่รันขึ้นมา

สำหรับ Cloud Run Instance ที่สร้างขึ้นมาจะมีโดเมนให้ใช้งานทันทีโดยอยู่ในรูปแบบ

*.run.app

ก็สามารถเรียกได้ตรง ๆ เลย หาก Traffic เยอะกว่าที่ Instance ที่เปิดไว้รับไหว Instance ใหม่ก็จะถูกสร้าง (Spawn) ขึ้นมาเพื่อรับ Traffic เพิ่มได้ทันที

ข้อจำกัด

Cloud Run เป็นอะไรที่เจ๋งมากถ้าระบบที่ทำมาเป็น Stateless แต่ก็ยังมีข้อจำกัดบางอย่างที่ทำให้ไม่ใช่ทุกอย่างจะไปรันบนนั้นได้ ซึ่งก็มีตามนี้

- CPU สูงสุดได้แค่ 1GHz

- RAM จำกัดแค่ 2GB ไม่มี Swap ถ้าใช้แรมเกินที่กำหนดไว้จะแครชทันที

- Request Timeout มากสุด 15 นาที

- Maximum Concurrency อยู่ที่ 80 รีเควสท์

- จำนวน Instance ที่รันได้มากสุดอยู่ที่ 1,000 instances

- Request size ใหญ่สุด 32MB

- Respond size ใหญ่สุด 32MB

- ระบบ File System เป็นแบบ In-Memory ถือว่าเป็นการเขียนลงแรม รวมอยู่ใน Memory ที่จำกัดไว้ใน Instance ด้วย (ถ้าใช้เยอะก็แครช)

- ไม่มี GPU ให้ใช้

โดยรวมก็ถือว่าเหมาะกับงานจำนวนมากในโลกนะ แต่สำหรับงานเฉพาะทางบางอย่างก็อาจจะเปิด Cloud Run ไม่ได้ ต้องไปใช้ Compute Engine เต็ม ๆ อยู่ดี

แต่อย่างเราเอา AI (Tensorflow, PyTorch) ไปรันบน Cloud Run ก็ได้อยู่นะ แค่ติดว่าช้านิดโหน่ยยย

ความเร็วในการเพิ่มเครื่อง (Cold Start)

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

1) Instance Creation: การสร้างเครื่องใหม่ทำได้เร็วมากระดับ Instant อย่างมากก็ไม่เกิน 3 วินาที ต่อให้ Docker Image ใหญ่แค่ไหนก็ไม่ใช่ปัญหา แต่ความเร็วอันนี้เราควบคุมไม่ได้นะ

2) Docker Initialization: คือเวลาที่ใช้ในการสตาร์ท Docker Container ก็ขึ้นอยู่กับเราเตรียม Docker Image มาดีแค่ไหน ถ้าเบา ๆ ก็คือ 1 วินาทีได้เลย

สรุป ถ้าเป็นอิมเมจเบา ๆ ก็คือแทบจะ Instant เลยนั่นแหละ ดังนั้นถือว่าระบบทำมาได้ชิวมากครับ แต่ถ้าอิมเมจทำมาหนัก กว่าจะเริ่มรันได้ก็อาจจะเป็นสิบวินาทีได้ ก็ต้องออกแบบระบบมาดี ๆ ครับ ไม่งั้นก็จะไม่เหมาะกับการทำงานบน Cloud Run ได้

สำหรับการเรียกใช้งานครั้งแรกก็จะช้าหน่อยเพราะต้องรอ Cold Start แต่หลังจากนั้นก็จะเร็วแล้วเพราะเครื่องมันขึ้นมาแล้ว และก็จะช้าอีกครั้งถ้ามีการเพิ่มเครื่องขึ้นมา

เครื่องที่ Warm แล้วจะอยู่ต่ออีกหลายนาที

สำหรับเครื่องที่ถูกรันขึ้นมาแล้วจะอยู่ต่อไปอีกหลายนาทีก่อนจะถูกทำลายทิ้ง หรือถ้ามีรีเควสต์เข้ามาเรื่อย ๆ เครื่องก็จะไม่หายไปไหน (เรียกว่าเครื่องที่ Warm ไว้แล้ว)

และเทคนิคนึงที่ทำได้กันคือการยิง Request แบบง่าย ๆ (เช่น Ping) ทุก 5 นาทีเพื่อ Warm เครื่องตลอดไป แล้วมันจะเร็วตลอดเวลาครับ

Cold Start ทำให้เกิด Downtime ระหว่างการ Deploy ตัวใหม่ได้

ข้อเสียนึงที่ทำให้อึดอัดเล็กน้อยและคิดว่าน่าจะปรับปรุงได้ในอนาคตคือ หาก Cold Start นานหน่อย เว็บก็จะเกิด Downtime ตอน Deploy ได้ อนาคตก็แอบหวังว่าจะมีดีเลย์ก่อนเริ่มสลับ Routing ได้ ก็น่าจะ Zero Downtime ได้ครับ

คิดค่าใช้จ่ายตามจริงในหน่วย 100ms

สิ่งที่ชอบมากกกกของ Cloud Run คือ "มันคิดราคาตามจริงเป็น 0.1 วินาที" โดยคิดเฉพาะตอนที่ยังมี Request เรียกอยู่ ถ้าหลาย ๆ Request เข้าพร้อมกันก็ไม่ได้คิดเบิ้ลด้วย คิดตาม "เวลา" ที่เครื่องนั้น ๆ มันรันเลยเท่านั้นตามรูป

ซึ่งเวลาที่คิดก็คิดในหลัก 100ms ส่วนที่น้อยกว่า 100ms ถือว่าคิดเป็น 100ms ไป ซึ่งมันถือว่าแฟร์มากกกกก เพราะอย่างบริการอื่นนี่คือคิดตามจริงเป็นหลักนาที ใช้วิเดียวก็คิดเป็นนาทีอะไรงี้ ใช้ Cloud Run แล้วรู้สึกสบายใจกับ Billing สุด ๆ

(ก็ต้อง Note ไว้หน่อยนึงว่าตอนไม่มี Request ตัว CPU จะลดเหลือ 0 Hz เลย ดังนั้นอย่าทำอะไรที่รันใน Background โดยไม่อิงกับรีเควสต์ อันนั้นจะทำงานไม่ได้)

สำหรับราคาก็จะคิดตามตารางด้านล่างนี้ ฟรี 180,000 วินาทีแรกสำหรับ CPU และ 360,000 GB แรกสำหรับแรม (คูณตามเวลาเอา) ส่วนที่เกินมาก็แค่วินาทีละ 0.00073 บาทต่อ CPU-s และ 0.000076 บาทต่อ GB-s เท่าน้านนนน คือถูกมากกกกกก

นี่เลยทำให้ระบบที่ Deploy ไปจ่ายน้อยมาก ๆ ถูกลงกว่าตอนอยู่บน GCE เป็นร้อยเท่าเลย โหะ ๆ ๆ ๆ แฮปปี้ !

การดู Log

เรื่อง Log ก็ไม่ใช่ปัญหา ถึงแม้เราจะไม่สามารถ ssh เข้าไปดูอะไรได้ แต่ Log ทั้งหมด (stdout, stderr) ก็จะถูกพ่นมาที่ Logging ของ Google Cloud Console ให้เราดูได้แบบ(เกือบจะ) Real Time เลย

ก็ถ้ามีปัญหาอะไรก็กดดูง่าย ๆ เลยครับ

Cloud Run เหมาะกับอะไร

คือจริง ๆ อะไรที่เป็น Stateless และทำงานในข้อจำกัดที่ระบุไว้ด้านบนได้ก็เหมาะกับ Cloud Run หมดแหละ แต่ถ้าให้ Scope ลงมาหน่อย จากที่ลอง ๆ มาดู เราว่าสองงานนี้เหมาะกับ Cloud Run มาก

1) งานที่มี Request เข้ามาไม่เยอะ การเปิด Cloud Run นี่คือแทบจะฟรีเลยนะ (หรืออาจจะฟรีเลย)

2) งานที่ต้องใช้ Computation Time นานหลายวินาที คือถ้าเปิด Server เองมันจะมีคอขวดที่ Concurrent Connection จนไม่สามารถรับ Request ใหม่ได้ แต่กับ Cloud Run มันจะเพิ่มเครื่องให้อัตโนมัติจึงไม่ใช่ปัญหาอีกต่อไป แถม Timeout ยังนานตั้ง 15 นาทีอีกต่างหาก (อย่างงานของเรา เราเอา AI ไปรันบนนั้นเยอะมาก สเกลสบายเลย)

3) งาน Stateless ที่ต้องการสเกลเยอะ ๆ ยิ่งถ้าไม่ยุ่งกับ Environment อื่นนอกจากตัวมันเองเลยนะ ตัว Cloud Run จะช่วยได้มาก

บางทีไม่จำเป็นต้องเอางานทั้งก้อนไปรันบนนั้นก็ได้ แต่หากมีส่วนไหนที่แยกออกไปได้แล้วไปแปะบน Cloud Run แทน บางทีระบบก็จะสเกลได้อย่างง่ายดายเลยหละ

Cloud Run ไม่เหมาะกับอะไร

อย่างที่บอกแต่ต้น Cloud Run เกิดมาเพื่องานแบบ Stateless เท่านั้น หากเว็บไหนทำมาเป็น Stateful ก็เอามารันบน Cloud Run ไม่ได้นาจา เพราะข้อมูลที่เขียนลงไปจะหายหมดดดด ต้องทำ Database แยกออกมาวางไว้บนพวก Stateful Infra เช่น Compute Engine แล้วใช้ Cloud Run Instance ต่อเข้าไปถึงจะใช้งานได้

แต่พวกเว็บต่าง ๆ ที่ไม่ได้ออกแบบมาเป็น Stateless อย่างชัดเจน Cloud Run ก็คงไม่ได้เกิดมาเพื่องานนั้น ๆ แล

สรุป

ตอนนี้ Cloud Run ก็ถือว่าพร้อมแล้วนะสำหรับการใช้งานแบบ Production แต่ก็ไม่ใช่สำหรับทุกงานอยู่ดี ที่สำคัญ ๆ เลยคือต้องเป็น Stateless และ Start time ต่ำ ใช้แรมน้อย ใช้พลังประมวลผลไม่เยอะ บริการนั้น ๆ ก็จะใช้บน Cloud Run ได้อย่างสวยงามละ

ก็ลองดูกันน้าาาา เวอร์ค ๆ นี่ Deploy งานไปบนนั้น 7 ตัวละ =D

บทความที่เกี่ยวข้อง

Jun 16, 2019, 13:16
31911 views
Divide and Conquer เทคนิคแก้ปัญหาซับซ้อนด้วยการแบ่งเป็นปัญหาย่อย ๆ แล้วจัดการสอยมันให้ร่วงทีละอย่าง
Apr 9, 2020, 20:12
60332 views
สัมภาษณ์ทีมพัฒนาแอป "หมอชนะ" บันทึกการเดินทาง วิเคราะห์ความเสี่ยง COVID-19 ให้โดยอัตโนมัติ
0 Comment(s)
Loading