Commit ปุ๊ป Deploy ปั๊ปด้วย Cloud Build และ Cloud Run

Nicha Rojsrikul
Nextzy
Published in
5 min readJul 31, 2019

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

ก็เลยเป็นที่มาของบทความนี้ ที่จะมาสอนวิธีการ deploy เซิฟเวอร์ขึ้น Cloud Run โดยอัตโนมัติ ตั้งแต่ push ขึ้น git ไป โดยใช้ Cloud Build กัน

Cloud Build + Cloud Run

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 อย่างดังนี้

  1. สร้าง GCP Project
  2. เช็คว่าเราได้อนุญาตให้ GCP เก็บเงินกับเรา ไม่ต้องกังวลว่าจะเสียเงินถ้าหากลองเล่น เพราะทาง Google มีโควต้าให้ฟรีจำนวนมาก เป็นไปได้ยากที่จะถึงโควต้าหากใช้เพียงแค่คนเดียว
  3. อนุญาตให้โปรเจ็คใช้ Cloud Build และ Cloud Run
  4. ลง Cloud SDK
  5. ลง gcloud beta component ด้วย command gcloud components install beta
  6. อัพเดตด้วย 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" ]

ลองมาดูกันดีกว่าว่าแต่ละส่วนหมายถึงอะไร

  1. FROM node:10 เป็นตัวบอกว่าให้ใช้ image อะไร ในที่นี้คือ nodeเวอร์ชั่น 10 ซึ่งจะต่างจากปกติที่ต้องระบุ OS แล้วลง node ถึงจะใช้ได้
  2. WORKDIR /usr/src/app คือการสร้างและย้ายการทำงานไปไว้ใน folder /usr/src/app โดย path จะไม่ใช่บนคอมพิวเตอร์แต่เป็นภายใน Container Image เพราะฉะนั้นเราไม่จำเป็นต้องเปลี่ยนตรงนี้
  3. COPY package*.json ./ ก็อปไฟล์ package.json และ package-lock.json เข้าไปยังโฟลเดอร์ปัจจุบันภายใน Container Image
  4. RUN yarn --production ตามตัวเลย คือ run คำสั่ง yarn --production ใน โฟลเดอร์ที่อยู่ใน Container Image หากใครใช้ npm ก็ใช้ npm install --only=production ก็ได้
  5. COPY . . ก็อปโค้ดทุกอย่างเข้าไปใน Container Image
  6. CMD [ "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 ได้เลย

หาก Container Image build และ push สำหรับก็จะขึ้นมาที่นี่

2. Deploy ขึ้น Cloud Run

ในขั้นตอนนี้เราสามารถทำได้ 2 วิธีคือ

  1. รันคำสั่ง 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 นี้

หน้า Console ของ Cloud Run

นอกจากนี้ หากอยากจะใส่ 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 จะไม่ใช้อีเมลของเราเอง แต่ใช้อีเมลนี้แทน

IAM console

เมื่อหาอีเมลนี้เจอแล้ว เราก็ต้องติด Permission 2 อย่าง คือ Cloud Run Admin และ Service Account User จากนั้นเราก็กด Save ได้เลย

2. สร้าง Cloud Build Trigger

ให้เราเข้าไปหน้า Cloud Build Trigger ก่อน จากนั้นให้เรากด Create Trigger หากเราไม่เคยใช้มาก่อน เราก็จะโดนถามให้เชื่อมต่อกับ GitHub, BitBucket, หรืออื่นๆ ให้เราเชื่อมต่อกับบริการที่เก็บโค้ดของเราไว้ มันก็จะเด้งให้เรา Authenticate ต่อไป

หน้า Trigger setting หลังจากเลือก repository

เมื่อ 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สเต็ปด้วยกัน

  1. สร้าง Container Image แทนที่เราจะใช้ Cloud SDK ในการสร้าง Container Image เราจะใช้ Docker สร้างแทน ซึ่งจาก name และ args ที่เราใส่ไป จะเสมือนเราสั่งคำสั่ง docker build -t gcr.io/$PROJECT_ID/[SERVICE-NAME] . นั่นเอง
  2. push Container Image หากเราต้องเก็บ Container Image ไว้บน Container Registry เราจะทำในขั้นตอนนี้ เสมือนรันคำสั่ง docker push gcr.io/$PROJECT_ID/[SERVICE-NAME]
  3. deploy ขึ้น Cloud Run ในสเต็ปนี้จะรันคำสั่งเหมือนตอน deploy Cloud Run ด้วยมือเลย คือ gcloud beta run deploy [SERVICE-NAME] --image gcr.io/$PROJECT_ID/[SERVICE-NAME] --region [REGION --platform [PLATFORM] --quiet
  4. 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 ให้เลย

Cloud Build ถูกทริกเกอร์ทุกครั้งที่ push ขึ้นไป

ถ้าอยากเห็นว่าเราเอาไปใช้จริงยังไง สามารถไปดูได้บน GitHub ที่เราใช้ Cloud Build deploy backend จากบทความที่แล้วของเราขึ้น Cloud Run อัตโนมัติได้เลย

ส่งท้าย

จะเห็นได้ว่าการทำระบบ deploy อัตโนมัตินั้นทำไม่ยากเลย แต่มันจะช่วยให้ทีมของเราทำงานได้อย่างมีประสิทธิภาพขึ้น และทำให้เกิดข้อผิดพลาดน้อยลงด้วย

ในบทความหน้า เราจะมาเขียนถึงการ deploy อัตโนมัติด้วย Cloud Build เหมือนกัน แต่จะทำใน Firebase Project แทน

หากบทความนี้ช่วยให้คุณเขียนโค้ดได้ง่ายขึ้น ไม่ต้องไปอ่าน doc ให้ยุ่งยาก ก็ฝากปรบมือให้ด้วยนะคะ

--

--