Lời giới thiệu đầu
Thời gian gần đây, Serverless là một trong những từ khoá hot nhất trong cộng đồng phát triển phần mềm. Serverless là gì? Đúng như cái tên của nó, nghĩa là người lập trình khi phát triển ứng dụng của mình sẽ "không còn cần" đến Server nữa. Thật ra thì "không còn cần" ở đây có nghĩa là "không cần quan tâm", thay vì phải tìm hiểu, thiết kế, setup server, quy trình deploy các kiểu thì giờ đây ta chỉ cần push code lên là xong. Các việc khác đã có dịch vụ Serverless của các nhà cung cấp Cloud lo cho hộ rồi. Hiện tại thì 3 đại gia cloud AWS, Azure, Google Cloud đều cung cấp các dịch vụ Serverless của mình. Bài viết hôm nay sẽ tìm hiểu sơ qua về Cloud Function và tạo một ứng dụng đơn giản với dịch vụ này
Google Cloud Function
Hiện tại thì Google đang cung cấp 3 dịch vụ Serverless là Google Cloud Run, App Engine và Cloud Function. Để hình dùng sự khác biệt giữa 3 dịch vụ này, ta có thể tham khảo hình ảnh sau (được cung cấp chính hãng bởi Google)
Như vậy ta có thể thấy trong 3 lựa chọn, Gcloud Function là lựa chọn đơn giản nhất, thích hợp cho việc xử lý event với đơn vị deploy là Function. Nói một cách đơn giản là bạn viết một function, function đó được deploy và GCloud Function sẽ thực thi function đó giúp bạn.
Hiện tại GCloud Function support 3 runtime để xử lý Function là Go, Node.JS và Python. Việc viết code và deploy thì vô cùng đơn giản, ta có thể tham khảo ví dụ Hello World từ chính Google.
Trước hết, ta cần cài đặt Cloud SDK của Google và khởi tạo 1 project để có thể test GCloud Function. Phần này thì là thủ tục phải có rồi nên mình sẽ không ghi chi tiết ở đây.
Tạo 1 folder helloworld
với 1 file helloworld.go
với nội dung như sau:
// Package helloworld provides a set of Cloud Functions samples.
package helloworld
import (
"encoding/json"
"fmt"
"html"
"net/http"
)
// HelloHTTP is an HTTP Cloud Function with a request parameter.
func HelloHTTP(w http.ResponseWriter, r *http.Request) {
var d struct {
Name string `json:"name"`
}
if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
fmt.Fprint(w, "Hello, World!")
return
}
if d.Name == "" {
fmt.Fprint(w, "Hello, World!")
return
}
fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}
Function HelloHTTP có thể được deploy 1 cách vô cùng đơn giản lên GCloud bằng câu lệnh sau:
$ gcloud functions deploy HelloHTTP --runtime go113 --trigger-http --allow-unauthenticated
Ở đây, runtime được chọn cho function là go 1.13 và trigger cho function sẽ là HTTP, tức là function này sẽ xử lý 1 request HTTP gửi đến. Sau khi deploy xong Fucntion sẽ có 1 HTTP endpoint dưới dạng
https://GCP_REGION-PROJECT_ID.cloudfunctions.net/HelloHTTP
Ta có thể test bằng cách gửi request đến endpoint này
curl -X POST https://GCP_REGION-PROJECT_ID.cloudfunctions.net/HelloHTTP -H "Content-Type:application/json" -d '{"name":"Dep Trai"}'
Sử dụng Google Cloud Function để nhận và gửi thông tin
Nếu các bạn đã sử dụng Google Cloud thì chắc chắn sẽ phải sử dụng Cloud Monitoring, dịch vụ theo dõi tình hình sức khoẻ hệ thông của Google. Dịch vụ này có thể giúp chúng ta theo dõi trạng thái hệ thống, đồng thời tạo thông báo khi một điều kiện nào đó được thoả mãn (như là tổng cpu, memory bị chiếm hữu vượt 1 ngưỡng nhất định). Tuy nhiên hiện tại liên kết kênh thông báo chỉ hỗ trợ 1 số kênh nhất định và không support Webhook của Mattermost (chương trình chat free, siêu việt mà công ty mình tự host để dùng). Do vậy mình quyết tâm tạo một webhook dùng Google Cloud, sau đó dùng webhook này post lên Mattermost
Trước tiên, cứ phải tạo một Incomming Webhook trên Mattermost đã.
Sau đó ta bắt đầu tạo Function của mình. Để tăng thêm chút bảo mật thì ta thêm một param token để check quyền truy cập
// alert_webhook.go
func AlertFunc(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("auth_token")
if token != os.Getenv("AUTH_TOKEN") {
log.Printf("Unauthorized Access with Token: %s\n", token)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
...
}
Parse request to Cloud Monitoring
Request từ Cloud Monitoring gửi đến webhook sẽ có định dạng như sau
{
"incident": {
"incident_id": "f2e08c333dc64cb09f75eaab355393bz",
"resource_id": "i-4a266a2d",
"resource_name": "webserver-85",
"state": "open",
"started_at": 1385085727,
"ended_at": null,
"policy_name": "Webserver Health",
"condition_name": "CPU usage",
"url": "https://console.cloud.google.com/monitoring/alerting/incidents?project=PROJECT_ID",
"summary": "CPU for webserver-85 is above the threshold of 1% with a value of 28.5%"
},
"version": "1.1"
}
Do vậy ta cũng phải định nghĩa struct tương tự để parse data
// alert_webhook.go
type StackDriveIncident struct {
IncidentID string `json:"incident_id"`
ResourceID string `json:"resource_id"`
ResourceName string `json:"resource_name"`
State string `json:"state"`
StartedAt Time `json:"started_at"`
EndedAt Time `json:"ended_at"`
PolicyName string `json:"policy_name"`
ConditionName string `json:"condition_name"`
URL string `json:"url"`
Summary string `json:"summary"`
}
type StackDriveAlert struct {
Incident StackDriveIncident `json:"incident"`
Version string `json:"version"`
}
Quay lại hàm AlertFunc
, việc parse request gửi đến từ Cloud Monitoring cũng rất đơn giản.
// alert_webhook.go
...
func AlertFunc(w http.ResponseWriter, r *http.Request) {
...
var alert StackDriveAlert
if err := json.NewDecoder(r.Body).Decode(&alert); err != nil {
log.Printf("%+v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
...
Gửi data đến Mattermost
Gửi data đến Webhook của Mattermost cũng vô cùng đơn giản ta chỉ cần gửi một request với data json có định dạng như dưới đây là Mattermost sẻ post message theo account và config đã thiết lập cho webhook này
{ "text": "Test test test" }
Đoạn code để gửi data trong AlertFunc
sẽ như sau:
// alert_webhook.go
...
type WebhookRequest struct {
Text string `json:"text"`
}
func AlertFunc(w http.ResponseWriter, r *http.Request) {
...
webhookReq := WebhookRequest{
Text: fmt.Sprintf("##### %s\n%s\n%s\n_%s_", alert.Incident.PolicyName,
alert.Incident.Summary, alert.Incident.URL,
alert.Incident.StartedAt.Time().Format(time.RFC1123Z)),
}
reqBody, err := json.Marshal(webhookReq)
if err != nil {
log.Printf("%+v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_, err = http.Post(os.Getenv("WEBHOOK_URL"), "application/json", bytes.NewBuffer(reqBody))
if err != nil {
log.Printf("%+v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(([]byte("OK")))
}
Sau đó chỉ cần tạo 1 file .env.yaml
để chứa các biến môi trường cần thiết như sau:
AUTH_TOKEN: xxxxxxx
WEBHOOK_URL: https://webhook.url
BASE_URL: https://base.ur
Là ta đã có thể sẵn sàng deploy function của mình lên Google Cloud rồi (yêu cầu đã download SDK, tạo project):
$ gcloud functions deploy AlertFunc --region asia-northeast1 --env-vars-file .env.yaml --runtime go113 --trigger-http --allow-unauthenticated
Vì option trigger là HTTP nên function sẽ được deploy ở địa chỉ như sau
https://{REGION}-{PROJECT_ID}.cloudfunctions.net/AlertFunc
Setup ở Cloud Monitoring và thành quả
Từ màn hình quản lý của Google Cloud truy cập vào phần Monitoring -> Alert -> Edit Notification Channel
(https://console.cloud.google.com/monitoring/alerting/notifications), ta có thể tạo một Webhook Notification Channel sử dụng function đã deploy của mình.
Khi có alert thì Mattermost sẽ push alert dưới dạng text message vào channel đã được thiết lập, ví dụ như sau:
Tổng kết
Rất đơn giản và dễ dàng, ta đã có thể sử dụng Google Cloud Function để tạo một webhook nhận và gửi data. Với các bài toán xử lý event đơn giản và stateless thì Google Cloud Function hoàn toàn là giải pháp hợp lý và có nhiều ưu điểm hơn so với các lựa chọn deploy truyền thống. Với các bài toán phức tạp hơn thì Google Cloud Run và App Engine có lẽ sẽ là các lựa chọn Serverless phù hợp hơn.