Deploy แอพ Ruby on Rails บน Google App Engine

ในโพสนี้เราจะทำการ deploy แอพ Ruby on Rails ซึ่งเป็นแอพตัวอย่างคล้ายๆ กับ hello world ไปยัง Google App Engine ผมขอเรียกสั้นๆ ว่า App Engine แล้วกันนะครับ

รู้จัก Google App Engine

App Engine เป็นหนึ่งใน Google Cloud Platform (GCP) โดย App Engine เป็นบริการ serverless platform โดย serverless ในที่นี้ไม่ได้หมายความว่าไม่มี server ความหมายคือยังคงมี server อยู่แต่เราไม่ต้องมานั่งเตรียมหรือคอนฟิก server อีกต่อไป เราแค่บอก App Engine ว่าเราจะเอาแอพอะไรไปรันเช่น แอพของ Ruby on Rails, NodeJs หรือ PHP เป็นต้น App Engine จะจัดการเตรียมการเรื่องของการเซตอัพ server ที่แอพนั้นต้องการทั้งหมดให้เองโดยอัตโนมัติ ที่สำคัญคือมันสามารถขยายขนาดของ resource เพื่อรองรับการใช้งานแอพที่มากขึ้นได้โดยอัตโนมัติ (automatically scale) โดยที่เราไม่ต้องกังวลว่าจะต้องเซต server ยังไงให้รองรับการเติบโตที่เกิดขึ้น App Engine จัดการให้หมด

ผมมองว่าบริการ App Engine หรือ serverless ช่วยลดภาระการทำงานของเราได้เยอะมาก อย่างผมเองจะเสียเวลานานและเจอความลำบากทุกครั้งที่ต้องเซตอัพ VPS หรือ server เพื่อให้สามารถรันเว็บแอพได้ ขนาดทำอยู่บ่อยๆ ก็ยังไม่ชินและไม่ค่อยโอเคอยู่ดี เพราะผมชอบและถนัดที่จะเขียนโค้ดและทำงานในส่วนของแอพมากกว่า ที่จะโฟกัสเรื่องของ server

ข้อดีอีกอย่างของ serverless อย่าง App Engine ก็คือ เราจ่ายตังค์ค่าบริการตามการใช้งานจริง App Engine จะคำนวณค่าใช้จ่ายจากบริการการรันงานของ Compute และ Storage ของแอพที่ถูกใช้จริง ดูรายละเอียดได้จาก ค่าใช้จ่ายและการคำนวณค่าใช่จ่ายของ Google Cloud Platform

สร้าง Project ใหม่ บน Google Cloud Platform

ก่อนอื่นเราจะต้องสมัครบริการ Google Cloud Platform ซึ่งตอนนี้ (12 May 2019) มี promotion ทดลองใช้ฟรี 12 เดือนอยู่ เข้าไปสมัครกันได้ที่หน้า Sign up ของ Google Cloud Platform เลยครับ

พอสมัครเสร็จแล้วให้เข้ามาที่หน้า Dashboard ของตัวเอง ซึ่งมีหน้าตาประมาณนี้ หน้า Google Cloud Console

สร้างโปรเจคใหม่โดยคลิ๊กที่ปุ่มดร็อปดาวน์ MyFirstProject ตรงเมนูบาร์ด้านบนแล้วกดเลือก New Project ตรงปุ่มขวาบน จะปรากฏหน้าต่างสำหรับใส่ข้อมูลของโปรเจคใหม่ ก็ให้ใส่ชื่อของ project ลงไป จากตัวอย่างโปรเจคใหม่ของผมชื่อ hello-rails จากนั้นกดปุ่ม CREATE สร้างโปรเจคใหม่บน Google Cloud Platform

ชื่อโปรเจคปรากฏบนเมนู ในหน้า Google Cloud Console

เราจะเห็นชื่อโปรเจคที่เราสร้างขึ้นอยู่บนดร็อปดาวน์เมนู ให้เลือกโปรเจค hello-rails

ดาวโหลดและติดตั้ง Cloud SDK

เพื่อให้สามารถติดต่อกับโปรเจคของเราที่อยู่บน GCP ได้นั้น เราจะต้องดาวโหลดและติดตั้ง Cloud SDK ซึ่งจะประกอบไปด้วยไลบรารี่และ command line tool ที่เราจะใช้คุยระหว่างเครื่อง local ของเรากับโปรเจคที่อยู่บน GCP ต่อไป เราจะติดตั้ง Cloud SDK ตามข้อมูลในหน้าติดตั้ง Cloud SDK นี้ โดยให้ดาวโหลด Cloud SDK Installer จากหน้าเพจหรือจากลิ้งในหน้านี้ พอดาวโหลดมาแล้วก็ให้คลิ๊กเพื่อติดตั้งเลย ขนาดของไฟล์ที่จะดาวโหลดประมาณ 90mb รวมเวลาติดตั้งด้วยแล้วจะใช้เวลาสักพักนึง ชงกาแฟรอได้เลยครับ

ติดตั้ง Google Cloud SDK

ในขั้นตอนสุดท้ายของการติดตั้ง พอกดปุ่ม Finish มันจะทำการเปิด Google Cloud SDK Shell แล้วรันคำสั่ง gcloud init บน shell ให้เราเพื่อตั้งค่าเริ่มต้นของ cloud SDK ระหว่างนี้คุณจะถูกถามว่าจะคอนฟิกค่าเริ่มต้น(default) สำหรับ Compute region and zone หรือไม่ ให้ตอบเป็น Y แล้วให้เลือก region ที่เหมาะสม โดยในที่นี้ผมเลือกเป็น asia-southeast1-a

หลังจากตั้งค่าเสร็จ หน้า shell จะให้เรา login ไปยัง google account เพื่อทำการ authenticate สำหรับการใช้งาน gcloud command line interface ให้เราตอบ Y แล้วกด enter shell จะเปิดเบราเซอร์แล้วให้เราดำเนินการ login ต่อไป จากนั้น shell จะให้เราเลือก project ที่เราจะทำงานด้วยก็ให้เลือกเป็น hello-rails

จากนี้ผมขอเรียก Google Cloud SDK shell สั้นๆ ว่า Cloud shell ละกันนะครับ ความพิเศษของ Cloud shell ที่เราดาวโหลดมาก็คือมันจะมี ruby และ rails ติดตั้งมาพร้อมกับ shell นั้นเรียบร้อยแล้ว สำหรับ Cloud shell ที่ผมโหลดมานี้จะเป็น ruby เวอร์ชั่น 2.5.5p157 และ Rails 5.2.3 เราสามารถเช็คได้โดยใช้คำสั่ง ruby -v และ rails -v บน Cloud shell ครับ

สร้าง Rails แอพ

เราจะสร้าง Rails แอพ ที่เครื่อง local โดยในที่นี้เครื่อง local ของผมเป็น windows และผมถือว่าคุณลง Ruby และ Rails ไปเรียบร้อยแล้วนะครับ เปิด console windows ขึ้นมาแล้ว พิมพ์คำสั่งด้านล่างเพื่อสร้างแอพชื่อ hellorails

$ rails new hellorails

เสร็จแล้ว cd เข้าไปในโฟลเดอร์ hellorails ผมขอตรวจสอบดูก่อนว่า Rails แอพ ของเรารันได้หรือไม่ โดยเปิดหน้าจอ console windows ขึ้นมาอีกอันแล้วรัน $ rails server จากนั้นเปิด browser ขึ้นมาแล้วเข้าไปที่ localhost:3000 เราควรจะได้ผลลัพธ์เป็นหน้าจอ welcome ของ Rails แบบนี้

หน้า welcome page ของ Rails

โอเค เราจะเริ่มสร้างแอพตัวอย่างในแบบของเราดูบ้าง เริ่มจากหน้า landing โดยให้สร้าง PagesController ขึ้นมาก่อน โดยพิมพ์คำสั่งต่อไปนี้

$ rails g controller Pages

Rails จะสร้าง PagesController ขึ้นมาในไฟล์ app/controllers/pages_controller.rb ให้เปิดไฟล์นี้ขึ้นมาเพื่อเพิ่มแอคชั่นชื่อ home เข้าไป

class PagesController < ApplicationController
  def home
  end
end

แก้ไขไฟล์ config/routes.rb เพื่อบอก Rails ว่าจะให้ url path /home วิ่งไปที่ไหน ซึ่งเราจะให้มันวิ่งไปที่แอคชั่น home ของ PagesController และเซตค่า root url ใหม่เป็นที่เดียวกับ pages#home

Rails.application.routes.draw do
  get '/home', to: 'pages#home'
  root 'pages#home'
end

จากนั้นสร้างไฟล์ app/views/pages/home.html.erb ขึ้นมา โดยมีโค้ดหน้าตาแบบนี้

<div class="home">
  <h1>Deploy Ruby on Rails application to Google App Engine</h1>
  <p>by <a href="https://devwoot.com">Devwoot.com</a></p>
</div>

ผมขอปรับหน้าตาของ home สักเล็กน้อย โดยการแก้ไขโค้ด scss ในไฟล์ assets/stylesheets/pages.scss ด้านล่างนี้

html { height: 100vh; }
body {
  height: 100%;
  margin: 0;
  background: darkslateblue;
}
h1 {
  font-size: 3rem;
  color: white;
  margin: 0 0 1rem;
}
p {
  font-size: 1.25rem;
  color: hotpink;
}
a { color: inherit; }

.home {
  max-width: 568px;
  text-align: center;
  margin: 0 auto;
  padding: 0 1rem;
  padding-top: calc(50vh - 8rem);
}

ลองใส่ url เป็น localhost:3000/home ลงในเบราเซอร์ เราจะได้ผลลัพธ์ดังรูป

หน้า Home ของแอพ Ruby on Rails ตัวอย่าง

Deploy แอพ Rails ไปยัง App Engine ของ Google Cloud Platform

ในการ deploy แอพไปยัง App Engine เราจะต้องระบุคอนฟิกของแอพที่จะ deploy ให้ App Engine รู้ เช่น จะ deploy อย่างไร จะให้ App Engine รันคำสั่งอะไรในการ deploy, deploy ภายใต้ runtime อะไร เป็นต้น ให้สร้างไฟล์ที่ชื่อ app.yaml ตรง root directory ของ Rails แอพ โดยระบรายละเอียดในไฟล์ดังนี้

entrypoint: bundle exec rackup --port 8080
env: flex
runtime: ruby

การ deploy ที่เรากำลังทำต่อไปก็คือการรัน Rails แอพในโหมด production ซึ่งจะต้องระบุค่าของ env_variables ในไฟล์ app.yaml เพื่อบอก App Engine ว่าตัวแปร environment ที่จะใช้ในโหมด production ของ Rails แอพ นั้นคืออะไรและมีค่าเป็นอะไร ซึ่งตัวแปรที่เราจะต้องเซตค่าคือ SECRET_KEY_BASE เราจะ generate ค่าของ secret key ของแอพโดยการรันคำสั่ง $ bundle exec rails secret เราจะได้ผลลัพธ์เป็นค่าของ secret key เป็นสตริงยาวๆ ให้ copy ค่านี้ เพื่อเอาไปใส่ในไฟล์ app.yaml ในส่วนของตัวแปล SECRET_KEY_BASE ไฟล์ app.yaml ของเราจะมีหน้าตาประมาณนี้

entrypoint: bundle exec rackup --port 8080
env: flex
runtime: ruby
env_variables:
  SECRET_KEY_BASE: ค่าของ-secret-key-ที่-copy-มา

จากนั้นทำการรัน asset precompile ที่เครื่อง local

$ bundle exec rails assets:precompile RAILS_ENV=production

application-xxx.js และ application-xxx.css ที่คอมไฟล์แล้วที่ไดเรกทอรี่ /public/assets โดย xxx เป็นเลข MD5 hash

ทำการ deploy ด้วยคำสั่ง

$ gcloud deploy

Google App Engine จะเริ่มดำเนินการสร้างแอพซึ่งมันจะขึ้นรายการสรุปการ deploy ให้เราคอนเฟิมอีกครั้งก็ให้ตอบ Y (น่าจะเป็นการคอนเฟิมเพื่อเริ่มเก็บตังหรือป่าว :)) จากนั้น App Engine ก็จะเริ่มขั้นตอนการ deploy จริง ซึ่งจะใช้เวลาซักพัก ระหว่างนี้ถ้ามีข้อผิดพลาดเกิดขึ้น App Engine จะฟ้องมา ซึ่งเราสามารถเข้าไปตรวจสอบ log ได้จากลิ้งที่ App Engine แจ้งทางหน้า console เลย ซึ่ง error ที่ผมเจอมันขึ้นว่า

Step #1: Your bundle only supports platforms ["x64-mingw32"] but your local platforms are
Step #1: ["ruby", "x86_64-linux"], and there's no compatible match between those two
...
ERROR: build step 1 "gcr.io/cloud-builders/docker@sha256:76a520ab60dc0d84b128377baf320d61fe74717a39e1dc1c03db6085e2d73ed2" failed: exit status 16

เนื่องจากผมสร้าง Rails app จาก Windows 10 ดังนั้น ผมทำการแก้ไขโดยรันคำสั่งด้านล่างเพื่อเพิ่ม platform ruby เข้าไปใน Gemfile.lock

$ bundle lock --add-platform ruby

แล้วรันคำสั่ง $ gcloud deploy อีกครั้ง คราวนี้ไม่มี error ขึ้นแล้ว หลังรันอยู่ซักพักใหญ่ (ช่วงนี้แบรกไปชงกาแฟอีกแก้วรอได้เลย มีทำการ update service เองด้วย จังหวะนี้ใช้เวลาอยู่นาน 10 กว่านาทีเลย)

พอ Deploy เสร็จแล้ว เราจะสามารถเข้าไปดูแอพได้โดยเปิดเบราเซอร์แล้วใส่ url เป็น https://หมายเลข-project-id.appspot.com/ ซึ่งจากโปรเจคตัวอย่างนี้จะเป็น https://hellorails-240407.appspot.com เราจะเห็นแอพของเรารันออนไลน์ได้เหมือนที่รันจากเครื่อง local เลย

Note ถ้าคุณรันโปรเจคด้วย gcloud ต่อ คำสั่งพื้นฐานของ gcloud ที่ควรจะรู้ไว้คือ gcloud info ใช้ดูสถานะและค่าทั่วไปของโปรเจคปัจจุบัน

$ gcloud config set project <project_id> ใช้เลือกหรือเปลี่ยน project ที่ต้องการให้เป็นโปรเจคที่มีสถานะ active เป็นโปรเจคงานปัจจุบัน

$ gcloud projects list ใช้เรียกดูรายการโปรเจคทั้งหมด รวมทั้งค่า project_id ของแต่ละโปรเจค

สรุป

จะเห็นว่าการ deploy แอพ Rails ขึ้นไปใช้งานจริงบน App Engine นั้น ไม่ยากเลย เอาจริงๆ คือแค่พิมพ์คำสั่งเพียงไม่กี่คำสั่งเท่านั้น เราแทบไม่ต้องใช้เวลาไปกับ การลง Ruby ลง package manager หรือคอนฟิกเว็บเซอร์เวอร์อย่าง Apache หรือ Nginx ไม่ต้องยุ่งกับการติดตั้งแอพลิเชั่นเซอร์เวอร์เลยด้วย ทำให้เราโฟกัสกับการเขียนโค้ดให้กับแอพได้อย่างมั่นใจจริงๆ

อย่างไรก็ตามยังมีจุดที่ผมคิดว่ายังต้องปรับปรุงอีกคือเรื่องของ documents บนเว็บไซต์ของ google cloud พบว่าไม่ค่อยตรงกับการใช้งานในปัจจุบันเท่าไหร่ การ deploy แต่ละครั้งก็ใช้เวลานานพอสมควร และการปรับแต่งค่า resource ต่างๆ ยังทำได้จำกัดอยู่ คนที่ชอบ customize อาจจะชอบ VPS มากกว่า

โดยรวมแล้วผมว่า App Engine เป็นตัวเลือกที่ดีมากๆ สำหรับคนที่เริ่มเรียนรู้ หรือเพิ่งเริ่มเขียนแอพ Rails ที่ต้องการจะลอง deploy งานของตัวเองไปยัง production ครับ

แชร์โพสได้จากลิ้งด้านล่าง ขอบคุณครับ