GCP-託管運算資源服務簡介
雲端運算的一般性名詞
傳統的地端機房就是把Application放在實體機或VM中運行。但是在雲端,運作Application的方式確有以下這些:
- IaaS (Infrastructure as a Service)
- PaaS (Platform as a Service)
- FaaS (Function as a Service)
- CaaS (Container as a Service)
- Serverless
所謂的IaaS就是我們需要自行管理含OS內的所有安裝設定(如下圖的藍色部分)。
而PaaS則再更進一階,只需管理到Application(如下圖)。
通常CSP(Cloud Service Provider)會負責Auto scaling, Availability & Load balancing等等的底層基礎設施。而我們通常只需負責代碼與配置(Configuration)。其他像是CAAS (Container as a Service), FAAS (Function as a Service)還有Database也是PaaS的服務類型。
GCP App Engine
支援多種語言(Go, Java, .NET, Node.js, PHP, Python, Ruby),runtime有pre-configured與Custom兩種。只有resource provision時才需要付費。其他重要的功能有:
- Automatic load balancing & Auto scaling
- Managed platform updates & Application health monitoring
- Application versioning
- Traffic splitting
App Engine的環境
App Engine environment分為以下兩種
Standard:
應用程式在特定於語言的沙箱(sandbox)中運作,並且與OS/磁碟/其他Application完全隔離。其中又分V1/V2兩種版本
- V1
適用語言 — Java、Python、PHP、Go
僅適用於 Python 和 PHP runtime:
網路存取受限/僅允許白名單extensions和libraries。對Java 和 Go runtime沒有限制。 - V2(支援範圍廣泛)
支援Java、Python、PHP、Node.js、Ruby、Go。Full Network Access且沒有Language Extensions的限制
Flexible:(Application instance是運作在Docker Container裡的)
其底層是GCE組成的。支援任何的runtime(內建對 Python、Java、Node.js、Go、Ruby、PHP 或 .NET 的支援)。提供對background processes與local disks的存取。
Application Component Hierarchy
App Engine的層級可以分為三個層次(如上圖所示)。
最上層是Application — 一個Project只能有一個App。再來是Service,這可以是多個微服務或App Component。一個Application中可以有多個Servcie,而每個Service可以有不同的設定。最後是Version,每個version都可以是不一樣的code and configuration。每個Version可以有一個以上的Instance來運作。所以意味著同一個Application可以存在多個版本,這樣可以讓我們可以rollback或是split traffic。
以下是App engine standard與Flexible的功能比較。
我們可以看到Flexible的控制程度比Standard還要深。所以其資源使用方式與計價方式都有所不同。Flexible的計價方式更接近GCE。其中比較值得注意的是Scaling的方式。Standard有三種,Flexilbe只有兩種,以下是三種模式的說明。
Automatic — 根據負載自動縮放資源
這是適合持續運作的負載。其自動縮放基於以下三種模式:
- Target CPU Utilization —CPU使用率的閥值
- Target Throughput Utilization — throughput的使用閥值
- Max Concurrent Requests — 每一個instance最大同時間的concurrent requests
另外也可以設定最大/最小的instance。
Basic — 當request時,instance才會被create
這適用於臨時性的工作負載。好處是沒有request時,不會有instance,也意味著不會有費用。但是也因為這樣,處理request的起始時間就很慢(因為處理冷開機的問題)。可以設定最大的Instances 與Idle Timeout。
Manual — 手動設定固定數量的instance來運行Application
GKE(Google Kubernetes Engine)
K8S應該是目前最流行的container orchestration(包含Cluster Management)。同時也提供了以下的功能:
- Auto Scaling
- Service Discovery
- Load Balancer
- Self Healing
- Zero downtime deployments
而GKE就是GCP的K8S託管服務。GKE最小化了維運的需求,並且能進行auto-repair (替換有問題的 nodes)與auto-upgrade (可以根據我們的意願升級K8S的版本)。另外Cluster的node能自動擴展,並且直接於Cloud Logging 與Cloud Monitoring的GCP監控服務整合。在其node的OS中預設使用"Container-Optimized OS",這是GCP進行安全強化之後的OS,node的Disk支援Persistent disks與Local SSD。
以下是我們在操作GKE時經常會用到的command範例,有gcloud與kubectl
gcloud config set project <peojct ID>
#這通常可以在GKE cluster的三個點點的選項取得(connect)
gcloud container clusters get-credentials my-cluster --zone us-central1-c --project my-kubernetes-project-304910
#
kubectl create deployment <service name> --image=<image path/image name>
kubectl get deployment
kubectl expose deployment <service name> --type=LoadBalancer --port=<port number>
kubectl get services
kubectl get services --watch
kubectl scale deployment <service name> --replicas=<number>
gcloud container clusters resize <cluster name> --node-pool default-pool --num-nodes=2 --zone=us-central1-c
kubectl autoscale deployment hello-world-rest-api --max=4 --cpu-percent=70
kubectl get hpa
kubectl create configmap hello-world-config --from-literal=RDS_DB_NAME=todos
kubectl get configmap
kubectl describe configmap hello-world-config
kubectl create secret generic hello-world-secrets-1 --from-literal=RDS_PASSWORD=dummytodos
kubectl get secret
kubectl describe secret hello-world-secrets-1
kubectl apply -f deployment.yaml
gcloud container node-pools list --zone=us-central1-c --cluster=my-cluster
kubectl get pods -o wide
kubectl set image deployment hello-world-rest-api hello-world-rest-api=in28min/hello-world-rest-api:0.0.2.RELEASE
kubectl get services
kubectl get replicasets
kubectl get pods
kubectl delete pod hello-world-rest-api-58dc9d7fcc-8pv7r
kubectl scale deployment hello-world-rest-api --replicas=1
kubectl get replicasets
gcloud projects list
kubectl delete service hello-world-rest-api
kubectl delete deployment hello-world-rest-api
gcloud container clusters delete my-cluster --zone us-central1-c
GKE的組件
Cluster : 一群GCE的集合,分為:
- Master Node(s) —管理整個cluster
- Worker Node(s) — 運行我們的Application(也就是pods)
Master Node (Control plane) 組件中又細分:
- API Server —處理所有 K8S cluster的通訊 (從nodes內部到cluster外部)
- Scheduler — 資源配置,決定Pod要放在哪裡
- Control Manager — 管理deployments & replicasets
- etcd — 分散式資料庫,儲存cluster state
Worker Node的組件有:
- Pod(s)
- Kubelet — 與master node之間的通訊
GKE Cluster Types
分為以下四種
- Zonal Cluster
又分為Single zone與multi-zonal。都是Single Control plane但node運行在一個(Single)或多個(multi) zone中。 - Regional cluster
control plane的replica在一個region中的多個zone中運作。 node也在control plane運作的同一zone中運作 - Private Cluster
VPC-native cluster。node只有internal IP。 - Alpha cluster
具有 alpha API 的cluster — 早期功能 API。 用於測試新的 K8S 功能
K8S Pods
Pod就是一個在K8S中最小的可佈署單元。每一個Pod可以有含一個以上的container,並且Pod的IP都是短暫的(ephemeral)。在同一個Pod中的container共享以下幾種資源:
- Network
- Storage
- IP Address
- Ports
- Volumes (Shared persistent disks)
Pod的狀態有:Running /Pending /Succeeded /Failed /Unknown/Waiting。其中的Unknown是指control plane無法探知目前的狀態。如果是Pending狀態,有可能是資源不夠scheduler決定要放哪一個node。如果是waiting的話,通常是pull image失敗。
Deployment vs Replica Set
每一個Deployment都代表一個微服務。並且在我們更換新版本的Application時,Deployment的機制確保在更版時不會有downtime。
而Replica set是指有一個我們所規定數量的Pod要能運行。不論發生何事,Replica set都確保規定數量的PoD都能運作。Replica set也可以有新版本的設定,例如:
kubectl set image deployment j1 j1=j1:v2
這時就會產生同一個deployment name,卻有不同版號的Replica Set。
Service
我們上面提到,每一個Pod都有自己的IP。但是同一群Pod卻跑相同的Application,哪我們要怎麼把它們集合起來呢?這時我們就會create一個對外的service(就是一個Load Balancer):
kubectl expose deployment <name> --type=LoadBalancer --port=<port number>
這個做法跟我們在GCE(MIG)前面加上一個LB是一樣的。不論後面的instance發生何事,最前端都會有一個固定的IP讓user可以存取。而service的種類有以下三種:
- ClusterIP:這只存在於Cluster的內部使用,所以該service只有內部IP。
- LoadBalancer: 這是讓Applicaion可以對cluster外部服務。通常是一個微服務對應一個LB。
- NodePort:這是把Node(該台VM)的port直接拿來用。通常我們不想微服務建立外部的LB。直接用某台node的port,但有其問題所在。通常應該使用Ingress的方式。我們可以用一個Ingress來對應多個微服務。
以下為一個Ingress範例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gateway-ingress
spec:
rules:
- http:
paths:
- path: /jason-exchange/*
backend:
serviceName: jason-exchange
servicePort: 8000
- path: /jason-conversion/*
backend:
serviceName: jason-conversion
servicePort: 8100
如果我們的Cluster有很多個微服務,哪麼應該要用一個Ingress來取代一堆LB。Ingress提供以下功能
- load balancing與SSL termination
- 可以用規則來控制流量
Container Registry — Image Repository
這是一個GCP提供類似Docker Hub的功能的託管服務。可以整合在GCP的CI/CD tools中。我們可以在這個服務中進行一些弱點分析與執行一些deployment policies。Image的命名方式如下:
HostName/ProjectID/Image:Tag — gcr.io/projectname/helloworld:1
GKE的使用場景
- 希望保持較低的成本並優化 GKE
方法:考慮Spot VM、較便宜的region、使用CUD。用E2 機器類型(最便宜的)。另外選擇適合工作負載類型的正確環境(如果需要,請使用多個node pool) - 要高效、完全的Auto scaling
方法:可以使用GKE解決方案,如配置 Horizontal Pod Autoscaler 以進行部署和針對node pool的Cluster Autoscaler - 想要在 K8s Cluster中執行不受信任的第三方代碼
方法:使用 GKE Sandbox 建立新的node pool。 將不受信任的代碼部署到Sandbox node pool - 只想在K8S cluster中的微服務部署之間啟用內部通訊
方法:建立 Cluster IP 類型的服務
Cloud Function(GCP的serverless/even based)
當我們想要單純的運行某些小量代碼(簡單的事情)而不想管理其底層的所有基礎設施。就可以選用Cloud Function。例如將檔案upload到Cloud Storage或把error log寫到Cloud logging或接受 Http request等這一類event based的行為。
因為不用管理其底層基礎架構,所以其擴充性(scaling)與可用性(availability)就由GCP來承擔並且進行管理。我們只要專注於我們的代碼就可以了。
而其計費的方式需要考慮以下三種維度:
- 被呼叫的次數
- 每次被呼叫後的運算時間(最短一分鐘,最長60分鐘)
- 啟動的CPU/Memory
Cloud Function有兩個世代,第二代的支援較多。它是建立在Cloud Run與Eventarc之上的。
由於Cloud Function是event based的服務。所以在其整個概念中會分為:
- Trigger — 當事件發生時,哪一個function會被觸發?
- Functions — 取得事件的資料並進行某些動作
事件的觸發來源可以來自:
- Cloud Storage
- Cloud Pub/Sub
- HTTP POST/GET/DELETE/PUT/OPTIONS
- Firebase Cloud Firestore
- Cloud logging
Http類型的Node.js代碼範例
const escapeHtml = require('escape-html');
exports.helloHttp = (req, res) => {
res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};
Pub/Sub的Node.Js代碼範例
/**
* Background Cloud Function to be triggered by Pub/Sub.
* This function is exported by index.js, and executed when
* the trigger topic receives a message.
*
* @param {object} message The Pub/Sub message.
* @param {object} context The event metadata.
*/
exports.helloPubSub = (message, context) => {
const name = message.data
? Buffer.from(message.data, 'base64').toString()
: 'World';
console.log(`Hello, ${name}!`);
};
Cloud Run for Anthos
Cloud Run也是一個Container的服務,只是它不用像GKE哪樣需要設計與管理整個K8S Cluster。它是建立在Knative之上的,也就是K8s+Serverless。一種針對containerized applications的全託管式serverless平台。號稱:
- ZERO infrastructure management
- Pay-per-use (For used CPU, Memory, Requests and Networking)
可以高度的與整個軟體開發流程進行整合,如Cloud Code, Cloud Build, Cloud Monitoring & Cloud Logging Integrations。而Anthos就是將我們的container運行在任何地方(雲端/地端/多雲),不過Control plane還是在GCP身上。
Cloud Run for anthos就是將工作負載部署到 Anthos cluster上,不論是在地端或GCP上。並利用我們現有的資產來運作Container的serverless platform。
第二代Cloud Function
第二代的加強功能如下:
- 針對HTTP-triggered functions,timeout時間可以設定到60分鐘
- 更大的instance size — 16GiB RAM/4 vCPU (一代只能到 8GB RAM/2 vCPU)
- 每個Function instance可以有1000 Concurrency requests
- 同一個Function有多個版本,所以可以做到Traffic splitting
- 可以90以上的 event types
Scaling and Concurrency
一般serverless function的架構是:
- Autoscaling — 當負載/流量變大時,系統會自行增加/縮減 instance
- 一個instance function,同一時間只能接收一個request(第二代沒有這種限制)
而serverless通常會有cold start(冷開機)的問題。也就是當有request進來時,instance才會被create。通常會設定最小數量的instance,但是會有成本增加的問題。
最佳實踐
- 不設定最大的instance上限,以防大流量的需求
- 使用Cloud Endpoints(或Apigee)來進行版本導流
- 使用 Cloud Run(和 Cloud Functions gen 2)
配置哪些不同的版本應接收流量以及接收流量的數量
如果需要,我們可以rollback到先前的版本 - 使用Secret Manager來儲存我們的Secrets,例如API keys.
- 每一個Cloud function使用不同的Service Accounts(給予roles/cloudfunctions.invoker)。