Commit ปุ๊ป Deploy ปั๊ปด้วย Cloud Build และ Cloud Run
ถ้าใครเคย deploy เซิฟเวอร์ด้วยมือมาก่อน ก็คงจะทราบว่าเป็นกระบวนการที่มีขั้นตอนมากมายและยุ่งยาก แม้ว่าตอนนี้จะมีบริการออกมาทำให้การ deploy สะดวกขึ้นแล้ว แต่ก็ยังมีโอกาสที่จะเกิดความผิดพลาดได้ หากใครที่ไม่รู้ว่าการตั้งเซิฟเวอร์ปกติยุ่งยากยังไง ให้ลองไปอ่าน Instructable อันนี้ ซึ่งสอนวิธีการตั้งเซิฟเวอร์เพื่อใช้ส่วนตัว ยังไม่นับขั้นตอนเพิ่มเติมที่จะต้องทำให้เซิฟเวอร์สามารถรองรับจำนวนคนได้มาก มีความปลอดภัย และสามารถขยายธุรกิจได้ในอนาคต
ก็เลยเป็นที่มาของบทความนี้ ที่จะมาสอนวิธีการ deploy เซิฟเวอร์ขึ้น Cloud Run โดยอัตโนมัติ ตั้งแต่ push ขึ้น git ไป โดยใช้ Cloud Build กัน
Cloud Build & Cloud Run
แต่ก่อนที่จะเริ่ม เราควรเข้าใจก่อนว่า Cloud Build และ Cloud Run คืออะไรก่อน
Cloud Build คือบริการของ Google อันนึงที่จะช่วย build, test และ deploy โค้ดของเราอัตโนมัติ โดยเราสามารถตั้งทริกเกอร์ให้มันทำงานทุกครั้งที่เรา push ขึ้นไปได้
Cloud Run คือ Serverless Platform ใหม่ของ Google ที่จะช่วยให้เราสามารถ deploy เซิฟเวอร์ขึ้นไปได้โดยที่เราไม่ต้องสนว่าจะตั้งค่าเซิฟเวอร์ยังไง หรือจะต้องมาเสียค่าใช้จ่ายเพื่อให้เซิฟเวอร์รันตลอดแม้ว่าจะไม่มีใครเรียกอีกด้วย
แต่ก่อนที่จะทำให้ deploy เซิฟเวอร์ขึ้น Cloud Run อัตโนมัติด้วย Cloud Build ยังไง เรามาลอง deploy ขึ้น Cloud Run ด้วยตัวเองกันก่อนดีกว่า
Deploy ขึ้น Cloud Run ด้วยมือ
วิธีการ deploy ขึ้น Cloud Run นั้นทำได้ไม่ยากหากเรามีโค้ดที่พร้อมจะ deploy ขึ้นอยู่แล้ว ในบทความนี้ เราจะใช้โค้ดจากบทความก่อนของเรา ซึ่งสามารถไปหาได้ที่นี่เลย
ก่อนที่เราจะเริ่ม ยังมีสิ่งที่เราต้องทำก่อน 5 อย่างดังนี้
- สร้าง GCP Project
- เช็คว่าเราได้อนุญาตให้ GCP เก็บเงินกับเรา ไม่ต้องกังวลว่าจะเสียเงินถ้าหากลองเล่น เพราะทาง Google มีโควต้าให้ฟรีจำนวนมาก เป็นไปได้ยากที่จะถึงโควต้าหากใช้เพียงแค่คนเดียว
- อนุญาตให้โปรเจ็คใช้ Cloud Build และ Cloud Run
- ลง Cloud SDK
- ลง
gcloud beta component
ด้วย commandgcloud components install beta
- อัพเดตด้วย command
gcloud components update
เมื่อทำขั้นตอนทั้งหมดเสร็จแล้ว ก็ไปเขียนโค้ดได้เลย!
1. สร้าง Container จากโค้ดและเอาขึ้น Container Registry
ในการ deploy ขึ้น Cloud Run นั้น เราจะไม่เอาโค้ดขึ้นเซิฟเวอร์โดยตรง(เพราะไม่มีเซิฟเวอร์) แต่จะ build โค้ดภายใน Container แล้วค่อยส่ง Container ขึ้น Cloud Run แทน ซึ่งเมื่อมีผู้ใช้ยิงเข้า “เซิฟเวอร์” Cloud Run ก็จะเรียกให้ Container ทำงานแทน
หากอยากรู้ว่า Container คืออะไร สามารถไปอ่านคำอธิบายได้ที่นี่
ในการสร้าง Container จากโค้ดของเรานั้น เราต้องเขียน Dockerfile
ซึ่งจะเป็นเสมือนคู่มือที่จะบอกว่าจะสร้าง Container Image ยังไง ไว้ในโฟลเดอร์ที่มีโค้ดที่เราอยากเขียน หากใน repo ของเรามีแยก frontend และ backend เราอาจเขียนไฟล์นี้ในโฟลเดอร์ backend ก็ได้
ใน Dockerfile
จะมีโค้ดดังนี้
# Use the official Node.js 10 image.
# https://hub.docker.com/_/node
FROM node:10
# Create and change to the app directory.
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure both package.json AND package-lock.json are copied.
# Copying this separately prevents re-running npm install on every code change.
COPY package*.json ./
# Install production dependencies.
RUN yarn --production
# Copy local code to the container image.
COPY . .
# Run the web service on container startup.
CMD [ "yarn", "build" ]
ลองมาดูกันดีกว่าว่าแต่ละส่วนหมายถึงอะไร
FROM node:10
เป็นตัวบอกว่าให้ใช้ image อะไร ในที่นี้คือnode
เวอร์ชั่น 10 ซึ่งจะต่างจากปกติที่ต้องระบุ OS แล้วลงnode
ถึงจะใช้ได้WORKDIR /usr/src/app
คือการสร้างและย้ายการทำงานไปไว้ใน folder/usr/src/app
โดยpath
จะไม่ใช่บนคอมพิวเตอร์แต่เป็นภายใน Container Image เพราะฉะนั้นเราไม่จำเป็นต้องเปลี่ยนตรงนี้COPY package*.json ./
ก็อปไฟล์package.json
และpackage-lock.json
เข้าไปยังโฟลเดอร์ปัจจุบันภายใน Container ImageRUN yarn --production
ตามตัวเลย คือ run คำสั่งyarn --production
ใน โฟลเดอร์ที่อยู่ใน Container Image หากใครใช้npm
ก็ใช้npm install --only=production
ก็ได้COPY . .
ก็อปโค้ดทุกอย่างเข้าไปใน Container ImageCMD [ "yarn", "build" ]
สั่งyarn build
เหมือนในข้อ 4 เราสามารถเปลี่ยนไปใช้npm
ก็ได้
เมื่อเราสร้างไฟล์นี้เสร็จแล้ว เราก็จะสร้าง Container Image กัน โดยเราจะสร้างผ่าน command line ที่เราได้ลง Cloud SDK ไว้แล้ว โดยคำสั่งคือ
gcloud builds submit --tag gcr.io/[PROJECT ID]/[IMAGE NAME]
โดยเราจะต้องแทน [PROJECT ID]
และ [IMAGE NAME]
ด้วย GCP Project ID และชื่อ Image ที่เราอยากใช้ เราสามารถรันคำสั่ง gcloud config get-value project
เพื่อเอา Project ID ได้เลย ส่วน [IMAGE NAME]
เราไม่จำเป็นต้องเผื่อถึงเวอร์ชั่นหรือชื่อ service
เมื่อ Container Image สร้างเสร็จเรียบร้อยแล้ว (จะมี SUCCESS ขึ้นบอก) มันจะถูกเก็บบน Container Registry และเราสามารถเรียกใช้จาก Cloud Run ได้เลย
2. Deploy ขึ้น Cloud Run
ในขั้นตอนนี้เราสามารถทำได้ 2 วิธีคือ
- รันคำสั่ง
gcloud beta run deploy --image gcr.io/[PROJECT ID]/[IMAGE NAME] --platform managed
ใน command line ซึ่งจะมีตัวเลือกให้เราตอบเพิ่มเติม
- อันแรกจะถามชื่อ service ที่อยากให้สร้าง
- อันที่สองจะถาม region ที่อยากให้สร้าง service นี้ แนะนำให้สร้างให้ใกล้กลุ่มลูกค้ามากที่สุด
- อันสุดท้ายจะถามว่าจะอนุญาต unauthenticated invocations มั้ย แนะนำว่าให้ตอบตกลงไปก่อน เพื่อที่เราจะสามารถติดต่อกับเซิฟเวอร์ได้ทันที ตัวนี้เราสามารถไปยกเลิกทีหลังได้
2. ผ่าน GCP Console ให้เราเข้าไปใน Cloud Run แล้วกด Create Service มันจะพาเราไปหน้าให้สร้าง Service ได้ ให้เราเลือก Container Image ที่เราเพิ่งสร้างไปเมื่อสักครู่ และเลือกชื่อและ region ที่เราอยากให้สร้าง Service นี้
นอกจากนี้ หากอยากจะใส่ Environment Variables ให้กด Show Optional Settings ละใส่เข้าไปได้เลย
หากไม่มีปัญหาอะไร เราก็ได้ deploy ขึ้น Cloud Run ด้วยมือเสร็จเป็นที่เรียบร้อยแล้ว ต่อมาเรามาทำให้ขั้นตอนทุกอย่างนี้อัตโนมัตผ่าน Cloud Build กันดีกว่า
Deploy อัตโนมัติด้วย Cloud Build
ทีนี้พอเราเข้าใจแล้วว่า deploy ขึ้น Cloud Run ยังไง เราก็สามารถเขียน script ให้ Cloud Build deploy อัตโนมัติได้แล้ว มาเริ่มกันเลยดีกว่า
1. ติด Permission ให้ Service Account
ให้เราเข้าไปหน้า IAM and Admin page บน GCP console แล้วหาอีเมลที่ลงท้ายด้วย @cloudbuild.gserviceaccount.com
เวลาที่ Cloud Build deploy จะไม่ใช้อีเมลของเราเอง แต่ใช้อีเมลนี้แทน
เมื่อหาอีเมลนี้เจอแล้ว เราก็ต้องติด Permission 2 อย่าง คือ Cloud Run Admin
และ Service Account User
จากนั้นเราก็กด Save
ได้เลย
2. สร้าง Cloud Build Trigger
ให้เราเข้าไปหน้า Cloud Build Trigger ก่อน จากนั้นให้เรากด Create Trigger หากเราไม่เคยใช้มาก่อน เราก็จะโดนถามให้เชื่อมต่อกับ GitHub, BitBucket, หรืออื่นๆ ให้เราเชื่อมต่อกับบริการที่เก็บโค้ดของเราไว้ มันก็จะเด้งให้เรา Authenticate ต่อไป
เมื่อ Authenticate เสร็จเรียบร้อยแล้ว ให้เราเลือก repository ที่มีโค้ดของเราอยู่ จากนั้นก็ใส่รายละเอียดเพิ่มเติม หากเราอยากให้มันสร้างแค่เฉพาะบาง branch เราก็สามารถระบุได้เลยใน Branch (regex) เช่น ถ้าอยากให้ใส่เฉพาะ master
ก็ใส่ master
ลงไปได้เลย
ลงมาตรง Build configuration ให้เราเลือก Cloud Build configuration file (yaml or json) และใส่ที่อยู่ของไฟล์ cloudbuild.yaml
ลงไป เช่นถ้าอยู่ในโฟลเดอร์ backend
ก็ใส่ backend/cloudbuild.yaml
ก็ได้ หรือถ้าไฟล์มีชื่อต่างกันก็ใส่ชื่อไฟล์นั้นๆลงไปได้เลย
เมื่อเราสร้าง Trigger เสร็จแล้ว เราก็จะมาสร้าง cloudbuild.yaml
เพื่อบอกให้ Cloud Build deploy ขึ้น Cloud Run กัน
3. เขียน cloudbuild.yaml
ให้เราสร้างไฟล์ cloudbuild.yaml
ในโฟลเดอร์ที่เราใส่ลงไปใน Cloud Build Trigger ในขั้นตอนที่แล้ว โดยข้างในจะมีโค้ดดังนี้
steps:
# build the container image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/[SERVICE-NAME]', '.']
# push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/[SERVICE-NAME]']
# Deploy container image to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
args: ['beta', 'run', 'deploy', '[SERVICE-NAME]', '--image', 'gcr.io/$PROJECT_ID/[SERVICE-NAME]', '--region', '[REGION]','--platform', '[PLATFORM]', '--quiet']
images:
- gcr.io/$PROJECT_ID/[SERVICE-NAME]
โดยเราจะต้องแทน [SERVICE-NAME]
[REGION]
และ [PLATFORM]
เหมือนที่เราใส่ไปตอน deploy ขึ้น Cloud Run แต่เราไม่ต้องเปลี่ยน $PROJECT_ID
เพราะ Cloud Run จะใส่ให้เลย
ในไฟล์นี้ก็จะมีสเต็ปที่ Cloud Build จะทำตามอยู่ 4สเต็ปด้วยกัน
- สร้าง Container Image แทนที่เราจะใช้ Cloud SDK ในการสร้าง Container Image เราจะใช้ Docker สร้างแทน ซึ่งจาก
name
และargs
ที่เราใส่ไป จะเสมือนเราสั่งคำสั่งdocker build -t gcr.io/$PROJECT_ID/[SERVICE-NAME] .
นั่นเอง - push Container Image หากเราต้องเก็บ Container Image ไว้บน Container Registry เราจะทำในขั้นตอนนี้ เสมือนรันคำสั่ง
docker push gcr.io/$PROJECT_ID/[SERVICE-NAME]
- deploy ขึ้น Cloud Run ในสเต็ปนี้จะรันคำสั่งเหมือนตอน deploy Cloud Run ด้วยมือเลย คือ
gcloud beta run deploy [SERVICE-NAME] --image gcr.io/$PROJECT_ID/[SERVICE-NAME] --region [REGION --platform [PLATFORM] --quiet
- push Container Image สเต็ปนี้ก็จะ push Container Image ขึ้น Container Registry อีกรอบด้วยเหมือนกัน
เอาตรงๆ เราก็ไม่ทราบเหมือนกันว่าทำไม Google ถึงให้เรา push ขึ้นสองรอบ แต่ถ้าเราไม่อยากเก็บ Image ที่สร้างไว้ใน Container Registry เราก็สามารถลบขั้นตอนที่ 2 และ 4 ออกได้เลย
เมื่อเรา save ไฟล์นี้แล้ว ก็เป็นอันเสร็จเรียบร้อย เมื่อเรา push ขึ้นไป Cloud Build ก็จะถูกทริกเกอร์ให้ไปอ่าน cloudbuild.yaml
ที่เราเขียนไว้ และก็จะ build และ deploy ขึ้น Cloud Run ให้เลย
ถ้าอยากเห็นว่าเราเอาไปใช้จริงยังไง สามารถไปดูได้บน GitHub ที่เราใช้ Cloud Build deploy backend จากบทความที่แล้วของเราขึ้น Cloud Run อัตโนมัติได้เลย
ส่งท้าย
จะเห็นได้ว่าการทำระบบ deploy อัตโนมัตินั้นทำไม่ยากเลย แต่มันจะช่วยให้ทีมของเราทำงานได้อย่างมีประสิทธิภาพขึ้น และทำให้เกิดข้อผิดพลาดน้อยลงด้วย
ในบทความหน้า เราจะมาเขียนถึงการ deploy อัตโนมัติด้วย Cloud Build เหมือนกัน แต่จะทำใน Firebase Project แทน
หากบทความนี้ช่วยให้คุณเขียนโค้ดได้ง่ายขึ้น ไม่ต้องไปอ่าน doc ให้ยุ่งยาก ก็ฝากปรบมือให้ด้วยนะคะ