# 理解Serverless
No silver bullet. - The Mythical Man-Month
許多年前,我們開發的軟件還是C/S(客戶端/服務器)和MVC(模型-試圖-控制器)的形式,再后來有了SOA,最近幾年又出現了微服務架構,更新一點的有Cloud Native(云原生)應用,企業應用從單體架構,到服務化,再到更細粒度的微服務化,應用開發之初就是為了應對互聯網的特有的高并發、不間斷的特性,需要很高的性能和可擴展性,人們對軟件開發的追求孜孜不倦,希望力求在軟件開發的復雜度和效率之間達到一個平衡。但可惜的是,NO SILVER BULLET!幾十年前(1975年)Fred Brooks就在The Mythical Man-Month中就寫到了這句話。那么Serverlss會是那顆銀彈嗎?
云改變了我們對操作系統的認知,原來一個系統的計算資源、存儲和網絡是可以分離配置的,而且還可以彈性擴展,但是長久以來,我們在開發應用時始終沒有擺脫的服務器的束縛(或者說認知),應用必須運行在不論是實體還是虛擬的服務器上,必須經過部署、配置、初始化才可以運行,還需要對服務器和應用進行監控和管理,還需要保證數據的安全性,這些云能夠幫我們簡化嗎?**讓我們只要關注自己代碼的邏輯就好了,其它的東西讓云幫我實現就好了。**
## Serverless介紹
Serverless架構是云的自然延伸,為了理解serverless,我們有必要回顧一下云計算的發展。
### IaaS
2006年AWS推出EC2(Elastic Compute Cloud),作為第一代IaaS(Infrastructure as a Service),用戶可以通過AWS快速的申請到計算資源,并在上面部署自己的互聯網服務。IaaS從本質上講是服務器租賃并提供基礎設施外包服務。就比如我們用的水和電一樣,我們不會自己去引入自來水和發電,而是直接從自來水公司和電網公司購入,并根據實際使用付費。
EC2真正對IT的改變是硬件的虛擬化(更細粒度的虛擬化),而EC2給用戶帶來了以下五個好處:
- 降低勞動力成本:減少了企業本身雇傭IT人員的成本
- 降低風險:不用再像自己運維物理機那樣,擔心各種意外風險,EC2有主機損壞,再申請一個就好了。
- 降低基礎設施成本:可以按小時、周、月或者年為周期租用EC2。
- 擴展性:不必過早的預期基礎設施采購,因為通過云廠商可以很快的獲取。
- 節約時間成本:快速的獲取資源開展業務實驗。
以上說了是IaaS或者說基礎設施外包的好處,當然其中也有弊端,我們將在后面討論。
以上是AWS為代表的公有云IaaS,還有使用[OpenStack](https://www.openstack.org/)構建的私有云也能夠提供IaaS能力。
### PaaS
PaaS(Platform as a Service)是構建在IaaS之上的一種平臺服務,提供操作系統安裝、監控和服務發現等功能,用戶只需要部署自己的應用即可,最早的一代是Heroku。Heroko是商業的PaaS,還有一個開源的PaaS——[Cloud Foundry](https://www.cloudfoundry.org/),用戶可以基于它來構建私有PaaS,如果同時使用公有云和私有云,如果能在兩者之間構建一個統一的PaaS,那就是“混合云”了。
在PaaS上最廣泛使用的技術就要數[docker](https://www.docker.com/)了,因為使用容器可以很清晰的描述應用程序,并保證環境一致性。管理云上的容器,可以稱為是CaaS(Container as a Service),如[GCE(Google Container Engine)](https://cloud.google.com/container-engine/)。也可以基于[Kubernetes](https://kubernetes.io)、[Mesos](http://mesos.apache.org/)這類開源軟件構件自己的CaaS,不論是直接在IaaS構建還是基于PaaS。
PaaS是對軟件的一個更高的抽象層次,已經接觸到應用程序的運行環境本身,可以由開發者自定義,而不必接觸更底層的操作系統。
## Serverless的定義
Serverless不如IaaS和PaaS那么好理解,因為它通常包含了兩個領域BaaS(Backend as a Service)和FaaS(Function as a Service)。
### BaaS
BaaS(Backend as a Service)后端即服務,一般是一個個的API調用后端或別人已經實現好的程序邏輯,比如身份驗證服務Auth0,這些BaaS通常會用來管理數據,還有很多公有云上提供的我們常用的開源軟件的商用服務,比如亞馬遜的RDS可以替代我們自己部署的MySQL,還有各種其它數據庫和存儲服務。
### FaaS
FaaS(Functions as a Service)函數即服務,FaaS是無服務器計算的一種形式,當前使用最廣泛的是AWS的Lambada。
現在當大家討論Serverless的時候首先想到的就是FaaS,有點甚囂塵上了。FaaS本質上是一種事件驅動的由消息觸發的服務,FaaS供應商一般會集成各種同步和異步的事件源,通過訂閱這些事件源,可以突發或者定期的觸發函數運行。

傳統的服務器端軟件不同是經應用程序部署到擁有操作系統的虛擬機或者容器中,一般需要長時間駐留在操作系統中運行,而FaaS是直接將程序部署上到平臺上即可,當有事件到來時觸發執行,執行完了就可以卸載掉。

### 總結
兩者都為我們的計算資源提供了彈性的保障,BaaS其實依然是服務外包,而FaaS使我們更加關注應用程序的邏輯,兩者使我們不需要關注應用程序所在的服務器,但實際上服務器依然是客觀存在的。
當我們將應用程序遷移到容器和虛擬機中時,其實對于應用程序本身的體系結構并沒有多少改變,只不過有些流程和規定需要遵守,比如12因素應用守則,但是serverlss對應用程序的體系結構來說就是一次顛覆了,通常我們需要考慮事件驅動模型,更加細化的不熟形式,以及在FaaS組件之外保持狀態的需求。
## Serverless應用
我們以一個游戲應用為例,來說明什么是serverless應用。
一款移動端游戲至少包含如下幾個特性:
- 移動端友好的用戶體驗
- 用戶管理和權限認證
- 關卡、升級等游戲邏輯,游戲排行,玩家的等級、任務等信息
傳統的應用程序架構可能是這樣的:

- 一個app前端,iOS后者安卓
- 用Java寫的后端,使用JBoss或者Tomcat做server運行
- 使用關系型數據庫存儲用戶數據,如MySQL
這樣的架構可以讓前端十分輕便,不需要做什么應用邏輯,只是負責渲染用戶界面,將請求通過HTTP發送給后端,而所有的數據操作都是有由后端的Java程序來完成的。
這樣的架構開發起來比較容易,但是維護起來確十分復雜,前端開發、后端的開發都需要十分專業的人員、環境的配置,還要有人專門維護數據庫、應用的更新和升級。

而在serverless架構中,我們不再需要在服務器端代碼中存儲任何會話狀態,而是直接將它們存儲在NoSQL中,這樣將使應用程序無狀態,有助于彈性擴展。前端可以直接利用BaaS而減少后端的編碼需求,這樣架構的本質上是減少了應用程序開發的人力成本,降低了自己維護基礎設施的風險,而且利用云的能力更便于擴展和快速迭代。
## Serverless的優勢
在最前面我們提到了使用IaaS給我們帶來了五點好處,FaaS當然也包括了這些好處,但是它給我們帶來的最大的好處就是**多快好省**。減少從概念原型到實施的等待時間,比自己維護服務更省錢。
**降低人力成本**
不需要再自己維護服務器,操心服務器的各種性能指標和資源利用率,而是關心應用程序本身的狀態和邏輯。而且serverless應用本身的部署也十分容易,我們只要上傳基本的代碼但愿,例如Javascript或Python的源代碼的zip文件,以及基于JVM的語言的純JAR文件。不需使用Puppet、Chef、Ansible或Docker來進行配置管理,降低了運維成本。同時,對于運維來說,也不再需要監控那些更底層的如磁盤使用量、CPU使用率等底層和長期的指標信息,而是監控應用程序本身的度量,這將更加直觀和有效。
在此看來有人可能會提出“NoOps”的說法,其實這是不存在的,只要有應用存在的一天就會有Ops,只是人員的角色會有所轉變,部署將變得更加自動化,監控將更加面向應用程序本身,更底層的運維依然需要專業的人員去做。
**降低風險**
對于組件越多越復雜的系統,出故障的風險就越大。我們使用BaaS或FaaS將它們外包出去,讓專業人員來處理這些故障,有時候比我們自己來修復更可靠,利用專業人員的知識來降低停機的風險,縮短故障修復的時間,讓我們的系統穩定性更高。
**減少資源開銷**
我們在申請主機資源一般會評估一個峰值最大開銷來申請資源,往往導致過度的配置,這意味著即使在主機閑置的狀態下也要始終支付峰值容量的開銷。對于某些應用來說這是不得已的做法,比如數據庫這種很難擴展的應用,而對于普通應用這就顯得不太合理了,雖然我們都覺得即使浪費了資源也比當峰值到來時應用程序因為資源不足而掛掉好。
解決這個問題最好的辦法就是,不計劃到底需要使用多少資源,而是根據實際需要來請求資源,當然前提必須是整個資源池是充足的(公有云顯然更適合)。根據使用時間來付費,根據每次申請的計算資源來付費,讓計費的粒度更小,將更有利于降低資源的開銷。這是對應用程序本身的優化,例如讓每次請求耗時更短,讓每次消耗的資源更少將能夠顯著節省成本。
**增加縮放的靈活性**
以AWS Lamba為例,當平臺接收到第一個觸發函數的事件時,它將啟動一個容器來運行你的代碼。如果此時收到了新的事件,而第一個容器仍在處理上一個事件,平臺將啟動第二個代碼實例來處理第二個事件。AWS lambad的這種自動的零管理水平縮放,將持續到有足夠的代碼實例來處理所有的工作負載。
但是,AWS仍然只會向您收取代碼的執行時間,無論它需要啟動多少個容器實例要滿足你的負載請求。例如,假設所有事件的總執行時間是相同的,在一個容器中按順序調用Lambda 100次與在100個不同容器中同時調用100次Lambda的成本是 一樣的。當然AWS Lambada也不會無限制的擴展實例個數,如果有人對你發起了DDos攻擊怎么辦,那么不就會產生高昂的成本嗎?AWS是有默認限制的,默認執行Lambada函數最大并發數是1000。
**縮短創新周期**
小團隊的開發人員正可以在幾天之內從頭開始開發應用程序并部署到生產。使用短而簡單的函數和事件來粘合強大的驅動數據存儲和服務的API。完成的應用程序具有高度可用性和可擴展性,利用率高,成本低,部署速度快。
以docker為代表的容器技術僅僅是縮短了應用程序的迭代周期,而serverless技術是直接縮短了創新周期,從概念到最小可行性部署的時間,讓初級開發人員也能在很短的時間內完成以前通常要經驗豐富的工程師才能完成的項目。
## Serverless的劣勢
我們知道沒有十全十美的技術,在說了serverless的那么多優勢之后,我們再來探討以下serverless的劣勢,或者說局限性和適用場景。
**狀態管理**
要想實現自由的縮放,無狀態是必須的,而對于有狀態的服務,使用serverless這就喪失了靈活性,有狀態服務需要與存儲交互就不可避免的增加了延遲和復雜性。
**延遲**
應用程序中不同組件的訪問延遲是一個大問題,我們可以通過使用專有的網絡協議、RPC調用、數據格式來優化,或者是將實例放在同一個機架內或同一個主機實例上來優化以減少延遲。
而serverless應用程序是高度分布式、低耦合的,這就意味著延遲將始終是一個問題,單純使用serverless的應用程序是不太現實的。
**本地測試**
Serverless應用的本地測試困難是一個很棘手的問題。雖然可以在測試環境下使用各種數據庫和消息隊列來模擬生產環境,但是對于無服務應用的集成或者端到端測試尤其困難,很難在本地模擬應用程序的各種連接,并與性能和縮放的特性結合起來測試,并且serverless應用本身也是分布式的,簡單的將無數的FaaS和BaaS組件粘合起來也是有挑戰性的。
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能