Setup Cloud SQL for Postgres สำหรับแอพ Rails ที่รันบน App Engine (Deploy แอพ Ruby on Rails บน Google Cloud ภาค 2)

ก่อนหน้านี้เราเคยทดลอง deploy แอพ Rails บน App Engine ไปแล้ว โดยแอพที่เรา deploy เป็นเพียงแค่แอพตัวอย่างที่แสดงแค่ static page เพื่อโชว์หน้า landing เท่านั้น

คราวนี้เราจะลองเพิ่มฟังชั่นในการสร้าง, แก้ไข และลบบทความ ให้กับแอพ ซึ่งเราจะทำการ setup database เพื่อรองรับกระบวนการสร้าง, แก้ไข และลบข้อมูลพื้นฐาน หรือที่เรียกกันว่ากระบวนการ CRUD (Create, Read, Update and Delete) โดยเราจะใช้ฟีเจอร์ของ Google Cloud Platform ที่เกี่ยวกับการจัดการฐานข้อมูลที่จะนำมาใช้กับ App Engine ที่ชื่อ Cloud SQL for Postgres

สร้างโปรเจคใหม่สำหรับ App Engine

ก่อนอื่นเราจะทำการสร้างแอพ Rails ขึ้นมารันบน App Engine ก่อน เหมือนกับที่เราเคยทำให้ดูในโพส Deploy แอพ Ruby on Rails บน Google App Engine ก่อนหน้านี้ คราวนี้เราตั้งชื่อโปรเจคว่า devwootpost เราจะเขียนโค้ดเพื่อเพิ่มฟีเจอรในการโพสบทความ หลังจากที่เรา setup database เสร็จเรียบร้อยในหัวข้อถัดไป

สร้าง Post

เราจะเพิ่มฟีเจอร์ Post เข้ามาในแอพ devwootpost เป้าหมายของเราคือ ต้องการทดลองและทดสอบการทำงานของแอพ Rails ร่วมกับ Cloud SQL for Postgres ที่เป็น service อีกตัวของ Google Cloud Platform ผ่านกระบวนการของ CRUD operation

เพื่อความรวดเร็วเราจะใช้ scaffold ในการสร้าง model, controller, view รวมถึงกำหนด route ให้กับ Post ในคราวเดียว โดยการรันคำสั่ง

$ rails g scaffold Post title:string body:text

หลังจากรันคำสั่งแล้ว จะเห็นว่ามีไฟล์ถูกสร้างขึ้นมาเพียบเลย ซึ่งก็คือไฟล์ที่ scaffold สร้างมาให้สำหรับ model, controller, view และ routes อย่างที่บอกในตอนแรก จากนั้นทำ database migration โดยรัน

$ rails db:migrate

ไปที่ไฟล์ config/routes.rb แล้วแก้ไขไฟล์โดยเพิ่ม root เข้าไป ซึ่งจะเป็นการกำหนดใแอพแสดงหน้า index ของ Page เป็นหน้าแรกดังนี้

Rails.application.routes.draw do
  resources :posts

  root "posts#index"
end

ทดลองรัน rails server แล้วเปิดเบราเซอร์เข้าไปที่ localhost:3000 เราจะเห็นหน้าแรกปรากฏดังรูปด้านล่าง รันแอพ Post บนเครื่อง local เป็นปกติ

ณ จุดนี้ ถ้าเรารัน $ gcloud app deploy ดู จะเห็นว่าสามารถ deploy ได้สำเร็จแต่พอลองเข้าไปที่หน้าเว็บแอพ ในที่นี้คือ https://devwootpost.appspot.com/ เราจะเจอ error We're sorry, but something went wrong. เราสามารถตรวจสอบ log error ได้ โดยคลิ๊กที่ปุ่มเมนู 3 ขีดด้านซ้ายบน แล้วสกอร์ดาวน์ลงมาที่หัวข้อ Logging แล้วเลือก Log Viewer พอลองไล่ดูจะเห็น error โชว์ว่า ActionView::Template::Error (SQLite3::SQLException: no such table: posts: SELECT "posts".* FROM "posts") ทำให้เราเดาได้ว่าฐานข้อมูลที่เราใช่อยู่ตอนนี้เป็น SQLite3 ซึ่งเป็นฐานข้อมูลตั้งต้นที่กำหนดมาตั้งแต่เราสร้างโปรเจค Rails ขึ้นมา แต่ทางฝั่งของ Google Cloud App ของเรานั้นมีแต่ App Engine ที่ถูกสร้างขึ้นมาอย่างเดียวเท่านั้น ยังไม่มีในส่วนของฐานข้อมูลเลย เราจะต้อง setup ขึ้นมา ซึ่งฐานข้อมูลที่ว่าก็คือตัว Cloud SQL for Postgres นั่นเอง

setup Cloud SQL for Postgres

ในการเซตอัพ Cloud SQL for Postgres เราจะต้องเข้าไป enable API สำหรับ Cloud SQL Administration ขึ้นมา โดยให้เข้าไปที่หน้า Google Cloud console แล้ว make sure ว่าโปรเจคที่ active อยู่เป็นโปรเจคที่เราต้องทำงานด้วย ในที่นี้คือโปรเจค devwootpost จากนั้นคลิ๊กปุ่มเมนู (ปุ่ม 3 ขีด) ตรงมุมซ้ายบน scroll down ลงมาที่หัวข้อ Stroage แล้วเลือกเมนู SQL

จากนั้นคลิ๊กปุ่ม Create Instance เพื่อทำการสร้าง Cloud SQL instance สร้าง Cloud instance สำหรับโปรเจคของเรา

คลิ๊กที่ Choose PostgreSQL เพื่อเลือก database เป็น PostgresQL รอแป๊บนึงแล้ว Google compute engine จะทำการติดตั้ง API เพื่อใช้กับฐานข้อมูล PostgreSQL ให้เรา เสร็จแล้วให้ใส่ instance ID ในที่นี้ผมใส่ชื่อเดียวกับโปรเจคคือ hellorails จากนั้นให้กำหนดพาสเวิดของยูสเซอร์ postgres ตรงนี้จำเป็นต้องจำหรือจดเอาไว้นะครับ เพราะเราจะต้อง login เข้ามายังฐานข้อมูล postgresql ด้วยยูสเซอร์นี้ในภายหลัง

ทำการเลือก region เป็น asia-southeast1 ซึงก็คือ Singapore ส่วน zone เป็น Any แล้วกดปุ่ม “Create” อีกทีก็เสร็จเรียบร้อยสำหรับการเซตอัพ Cloud SQL สำหรับ PostgresQL

กำหนดค่าคอนฟิกตั้งต้นให้กับ Cloud SQL instance

Enable Cloud SQL Admin API

ทำการเปิดใช้ API ที่ชื่อ Cloud SQL Admin โดยคลิ๊กที่เมนูสามขีด แล้วเลือก API/Services > Dashboard แล้วกดที่ปุ่ม +ENABLE API AND SERVICES จากนั้นให้พิมพ์ Cloud SQL Admin ลงไปในช่อง search ก็จะปรากฎชื่อ API นี้ขึ้นมา ก็ให้เลือก แล้วกดปุ่ม Enable ก็เรียบร้อย

เปลี่ยนฐานข้อมูลของแอพ Rails จาก SQLite3 ไปเป็น Postgres

ในขั้นตอนนี้เราจะเปลี่ยนฐานข้อมูลสำหรับแอพ Rails ที่ระบุอยู่ใน config/database.yml จาก SQLite3 ไปเป็น Postgres โดยให้ทำตามขั้นตอนต่อไปนี้

เปิดไฟล์ Gemfile ขึ้นมาแล้วเพิ่มบรรทัดสำหรับ gem pg และ appengine

gem 'pg'
gem 'appengine', '~> 0.4.1'

แล้วรันคำสั่ง $ bundle install

จากนั้นพิมพ์คำสั่งด้านล่างเพื่อหา connection name ของ Cloud SQL instance

$ gcloud sql instances describe devwootpost | grep “connectionName”

copy ค่าของ connectionName จากผลลัพธ์ที่ได้แล้วเอามาใส่ในไฟล์ config/database.yml โดยแก้ไขไฟล์ config/database.yml ในส่วนของ production ให้มีหน้าตาประมาณนี้

production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  timeout: 5000
  username: "[postgres user name]"
  password: "[postgres password]"
  database: "devwootpost_production"
  host: "/couldsql/[Cloud SQL instance connectionName]"

จากนั้นเข้าไปตรวจสอบว่าเรามีฐานข้อมูลชื่อ devwootpost_production หรือยัง โดยคลิ๊กเข้าไปที่เมนูสามขีด ที่หัวข้อ Storage เลือก SQL แล้วคลิ๊กที่ชื่อ instance ที่เราเพิ่งสร้างไปคือ devwootpost แล้วเลือกแท็ปชื่อ DATABASES จะเห็นรายการของ database ที่มีอยู่ ถ้ายังไม่มี devwootpost_production ให้สร้างขึ้นมาโดยกดปุ่ม Create Database

เสร็จแล้วให้อัพเดตไฟล์ app.yml เพื่อบอกให้ App Engine รู้ว่าเราจะเชื่อมต่อ Cloud SQL instance ตัวไหน โดยเพิ่มบรรทัดด้านล่างนี้ลงใน app.yml

beta_settings:
  cloud_sql_instances: [Cloud SQL instance connectionName]

จากนั้นทำ asset precompile ด้วยคำสั่ง $ bundle exec bin/rails assets:precompile ลองทำการ deploy ดูโดยใช้คำสั่ง $ gcloud app deploy --no-promote พอเข้าไปตรวจสอบที่หน้าเว็บจะเห็นว่าเว็บเราขึ้นยังขึ้นหน้า error อยู่ นั่นเป็นเพราะว่า เราจะต้องทำ database migration สำหรับฐานข้อมูล devwootpost_production ก่อน นั่นเป็นเหตผลที่เราโหลด gem appengine มาใช้ เพื่อทำสิ่งนี้โดยเฉพาะ ก่อนอื่นเราจะต้องกำหนดสิทธิในการแก้ไขโปรเจคบน Google Cloud ให้กับ appengine ก่อน โดยมีขั้นตอนดังนี้ ใช้คำสั่ง gcloud projects list เพื่อดู PROJECT_ID และ PROJECT_NUMBER ของ devwootpost

$ gcloud projects list
devwootpost-240802      devwootpost         159851706374
hellorails-240407       hellorails          332464571027

ซึ่งในที่นี้ PROJECT_ID คือ devwootpost-240802 ส่วน PROJECT_NAME คือ 159851706374 จากนั้นพิมพ์คำสั่งด้านล่าง เพื่อกำหนดสิทธิให้เราสามารถใช้ appengine ในการรัน database migration ได้ โดยใส่ PROJECT_ID และ PROJECT_NAME ที่ได้มาจากขั้นตอนเมื่อกี้ลงไป

$ gcloud projects add-iam-policy-binding [PROJECT_ID] \
  --member=serviceAccount:[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com \
  --role=roles/editor

จากนั้นทำการ migrate database devwootpost_production โดยการรันคำสั่งด้านล่าง

$ bundle exec rake appengine:exec -- bundle exec rake db:migrate

เสร็จแล้วให้ลองเข้าเว็บโดยใช้ url เป็น [YOUR-VERSION]-dot-[YOUR-PROJECT-ID].appspot.com โดยหมายเลข version ในคำสั่งด้านบนเป็น version ล่าสุด ที่ดูได้จากการใช้คำสั่ง $ gcloud app versions list

เราจะเห็นว่าหน้าเว็บแอพเราแสดงผลได้เหมือนกันตอนที่รันบนเครื่อง local แล้ว ซึ่งคุณสามารถลองสร้าง Post แก้ไข อัพเดตและลบโพสได้ แอพของเราแสดงผลในหน้าแรกได้แล้ว และทำ CRUD ได้ด้วย

ขั้นตอนสุดท้านเราจะทำการโยก traffic ทั้งหมดไปที่ version ล่าสุด โดยใช้คำสั่งต่อไนี้

$ gcloud app services set-traffic default --splits [YOUR-VERSION]=1

ซึ่งจะทำให้เราสามารถเข้าหน้าเว็บแอพที่อัพเดตล่าสุดจาก url https://[PROJECT_ID].appspot.com ได้เลย

วิธีเข้าใช้ rails console บน Google App Engine

เราสามารถใช้ rails console เพื่อ access เข้าไปในแอพได้ โดยคลิ๊กปุ่มเมนูสามขีด เลือก AppEngine แล้วเข้าไปที่หน้า Instances คลิ๊ก “SSH” ที่เมนู dropdown เพื่อเรียกใช้ ssh ผ่านทางหน้าเว็บไปยัง instance ของ App Engine ตำแหน่งในการคลิ๊กเมนู SSH ที่ instance ของ App engine

เมื่อ ssh เข้ามาได้แล้ว ให้ใช้คำสั่ง sudo docker ps เพื่อเรียกดูว่าแอพของเรารันอยู่บน docker container หมายเลขอะไร (ให้สังเกตุดูที่คอลั่ม COMMAND ถ้ามีค่าเป็น “bin/sh -c exec bundle..” แสดงว่ายรรทัดนั้นเป็น container ที่รันแอพของเราอยู่) เสร็จแล้วรันคำสั่งด้านล่างเพื่อเข้าสู่ bash shell ของ container ของแอพเราโดยใส่หมายเลขของ container ที่ได้จากขั้นตอนเมื่อกี้ลงไป

docker exec -it <container_id> /bin/bash

จากนั้นรันคำสั่งด้านล่างเพื่อเข้า rails console ได้เลย

bundle exec rails c production

เข้าใช้ rails console จากหน้า App Engine instance ssh windows

สรุป

จากโพสนี้เราได้เห็นขึ้นตอนการ setup แอพ Rails เข้ากับ Google Cloud SQL for Postgres เราได้ทำการสร้าง SQL instance และได้สร้างฐานข้อมูล devwootpost_production ลงใน instance อีกทอดหนึ่ง ซึ่งการจะใข้ Cloud SQL ร่วมกับแอพ Rails ของเราที่รันอยู่บน App Engine ได้นั้นต้องทำการเปิดใช้ Cloud Admin API และต้องทำการกำหนดค่าคอนฟิกให้ถูกต้อง และที่สำคัญคือจะต้องทำการ migrate database ผ่านทาง gem appengine เพื่อให้Cloud SQL ทำการอัพเดต structure ของฐานข้อมูลได้อย่างถูกต้อง

จะเห็นว่าการเซตอัพทั้งหมดนี้ เราไม่ต้องทำการติดตั้ง Postgres เอง หรือไม่จำเป็นต้องทำการรันคอมมานใน psql เพื่อคอนฟิกฐานข้อมูลเอง Cloud SQL เป็นคนจัดการให้ทั้งหมดซึ่งผมว่าก็สะดวกดีเหมือนกัน แต่ก็ต้องแลกมาด้วยการเรียนรู้สิ่งใหม่อย่างการใช้คำสั่งของ gcloud เพื่อจัดการคอนฟิก Cloud SQL อยู่ดี

Note: ดูเพิ่มเติมได้จาก Official โพสของ Google Cloud เรื่อง Rails 5 using Cloud SQL for PostgreSQL

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